FFT Programming (Magnitude and Phase), Please Help

Hi everyone,

I've been using Igor Pro for about five years now, however I have only just ventured into the art of programming. Currently I am attempting to modify a prexisting experiment that performs FFT on interferometric fringe patterns. The original function listed below works well but only outputs the Magnitude of the Fourier Transform:



Function RealTimeFFT2(w,windowing,resolution,limits)	//designed to do a fast FFT of wave named "w" 
//w is considered to be an optical spectrum from the ocean optics CCD.  The routine takes the wavelength x-axis from 
//"Wavelength" and converts it to the proper frequency scaling before FFT.
	string w
	variable windowing	//This is the windowing: 1= None; 2=Hanning
	variable resolution	//Resolution enhancement: 1, 2, 3, 4, 5, 6 are: none;2;4;8;16;32 respectively.  Actually just determines how many data points are in the FFT
	variable limits
//Modified from Wavemetrics Library
//Given an input wave ( doesn't have to be a power of two), creates a new wave
//with the suffix "_Mag" that contains the normalized frequency response
//Several levels of resolution enhancement (really just sin x/x interpolation) are provided.
//Options include windowing (Hann) vs no windowing.
	string destw=w+"_Mag"
	Variable n		//= numpnts($w)
	variable start_point, end_point
	if (limits)	//want to do FFT only on portion of spectrum selected between the cursors in the spectrum window
		NVAR GCursorAPoint_spectrum=GCursorAPoint_spectrum
		NVAR GCursorBPoint_spectrum=GCursorBPoint_spectrum
		GCursorAPoint_spectrum=pcsr(A,"Graph4")
		GCursorBPoint_spectrum=pcsr(B,"Graph4")
		start_point=GCursorAPoint_spectrum
		end_point=GCursorBPoint_spectrum
	else	//do FFT on entire spectrum
		start_point=0
		end_point=numpnts($w)-1
	endif
	if( (resolution<1) %| (resolution>6) )
		Abort "resolution out of range"
	endif
	resolution=2^resolution		//convert this resolution value to the appropriate binary number (if file length is 2^n, it speeds up the FFT algorithm)
	//resolution -= 1
	Duplicate/O/R=[start_point,end_point] Wavelength, xData
	xData=1/xData		//X axis will be 1/nm so FT will be in nm
	Duplicate/O/R=[start_point,end_point] $w, yData
	Sort xData, xData, yData	// sort by increasing X
	Duplicate/O yData, tempFFT	// make tempFFT file to contain properly scaled waveform
	SetScale/I x xData[0], xData[(numpnts(xData)-1)], tempFFT	// set the range of X values
	tempFFT = interp(x, xData, yData)
	if(windowing==2)
		Hanning tempFFT; tempFFT *= 2			//assumes continuous rather than pulsed data
	endif
	n= numpnts(yData)
	Redimension/N=(CeilPwr2(n)*2^resolution) tempFFT		//pad with zeros to power of 2
	//print "resolution is " +num2str(resolution)
	//print "Ceilpwr2(n) is " +num2str(CeilPwr2(n))
	//print "number of new data points is " +num2str(CeilPwr2(n)*2^resolution)
	FFT tempFFT
	Wave/C tempFFTC= tempFFT		//this resets the complex part of FTspectrum, which was generated in the fft
	tempFFTC=r2polar(tempFFT)
	Redimension/R tempFFTC
	//NOTE: depending on your application you may want to un-comment the next line
//	$destw[0] /= 2								//dc is special
	tempFFT /= n/2
	SetScale y,0,0,"Cts",tempFFT
	Redimension/R tempFFT
	Duplicate/O tempFFT $destw
end



I would like to also calculate the phase angle from the FFT. I am basing my modification on the example experiement "MagPhase Demo". As I have previously mentioned this is my first attempt at igor pro programming and while I am slowly learning how it all works, I keep getting an error code near the end of the function, "Function not available for this numbertype", relating to the line ----- tempFFT_Phase = imag(r2polar(tempFFT2)). I believe that this indicates that the wave has not been identified as complex, however I thought I had addressed this previously with ----- Wave/C tempFFT_Phase= tempFFT2.

Any help would be greatly appreciated and as I am still a beginner, example code would be fantastic.



Function RealTimeFFTMagPhase(w,windowing,resolution,limits)	//designed to do a fast FFT of wave named "w" 
//w is considered to be an optical spectrum from the ocean optics CCD.  The routine takes the wavelength x-axis from 
//"Wavelength" and converts it to the proper frequency scaling before FFT.
	string w
	variable windowing	//This is the windowing: 1= None; 2=Hanning
	variable resolution	//Resolution enhancement: 1, 2, 3, 4, 5, 6 are: none;2;4;8;16;32 respectively.  Actually just determines how many data points are in the FFT
	variable limits
