Skip to content

Commit e01aee0

Browse files
committed
FftSharp.FFT
Simple methods which act on System.Numeric.Complex data types #61
1 parent 194281b commit e01aee0

File tree

3 files changed

+125
-0
lines changed

3 files changed

+125
-0
lines changed

src/FftSharp.Tests/FftTests.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using NUnit.Framework;
2+
3+
namespace FftSharp.Tests;
4+
5+
internal class FftTests
6+
{
7+
[Test]
8+
public void Test_FFT_Forward()
9+
{
10+
double[] sample = LoadData.Double("sample.txt");
11+
System.Numerics.Complex[] fft = FftSharp.FFT.Forward(sample);
12+
13+
Complex[] numpyFft = LoadData.Complex("fft.txt");
14+
Assert.AreEqual(numpyFft.Length, fft.Length);
15+
16+
for (int i = 0; i < fft.Length; i++)
17+
{
18+
Assert.AreEqual(numpyFft[i].Real, fft[i].Real, 1e-10);
19+
Assert.AreEqual(numpyFft[i].Imaginary, fft[i].Imaginary, 1e-10);
20+
}
21+
}
22+
23+
[Test]
24+
public void Test_FFT_Inverse()
25+
{
26+
double[] sample = LoadData.Double("sample.txt");
27+
System.Numerics.Complex[] fft = FftSharp.FFT.Forward(sample);
28+
FftSharp.FFT.Inverse(fft);
29+
30+
for (int i = 0; i < fft.Length; i++)
31+
{
32+
Assert.AreEqual(sample[i], fft[i].Real, 1e-10);
33+
}
34+
}
35+
36+
[Test]
37+
public void Test_FFT_FrequencyScale()
38+
{
39+
double[] numpyFftFreq = LoadData.Double("fftFreq.txt");
40+
double[] fftFreq = FftSharp.FFT.FrequencyScale(length: numpyFftFreq.Length, sampleRate: 48_000);
41+
42+
Assert.AreEqual(numpyFftFreq.Length, fftFreq.Length);
43+
44+
for (int i = 0; i < fftFreq.Length; i++)
45+
{
46+
Assert.AreEqual(fftFreq[i], fftFreq[i], 1e-10);
47+
}
48+
}
49+
}

src/FftSharp/Extensions.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
namespace FftSharp;
2+
3+
internal static class Extensions
4+
{
5+
/// <summary>
6+
/// Return a Complex array with the given values as the real component
7+
/// </summary>
8+
/// <param name="real"></param>
9+
/// <returns></returns>
10+
public static System.Numerics.Complex[] ToComplexArray(this double[] real)
11+
{
12+
System.Numerics.Complex[] buffer = new System.Numerics.Complex[real.Length];
13+
14+
for (int i = 0; i < real.Length; i++)
15+
buffer[i] = new(real[i], 0);
16+
17+
return buffer;
18+
}
19+
}

src/FftSharp/FFT.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using System;
2+
3+
namespace FftSharp;
4+
5+
// TODO: support spans
6+
7+
/// <summary>
8+
/// Fast Fourier Transform (FFT) operations using System.Numerics.Complex data types.
9+
/// </summary>
10+
public static class FFT
11+
{
12+
/// <summary>
13+
/// Apply the Fast Fourier Transform (FFT) to the Complex array in place.
14+
/// Length of the array must be a power of 2.
15+
/// </summary>
16+
public static void Forward(System.Numerics.Complex[] samples)
17+
{
18+
if (!Transform.IsPowerOfTwo(samples.Length))
19+
throw new ArgumentException($"{nameof(samples)} length must be a power of 2");
20+
21+
FftOperations.FFT_WithoutChecks(samples);
22+
}
23+
24+
/// <summary>
25+
/// Create a Complex array from the given data, apply the FFT, and return the result.
26+
/// Length of the array must be a power of 2.
27+
/// </summary>
28+
public static System.Numerics.Complex[] Forward(double[] real)
29+
{
30+
if (!Transform.IsPowerOfTwo(real.Length))
31+
throw new ArgumentException($"{nameof(real)} length must be a power of 2");
32+
33+
System.Numerics.Complex[] samples = real.ToComplexArray();
34+
FftOperations.FFT_WithoutChecks(samples);
35+
return samples;
36+
}
37+
38+
/// <summary>
39+
/// Apply the inverse Fast Fourier Transform (iFFT) to the Complex array in place.
40+
/// Length of the array must be a power of 2.
41+
/// </summary>
42+
public static void Inverse(System.Numerics.Complex[] samples)
43+
{
44+
if (!Transform.IsPowerOfTwo(samples.Length))
45+
throw new ArgumentException($"{nameof(samples)} length must be a power of 2");
46+
47+
FftOperations.IFFT_WithoutChecks(samples);
48+
}
49+
50+
/// <summary>
51+
/// Return frequencies for each index of a FFT.
52+
/// </summary>
53+
public static double[] FrequencyScale(int length, double sampleRate, bool positiveOnly = true)
54+
{
55+
return Transform.FFTfreq(sampleRate: sampleRate, pointCount: length, oneSided: positiveOnly);
56+
}
57+
}

0 commit comments

Comments
 (0)