view src/samer/units/FFT.java @ 8:5e3cbbf173aa tip

Reorganise some more
author samer
date Fri, 05 Apr 2019 22:41:58 +0100
parents bf79fb79ee13
children
line wrap: on
line source
/*
 *	Copyright (c) 2000, Samer Abdallah, King's College London.
 *	All rights reserved.
 *
 *	This software is provided AS iS and WITHOUT ANY WARRANTY; 
 *	without even the implied warranty of MERCHANTABILITY or 
 *	FITNESS FOR A PARTICULAR PURPOSE.
 */

package samer.units;
import  samer.maths.*; // uses ComplexVector and Vec

/**
  *	<h2>Fast Fourier Transform</h2>
  *	<h4>by Samer Abdallah</h4>
  *	<h4>15 December 1999</h4>
  *
  *	<p>
  *	Mostly from my old C++ version, though I should credit the following
  *	FFT code for the idea of using a lookup table for the bit reversal
  *   and also a complete lookup table of sines:
  *
  *	<pre>
  *	>	From the Unix Version 2.4 by Steve Sampson, Public Domain,
  *	>	September 1988.
  *	>	Adapted for Java by Ben Stoltz <stoltz@sun.com>, September 1997
  *	>
  *	>	Refer to http://www.neato.org/~ben/Fft.html for updates and related
  *	>	resources.
  *	</pre>
  *
  *	<p>
  *	My old version computed the bit-reversed indices on the fly - silly
  *	really - don't know why I didn't think of a lookup table myself.
  *	Also, it had a lookup table of sines and cosines of the form
  *	sin( 2*PI/k) with k going 1, 2, 4, 8 .. N. This version has complete
  *	tables for sin and cosine - which is a bit of a waste of memory,
  *	but frankly, who cares?
  *
  *	<h3>Usage</h3>
  *	<p>
  *	It is the user's responsibility to fill the arrays real
  *   and imag with data IN THE CORRECT BITREVERSED ORDER. This
  *	is easy enough using the bitrev lookup table - something like this
  *	would do the trick:
  *	<pre>
  *		for (int i=0; i<N; i++) {
  *			int k=fft.bitrev[i]
  *			fft.real[k] = theRealData[i];
  *			fft.imag[k] = 0;
  *		}				
  *	</pre>
  *	The arrays are used destructively, so it is also the user's 
  *	responsibility, eg, to reset the imaginary parts to zero even
  *	if they are always zero.
  *
  *	<p>
  *	The results ends up in the arrays real and imag
  */


public class FFT 
{
	public	int				bitrev[];	// lookup table
	public	double			H[];			// window - not yet used internally
	public	double			real[];			
	public	double			imag[];			

	protected	ComplexVector	W;				// sines and cosines lookup table
	protected	int				N;
	protected	double			zeros[];

	public FFT( int n)
	{
		N=n;
		real = new double[N];
		imag = new double[N];
		W = new ComplexVector(N);
		bitrev = new int[N];
		H = new double[N];

		// calculate bit reversal lookup table
		int m=N/2, j=0;
		for (int i=0; i<n; i++) {
			bitrev[i]=j;
			int b;
			for (b=m; (b>1) && (j>=b); b>>=1) j-=b;
			j+=b; 
		}

		// calculate sines and cosines lookup
		double th=2*Math.PI/N;
		for (int i=0; i<n; i++) {
			W.real[i] = Math.cos(i*th); 
			W.imag[i] = Math.sin(i*th); 
		}

		// table of zeros for fast initialisation
		zeros = new double[N];
		for (int i=0; i<N; i++) zeros[i]=0.0;
	}

	public int size() { return N; }
	public final void input( double Y[])
	{
		System.arraycopy(zeros,0,imag,0,N);
		for (int i=0; i<N; i++) {
			real[bitrev[i]] = H[i]*Y[i];
		}
	}				

	public final void brevcopy( double src[], double dst[] )
	{
		for (int i=0; i<src.length; i++) {
			dst[bitrev[i]] = src[i];
		}
	}				

	public final void input( Vec.Iterator it)
	{
		System.arraycopy(zeros,0,imag,0,N);
		for (int i=0; i<N; i++) {
			real[bitrev[i]] = H[i]*it.next();
		}
	}				

	public final void outputPower( double S[])
	{
		// extract power
		for (int i=0; i<S.length; i++) {
			S[i] = real[i]*real[i] + imag[i]*imag[i];
		}
	}

	public final void calculate()
	{
		double	a1,a2,b1,b2;
		int		step, jump, i, k;
		int		m=N/2, p;
		double	wReal, wImag;

		// these are cached for performance -
		// the COMPILER ought to be doing this!
		double	Real[] = real;
		double	Imag[] = imag;
		double	wr[] = W.real;
		double	wi[] = W.imag;

		step=1; jump=2;
		while (step<N) {

			p = 0; 
			for (k=0; k<step; k++) {

				// look up exp( (2*PI*i/N)*p );
				wReal = wr[p]; 
				wImag = wi[p]; 
				p+=m;

				for (i=k; i<N; i+=jump) {

					int j=i+step;

					// complex butterfly between i and j
					a1 = Real[i];
					a2 = Imag[i];


					b1 = wReal*Real[j] - wImag*Imag[j];
					b2 = wImag*Real[j] + wReal*Imag[j];

					Real[i] = a1 + b1;
					Imag[i] = a2 + b2;
					Real[j] = a1 - b1;
					Imag[j] = a2 - b2;
				}
			}
			step=jump; jump<<=1; m>>=1;
		}
	}

	// make everything conjugate - does inverse fft
	public void invert()	{
		for (int i=0; i<W.length; i++) W.imag[i] = -W.imag[i];
	}

	// useful window functions
	public void setWindow(Function fn) {
		for (int i=0; i<N; i++) H[i] = (double)i/N;
		fn.apply(H); Mathx.mul(H,1/Math.sqrt(N));
	}
}