You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

418 lines
14 KiB

  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. /** \ingroup Unsupported_modules
  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 OutputDerived::Scalar dst_type;
  255. const bool realfft= (NumTraits<dst_type>::IsComplex == 0);
  256. EIGEN_STATIC_ASSERT_VECTOR_ONLY(OutputDerived)
  257. EIGEN_STATIC_ASSERT_VECTOR_ONLY(ComplexDerived)
  258. EIGEN_STATIC_ASSERT_SAME_VECTOR_SIZE(ComplexDerived,OutputDerived) // size at compile-time
  259. EIGEN_STATIC_ASSERT((internal::is_same<src_type, Complex>::value),
  260. YOU_MIXED_DIFFERENT_NUMERIC_TYPES__YOU_NEED_TO_USE_THE_CAST_METHOD_OF_MATRIXBASE_TO_CAST_NUMERIC_TYPES_EXPLICITLY)
  261. EIGEN_STATIC_ASSERT(int(OutputDerived::Flags)&int(ComplexDerived::Flags)&DirectAccessBit,
  262. THIS_METHOD_IS_ONLY_FOR_EXPRESSIONS_WITH_DIRECT_MEMORY_ACCESS_SUCH_AS_MAP_OR_PLAIN_MATRICES)
  263. if (nfft<1) { //automatic FFT size determination
  264. if ( realfft && HasFlag(HalfSpectrum) )
  265. nfft = 2*(src.size()-1); //assume even fft size
  266. else
  267. nfft = src.size();
  268. }
  269. dst.derived().resize( nfft );
  270. // check for nfft that does not fit the input data size
  271. Index resize_input= ( realfft && HasFlag(HalfSpectrum) )
  272. ? ( (nfft/2+1) - src.size() )
  273. : ( nfft - src.size() );
  274. if ( src.innerStride() != 1 || resize_input ) {
  275. // if the vector is strided, then we need to copy it to a packed temporary
  276. Matrix<src_type,1,Dynamic> tmp;
  277. if ( resize_input ) {
  278. size_t ncopy = (std::min)(src.size(),src.size() + resize_input);
  279. tmp.setZero(src.size() + resize_input);
  280. if ( realfft && HasFlag(HalfSpectrum) ) {
  281. // pad at the Nyquist bin
  282. tmp.head(ncopy) = src.head(ncopy);
  283. tmp(ncopy-1) = real(tmp(ncopy-1)); // enforce real-only Nyquist bin
  284. }else{
  285. size_t nhead,ntail;
  286. nhead = 1+ncopy/2-1; // range [0:pi)
  287. ntail = ncopy/2-1; // range (-pi:0)
  288. tmp.head(nhead) = src.head(nhead);
  289. tmp.tail(ntail) = src.tail(ntail);
  290. if (resize_input<0) { //shrinking -- create the Nyquist bin as the average of the two bins that fold into it
  291. tmp(nhead) = ( src(nfft/2) + src( src.size() - nfft/2 ) )*src_type(.5);
  292. }else{ // expanding -- split the old Nyquist bin into two halves
  293. tmp(nhead) = src(nhead) * src_type(.5);
  294. tmp(tmp.size()-nhead) = tmp(nhead);
  295. }
  296. }
  297. }else{
  298. tmp = src;
  299. }
  300. inv( &dst[0],&tmp[0], nfft);
  301. }else{
  302. inv( &dst[0],&src[0], nfft);
  303. }
  304. }
  305. template <typename _Output>
  306. inline
  307. void inv( std::vector<_Output> & dst, const std::vector<Complex> & src,Index nfft=-1)
  308. {
  309. if (nfft<1)
  310. nfft = ( NumTraits<_Output>::IsComplex == 0 && HasFlag(HalfSpectrum) ) ? 2*(src.size()-1) : src.size();
  311. dst.resize( nfft );
  312. inv( &dst[0],&src[0],nfft);
  313. }
  314. /*
  315. // TODO: multi-dimensional FFTs
  316. inline
  317. void inv2(Complex * dst, const Complex * src, int n0,int n1)
  318. {
  319. m_impl.inv2(dst,src,n0,n1);
  320. if ( HasFlag( Unscaled ) == false)
  321. scale(dst,1./(n0*n1),n0*n1);
  322. }
  323. */
  324. inline
  325. impl_type & impl() {return m_impl;}
  326. private:
  327. template <typename T_Data>
  328. inline
  329. void scale(T_Data * x,Scalar s,Index nx)
  330. {
  331. #if 1
  332. for (int k=0;k<nx;++k)
  333. *x++ *= s;
  334. #else
  335. if ( ((ptrdiff_t)x) & 15 )
  336. Matrix<T_Data, Dynamic, 1>::Map(x,nx) *= s;
  337. else
  338. Matrix<T_Data, Dynamic, 1>::MapAligned(x,nx) *= s;
  339. //Matrix<T_Data, Dynamic, Dynamic>::Map(x,nx) * s;
  340. #endif
  341. }
  342. inline
  343. void ReflectSpectrum(Complex * freq, Index nfft)
  344. {
  345. // create the implicit right-half spectrum (conjugate-mirror of the left-half)
  346. Index nhbins=(nfft>>1)+1;
  347. for (Index k=nhbins;k < nfft; ++k )
  348. freq[k] = conj(freq[nfft-k]);
  349. }
  350. impl_type m_impl;
  351. int m_flag;
  352. };
  353. template<typename T_SrcMat,typename T_FftIfc>
  354. template<typename T_DestMat> inline
  355. void fft_fwd_proxy<T_SrcMat,T_FftIfc>::evalTo(T_DestMat& dst) const
  356. {
  357. m_ifc.fwd( dst, m_src, m_nfft);
  358. }
  359. template<typename T_SrcMat,typename T_FftIfc>
  360. template<typename T_DestMat> inline
  361. void fft_inv_proxy<T_SrcMat,T_FftIfc>::evalTo(T_DestMat& dst) const
  362. {
  363. m_ifc.inv( dst, m_src, m_nfft);
  364. }
  365. }
  366. #endif
  367. /* vim: set filetype=cpp et sw=2 ts=2 ai: */