package org.signalml.math.fft;
import org.apache.commons.math.complex.Complex;
import org.apache.commons.math.transform.FastFourierTransformer;
import org.signalml.math.ArrayOperations;
/**
* This class can be used to calculate FFT of a signal.
*
* @author Piotr Szachewicz
*/
public class FourierTransform {
/**
* The {@link WindowFunction} to be used to window the input signal.
*/
private WindowFunction windowFunction = new WindowFunction(WindowType.RECTANGULAR);
/**
* Constructor.
* @param windowType the window to be used before performing FFT
* @param parameter the parametr for the specified window
*/
public FourierTransform(WindowType windowType, double parameter) {
this.windowFunction = new WindowFunction(windowType, parameter);
}
/**
* Constructor.
* @param windowType the window to be applied to the signal before actual
* FFT is performed
*/
public FourierTransform(WindowType windowType) {
this.windowFunction = new WindowFunction(windowType);
}
public FourierTransform() {
}
/**
* Calculates the FFT of the given signal.
*
* If a {@link WindowType} was specified while calling a constructor
* it is applied to the signal before processing.
*
* If the signal's length is not a power of 2 (which is required by the
* FFT algorithm) this function automatically pads it with zeros.
*
* @param signal the signal for which the FFT should be calculated
* @return the result of the FFT
*/
public Complex[] forwardFFT(double[] signal) {
FastFourierTransformer transformer = new FastFourierTransformer();
double[] windowedSignal = windowFunction.applyWindow(signal);
double[] paddedSignal = padWithZeroIfNecessary(windowedSignal);
return transformer.transform(paddedSignal);
}
/**
* Calculates the inverse FFT of the frequency domain representation
* of the signal. That is: converts the given data from frequency domain
* to the time domain.
* @param fft the frequency domain representation of the signal
* (e.g. the output of the {@link FourierTransform#forwardFFT(double[])}
* @return the time domain representation of the signal
*/
public double[] inverseFFT(Complex[] fft) {
FastFourierTransformer transformer = new FastFourierTransformer();
Complex[] complexSignal = transformer.inversetransform(fft);
double[] signal = new double[complexSignal.length];
for (int i = 0; i < complexSignal.length; i++) {
signal[i] = complexSignal[i].getReal();
}
return signal;
}
/**
* Returns a number that is greater or equal to the given and is
* power of 2.
* @param initialSize the value which is smaller or equal to the returned
* value
* @return a number that is a power of two and is not less that initialSize
*/
public static int getPowerOfTwoSize(int initialSize) {
double log_of_initialSize_to_base_2 = Math.log(initialSize) / Math.log(2);
return (int) Math.pow(2, Math.ceil(log_of_initialSize_to_base_2));
}
/**
* Pads the given signal so that its length is a power of two
* (or performs no padding if its length is already a power of two).
* @param signal the signal to be padded
* @return a padded with zeros version of the signal
*/
public static double[] padWithZeroIfNecessary(double[] signal) {
int powerOfTwoSize = getPowerOfTwoSize(signal.length);
if (powerOfTwoSize != signal.length) {
return ArrayOperations.padArrayWithZerosToSize(signal, powerOfTwoSize);
} else {
return signal;
}
}
/**
* Returns frequencies for elements returned by the {@link FourierTransform#forwardFFT(double[])}.
* @param signal
* @param samplingFrequency
* @return
*/
public static double[] getFrequencies(double[] signal, double samplingFrequency) {
int size = getPowerOfTwoSize(signal.length);
// the other half of frequencies are the same (mirrored)
double[] frequencies = new double[size / 2];
for (int i = 0; i < frequencies.length; i++) {
frequencies[i] = i * samplingFrequency / size;
}
return frequencies;
/*/double step = samplingFrequency / N;
double[] result = new double[N];
for (int i = 0; i < (N + 1) / 2; i++) {
result[i] = step * i;
}
for (int i = (N + 1) / 2; i < N; i++) {
result[i] = -(step * (N - i));
}
return result;*/
}
/**
* Calculates the power spectrum of the provided real sample
* data using FFT.
* @param samples the data (real numbers)
* @return estimates of the power spectrum
*/
public double[] calculatePowerSpectrum(double[] samples) {
Complex[] fftTransformedData = forwardFFT(samples);
int dataLength = samples.length;
int size = dataLength/2;
double[] powerSpectrum = new double[size];
powerSpectrum[0] = square(fftTransformedData[0].getReal()) + square(fftTransformedData[0].getImaginary());
for (int i = 1; i < size ; ++i) {
powerSpectrum[i] = square(fftTransformedData[i].getReal())
+ square(fftTransformedData[i].getImaginary())
+ square(fftTransformedData[dataLength - i].getReal())
+ square(fftTransformedData[dataLength-i].getImaginary());
}
for (int i = 1; i < size ; ++i) {
powerSpectrum[i] = 2.0D * powerSpectrum[i] / (windowFunction.getWindowWeightsSqueredSum()*dataLength);
}
return powerSpectrum;
}
protected double square(double x) {
return x*x;
}
}