Search code examples
javaapachemathfftjfreechart

How to use complex coefficient in apache FFT


I'm working with a basic example of FFT with Apache Commons library. I have two classes:

   public class Fourier {
    private static XYSeries data;
    private static XYSeriesCollection collection;

    Fourier(){
        collection =new XYSeriesCollection();
        createSquare();
        createFourier();
        showGraph();
    }

    private static void createSquare(){
        data=new XYSeries("Dati");
        for(double i=-8;i<8;i+=1d/128){
            data.add(i,((i<-4||(i<4&&i>0)?1:0)));
            //data.add(i,(i<0?i+1:-i+1));
        }
        collection.addSeries(data);
    }

    private static void createFourier(){
        double[] arrayFourier= new double[data.getItemCount()];
        for(int i=0;i<data.getItemCount();i++){
            arrayFourier[i]=data.getDataItem(i).getYValue();
        }
        FastFourierTransformer transformer=new FastFourierTransformer(DftNormalization.STANDARD);
        Complex[] coeff=transformer.transform(arrayFourier, TransformType.INVERSE);
        double norm = 0;
        for(Complex Z: coeff){
            System.out.println(Z.abs()+"\t"+Z.toString());
            norm+=(Z.abs())*(Z.abs());
        }
        System.out.println(norm);

        XYSeries fourier=new XYSeries("Fourier");
        FourierSeries series=new FourierSeries(coeff,8);
        for(double i=data.getMinX();i<data.getMaxX();i+=0.05){
            fourier.add(i,series.getSeries(i));
        }
        collection.addSeries(fourier);

    }

    private static void showGraph(){
        JFreeChart chart = ChartFactory.createXYLineChart("Fourier", "x", "f(x)", collection, PlotOrientation.VERTICAL, true, false, false);
        ChartFrame window=new ChartFrame("Fourier", chart, false);
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.pack();
        window.setVisible(true);
    }

    public static void main(String[] args) {
        Thread t=new Thread(new Runnable() {

            @Override
            public void run() {
                new Fourier();
                try {
                    Thread.sleep(60000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.exit(0);
            }
        });
        t.start();
    }

}

and another one

public class FourierSeries {
    Complex[] coeff;
    double T;

    public FourierSeries(Complex[] coeff, double T) {
        this.coeff=coeff;
        this.T=T;
    }

    public double getSeries(double x){
        double k=Math.PI/T;
        double value=0; //primo coefficiente
        int i=0;
        /*for(Complex iter:coeff){
            if (i!=0&&i<coeff.length/2.) {
                if (i % 2 == 0) {
                    value += iter.abs() * Math.cos(k * i * x);
                    System.out.println(iter.abs()+"cos"+i);
                } else {
                    value += iter.abs() * Math.sin(k * i * x); //npari (i dispari) modulo*cos()
                    System.out.println(iter.abs()+"sin"+i);
                }
            }
            i++;
        }*/
        for (Complex iter : coeff) {
            if(i<coeff.length/2.)   
            value+=iter.getReal()*Math.cos(x*k*i)+iter.getImaginary()*Math.sin(x*k*i);
        i++;
        }
        return value;
    }
}

I introduce in my createSquare() method a function (square wave).

In order I do the following actions:

I perform the FFT.

Can you explain me how the array returned by transform.transform(array, TransformType) is composed?

Why the Fourier series is not correctly drawn?

The inverse series is correctly a square wave with same frequency as initial wave, but not correctly scaled on y-axsis. What is wrong? This is the output: This is the output


Solution

  • In transforming the complex form of the Fourier series to the real form, you have to treat the constant term differently. Or in the case of the code in question, you have to correct the non-constant coefficients by a factor of 2. So that you get 0.5 +- 0.5 as result, not 0.5 +- 0.25 as is currently the case.

    You are using the inverse transform, where the resulting coefficients can then be interpreted as

    f(x) approx sum(m = -N/2+1, N/2-1) c[k]*exp(-i*m*k*x)
    

    For index m>=1 you combine two complex conjugate pairs of terms, the resulting real terms are, using c[k]=a+ib,

    (a+i*b)*exp(-i*m*k*x) + (a-i*b)*exp(+i*m*k*x) 
    
    = 2*a*cos(m*k*x) + 2*b*sin(m*k*x)
    

    Your Fourier series computation should thus look like

        value = coeff[0].getReal();
        for (int m=1; m<coeff.length/2; m++) {
            value += 2*coeff[m].getReal()*Math.cos(x*k*m) 
                   + 2*coeff[m].getImaginary()*Math.sin(x*k*m);
        }