//Modified from Wavemetrics Library
//Given an input wave ( doesn't have to be a power of two), creates a new wave
//with the suffix "_Mag" that contains the normalized frequency response
//Several levels of resolution enhancement (really just sin x/x interpolation) are provided.
//Options include windowing (Hann) vs no windowing.
	string destw_Mag, destw_Phase
	destw_Mag=w+"_Mag"		//Destination file for Magnitude
	destw_Phase=w+"_Phase"	//Destingation file for Phase
	
	Variable n		//= numpnts($w)
	variable start_point, end_point
	if (limits)	//want to do FFT only on portion of spectrum selected between the cursors in the spectrum window
		NVAR GCursorAPoint_spectrum=GCursorAPoint_spectrum
		NVAR GCursorBPoint_spectrum=GCursorBPoint_spectrum
		GCursorAPoint_spectrum=pcsr(A,"Graph4")
		GCursorBPoint_spectrum=pcsr(B,"Graph4")
		start_point=GCursorAPoint_spectrum
		end_point=GCursorBPoint_spectrum
	else	//do FFT on entire spectrum
		start_point=0
		end_point=numpnts($w)-1
	endif
	if( (resolution<1) %| (resolution>6) )
		Abort "resolution out of range"
	endif
	resolution=2^resolution		//convert this resolution value to the appropriate binary number (if file length is 2^n, it speeds up the FFT algorithm)
	//resolution -= 1
	Duplicate/O/R=[start_point,end_point] Wavelength, xData
	xData=1/xData		//X axis will be 1/nm so FT will be in nm
	Duplicate/O/R=[start_point,end_point] $w, yData
	Sort xData, xData, yData	// sort by increasing X
	Duplicate/O yData, tempFFT	// make tempFFT file to contain properly scaled waveform
	SetScale/I x xData[0], xData[(numpnts(xData)-1)], tempFFT	// set the range of X values
	tempFFT = interp(x, xData, yData)
	if(windowing==2)
		Hanning tempFFT; tempFFT *= 2			//assumes continuous rather than pulsed data
	endif
	n= numpnts(yData)
	Redimension/N=(CeilPwr2(n)*2^resolution) tempFFT		//pad with zeros to power of 2
	//print "resolution is " +num2str(resolution)
	//print "Ceilpwr2(n) is " +num2str(CeilPwr2(n))
	//print "number of new data points is " +num2str(CeilPwr2(n)*2^resolution)
	FFT tempFFT
	Duplicate/O/C tempFFT, tempFFT2		//Duplicated Fourier Transformed wave for Mag and Phase analysis
	Wave/C tempFFT_Mag= tempFFT		//this resets the complex part of FTspectrum, which was generated in the fft
	Wave/C tempFFT_Phase= tempFFT2
	tempFFT_Mag=r2polar(tempFFT)
	Redimension/R tempFFT_Mag
	//NOTE: depending on your application you may want to un-comment the next line
//	$destw_Mag[0] /= 2								//dc is special
	tempFFT /= n/2
	SetScale y,0,0,"Cts",tempFFT
	Redimension/R tempFFT
	Duplicate/O tempFFT $destw_Mag
	tempFFT_Phase = imag(r2polar(tempFFT2))  //**************ERROR**********  "Function not available for this numbertype"
	tempFFT_Phase *= 180/PI						// Convert phase to degrees
	Redimension/R tempFFT_Phase	
	tempFFT2 /= n/2
	SetScale y,0,0,"Cts",tempFFT2
	Redimension/R tempFFT2
	Duplicate/O tempFFT2 $destw_Phase					
end



Thanks

Andy
imag()
returns a real number, not a complex one. So Igor throws an error because you declared tempFFT_Phase to be complex.
Thanks 741, changing

Wave/C tempFFT_Phase= tempFFT2
to
Wave tempFFT_Phase= tempFFT2

cleared up the error associated with the imag() tag, however now I receive an error on the very next line:

tempFFT_Phase *= 180/PI wave assignment error: "Complex wave used in real expression."

I thought that the imag() tag converted the imaginary component of the complex wave into a real number, as shown in the example experiment MagPhase Demo.

Thanks again for your fast reply

baitzer wrote:
I thought that the imag() tag converted the imaginary component of the complex wave into a real number, as shown in the example experiment MagPhase Demo.


There is no conversion here. imag() is a function that returns a real value. If you assign it to a complex wave the compiler will find it objectionable.

The solution to many of these problems is to use MatrixOP. MatrixOP does not care if your input is real or complex. It attempts to give you the correct output as implied by the return values of all the tokens that appear on the right hand side of the MatrixOP expression.

Also note that you were apparently working from a very old example. FFT has a /DEST flag and /OUT flag that would make your code a bit more compact.

A.G.
WaveMetrics, Inc.
Thanks A.G.

Modified my function to include MatrixOP and now it works great.
Hi all,

Thanks again for your earlier help. I just have a few additional questions relating to the phase angle. I modified my original code according to A.G. and 741's suggestions which solved the associated error's I was receiving.

New Code:


