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. #include "../../Eigen/src/Core/util/DisableStupidWarnings.h"
  66. #ifdef EIGEN_FFTW_DEFAULT
  67. // FFTW: faster, GPL -- incompatible with Eigen in LGPL form, bigger code size
  68. # include <fftw3.h>
  69. # include "src/FFT/ei_fftw_impl.h"
  70. namespace Eigen {
  71. //template <typename T> typedef struct internal::fftw_impl default_fft_impl; this does not work
  72. template <typename T> struct default_fft_impl : public internal::fftw_impl<T> {};
  73. }
  74. #elif defined EIGEN_MKL_DEFAULT
  75. // TODO
  76. // intel Math Kernel Library: fastest, commercial -- may be incompatible with Eigen in GPL form
  77. # include "src/FFT/ei_imklfft_impl.h"
  78. namespace Eigen {
  79. template <typename T> struct default_fft_impl : public internal::imklfft_impl {};
  80. }
  81. #else
  82. // internal::kissfft_impl: small, free, reasonably efficient default, derived from kissfft
  83. //
  84. # include "src/FFT/ei_kissfft_impl.h"
  85. namespace Eigen {
  86. template <typename T>
  87. struct default_fft_impl : public internal::kissfft_impl<T> {};
  88. }
  89. #endif
  90. namespace Eigen {
  91. //
  92. template<typename T_SrcMat,typename T_FftIfc> struct fft_fwd_proxy;
  93. template<typename T_SrcMat,typename T_FftIfc> struct fft_inv_proxy;
  94. namespace internal {
  95. template<typename T_SrcMat,typename T_FftIfc>
  96. struct traits< fft_fwd_proxy<T_SrcMat,T_FftIfc> >
  97. {
  98. typedef typename T_SrcMat::PlainObject ReturnType;
  99. };
  100. template<typename T_SrcMat,typename T_FftIfc>
  101. struct traits< fft_inv_proxy<T_SrcMat,T_FftIfc> >
  102. {
  103. typedef typename T_SrcMat::PlainObject ReturnType;
  104. };
  105. }
  106. template<typename T_SrcMat,typename T_FftIfc>
  107. struct fft_fwd_proxy
  108. : public ReturnByValue<fft_fwd_proxy<T_SrcMat,T_FftIfc> >
  109. {
  110. typedef DenseIndex Index;
  111. fft_fwd_proxy(const T_SrcMat& src,T_FftIfc & fft, Index nfft) : m_src(src),m_ifc(fft), m_nfft(nfft) {}
  112. template<typename T_DestMat> void evalTo(T_DestMat& dst) const;
  113. Index rows() const { return m_src.rows(); }
  114. Index cols() const { return m_src.cols(); }
  115. protected:
  116. const T_SrcMat & m_src;
  117. T_FftIfc & m_ifc;
  118. Index m_nfft;
  119. };
  120. template<typename T_SrcMat,typename T_FftIfc>
  121. struct fft_inv_proxy
  122. : public ReturnByValue<fft_inv_proxy<T_SrcMat,T_FftIfc> >
  123. {
  124. typedef DenseIndex Index;
  125. fft_inv_proxy(const T_SrcMat& src,T_FftIfc & fft, Index nfft) : m_src(src),m_ifc(fft), m_nfft(nfft) {}
  126. template<typename T_DestMat> void evalTo(T_DestMat& dst) const;
  127. Index rows() const { return m_src.rows(); }
  128. Index cols() const { return m_src.cols(); }
  129. protected:
  130. const T_SrcMat & m_src;
  131. T_FftIfc & m_ifc;
  132. Index m_nfft;
  133. };
  134. template <typename T_Scalar,
  135. typename T_Impl=default_fft_impl<T_Scalar> >
  136. class FFT
  137. {
  138. public:
  139. typedef T_Impl impl_type;
  140. typedef DenseIndex Index;
  141. typedef typename impl_type::Scalar Scalar;
  142. typedef typename impl_type::Complex Complex;
  143. enum Flag {
  144. Default=0, // goof proof
  145. Unscaled=1,
  146. HalfSpectrum=2,
  147. // SomeOtherSpeedOptimization=4
  148. Speedy=32767
  149. };
  150. FFT( const impl_type & impl=impl_type() , Flag flags=Default ) :m_impl(impl),m_flag(flags) { }
  151. inline
  152. bool HasFlag(Flag f) const { return (m_flag & (int)f) == f;}
  153. inline
  154. void SetFlag(Flag f) { m_flag |= (int)f;}
  155. inline
  156. void ClearFlag(Flag f) { m_flag &= (~(int)f);}
  157. inline
  158. void fwd( Complex * dst, const Scalar * src, Index nfft)
  159. {
  160. m_impl.fwd(dst,src,static_cast<int>(nfft));
  161. if ( HasFlag(HalfSpectrum) == false)
  162. ReflectSpectrum(dst,nfft);
  163. }
  164. inline
  165. void fwd( Complex * dst, const Complex * src, Index nfft)
  166. {
  167. m_impl.fwd(dst,src,static_cast<int>(nfft));
  168. }
  169. /*
  170. inline
  171. void fwd2(Complex * dst, const Complex * src, int n0,int n1)
  172. {
  173. m_impl.fwd2(dst,src,n0,n1);
  174. }
  175. */
  176. template <typename _Input>
  177. inline
  178. void fwd( std::vector<Complex> & dst, const std::vector<_Input> & src)
  179. {
  180. if ( NumTraits<_Input>::IsComplex == 0 && HasFlag(HalfSpectrum) )
  181. dst.resize( (src.size()>>1)+1); // half the bins + Nyquist bin
  182. else
  183. dst.resize(src.size());
  184. fwd(&dst[0],&src[0],src.size());
  185. }
  186. template<typename InputDerived, typename ComplexDerived>
  187. inline
  188. void fwd( MatrixBase<ComplexDerived> & dst, const MatrixBase<InputDerived> & src, Index nfft=-1)
  189. {
  190. typedef typename ComplexDerived::Scalar dst_type;
  191. typedef typename InputDerived::Scalar src_type;
  192. EIGEN_STATIC_ASSERT_VECTOR_ONLY(InputDerived)
  193. EIGEN_STATIC_ASSERT_VECTOR_ONLY(ComplexDerived)
  194. EIGEN_STATIC_ASSERT_SAME_VECTOR_SIZE(ComplexDerived,InputDerived) // size at compile-time
  195. EIGEN_STATIC_ASSERT((internal::is_same<dst_type, Complex>::value),
  196. YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
  197. EIGEN_STATIC_ASSERT(int(InputDerived::Flags)&int(ComplexDerived::Flags)&DirectAccessBit,
  198. THIS_METHOD_IS_ONLY_FOR_EXPRESSIONS_WITH_DIRECT_MEMORY_ACCESS_SUCH_AS_MAP_OR_PLAIN_MATRICES)
  199. if (nfft<1)
  200. nfft = src.size();
  201. if ( NumTraits< src_type >::IsComplex == 0 && HasFlag(HalfSpectrum) )
  202. dst.derived().resize( (nfft>>1)+1);
  203. else
  204. dst.derived().resize(nfft);
  205. if ( src.innerStride() != 1 || src.size() < nfft ) {
  206. Matrix<src_type,1,Dynamic> tmp;
  207. if (src.size()<nfft) {
  208. tmp.setZero(nfft);
  209. tmp.block(0,0,src.size(),1 ) = src;
  210. }else{
  211. tmp = src;
  212. }
  213. fwd( &dst[0],&tmp[0],nfft );
  214. }else{
  215. fwd( &dst[0],&src[0],nfft );
  216. }
  217. }
  218. template<typename InputDerived>
  219. inline
  220. fft_fwd_proxy< MatrixBase<InputDerived>, FFT<T_Scalar,T_Impl> >
  221. fwd( const MatrixBase<InputDerived> & src, Index nfft=-1)
  222. {
  223. return fft_fwd_proxy< MatrixBase<InputDerived> ,FFT<T_Scalar,T_Impl> >( src, *this,nfft );
  224. }
  225. template<typename InputDerived>
  226. inline
  227. fft_inv_proxy< MatrixBase<InputDerived>, FFT<T_Scalar,T_Impl> >
  228. inv( const MatrixBase<InputDerived> & src, Index nfft=-1)
  229. {
  230. return fft_inv_proxy< MatrixBase<InputDerived> ,FFT<T_Scalar,T_Impl> >( src, *this,nfft );
  231. }
  232. inline
  233. void inv( Complex * dst, const Complex * src, Index nfft)
  234. {
  235. m_impl.inv( dst,src,static_cast<int>(nfft) );
  236. if ( HasFlag( Unscaled ) == false)
  237. scale(dst,Scalar(1./nfft),nfft); // scale the time series
  238. }
  239. inline
  240. void inv( Scalar * dst, const Complex * src, Index nfft)
  241. {
  242. m_impl.inv( dst,src,static_cast<int>(nfft) );
  243. if ( HasFlag( Unscaled ) == false)
  244. scale(dst,Scalar(1./nfft),nfft); // scale the time series
  245. }
  246. template<typename OutputDerived, typename ComplexDerived>
  247. inline
  248. void inv( MatrixBase<OutputDerived> & dst, const MatrixBase<ComplexDerived> & src, Index nfft=-1)
  249. {
  250. typedef typename ComplexDerived::Scalar src_type;
  251. typedef typename ComplexDerived::RealScalar real_type;
  252. typedef typename OutputDerived::Scalar dst_type;
  253. const bool realfft= (NumTraits<dst_type>::IsComplex == 0);
  254. EIGEN_STATIC_ASSERT_VECTOR_ONLY(OutputDerived)
  255. EIGEN_STATIC_ASSERT_VECTOR_ONLY(ComplexDerived)
  256. EIGEN_STATIC_ASSERT_SAME_VECTOR_SIZE(ComplexDerived,OutputDerived) // size at compile-time
  257. EIGEN_STATIC_ASSERT((internal::is_same<src_type, Complex>::value),
  258. YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
  259. EIGEN_STATIC_ASSERT(int(OutputDerived::Flags)&int(ComplexDerived::Flags)&DirectAccessBit,
  260. THIS_METHOD_IS_ONLY_FOR_EXPRESSIONS_WITH_DIRECT_MEMORY_ACCESS_SUCH_AS_MAP_OR_PLAIN_MATRICES)
  261. if (nfft<1) { //automatic FFT size determination
  262. if ( realfft && HasFlag(HalfSpectrum) )
  263. nfft = 2*(src.size()-1); //assume even fft size
  264. else
  265. nfft = src.size();
  266. }
  267. dst.derived().resize( nfft );
  268. // check for nfft that does not fit the input data size
  269. Index resize_input= ( realfft && HasFlag(HalfSpectrum) )
  270. ? ( (nfft/2+1) - src.size() )
  271. : ( nfft - src.size() );
  272. if ( src.innerStride() != 1 || resize_input ) {
  273. // if the vector is strided, then we need to copy it to a packed temporary
  274. Matrix<src_type,1,Dynamic> tmp;
  275. if ( resize_input ) {
  276. size_t ncopy = (std::min)(src.size(),src.size() + resize_input);
  277. tmp.setZero(src.size() + resize_input);
  278. if ( realfft && HasFlag(HalfSpectrum) ) {
  279. // pad at the Nyquist bin
  280. tmp.head(ncopy) = src.head(ncopy);
  281. tmp(ncopy-1) = real(tmp(ncopy-1)); // enforce real-only Nyquist bin
  282. }else{
  283. size_t nhead,ntail;
  284. nhead = 1+ncopy/2-1; // range [0:pi)
  285. ntail = ncopy/2-1; // range (-pi:0)
  286. tmp.head(nhead) = src.head(nhead);
  287. tmp.tail(ntail) = src.tail(ntail);
  288. if (resize_input<0) { //shrinking -- create the Nyquist bin as the average of the two bins that fold into it
  289. tmp(nhead) = ( src(nfft/2) + src( src.size() - nfft/2 ) )*real_type(.5);
  290. }else{ // expanding -- split the old Nyquist bin into two halves
  291. tmp(nhead) = src(nhead) * real_type(.5);
  292. tmp(tmp.size()-nhead) = tmp(nhead);
  293. }
  294. }
  295. }else{
  296. tmp = src;
  297. }
  298. inv( &dst[0],&tmp[0], nfft);
  299. }else{
  300. inv( &dst[0],&src[0], nfft);
  301. }
  302. }
  303. template <typename _Output>
  304. inline
  305. void inv( std::vector<_Output> & dst, const std::vector<Complex> & src,Index nfft=-1)
  306. {
  307. if (nfft<1)
  308. nfft = ( NumTraits<_Output>::IsComplex == 0 && HasFlag(HalfSpectrum) ) ? 2*(src.size()-1) : src.size();
  309. dst.resize( nfft );
  310. inv( &dst[0],&src[0],nfft);
  311. }
  312. /*
  313. // TODO: multi-dimensional FFTs
  314. inline
  315. void inv2(Complex * dst, const Complex * src, int n0,int n1)
  316. {
  317. m_impl.inv2(dst,src,n0,n1);
  318. if ( HasFlag( Unscaled ) == false)
  319. scale(dst,1./(n0*n1),n0*n1);
  320. }
  321. */
  322. inline
  323. impl_type & impl() {return m_impl;}
  324. private:
  325. template <typename T_Data>
  326. inline
  327. void scale(T_Data * x,Scalar s,Index nx)
  328. {
  329. #if 1
  330. for (int k=0;k<nx;++k)
  331. *x++ *= s;
  332. #else
  333. if ( ((ptrdiff_t)x) & 15 )
  334. Matrix<T_Data, Dynamic, 1>::Map(x,nx) *= s;
  335. else
  336. Matrix<T_Data, Dynamic, 1>::MapAligned(x,nx) *= s;
  337. //Matrix<T_Data, Dynamic, Dynamic>::Map(x,nx) * s;
  338. #endif
  339. }
  340. inline
  341. void ReflectSpectrum(Complex * freq, Index nfft)
  342. {
  343. // create the implicit right-half spectrum (conjugate-mirror of the left-half)
  344. Index nhbins=(nfft>>1)+1;
  345. for (Index k=nhbins;k < nfft; ++k )
  346. freq[k] = conj(freq[nfft-k]);
  347. }
  348. impl_type m_impl;
  349. int m_flag;
  350. };
  351. template<typename T_SrcMat,typename T_FftIfc>
  352. template<typename T_DestMat> inline
  353. void fft_fwd_proxy<T_SrcMat,T_FftIfc>::evalTo(T_DestMat& dst) const
  354. {
  355. m_ifc.fwd( dst, m_src, m_nfft);
  356. }
  357. template<typename T_SrcMat,typename T_FftIfc>
  358. template<typename T_DestMat> inline
  359. void fft_inv_proxy<T_SrcMat,T_FftIfc>::evalTo(T_DestMat& dst) const
  360. {
  361. m_ifc.inv( dst, m_src, m_nfft);
  362. }
  363. }
  364. #include "../../Eigen/src/Core/util/ReenableStupidWarnings.h"
  365. #endif