/* -*-C-*- $Id: fft.c,v 9.33 1999/01/02 06:11:34 cph Exp $ Copyright (c) 1987-1999 Massachusetts Institute of Technology This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Time-Frequency Transforms (pas) */ #include "scheme.h" #include "prims.h" #include "zones.h" #include #include "array.h" #include "image.h" /* SUMMARY - pas_cft (complex data, DIF, split-radix) - pas_rft (real data, DIT, split-radix) output is conjugate-symmetric - pas_csft (cs data, DIF, split-radix) output is real - pas_cft - pas_rft2d - pas_csft2d Stuff before 4-15-1989 - C_Array_FFT (complex data, radix=2, NOT-in-place) - CZT (chirp-z-transform) uses the old cft (hence slow). - 2d DFT */ /* The DFT is as defined in Siebert 6003 book, i.e. forward DFT = Negative exponent and division by N backward DFT = Positive exponent (note Seibert forward DFT is Oppenheim backward DFT) */ /* mathematical constants */ #ifdef PI #undef PI #endif #define PI 3.141592653589793238462643 #define TWOPI 6.283185307179586476925287 #define SQRT_2 1.4142135623730950488 #define ONE_OVER_SQRT_2 .7071067811865475244 /* Abramowitz and Stegun */ DEFINE_PRIMITIVE ("PAS-CFT!", Prim_pas_cft, 5, 5, 0) { long i, length, power, flag; REAL *f1,*f2, *wcos,*w3cos,*w3sin; void pas_cft(); PRIMITIVE_HEADER (5); CHECK_ARG (2, ARRAY_P); /* real part */ CHECK_ARG (3, ARRAY_P); /* imag part */ CHECK_ARG (4, ARRAY_P); /* twiddle tables, total length = 3*(length/4) */ CHECK_ARG (5, FIXNUM_P); /* (1)=tables precomputed, else recompute */ flag = (arg_integer (1)); length = ARRAY_LENGTH(ARG_REF(2)); if (length != (ARRAY_LENGTH(ARG_REF(3)))) error_bad_range_arg(2); for (power=0, i=length; i>1; power++) { if ( (i % 2) == 1) error_bad_range_arg(2); i=i/2; } f1 = ARRAY_CONTENTS(ARG_REF(2)); f2 = ARRAY_CONTENTS(ARG_REF(3)); if (f1==f2) error_wrong_type_arg(2); wcos = ARRAY_CONTENTS(ARG_REF(4)); /* twiddle tables */ if (ARRAY_LENGTH(ARG_REF(4)) != (3*length/4)) error_bad_range_arg(4); w3cos = wcos + length/4; w3sin = w3cos + length/4; if ((arg_nonnegative_integer(5)) == 1) pas_cft(1, flag, f1,f2, length, power, wcos,w3cos,w3sin); else pas_cft(0, flag, f1,f2, length, power, wcos,w3cos,w3sin); /* 1 means tables are already made 0 means compute new tables */ PRIMITIVE_RETURN (UNSPECIFIC); } DEFINE_PRIMITIVE ("PAS-CFT-MAKE-TWIDDLE-TABLES!", Prim_pas_cft_make_twiddle_tables, 2, 2, 0) { long length, power, i; REAL *wcos,*w3cos,*w3sin; void pas_cft_make_twiddle_tables_once(); PRIMITIVE_HEADER (2); length = arg_nonnegative_integer(1); /* length of cft that we intend to compute */ CHECK_ARG (2, ARRAY_P); /* storage for twiddle tables */ if (ARRAY_LENGTH(ARG_REF(2)) != (3*length/4)) error_bad_range_arg(2); power=0; for (power=0, i=length; i>1; power++) { if ( (i % 2) == 1) error_bad_range_arg(1); i=i/2; } wcos = ARRAY_CONTENTS(ARG_REF(2)); /* twiddle tables */ w3cos = wcos + length/4; w3sin = w3cos + length/4; pas_cft_make_twiddle_tables_once(length, power, wcos,w3cos,w3sin); PRIMITIVE_RETURN (UNSPECIFIC); } /* C COMPLEX FOURIER TRANSFORM (Split-Radix, Decimation-in-frequency) C (adapted and optimized from Sorensen,et.al. ASSP-34 no.1 page 152, February 1986) */ /* Twiddle Tables for PAS_CFT; (tables for forward transform only) Inverse transform === forward CFT (without 1/N scaling) followed by time-reversal. / The tables contain (2pi/N)*i for i=0,1,2,..,N/4 (except i=0 is ignored, never used) / Table for wsin[i] is not needed because wsin[i]=wcos[n4-i]. Table for w3sin[i] is needed however. The previous relationship does not work for w3sin. */ /* There are two routines for making twiddle tables: a fast one, and a slower one but more precise. The differences in speed and accuracy are actually rather small, but anyway. Use the slow one for making permanent tables. */ void pas_cft_make_twiddle_tables (n,m, wcos,w3cos,w3sin) /* efficient version */ REAL *wcos, *w3cos, *w3sin; long n,m; { long i, n4; double tm; REAL costm,sintm; n4 = n/4; for (i=1; i= 4 */ REAL *x,*y, *wcos,*w3cos,*w3sin; long n,m; { /* REAL a,a3,e; no need anymore, use tables */ REAL r1,r2,s1,s2,s3, xt, cc1,cc3,ss1,ss3; long n1,n2,n4, i,j,k, is,id, i0,i1,i2,i3; long windex0, windex, windex_n4; /* indices for twiddle tables */ /********** fortran indices start from 1,... **/ x = x-1; /* TRICK---- x(0) is now illegal, but x(1) and x(n) are valid */ y = y-1; /********** fortran indices start from 1,... **/ /* c */ /* c-----first M-1 stages of transform */ /* c */ windex_n4 = n/4; /* need for indexing sin via wcos twiddle table */ n2 = 2*n; for (k=1; k>1; /* n2 = n2/2; */ n4 = n2>>2; /* n4 = n2/4; */ /* e = 6.283185307179586476925287 / ((REAL) n2); no need anymore, use tables */ /* a = 0.0; */ { /* j=1; */ /* The first iteration in the loop "DO 20 J = 1, N4" is done specially to save operations involving sin=0, cos=1 */ /* a = j*e; no need anymore, use tables */ is = 1; /* is = j; */ id = 2*n2; label40first: for (i0=is; i0= j) goto label101; /* if (i .ge. j) goto 101 */ xt = x[j]; x[j] = x[i]; x[i] = xt; xt = y[j]; y[j] = y[i]; y[i] = xt; label101: k = n>>1; /* k = n/2; */ label102: if (k>=j) goto label103; j = j - k; k = k>>1; /* k = k/2; */ goto label102; label103: j = j + k; } /* 104 CONTINUE */ /* c-------------------------------------*/ /* c */ } /* RETURN ^M END */ DEFINE_PRIMITIVE ("PAS-RFT-CSFT!", Prim_pas_rft_csft, 5, 5, 0) { long i, length, power, flag, ft_type; REAL *f1, *wcos,*w3cos,*w3sin; void pas_rft(), pas_csft(); PRIMITIVE_HEADER (5); CHECK_ARG (2, ARRAY_P); /* Input data (real or cs) */ CHECK_ARG (3, ARRAY_P); /* Twiddle tables, total length = 4*(length/8) */ CHECK_ARG (4, FIXNUM_P); /* (1)=tables precomputed, else recompute */ CHECK_ARG (5, FIXNUM_P); /* ft_type = 1 or 3 1 means compute rft, 3 means compute csft */ flag = (arg_integer (1)); f1 = ARRAY_CONTENTS(ARG_REF(2)); length = ARRAY_LENGTH(ARG_REF(2)); for (power=0, i=length; i>1; power++) { if ( (i % 2) == 1) error_bad_range_arg(2); i=i/2; } wcos = ARRAY_CONTENTS(ARG_REF(3)); /* twiddle tables */ if (ARRAY_LENGTH(ARG_REF(3)) != (4*length/8)) error_bad_range_arg(3); w3cos = wcos + (length/4); w3sin = w3cos + (length/8); ft_type = (arg_nonnegative_integer(5)); /* rft or csft */ if (ft_type == 1) { if ((arg_nonnegative_integer(4)) == 1) pas_rft (1, flag, f1, length, power, wcos,w3cos,w3sin); else pas_rft (0, flag, f1, length, power, wcos,w3cos,w3sin); } else if (ft_type == 3) { if ((arg_nonnegative_integer(4)) == 1) pas_csft (1, flag, f1, length, power, wcos,w3cos,w3sin); else pas_csft (0, flag, f1, length, power, wcos,w3cos,w3sin); /* 1 means tables are already made 0 means compute new tables */ } else error_bad_range_arg(5); PRIMITIVE_RETURN (UNSPECIFIC); } DEFINE_PRIMITIVE ("PAS-REALDATA-MAKE-TWIDDLE-TABLES!", Prim_pas_realdata_make_twiddle_tables, 2, 2, 0) { long length, power, i; REAL *wcos,*w3cos,*w3sin; void pas_realdata_make_twiddle_tables_once(); PRIMITIVE_HEADER (2); length = arg_nonnegative_integer(1); /* length of rft that we intend to compute */ CHECK_ARG (2, ARRAY_P); /* storage for twiddle tables */ if (ARRAY_LENGTH(ARG_REF(2)) != (4*length/8)) error_bad_range_arg(2); power=0; for (power=0, i=length; i>1; power++) { if ( (i % 2) == 1) error_bad_range_arg(1); i=i/2; } wcos = ARRAY_CONTENTS(ARG_REF(2)); /* twiddle tables */ w3cos = wcos + length/4; w3sin = w3cos + length/8; pas_realdata_make_twiddle_tables_once(length, power, wcos,w3cos,w3sin); PRIMITIVE_RETURN (UNSPECIFIC); } /* C REAL FOURIER TRANSFORM (Split-Radix, Decimation-in-time) C (adapted from Sorensen,et.al. ASSP-35 no.6 page 849, October 1986) C C the output is [Re(0),Re(1),...,Re(n/2), Im(n/2-1),...,Im(1)] */ /* Twiddle Tables for PAS_RFT and PAS_CSFT are identical. -> pas_realdata_make_twiddle_tables (but they are indexed differently in each case) / The tables contain (2pi/N)*i where i=0,1,2,..,N/4 for wcos and (2pi/N)*i where i=0,1,2,..,N/8 for w3cos w3sin (except i=0 is ignored, never used) / Table for wsin[i] is not needed because wsin[i]=wcos[n4-i]. Table for w3sin[i] is needed however. The previous relationship does not work for w3sin. / Instead of getting sin() from a wsin[i] table with i=1,..,N/8 we get it from wcos[n4-i]. This way we can use a CFT table which goes up to N/4 for RFT CSFT also. We do so in image-processing (rft2d-csft2d). */ /* There are two routines for making twiddle tables: a fast one, and a slower one but more precise. The differences in speed and accuracy are actually rather small, but anyway. Use the slow one for making tables that stay around. */ void pas_realdata_make_twiddle_tables(n,m, wcos,w3cos,w3sin) /* efficient version */ REAL *wcos, *w3cos, *w3sin; long n,m; { long i, n4, n8; double tm; REAL costm,sintm; n4 = n/4; n8 = n/8; for (i=1; i= j) goto label101; /* if (i .ge. j) goto 101 */ xt = x[j]; x[j] = x[i]; x[i] = xt; label101: k = n>>1; /* k = n/2; */ label102: if (k>=j) goto label103; j = j - k; k = k>>1; /* k = k/2; */ goto label102; label103: j = j + k; } /* 104 CONTINUE */ /* c-------------------------------------*/ /* c */ /* c ----length-two-butterflies----------- */ is = 1; id = 4; label70: for (i0=is; i0<=n; i0=i0+id) /* 70 DO 60 I0 = IS,N,ID */ { i1 = i0 + 1; r1 = x[i0]; x[i0] = r1 + x[i1]; x[i1] = r1 - x[i1]; } /* 60 CONTINUE */ is = 2*id - 1; id = 4*id; if (is < n) goto label70; /* IF (IS.LT.N) GOTO 70 */ /* C C -------L-shaped-butterflies-------- */ n2 = 2; for (k=2; k<=m; k++) /* DO 10 K = 2,M */ { n2 = n2 * 2; n4 = n2>>2; /* n4 = n2/4; */ n8 = n2>>3; /* n8 = n2/8; */ /* e = 6.283185307179586476925287 / ((REAL) n2); no need anymore, use tables */ is = 0; id = n2 * 2; label40: for (i=is; i>1; /* n2 = n2/2; */ n4 = n2>>2; /* n4 = n2/4; */ n8 = n4>>1; /* n8 = n4/2; */ /* e = 6.283185307179586476925287 / ((REAL) n2); no need anymore, use tables */ label17: for (i=is; i= j) goto label101; /* if (i .ge. j) goto 101 */ xt = x[j]; x[j] = x[i]; x[i] = xt; label101: k = n>>1; /* k = n/2; */ label102: if (k>=j) goto label103; j = j - k; k = k>>1; /* k = k/2; */ goto label102; label103: j = j + k; } /* 104 CONTINUE */ /* c */ } /* RETURN ^M END */ /* Image processing only for square images (old stuff handles non-square but is slow) For 2d FTs precomputed tables or not, make almost no difference in total time. */ DEFINE_PRIMITIVE ("PAS-CFT2D!", Prim_pas_cft2d, 5,5, 0) { long i, length, power, flag, rows,rowpower; REAL *f1,*f2, *wcos,*w3cos,*w3sin; void pas_cft2d(); PRIMITIVE_HEADER (5); CHECK_ARG (2, ARRAY_P); /* real part */ CHECK_ARG (3, ARRAY_P); /* imag part */ CHECK_ARG (4, ARRAY_P); /* twiddle tables, length = 3*(rows/4) */ flag = (arg_integer (1)); length = ARRAY_LENGTH(ARG_REF(2)); if (length != (ARRAY_LENGTH(ARG_REF(3)))) error_bad_range_arg(2); for (power=0, i=length; i>1; power++) /* length must be power of 2 */ { if ( (i % 2) == 1) error_bad_range_arg(2); i=i/2; } if ((power % 2) == 1) error_bad_range_arg(2); rowpower = (power/2); rows = (1<1; power++) /* length must be power of 2 */ { if ( (i % 2) == 1) error_bad_range_arg(2); i=i/2; } if ((power % 2) == 1) error_bad_range_arg(2); rowpower = (power/2); rows = (1<>1, a; long i, l, m; REAL tm; a = n; /* initially equal to length of data */ for (m=0; m do one more mult */ mult(g1,g2,f1,f2,w1,w2); /* f1 and f2 contain the result now */ for (m=0; m do one more mult */ mult(g1,g2,f1,f2,w1,w2); /* f1 and f2 contain the result now */ } } void C_Array_FFT_With_Given_Tables(flag, power, n, f1, f2, g1,g2,w1,w2) long flag, power, n; REAL f1[], f2[], g1[], g2[], w1[], w2[]; { long n2=n>>1, a; long i, l, m; REAL tm; a = n; /* initially equal to length */ for (m=0; m do one more mult */ mult(g1,g2,f1,f2,w1,w2); /* f1 and f2 contain the result now */ for (m=0; m do one more mult */ mult(g1,g2,f1,f2,w1,w2); /* f1 and f2 contain the result now */ } } /* CHIRP-Z-TRANSFORM (complex data) */ /* C_Array_CZT Generalization of DFT ;; Frequency is scaled as an L/2-point DFT of the input data (zero padded to L/2). ;; phi = starting point (on unit circle) -- Range 0,1 (covers 0,2pi like DFT angle) rho = resolution (angular frequency spacing) -- Range 0,1 (maps 0,2pi like DFT angle) N = input data length M = output data length log2_L = smallest_power_of_2_ge(N+M-1) ---- f1,f2 contain the input data (complex). f1,f2,fo1,fo2,g1,g2 must be of length L fft_w1,fft_w2 must be of length L/2 czt_w1,czt_w2 must be of length max(M,N) ---- ;; RESULT is left on f1,f2 (M complex numbers). */ C_Array_CZT(phi,rho, N,M,log2_L, f1,f2,fo1,fo2, g1,g2, fft_w1,fft_w2,czt_w1,czt_w2) double phi, rho; REAL *f1,*f2,*fo1,*fo2, *g1,*g2, *fft_w1,*fft_w2,*czt_w1,*czt_w2; long N,M,log2_L; { long i, maxMN, L, L2; void Make_CZT_Tables(), CZT_Pre_Multiply(), Make_Chirp_Filter(); void Make_FFT_Tables(), C_Array_FFT_With_Given_Tables(), C_Array_Complex_Multiply_Into_First_One(); maxMN = max(M,N); L = 1<1; ncols_power++) { /* FIND/CHECK POWERS OF ROWS,COLS */ if ( (i % 2) == 1) error_bad_range_arg (2); i=i/2; } for (nrows_power=0, i=nrows; i>1; nrows_power++) { if ( (i % 2) == 1) error_bad_range_arg (1); i=i/2; } #if (REAL_IS_DEFINED_DOUBLE != 0) ALIGN_FLOAT (Free); Free += 1; #endif Primitive_GC_If_Needed(Length*REAL_SIZE + ((max(nrows,ncols))*3*REAL_SIZE)); Work_Here = (REAL *) Free; g1 = Work_Here; g2 = Work_Here + ncols; w1 = Work_Here + (ncols<<1); w2 = Work_Here + (ncols<<1) + (ncols>>1); Make_FFT_Tables(w1,w2,ncols, flag); for (i=0;i>1); Make_FFT_Tables(w1,w2,nrows,flag); for (i=0;i1; nrows_power++) { /* FIND/CHECK POWERS OF ROWS */ if ( (i % 2) == 1) error_bad_range_arg (2); i=i/2; } #if (REAL_IS_DEFINED_DOUBLE != 0) ALIGN_FLOAT (Free); Free += 1; #endif Primitive_GC_If_Needed(nrows*3*REAL_SIZE); Work_Here = (REAL *) Free; g1 = Work_Here; g2 = Work_Here + nrows; w1 = Work_Here + (nrows<<1); w2 = Work_Here + (nrows<<1) + (nrows>>1); Make_FFT_Tables(w1, w2, nrows, flag); /* MAKE TABLES */ for (i=0;i1; ndeps_power++) { /* FIND/CHECK POWERS OF DEPS,ROWS,COLS */ if ( (l % 2) == 1) error_bad_range_arg (2); l=l/2; } for (nrows_power=0, m=nrows; m>1; nrows_power++) { if ( (m % 2) == 1) error_bad_range_arg (2); m=m/2; } for (ncols_power=0, n=ncols; n>1; ncols_power++) { if ( (n % 2) == 1) error_bad_range_arg (2); n=n/2; } printf("3D FFT implemented only for cubic-spaces.\n"); printf("aborted\n."); } } Cube_Space_3D_FFT_In_Scheme_Heap(flag, ndeps, Real_Array, Imag_Array) long flag, ndeps; REAL *Real_Array, *Imag_Array; { fast long l, m, n; fast long ndeps_power, Surface_Length; fast REAL *From_Real, *From_Imag; fast REAL *f1,*f2,*g1,*g2,*w1,*w2, *Work_Here; for (ndeps_power=0, l=ndeps; l>1; ndeps_power++) { /* FIND/CHECK POWER OF NDEPS */ if ( (l % 2) == 1) error_bad_range_arg (2); l=l/2; } #if (REAL_IS_DEFINED_DOUBLE != 0) ALIGN_FLOAT (Free); Free += 1; #endif Primitive_GC_If_Needed(ndeps*3*REAL_SIZE); Work_Here = (REAL *) Free; g1 = Work_Here; g2 = Work_Here + ndeps; w1 = Work_Here + (ndeps<<1); w2 = Work_Here + (ndeps<<1) + (ndeps>>1); Make_FFT_Tables(w1, w2, ndeps, flag); /* MAKE TABLES */ Surface_Length=ndeps*ndeps; From_Real = Real_Array; From_Imag = Imag_Array; for (l=0; l forward FFT, otherwise backward. */ DEFINE_PRIMITIVE ("ARRAY-FFT!", Prim_array_fft, 3, 3, 0) { long length, power, flag, i; SCHEME_OBJECT answer; REAL *f1,*f2,*g1,*g2,*w1,*w2; REAL *Work_Here; PRIMITIVE_HEADER (4); flag = arg_integer(1); /* forward or backward */ CHECK_ARG (2, ARRAY_P); /* input real */ CHECK_ARG (3, ARRAY_P); /* input imag */ length = ARRAY_LENGTH(ARG_REF(2)); if (length != (ARRAY_LENGTH(ARG_REF(3)))) error_bad_range_arg(2); for (power=0, i=length; i>1; power++) { if ( (i % 2) == 1) error_bad_range_arg(2); i=i/2; } f1 = ARRAY_CONTENTS(ARG_REF(2)); f2 = ARRAY_CONTENTS(ARG_REF(3)); if (f1==f2) error_wrong_type_arg(2); #if (REAL_IS_DEFINED_DOUBLE != 0) ALIGN_FLOAT (Free); Free += 1; #endif Primitive_GC_If_Needed(length*3*REAL_SIZE); Work_Here = (REAL *) Free; g1 = Work_Here; g2 = Work_Here + length; w1 = Work_Here + (length<<1); w2 = Work_Here + (length<<1) + (length>>1); C_Array_FFT(flag, power, length, f1,f2,g1,g2,w1,w2); PRIMITIVE_RETURN (UNSPECIFIC); } DEFINE_PRIMITIVE ("ARRAY-CZT!", Prim_array_czt, 6,6, 0) { double phi,rho; long N,M,L, i; long log2_L, maxMN; long smallest_power_of_2_ge(); int errcode; REAL *a,*b,*c,*d; REAL *f1,*f2,*fo1,*fo2, *g1,*g2, *fft_w1,*fft_w2,*czt_w1,*czt_w2, *Work_Here; PRIMITIVE_HEADER (6); phi = (arg_real_number (1)); /* starting point [0,1]*/ rho = (arg_real_number (2)); /* resolution [0,1] */ CHECK_ARG (3, ARRAY_P); /* input real */ CHECK_ARG (4, ARRAY_P); /* input imag */ CHECK_ARG (5, ARRAY_P); /* output real */ CHECK_ARG (6, ARRAY_P); /* output imag */ a = ARRAY_CONTENTS(ARG_REF(3)); b = ARRAY_CONTENTS(ARG_REF(4)); c = ARRAY_CONTENTS(ARG_REF(5)); d = ARRAY_CONTENTS(ARG_REF(6)); N = ARRAY_LENGTH(ARG_REF(3)); /* N = input length */ M = ARRAY_LENGTH(ARG_REF(5)); /* M = output length */ if (N!=(ARRAY_LENGTH(ARG_REF(4)))) error_bad_range_arg(3); if (M!=(ARRAY_LENGTH(ARG_REF(6)))) error_bad_range_arg(5); if ((M+N-1) < 4) error_bad_range_arg(5); log2_L = smallest_power_of_2_ge(M+N-1); L = 1<