/* * Copyright (c) 2002-2006 Samit Basu * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "Core.hpp" #include "Exception.hpp" #include "Array.hpp" #include "Malloc.hpp" #include #if HAVE_FFTW | HAVE_FFTWF #include "fftw3.h" #endif #if HAVE_FFTWF static fftwf_complex *inf, *outf; static fftwf_plan pf_forward; static fftwf_plan pf_backward; #endif #if HAVE_FFTW static int cN = 0; static fftw_complex *in, *out; static fftw_plan p_forward; static fftw_plan p_backward; static int zN = 0; #endif void complex_fft_init(int Narg) { #if HAVE_FFTWF if (cN == Narg) return; if (cN != 0) { fftwf_destroy_plan(pf_forward); fftwf_destroy_plan(pf_backward); fftwf_free(inf); fftwf_free(outf); } inf = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex)*Narg); outf = (fftwf_complex*) fftwf_malloc(sizeof(fftwf_complex)*Narg); pf_forward = fftwf_plan_dft_1d(Narg,inf,outf,FFTW_FORWARD,FFTW_ESTIMATE); pf_backward = fftwf_plan_dft_1d(Narg,inf,outf,FFTW_BACKWARD,FFTW_ESTIMATE); cN = Narg; #else throw Exception("Single precision FFT support not available. Please build the single precision version of FFTW and rebuild FreeMat to enable this functionality."); #endif } void complex_fft_forward(int Narg, float *dp) { #if HAVE_FFTWF if (cN != Narg) complex_fft_init(Narg); memcpy(inf,dp,sizeof(float)*Narg*2); fftwf_execute(pf_forward); memcpy(dp,outf,sizeof(float)*Narg*2); #else throw Exception("Single precision FFT support not available. Please build the single precision version of FFTW and rebuild FreeMat to enable this functionality."); #endif } void complex_fft_backward(int Narg, float *dp) { #if HAVE_FFTWF if (cN != Narg) complex_fft_init(Narg); memcpy(inf,dp,sizeof(float)*Narg*2); fftwf_execute(pf_backward); memcpy(dp,outf,sizeof(float)*Narg*2); for (int i=0;i<(2*cN);i++) dp[i] /= ((float) Narg); #else throw Exception("Single precision FFT support not available. Please build the single precision version of FFTW and rebuild FreeMat to enable this functionality."); #endif } void dcomplex_fft_init(int Narg) { #if HAVE_FFTW if (zN == Narg) return; if (zN != 0) { fftw_destroy_plan(p_forward); fftw_destroy_plan(p_backward); fftw_free(in); fftw_free(out); } in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*Narg); out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*Narg); p_forward = fftw_plan_dft_1d(Narg,in,out,FFTW_FORWARD,FFTW_ESTIMATE); p_backward = fftw_plan_dft_1d(Narg,in,out,FFTW_BACKWARD,FFTW_ESTIMATE); zN = Narg; #else throw Exception("Double precision FFT support not available. Please build the double precision version of FFTW and rebuild FreeMat to enable this functionality."); #endif } void dcomplex_fft_forward(int Narg, double *dp) { #if HAVE_FFTW if (zN != Narg) dcomplex_fft_init(Narg); memcpy(in,dp,sizeof(double)*Narg*2); fftw_execute(p_forward); memcpy(dp,out,sizeof(double)*Narg*2); #else throw Exception("Double precision FFT support not available. Please build the double precision version of FFTW and rebuild FreeMat to enable this functionality."); #endif } void dcomplex_fft_backward(int Narg, double *dp) { #if HAVE_FFTW if (zN != Narg) dcomplex_fft_init(Narg); memcpy(in,dp,sizeof(double)*Narg*2); fftw_execute(p_backward); memcpy(dp,out,sizeof(double)*Narg*2); for (int i=0;i<(2*zN);i++) dp[i] /= ((double) Narg); #else throw Exception("Double precision FFT support not available. Please build the double precision version of FFTW and rebuild FreeMat to enable this functionality."); #endif } Array complexFFTFunction(const Array& input, int FFTLen, int FFTDim, bool inverse) { Dimensions inDim(input.dimensions()); // Allocate the output vector... Dimensions outDim(inDim); outDim.set(FFTDim,FFTLen); // Calculate the stride... int d; int planecount; int planesize; int linesize; linesize = inDim.get(FFTDim); planesize = 1; for (d=0;d //The resulting plot is: //@figure fft1 // //The FFT can also be taken along different dimensions, and with padding //and/or truncation. The following example demonstrates the Fourier //Transform being computed along each column, and then along each row. //@< //A = [2,5;3,6] //real(fft(A,[],1)) //real(fft(A,[],2)) //@> //Fourier transforms can also be padded using the @|n| argument. This //pads the signal with zeros prior to taking the Fourier transform. Zero //padding in the time domain results in frequency interpolation. The //following example demonstrates the FFT of a pulse (consisting of 10 ones) //with (red line) and without (green circles) padding. //@< //delta(1:10) = 1; //plot((0:255)/256*pi*2,real(fft(delta,256)),'r-'); //hold on //plot((0:9)/10*pi*2,real(fft(delta)),'go'); //mprint('fft2'); //@> //The resulting plot is: //@figure fft2 //! ArrayVector FFTFunction(int nargout, const ArrayVector& arg) { // Get the data argument if (arg.size() < 1) throw Exception("FFT requires at least one argument"); Array input(arg[0]); Class argType(input.dataClass()); if (argType <= FM_COMPLEX && argType != FM_DOUBLE) input.promoteType(FM_COMPLEX); else input.promoteType(FM_DCOMPLEX); // Get the length of the zero pad int FFTLength = -1; if ((arg.size() > 1) && (!arg[1].isEmpty())) { Array FLen(arg[1]); FFTLength = FLen.getContentsAsIntegerScalar(); if (FFTLength <= 0) throw Exception("Length argument to FFT should be positive"); } // Get the dimension to FFT along. int FFTDim = -1; if (arg.size() > 2) { Array FDim(arg[2]); FFTDim = FDim.getContentsAsIntegerScalar() - 1; if (FFTDim < 0) throw Exception("Dimension argument to FFT should be positive"); } if (input.isScalar() || input.isEmpty()) { ArrayVector retval; retval.push_back(arg[0]); return retval; } // Was the dimension specified? If not, search for it... if (FFTDim == -1) { Dimensions inDim(input.dimensions()); int d = 0; while (inDim.get(d) == 1) d++; FFTDim = d; } if (FFTLength == -1) FFTLength = input.getDimensionLength(FFTDim); // Handle the fft based on the case... ArrayVector retval; if (input.dataClass() == FM_COMPLEX) retval.push_back(complexFFTFunction(input,FFTLength,FFTDim,false)); else retval.push_back(dcomplexFFTFunction(input,FFTLength,FFTDim,false)); return retval; } ArrayVector IFFTFunction(int nargout, const ArrayVector& arg) { // Get the data argument if (arg.size() < 1) throw Exception("IFFT requires at least one argument"); Array input(arg[0]); Class argType(input.dataClass()); if (argType <= FM_COMPLEX && argType != FM_DOUBLE) input.promoteType(FM_COMPLEX); else input.promoteType(FM_DCOMPLEX); // Get the length of the zero pad int FFTLength = -1; if ((arg.size() > 1) && (!arg[1].isEmpty())) { Array FLen(arg[1]); FFTLength = FLen.getContentsAsIntegerScalar(); if (FFTLength <= 0) throw Exception("Length argument to IFFT should be positive"); } // Get the dimension to FFT along. int FFTDim = -1; if (arg.size() > 2) { Array FDim(arg[2]); FFTDim = FDim.getContentsAsIntegerScalar() - 1; if (FFTDim < 0) throw Exception("Dimension argument to IFFT should be positive"); } if (input.isScalar() || input.isEmpty()) { ArrayVector retval; retval.push_back(arg[0]); return retval; } // Was the dimension specified? If not, search for it... if (FFTDim == -1) { Dimensions inDim(input.dimensions()); int d = 0; while (inDim.get(d) == 1) d++; FFTDim = d; } if (FFTLength == -1) FFTLength = input.getDimensionLength(FFTDim); // Handle the fft based on the case... ArrayVector retval; if (input.dataClass() == FM_COMPLEX) retval.push_back(complexFFTFunction(input,FFTLength,FFTDim,true)); else retval.push_back(dcomplexFFTFunction(input,FFTLength,FFTDim,true)); return retval; }