Function RealTimeFFTMagPhase(w,windowing,resolution,limits)	//designed to do a fast FFT of wave named "w" 
//w is considered to be an optical spectrum from the ocean optics CCD.  The routine takes the wavelength x-axis from 
//"Wavelength" and converts it to the proper frequency scaling before FFT.
	string w
	variable windowing	//This is the windowing: 1= None; 2=Hanning
	variable resolution	//Resolution enhancement: 1, 2, 3, 4, 5, 6 are: none;2;4;8;16;32 respectively.  Actually just determines how many data points are in the FFT
	variable limits
//Modified from Wavemetrics Library
//Given an input wave ( doesn't have to be a power of two), creates a new wave
//with the suffix "_Mag" that contains the normalized frequency response
//Several levels of resolution enhancement (really just sin x/x interpolation) are provided.
//Options include windowing (Hann) vs no windowing.
	string destw_Mag, destw_Phase
	destw_Mag=w+"_Mag"
	destw_Phase=w+"_Phase"
	
	Variable n		//= numpnts($w)
	variable start_point, end_point
	if (limits)	//want to do FFT only on portion of spectrum selected between the cursors in the spectrum window
		NVAR GCursorAPoint_spectrum=GCursorAPoint_spectrum
		NVAR GCursorBPoint_spectrum=GCursorBPoint_spectrum
		GCursorAPoint_spectrum=pcsr(A,"Graph4")
		GCursorBPoint_spectrum=pcsr(B,"Graph4")
		start_point=GCursorAPoint_spectrum
		end_point=GCursorBPoint_spectrum
	else	//do FFT on entire spectrum
		start_point=0
		end_point=numpnts($w)-1
	endif
	if( (resolution<1) %| (resolution>6) )
		Abort "resolution out of range"
	endif
	resolution=2^resolution		//convert this resolution value to the appropriate binary number (if file length is 2^n, it speeds up the FFT algorithm)
	//resolution -= 1
	Duplicate/O/R=[start_point,end_point] Wavelength, xData
	xData=1/xData		//X axis will be 1/nm so FT will be in nm
	Duplicate/O/R=[start_point,end_point] $w, yData
	Sort xData, xData, yData	// sort by increasing X
	Duplicate/O yData, tempFFT	// make tempFFT file to contain properly scaled waveform
	SetScale/I x xData[0], xData[(numpnts(xData)-1)], tempFFT	// set the range of X values
	tempFFT = interp(x, xData, yData)
	if(windowing==2)
		Hanning tempFFT; tempFFT *= 2			//assumes continuous rather than pulsed data
	endif
	n= numpnts(yData)
	Redimension/N=(CeilPwr2(n)*2^resolution) tempFFT		//pad with zeros to power of 2
	//print "resolution is " +num2str(resolution)
	//print "Ceilpwr2(n) is " +num2str(CeilPwr2(n))
	//print "number of new data points is " +num2str(CeilPwr2(n)*2^resolution)
	FFT tempFFT
	Duplicate/O/C tempFFT, tempFFT2
	Wave/C tempFFT_Mag= tempFFT		//this resets the complex part of FTspectrum, which was generated in the fft
	Wave tempFFT_Phase= tempFFT2
	tempFFT_Mag=(r2polar(tempFFT))
	Redimension/R tempFFT_Mag
	//NOTE: depending on your application you may want to un-comment the next line
//	$destw_Mag[0] /= 2								//dc is special
	tempFFT /= n/2
	SetScale y,0,0,"Cts",tempFFT
	Redimension/R tempFFT
	Duplicate/O tempFFT $destw_Mag
	Variable degree=(180/pi)
 	MatrixOP/O tempFFT_Phase=degree*imag(tempFFT2)  // Extracts the imaginary component of the complex wave (Phase) and then Convert phase to degrees via (180/PI)
	Print "test", atan(tempFFT2)
	Redimension/R tempFFT_Phase	
	tempFFT2 /= n/2		
	SetScale y,0,0,"Deg",tempFFT2
	Redimension/R tempFFT2		
	Duplicate/O tempFFT2 $destw_Phase
	Print "Phase", tempFFT2
end


I have now managed to plot both the Magnitude and Phase on a single graph as shown in the attached image (Magnitude = red, phase = blue), however the phase is massive >20 kDegrees. I thought that the phase extracted from the FFT was between -Pi and Pi or -180 to 180 degrees if converted.

http://www.igorexchange.com/files/images/FFT%20Magnitude%20&%20Phase%20(Zoom).JPG

Does anyone have a suggestion as to why my code is creating such a large phase?

Also the project I am modifying the code for requires the phase angle at the magnitude. Now that I have extracted the phase information from the FFT, how would I determine the phase angle. I can easily find the magnitude via peak fitting the red trace but I am unsure how to determine the related phase angle.

Cheers
You can convert between rectangular (real, imag) and polar (mag, phase) representations of complex numbers using the functions r2polar() and p2rect(). With phase variables sometimes the Unwrap operation comes in handy.