DFT.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. #region Copyright and License
  2. /*
  3. This file is part of FFTW.NET, a wrapper around the FFTW library
  4. for the .NET framework.
  5. Copyright (C) 2017 Tobias Meyer
  6. This program is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 2
  9. of the License, or (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. */
  15. #endregion
  16. using System;
  17. using System.Text;
  18. using System.Numerics;
  19. using System.Runtime.InteropServices;
  20. using System.Linq;
  21. namespace FFTW.NET
  22. {
  23. /// <summary>
  24. /// This class provides methods for convenience.
  25. /// However, for optimal performance you should consider using
  26. /// <see cref="FftwPlanC2C"/> or <see cref="FftwPlanRC"/> directly.
  27. /// </summary>
  28. public static class DFT
  29. {
  30. /// <summary>
  31. /// Large object heap threshold in bytes
  32. /// </summary>
  33. const int LohThreshold = 85000;
  34. /// <summary>
  35. /// Memory alignment in bytes
  36. /// </summary>
  37. const int MemoryAlignment = 16;
  38. static readonly BufferPool<byte> _bufferPool = new BufferPool<byte>(LohThreshold);
  39. /// <summary>
  40. /// Performs a complex-to-complex fast fourier transformation. The dimension is inferred from the input (<see cref="Array{T}.Rank"/>).
  41. /// </summary>
  42. /// <seealso cref="http://www.fftw.org/fftw3_doc/Complex-One_002dDimensional-DFTs.html#Complex-One_002dDimensional-DFTs"/>
  43. /// <seealso cref="http://www.fftw.org/fftw3_doc/Complex-Multi_002dDimensional-DFTs.html#Complex-Multi_002dDimensional-DFTs"/>
  44. public static void FFT(IPinnedArray<Complex> input, IPinnedArray<Complex> output, PlannerFlags plannerFlags = PlannerFlags.Default, int nThreads = 1) => Transform(input, output, DftDirection.Forwards, plannerFlags, nThreads);
  45. /// <summary>
  46. /// Performs a complex-to-complex inverse fast fourier transformation. The dimension is inferred from the input (<see cref="Array{T}.Rank"/>).
  47. /// </summary>
  48. /// <seealso cref="http://www.fftw.org/fftw3_doc/Complex-One_002dDimensional-DFTs.html#Complex-One_002dDimensional-DFTs"/>
  49. /// <seealso cref="http://www.fftw.org/fftw3_doc/Complex-Multi_002dDimensional-DFTs.html#Complex-Multi_002dDimensional-DFTs"/>
  50. public static void IFFT(IPinnedArray<Complex> input, IPinnedArray<Complex> output, PlannerFlags plannerFlags = PlannerFlags.Default, int nThreads = 1) => Transform(input, output, DftDirection.Backwards, plannerFlags, nThreads);
  51. static void Transform(IPinnedArray<Complex> input, IPinnedArray<Complex> output, DftDirection direction, PlannerFlags plannerFlags, int nThreads)
  52. {
  53. if ((plannerFlags & PlannerFlags.Estimate) == PlannerFlags.Estimate)
  54. {
  55. using (var plan = FftwPlanC2C.Create(input, output, direction, plannerFlags, nThreads))
  56. {
  57. plan.Execute();
  58. return;
  59. }
  60. }
  61. using (var plan = FftwPlanC2C.Create(input, output, direction, plannerFlags | PlannerFlags.WisdomOnly, nThreads))
  62. {
  63. if (plan != null)
  64. {
  65. plan.Execute();
  66. return;
  67. }
  68. }
  69. /// If with <see cref="PlannerFlags.WisdomOnly"/> no plan can be created
  70. /// and <see cref="PlannerFlags.Estimate"/> is not specified, we use
  71. /// a different buffer to avoid overwriting the input
  72. if (input != output)
  73. {
  74. using (var plan = FftwPlanC2C.Create(output, output, input.Rank, input.GetSize(), direction, plannerFlags, nThreads))
  75. {
  76. input.CopyTo(output);
  77. plan.Execute();
  78. }
  79. }
  80. else
  81. {
  82. using (var bufferContainer = _bufferPool.RequestBuffer(input.Length * Marshal.SizeOf<Complex>() + MemoryAlignment))
  83. using (var buffer = new AlignedArrayComplex(bufferContainer.Buffer!, MemoryAlignment, input.GetSize()))
  84. using (var plan = FftwPlanC2C.Create(buffer, buffer, input.Rank, input.GetSize(), direction, plannerFlags, nThreads))
  85. {
  86. input.CopyTo(plan.Input);
  87. plan.Execute();
  88. plan.Output.CopyTo(output, 0, 0, input.Length);
  89. }
  90. }
  91. }
  92. /// <summary>
  93. /// Performs a real-to-complex fast fourier transformation.
  94. /// </summary>
  95. /// <seealso cref="http://www.fftw.org/fftw3_doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data"/>
  96. /// <seealso cref="http://www.fftw.org/fftw3_doc/Multi_002dDimensional-DFTs-of-Real-Data.html#Multi_002dDimensional-DFTs-of-Real-Data"/>
  97. public static void FFT(IPinnedArray<double> input, IPinnedArray<Complex> output, PlannerFlags plannerFlags = PlannerFlags.Default, int nThreads = 1)
  98. {
  99. if ((plannerFlags & PlannerFlags.Estimate) == PlannerFlags.Estimate)
  100. {
  101. using (var plan = FftwPlanRC.Create(input, output, DftDirection.Forwards, plannerFlags, nThreads))
  102. {
  103. plan.Execute();
  104. return;
  105. }
  106. }
  107. using (var plan = FftwPlanRC.Create(input, output, DftDirection.Forwards, plannerFlags | PlannerFlags.WisdomOnly, nThreads))
  108. {
  109. if (plan != null)
  110. {
  111. plan.Execute();
  112. return;
  113. }
  114. }
  115. /// If with <see cref="PlannerFlags.WisdomOnly"/> no plan can be created
  116. /// and <see cref="PlannerFlags.Estimate"/> is not specified, we use
  117. /// a different buffer to avoid overwriting the input
  118. using (var bufferContainer = _bufferPool.RequestBuffer(input.Length * sizeof(double) + MemoryAlignment))
  119. using (var buffer = new AlignedArrayDouble(bufferContainer.Buffer!, MemoryAlignment, input.GetSize()))
  120. using (var plan = FftwPlanRC.Create(buffer, output, DftDirection.Forwards, plannerFlags, nThreads))
  121. {
  122. input.CopyTo(plan.BufferReal);
  123. plan.Execute();
  124. }
  125. }
  126. public static Complex[] IFFT(double[] data, double[] img)
  127. {
  128. if (data == null || data.Length == 0 || img == null || img.Length != data.Length) return new Complex[0];
  129. var input = new PinnedArray<Complex>(Enumerable.Range(0, data.Length).Select(x => new Complex(data[x], img[x])).ToArray());
  130. var output = new PinnedArray<Complex>(new Complex[data.Length]);
  131. IFFT(input, output);
  132. var result = output.Buffer.Cast<Complex>().ToArray();
  133. input.Dispose();
  134. output.Dispose();
  135. return result;
  136. }
  137. public static Complex[] FFT(double[] data, double[] img)
  138. {
  139. if (data == null || data.Length == 0 || img == null || img.Length != data.Length) return new Complex[0];
  140. var input = new PinnedArray<Complex>(Enumerable.Range(0, data.Length).Select(x => new Complex(data[x], img[x])).ToArray());
  141. var output = new PinnedArray<Complex>(new Complex[data.Length]);
  142. FFT(input, output);
  143. var result = output.Buffer.Cast<Complex>().ToArray();
  144. input.Dispose();
  145. output.Dispose();
  146. return result;
  147. }
  148. /// <summary>
  149. /// Performs a complex-to-real inverse fast fourier transformation.
  150. /// </summary>
  151. /// <seealso cref="http://www.fftw.org/fftw3_doc/One_002dDimensional-DFTs-of-Real-Data.html#One_002dDimensional-DFTs-of-Real-Data"/>
  152. /// <seealso cref="http://www.fftw.org/fftw3_doc/Multi_002dDimensional-DFTs-of-Real-Data.html#Multi_002dDimensional-DFTs-of-Real-Data"/>
  153. public static void IFFT(IPinnedArray<Complex> input, IPinnedArray<double> output, PlannerFlags plannerFlags = PlannerFlags.Default, int nThreads = 1)
  154. {
  155. if ((plannerFlags & PlannerFlags.Estimate) == PlannerFlags.Estimate)
  156. {
  157. using (var plan = FftwPlanRC.Create(output, input, DftDirection.Backwards, plannerFlags, nThreads))
  158. {
  159. plan.Execute();
  160. return;
  161. }
  162. }
  163. using (var plan = FftwPlanRC.Create(output, input, DftDirection.Backwards, plannerFlags | PlannerFlags.WisdomOnly, nThreads))
  164. {
  165. if (plan != null)
  166. {
  167. plan.Execute();
  168. return;
  169. }
  170. }
  171. /// If with <see cref="PlannerFlags.WisdomOnly"/> no plan can be created
  172. /// and <see cref="PlannerFlags.Estimate"/> is not specified, we use
  173. /// a different buffer to avoid overwriting the input
  174. using (var bufferContainer = _bufferPool.RequestBuffer(input.Length * Marshal.SizeOf<Complex>() + MemoryAlignment))
  175. using (var buffer = new AlignedArrayComplex(bufferContainer.Buffer!, MemoryAlignment, input.GetSize()))
  176. using (var plan = FftwPlanRC.Create(output, buffer, DftDirection.Backwards, plannerFlags, nThreads))
  177. {
  178. input.CopyTo(plan.BufferComplex);
  179. plan.Execute();
  180. }
  181. }
  182. /// <summary>
  183. /// Gets the required size of the complex buffer in a complex-to-real
  184. /// or rea-to-complex transormation from the size of the real buffer.
  185. /// </summary>
  186. /// <seealso cref="http://www.fftw.org/fftw3_doc/Multi_002dDimensional-DFTs-of-Real-Data.html#Multi_002dDimensional-DFTs-of-Real-Data"/>
  187. public static int[] GetComplexBufferSize(int[] realBufferSize)
  188. {
  189. int[] n = new int[realBufferSize.Length];
  190. Buffer.BlockCopy(realBufferSize, 0, n, 0, n.Length * sizeof(int));
  191. n[n.Length - 1] = realBufferSize[n.Length - 1] / 2 + 1;
  192. return n;
  193. }
  194. /// <summary>
  195. /// Provides access to FFTW's wisdom mechanism
  196. /// </summary>
  197. /// <seealso cref="http://www.fftw.org/fftw3_doc/Words-of-Wisdom_002dSaving-Plans.html#Words-of-Wisdom_002dSaving-Plans"/>
  198. public static class Wisdom
  199. {
  200. /// <summary>
  201. /// Exports the accumulated wisdom to a file.
  202. /// </summary>
  203. /// <seealso cref="http://www.fftw.org/fftw3_doc/Wisdom-Export.html#Wisdom-Export"/>
  204. /// <seealso cref="http://www.fftw.org/fftw3_doc/Caveats-in-Using-Wisdom.html#Caveats-in-Using-Wisdom"/>
  205. public static bool Export(string filename) { lock (FftwInterop.Lock) { return FftwInterop.GetDelegate<FftwInterop.fftw_export_wisdom_to_filename>()(filename); } }
  206. /// <summary>
  207. /// Imports wisdom from a file. The Current accumulated wisdom is replaced.
  208. /// Wisdom is hardware specific, thus importing wisdom created with different hardware
  209. /// can result in sub-optimal plans and should not be done.
  210. /// <seealso cref="http://www.fftw.org/fftw3_doc/Wisdom-Import.html#Wisdom-Import"/>
  211. /// <seealso cref="http://www.fftw.org/fftw3_doc/Caveats-in-Using-Wisdom.html#Caveats-in-Using-Wisdom"/>
  212. public static bool Import(string filename) { lock (FftwInterop.Lock) { return FftwInterop.GetDelegate<FftwInterop.fftw_import_wisdom_from_filename>()(filename); } }
  213. /// <summary>
  214. /// Clears the current wisdom.
  215. /// </summary>
  216. /// <seealso cref="http://www.fftw.org/fftw3_doc/Forgetting-Wisdom.html#Forgetting-Wisdom"/>
  217. public static void Clear() { lock (FftwInterop.Lock) { FftwInterop.GetDelegate<FftwInterop.fftw_forget_wisdom>()(); } }
  218. /// <summary>
  219. /// Gets or sets the current wisdom.
  220. /// </summary>
  221. public static string Current
  222. {
  223. get { lock (FftwInterop.Lock) { return FftwInterop.fftw_export_wisdom_to_string(); } }
  224. set
  225. {
  226. lock (FftwInterop.Lock)
  227. {
  228. if (string.IsNullOrEmpty(value))
  229. FftwInterop.GetDelegate<FftwInterop.fftw_forget_wisdom>()();
  230. else if (!FftwInterop.GetDelegate<FftwInterop.fftw_import_wisdom_from_string>()(value))
  231. throw new FormatException();
  232. }
  233. }
  234. }
  235. }
  236. }
  237. }