FFT 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. // This file is part of Eigen, a lightweight C++ template library
  2. // for linear algebra.
  3. //
  4. // Copyright (C) 2009 Mark Borgerding mark a borgerding net
  5. //
  6. // This Source Code Form is subject to the terms of the Mozilla
  7. // Public License v. 2.0. If a copy of the MPL was not distributed
  8. // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. #ifndef EIGEN_FFT_H
  10. #define EIGEN_FFT_H
  11. #include <complex>
  12. #include <vector>
  13. #include <map>
  14. #include <Eigen/Core>
  15. /**
  16. * \defgroup FFT_Module Fast Fourier Transform module
  17. *
  18. * \code
  19. * #include <unsupported/Eigen/FFT>
  20. * \endcode
  21. *
  22. * This module provides Fast Fourier transformation, with a configurable backend
  23. * implementation.
  24. *
  25. * The default implementation is based on kissfft. It is a small, free, and
  26. * reasonably efficient default.
  27. *
  28. * There are currently two implementation backend:
  29. *
  30. * - fftw (http://www.fftw.org) : faster, GPL -- incompatible with Eigen in LGPL form, bigger code size.
  31. * - MKL (http://en.wikipedia.org/wiki/Math_Kernel_Library) : fastest, commercial -- may be incompatible with Eigen in GPL form.
  32. *
  33. * \section FFTDesign Design
  34. *
  35. * The following design decisions were made concerning scaling and
  36. * half-spectrum for real FFT.
  37. *
  38. * The intent is to facilitate generic programming and ease migrating code
  39. * from Matlab/octave.
  40. * We think the default behavior of Eigen/FFT should favor correctness and
  41. * generality over speed. Of course, the caller should be able to "opt-out" from this
  42. * behavior and get the speed increase if they want it.
  43. *
  44. * 1) %Scaling:
  45. * Other libraries (FFTW,IMKL,KISSFFT) do not perform scaling, so there
  46. * is a constant gain incurred after the forward&inverse transforms , so
  47. * IFFT(FFT(x)) = Kx; this is done to avoid a vector-by-value multiply.
  48. * The downside is that algorithms that worked correctly in Matlab/octave
  49. * don't behave the same way once implemented in C++.
  50. *
  51. * How Eigen/FFT differs: invertible scaling is performed so IFFT( FFT(x) ) = x.
  52. *
  53. * 2) Real FFT half-spectrum
  54. * Other libraries use only half the frequency spectrum (plus one extra
  55. * sample for the Nyquist bin) for a real FFT, the other half is the
  56. * conjugate-symmetric of the first half. This saves them a copy and some
  57. * memory. The downside is the caller needs to have special logic for the
  58. * number of bins in complex vs real.
  59. *
  60. * How Eigen/FFT differs: The full spectrum is returned from the forward
  61. * transform. This facilitates generic template programming by obviating
  62. * separate specializations for real vs complex. On the inverse
  63. * transform, only half the spectrum is actually used if the output type is real.
  64. */
  65. #ifdef EIGEN_FFTW_DEFAULT
  66. // FFTW: faster, GPL -- incompatible with Eigen in LGPL form, bigger code size
  67. # include <fftw3.h>
  68. # include "src/FFT/ei_fftw_impl.h"
  69. namespace Eigen {
  70. //template <typename T> typedef struct internal::fftw_impl default_fft_impl; this does not work
  71. template <typename T> struct default_fft_impl : public internal::fftw_impl<T> {};
  72. }
  73. #elif defined EIGEN_MKL_DEFAULT
  74. // TODO
  75. // intel Math Kernel Library: fastest, commercial -- may be incompatible with Eigen in GPL form
  76. # include "src/FFT/ei_imklfft_impl.h"
  77. namespace Eigen {
  78. template <typename T> struct default_fft_impl : public internal::imklfft_impl {};
  79. }
  80. #else
  81. // internal::kissfft_impl: small, free, reasonably efficient default, derived from kissfft
  82. //
  83. # include "src/FFT/ei_kissfft_impl.h"
  84. namespace Eigen {
  85. template <typename T>
  86. struct default_fft_impl : public internal::kissfft_impl<T> {};
  87. }
  88. #endif
  89. namespace Eigen {
  90. //
  91. template<typename T_SrcMat,typename T_FftIfc> struct fft_fwd_proxy;
  92. template<typename T_SrcMat,typename T_FftIfc> struct fft_inv_proxy;
  93. namespace internal {
  94. template<typename T_SrcMat,typename T_FftIfc>
  95. struct traits< fft_fwd_proxy<T_SrcMat,T_FftIfc> >
  96. {
  97. typedef typename T_SrcMat::PlainObject ReturnType;
  98. };
  99. template<typename T_SrcMat,typename T_FftIfc>
  100. struct traits< fft_inv_proxy<T_SrcMat,T_FftIfc> >
  101. {
  102. typedef typename T_SrcMat::PlainObject ReturnType;
  103. };
  104. }
  105. template<typename T_SrcMat,typename T_FftIfc>
  106. struct fft_fwd_proxy
  107. : public ReturnByValue<fft_fwd_proxy<T_SrcMat,T_FftIfc> >
  108. {
  109. typedef DenseIndex Index;
  110. fft_fwd_proxy(const T_SrcMat& src,T_FftIfc & fft, Index nfft) : m_src(src),m_ifc(fft), m_nfft(nfft) {}
  111. template<typename T_DestMat> void evalTo(T_DestMat& dst) const;
  112. Index rows() const { return m_src.rows(); }
  113. Index cols() const { return m_src.cols(); }
  114. protected:
  115. const T_SrcMat & m_src;
  116. T_FftIfc & m_ifc;
  117. Index m_nfft;
  118. private:
  119. fft_fwd_proxy& operator=(const fft_fwd_proxy&);
  120. };
  121. template<typename T_SrcMat,typename T_FftIfc>
  122. struct fft_inv_proxy
  123. : public ReturnByValue<fft_inv_proxy<T_SrcMat,T_FftIfc> >
  124. {
  125. typedef DenseIndex Index;
  126. fft_inv_proxy(const T_SrcMat& src,T_FftIfc & fft, Index nfft) : m_src(src),m_ifc(fft), m_nfft(nfft) {}
  127. template<typename T_DestMat> void evalTo(T_DestMat& dst) const;
  128. Index rows() const { return m_src.rows(); }
  129. Index cols() const { return m_src.cols(); }
  130. protected:
  131. const T_SrcMat & m_src;
  132. T_FftIfc & m_ifc;
  133. Index m_nfft;
  134. private:
  135. fft_inv_proxy& operator=(const fft_inv_proxy&);
  136. };
  137. template <typename T_Scalar,
  138. typename T_Impl=default_fft_impl<T_Scalar> >
  139. class FFT
  140. {
  141. public:
  142. typedef T_Impl impl_type;
  143. typedef DenseIndex Index;
  144. typedef typename impl_type::Scalar Scalar;
  145. typedef typename impl_type::Complex Complex;
  146. enum Flag {
  147. Default=0, // goof proof
  148. Unscaled=1,
  149. HalfSpectrum=2,
  150. // SomeOtherSpeedOptimization=4
  151. Speedy=32767
  152. };
  153. FFT( const impl_type & impl=impl_type() , Flag flags=Default ) :m_impl(impl),m_flag(flags) { }
  154. inline
  155. bool HasFlag(Flag f) const { return (m_flag & (int)f) == f;}
  156. inline
  157. void SetFlag(Flag f) { m_flag |= (int)f;}
  158. inline
  159. void ClearFlag(Flag f) { m_flag &= (~(int)f);}
  160. inline
  161. void fwd( Complex * dst, const Scalar * src, Index nfft)
  162. {
  163. m_impl.fwd(dst,src,static_cast<int>(nfft));
  164. if ( HasFlag(HalfSpectrum) == false)
  165. ReflectSpectrum(dst,nfft);
  166. }
  167. inline
  168. void fwd( Complex * dst, const Complex * src, Index nfft)
  169. {
  170. m_impl.fwd(dst,src,static_cast<int>(nfft));
  171. }
  172. /*
  173. inline
  174. void fwd2(Complex * dst, const Complex * src, int n0,int n1)
  175. {
  176. m_impl.fwd2(dst,src,n0,n1);
  177. }
  178. */
  179. template <typename _Input>
  180. inline
  181. void fwd( std::vector<Complex> & dst, const std::vector<_Input> & src)
  182. {
  183. if ( NumTraits<_Input>::IsComplex == 0 && HasFlag(HalfSpectrum) )
  184. dst.resize( (src.size()>>1)+1); // half the bins + Nyquist bin
  185. else
  186. dst.resize(src.size());
  187. fwd(&dst[0],&src[0],src.size());
  188. }
  189. template<typename InputDerived, typename ComplexDerived>
  190. inline
  191. void fwd( MatrixBase<ComplexDerived> & dst, const MatrixBase<InputDerived> & src, Index nfft=-1)
  192. {
  193. typedef typename ComplexDerived::Scalar dst_type;
  194. typedef typename InputDerived::Scalar src_type;
  195. EIGEN_STATIC_ASSERT_VECTOR_ONLY(InputDerived)
  196. EIGEN_STATIC_ASSERT_VECTOR_ONLY(ComplexDerived)
  197. EIGEN_STATIC_ASSERT_SAME_VECTOR_SIZE(ComplexDerived,InputDerived) // size at compile-time
  198. EIGEN_STATIC_ASSERT((internal::is_same<dst_type, Complex>::value),
  199. YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
  200. EIGEN_STATIC_ASSERT(int(InputDerived::Flags)&int(ComplexDerived::Flags)&DirectAccessBit,
  201. THIS_METHOD_IS_ONLY_FOR_EXPRESSIONS_WITH_DIRECT_MEMORY_ACCESS_SUCH_AS_MAP_OR_PLAIN_MATRICES)
  202. if (nfft<1)
  203. nfft = src.size();
  204. if ( NumTraits< src_type >::IsComplex == 0 && HasFlag(HalfSpectrum) )
  205. dst.derived().resize( (nfft>>1)+1);
  206. else
  207. dst.derived().resize(nfft);
  208. if ( src.innerStride() != 1 || src.size() < nfft ) {
  209. Matrix<src_type,1,Dynamic> tmp;
  210. if (src.size()<nfft) {
  211. tmp.setZero(nfft);
  212. tmp.block(0,0,src.size(),1 ) = src;
  213. }else{
  214. tmp = src;
  215. }
  216. fwd( &dst[0],&tmp[0],nfft );
  217. }else{
  218. fwd( &dst[0],&src[0],nfft );
  219. }
  220. }
  221. template<typename InputDerived>
  222. inline
  223. fft_fwd_proxy< MatrixBase<InputDerived>, FFT<T_Scalar,T_Impl> >
  224. fwd( const MatrixBase<InputDerived> & src, Index nfft=-1)
  225. {
  226. return fft_fwd_proxy< MatrixBase<InputDerived> ,FFT<T_Scalar,T_Impl> >( src, *this,nfft );
  227. }
  228. template<typename InputDerived>
  229. inline
  230. fft_inv_proxy< MatrixBase<InputDerived>, FFT<T_Scalar,T_Impl> >
  231. inv( const MatrixBase<InputDerived> & src, Index nfft=-1)
  232. {
  233. return fft_inv_proxy< MatrixBase<InputDerived> ,FFT<T_Scalar,T_Impl> >( src, *this,nfft );
  234. }
  235. inline
  236. void inv( Complex * dst, const Complex * src, Index nfft)
  237. {
  238. m_impl.inv( dst,src,static_cast<int>(nfft) );
  239. if ( HasFlag( Unscaled ) == false)
  240. scale(dst,Scalar(1./nfft),nfft); // scale the time series
  241. }
  242. inline
  243. void inv( Scalar * dst, const Complex * src, Index nfft)
  244. {
  245. m_impl.inv( dst,src,static_cast<int>(nfft) );
  246. if ( HasFlag( Unscaled ) == false)
  247. scale(dst,Scalar(1./nfft),nfft); // scale the time series
  248. }
  249. template<typename OutputDerived, typename ComplexDerived>
  250. inline
  251. void inv( MatrixBase<OutputDerived> & dst, const MatrixBase<ComplexDerived> & src, Index nfft=-1)
  252. {
  253. typedef typename ComplexDerived::Scalar src_type;
  254. typedef typename ComplexDerived::RealScalar real_type;
  255. typedef typename OutputDerived::Scalar dst_type;
  256. const bool realfft= (NumTraits<dst_type>::IsComplex == 0);
  257. EIGEN_STATIC_ASSERT_VECTOR_ONLY(OutputDerived)
  258. EIGEN_STATIC_ASSERT_VECTOR_ONLY(ComplexDerived)
  259. EIGEN_STATIC_ASSERT_SAME_VECTOR_SIZE(ComplexDerived,OutputDerived) // size at compile-time
  260. EIGEN_STATIC_ASSERT((internal::is_same<src_type, Complex>::value),
  261. YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
  262. EIGEN_STATIC_ASSERT(int(OutputDerived::Flags)&int(ComplexDerived::Flags)&DirectAccessBit,
  263. THIS_METHOD_IS_ONLY_FOR_EXPRESSIONS_WITH_DIRECT_MEMORY_ACCESS_SUCH_AS_MAP_OR_PLAIN_MATRICES)
  264. if (nfft<1) { //automatic FFT size determination
  265. if ( realfft && HasFlag(HalfSpectrum) )
  266. nfft = 2*(src.size()-1); //assume even fft size
  267. else
  268. nfft = src.size();
  269. }
  270. dst.derived().resize( nfft );
  271. // check for nfft that does not fit the input data size
  272. Index resize_input= ( realfft && HasFlag(HalfSpectrum) )
  273. ? ( (nfft/2+1) - src.size() )
  274. : ( nfft - src.size() );
  275. if ( src.innerStride() != 1 || resize_input ) {
  276. // if the vector is strided, then we need to copy it to a packed temporary
  277. Matrix<src_type,1,Dynamic> tmp;
  278. if ( resize_input ) {
  279. size_t ncopy = (std::min)(src.size(),src.size() + resize_input);
  280. tmp.setZero(src.size() + resize_input);
  281. if ( realfft && HasFlag(HalfSpectrum) ) {
  282. // pad at the Nyquist bin
  283. tmp.head(ncopy) = src.head(ncopy);
  284. tmp(ncopy-1) = real(tmp(ncopy-1)); // enforce real-only Nyquist bin
  285. }else{
  286. size_t nhead,ntail;
  287. nhead = 1+ncopy/2-1; // range [0:pi)
  288. ntail = ncopy/2-1; // range (-pi:0)
  289. tmp.head(nhead) = src.head(nhead);
  290. tmp.tail(ntail) = src.tail(ntail);
  291. if (resize_input<0) { //shrinking -- create the Nyquist bin as the average of the two bins that fold into it
  292. tmp(nhead) = ( src(nfft/2) + src( src.size() - nfft/2 ) )*real_type(.5);
  293. }else{ // expanding -- split the old Nyquist bin into two halves
  294. tmp(nhead) = src(nhead) * real_type(.5);
  295. tmp(tmp.size()-nhead) = tmp(nhead);
  296. }
  297. }
  298. }else{
  299. tmp = src;
  300. }
  301. inv( &dst[0],&tmp[0], nfft);
  302. }else{
  303. inv( &dst[0],&src[0], nfft);
  304. }
  305. }
  306. template <typename _Output>
  307. inline
  308. void inv( std::vector<_Output> & dst, const std::vector<Complex> & src,Index nfft=-1)
  309. {
  310. if (nfft<1)
  311. nfft = ( NumTraits<_Output>::IsComplex == 0 && HasFlag(HalfSpectrum) ) ? 2*(src.size()-1) : src.size();
  312. dst.resize( nfft );
  313. inv( &dst[0],&src[0],nfft);
  314. }
  315. /*
  316. // TODO: multi-dimensional FFTs
  317. inline
  318. void inv2(Complex * dst, const Complex * src, int n0,int n1)
  319. {
  320. m_impl.inv2(dst,src,n0,n1);
  321. if ( HasFlag( Unscaled ) == false)
  322. scale(dst,1./(n0*n1),n0*n1);
  323. }
  324. */
  325. inline
  326. impl_type & impl() {return m_impl;}
  327. private:
  328. template <typename T_Data>
  329. inline
  330. void scale(T_Data * x,Scalar s,Index nx)
  331. {
  332. #if 1
  333. for (int k=0;k<nx;++k)
  334. *x++ *= s;
  335. #else
  336. if ( ((ptrdiff_t)x) & 15 )
  337. Matrix<T_Data, Dynamic, 1>::Map(x,nx) *= s;
  338. else
  339. Matrix<T_Data, Dynamic, 1>::MapAligned(x,nx) *= s;
  340. //Matrix<T_Data, Dynamic, Dynamic>::Map(x,nx) * s;
  341. #endif
  342. }
  343. inline
  344. void ReflectSpectrum(Complex * freq, Index nfft)
  345. {
  346. // create the implicit right-half spectrum (conjugate-mirror of the left-half)
  347. Index nhbins=(nfft>>1)+1;
  348. for (Index k=nhbins;k < nfft; ++k )
  349. freq[k] = conj(freq[nfft-k]);
  350. }
  351. impl_type m_impl;
  352. int m_flag;
  353. };
  354. template<typename T_SrcMat,typename T_FftIfc>
  355. template<typename T_DestMat> inline
  356. void fft_fwd_proxy<T_SrcMat,T_FftIfc>::evalTo(T_DestMat& dst) const
  357. {
  358. m_ifc.fwd( dst, m_src, m_nfft);
  359. }
  360. template<typename T_SrcMat,typename T_FftIfc>
  361. template<typename T_DestMat> inline
  362. void fft_inv_proxy<T_SrcMat,T_FftIfc>::evalTo(T_DestMat& dst) const
  363. {
  364. m_ifc.inv( dst, m_src, m_nfft);
  365. }
  366. }
  367. #endif
  368. /* vim: set filetype=cpp et sw=2 ts=2 ai: */