For testing a Fourier transform I need a more complex signal than an ordinary sinus. I want a mix of three frequenties f, 2f and 5f with an arbitrary phase. In order to keep Engine() simple I added a class Signal:
public class Signal
{
/** A class to represent an input-signal for the FFT
* the input-signal consists of 3 sinus-signals:
* a1*sin(2*pi*f*t) + a2*sin(2*pi*2*f*t+phi2)+a5*cos(2*pi*5*f*t)
*/
private double a1;
private double a2;
private double a5;
private double f;
private double phi2;
public Signal(double f, double a1, double a2, double a5, double phi2)
{
this.f = f;
this.a1 = a1;
this.a2 = a2;
this.a5 = a5;
this.phi2 = phi2;
}
public double getSample(double t)
{
double pi2 = Math.PI * 2;
return a1 * Math.cos(pi2 * f * t)
+ a2 * Math.sin(pi2 * 2 * f * t + phi2)
+ a5 * Math.sin (pi2 * 5 * f * t);
}
}
|
Signal is used in Engine():
package testFFT;
import util.ShowPlot2d;
import util.Signal;
public class Engine {
// a more complicated testsignal
public Engine() {
}
public void plotSignal() {
double f = 1.0E3;
double a1 = 1.0;
double a2 = 0.8;
double a5 = 1.2;
double phi = Math.PI / 4;
int numP = 109;
Signal mySignal = new Signal(f, a1, a2, a5, phi);
// 3 periods with 36 points per period
double[] plotData = new double[numP - 1];
for (int i = 0; i < numP - 1; i++) {
double degr = 10 * i;
double perT = 1 / f;
double t = (degr / 360) * perT;
double y = mySignal.getSample(t);
plotData[i] = y;
}
int numberOfPlots = 1;
ShowPlot2d myPlot = new ShowPlot2d(numberOfPlots);
double scaleFactor = 100;
myPlot.add(plotData, scaleFactor, "a complicated sinewave");
myPlot.showFigure();
}
}
|
with the following output:
What is the sample frequency of the above signal? There are 36 points per period of the base frequency f. So the sample-frequency is 36* f.
The next piece of java-code is fillArray3. This method calculaties the DFT, the Discrete Fourier Transform. For that I needed a dft-program. Because I don't (yet) need real-time respons I simply coded the DFT-formulas directly in java without any attempt for efficiency. The dft-method:
private Complex[] dft(double[] x)
{
int n = x.length;
Complex[] xX = new Complex[n]; // FFT-bins
for(int k = 0; k < n; k++)
{
Complex accum = new Complex(0.0, 0.0);
for (int nn=0; nn < n; nn++)
{
Complex wnkN = Complex.expjphi(-2*Math.PI*nn*k/n);
Complex prod = wnkN.multiply(x[nn]);
accum = accum.add(prod);
}
xX[k] = accum;
}
return xX;
}
|
Which is called by the following snippet of code:
...
for (int i = 0; i < numP - 1; i++) {
double degr = 10 * i;
double perT = 1 / f;
double t = (degr / 360) * perT;
double y = mySignal.getSample(t);
plotData[i] = y;
}
double x[] = plotData;
Fourier myFourier = new Fourier();
Complex bins[] = myFourier.dft(x);
for (int i = 0; i < bins.length; i++) {
plotData1[i] = bins[i].getReal();
plotData2[i] = bins[i].getImaginary();
plotData3[i] = bins[i].magnitude();
}
int numberOfPlots = 3;
ShowPlot2d myPlot = new ShowPlot2d(numberOfPlots, "Three plots");
double scaleFactor = 1;
myPlot.add(plotData1, scaleFactor, "real");
scaleFactor = 1;
myPlot.add(plotData2, scaleFactor, "imag");
scaleFactor = 1;
myPlot.add(plotData3, scaleFactor, "magnitude");
myPlot.showFigure();
...
|
and the output is:
The results are numerically shown in the following table:
Discrete Fourier Transform
| Bin-number | Magnitude | Real part | Imaginary part |
| 3 (f) | 54 | 54 | 0 |
| 6 (2f) | 43.2 | 30.5 | -30.5 |
| 15 (5f) | 64.8 | 0 | -64.8 |
| 93 (5f) | 64.8 | 0 | 64.8 |
| 102 (2f) | 43.2 | 30.5 | 30.5 |
| 105 (f) | 54 | 54 | 0 |
The basic frequency f is in bin 3. There are 36 points per period of the base frequency f. So the sample-frequency is 36* f. There are 108 points, so every bin is fs/108= 36*f/108 = f/3. So bin 3 is f, bin 6 is 2f and bin 15 is 5f. Because the dft-transforms of f only have real parts, f must be a cosine. You can find that in the code for "Signal". The dft-transform for 5f is obviously a sine. The signal 2f is complex. There is a 45 degree angle between the real and imaginary part. This can also be found in the java-code for Signal. The amplitude for f is 54+54 = 108. Divided by the number of bins ==> 1. That is correct. The other amplitudes also are in correspondence with the code in Signal.
If you transform a signal into its Fourier components there is also a way back; the Inverse Discrete Fourier Transform. The code is almost the same as for the dft:
private Complex[] idft(Complex[] X)
{
int n = X.length;
Complex[] x = new Complex[n];
for(int k=0; k < n; k++)
{
Complex accum = new Complex(0.0, 0.0);
for(int nn=0; nn < n; nn++)
{
Complex wnkN = Complex.expjphi(2*Math.PI*nn*k/n);
Complex prod = wnkN.multiply(X[nn]);
accum = accum.add(prod);
}
x[k] = accum.multiply( (double) (1.0/n));
}
return x;
}
|
By clearing bins 6, 15, 93 and 102 after the dft and before the idft an ideal lowpass-filter is the result: