Lab 4: FFT and Spectrum Analysis

Winter 2012
Prof. Teresa Meng
Instructions, AB Notes, C Notes

 


I. Introduction:

In this Lab you will learn how to use an efficient FFT routine for useful DSP applications. For example, you will use the FFT routine to perform filtering (linear convolution in the time domain) using the techniques of overlap-and-add and overlap-and-save. You will also be able to experiment with some FFT properties; e.g. a circular delay in the time domain means a linear phase in the frequency domain, or the duality of symmetry and real and imaginary components between the two domains. It is also essential that you understand the difference between the frequency response of a finite-duration signal and its DFT.

II. Readings:

  • Discrete-Time Signal Processing, Second Edition (text)

Section 9.3 - Decimation-in-time FFT algorithms.

Section 9.4 - Decimation-in-frequency FFT algorithms.

Section 8.7 - Linear Convolution Using the Discrete Fourier Transform. Focus on the discussion on overlap-and-save and overlap-and-add methods. 

  • C55x DSP Library Programmer's Guide

Refer to the DSP Library Reference Guide for descriptions of available library functions. (On-line Programming Reference->C55x DSP Library)

Sample code that may be useful is also available in the folder C:\CCStudio_v3.1\c5500\dsplib\examples.

 

III. Lab exercises:

In the first part of the Lab, you will implement frequency domain filtering in MATLAB.  You will then go through a DSP implementation of a small FFT to become familiar with using the DSP library functions and to get a basic understanding of their implementation and performance.  After you have done this, you will then go through frequency domain filtering on the DSP, using your knowledge of DSP programming to optimize the filtering algorithm. You will then compare the performance of the frequency-domain filter to the time-domain filter. And finally, you will experiment with some properties of the FFT.

A. Filtering in the Frequency Domain (MATLAB Implementation)

To start, you will first build the framework for filtering in the frequency domain through simulation in MATLAB. This will help you test your algorithm as well as specify the necessary characteristics of the filtering process.

  • Load h_bp40.mat and h_bp80.mat for filter coefficients.
  • Algorithm overview: 
    The algorithm for filtering in the frequency domain can be broken down into five parts:
    1. Record the signal and perform the necessary padding for FFT.
    2. Perform FFT.
    3. Multiply the frequency-domain filter coefficients with the FFT of the data signal.
    4. Perform IFFT to obtained the time-domain filtered result.
    5. Post- process the time-domain filtered result to extract useful data. This is your filtered output.
  • Design Considerations: 
    There are a number of things to consider when coding this algorithm:
    • The size of the FFT, the size of the signal buffer, and the size of the filter coefficients are somewhat related and constrained by the following:
      • FFT sizes are limited to powers of 2 on the DSP.
      • Multiplication in the frequency domain is equivalent to a circular convolution in the time domain (so steps must be taken to make the result the same as linear convolution).
      • If the data signal is of length L and the filter is of length P, then the size of the filter circularly convolved with the data signal is L + P - 1.
    • In the MATLAB code, you should parameterize the size of the data set, the size of the filter, and the FFT size so that you can change any one of these variables without having to go through major changes in the MATLAB code.
    • It may be useful to structure your MATLAB code to behave like a DSP implementation (for example, only getting one new frame of inputs each time, etc.)
    • Remember that the time-domain signal is always purely real.
    • You need to assume that the frequency domain filter coefficients are complex. You must perform complex multiplication of the frequency-domain filter coefficients and the FFT of the data signal for the filtering to work. MATLAB automatically performs complex multiplication when the signals are complex, but you will have to do this explicitly when you implement the algorithm for the DSP.
    • After you have completed IFFT, you will need to post-process the result to extract useful filtered data. There are two methods that you can employ to do this (see Readings for more information):
      • Overlap-and-add - where the overlapping section from the circular convolution is preserved. This is then summed with the corresponding overlapping section of the next filtered segment - See figure 8.23 in Discrete-Time Signal Processing, Oppenheim & Schafer, Second Edition.
      • Overlap-and-save - where the overlapping section is discarded and only the useful (un-corrupted) filtered outputs are retained - see figure 8.24 in O & S.

You are to choose one of the two methods that you think is the most efficient for the DSP. You will need to implement only one method in MATLAB and on the DSP.

  • MATLAB Warning: If you need to take the transpose of a vector/matrix of complex numbers in MATLAB, make sure that you use transpose operator ( .' ) instead of the conjugate transpose operator ( ' ). 
    • In order to take the simple transpose of a matrix, you will need to use the " .' " operator (example:  y_t = y.').   
    • The transpose operator commonly used is " " (example: y_ct = y'), which gives you the complex-conjugate transpose of y.  
    • Something to think about:  if you take the the complex-conjugate transpose of a signal's frequency domain coefficients, what does this correspond to in the time domain?
  • In MATLAB, you are to do the following experiments:
    • Experiments - implement frequency-domain filtering with the following fiter sizes and FFT sizes:
      • Filter size = 40 taps, FFT size = 128 points
      • Filter size = 80 taps, FFT size = 128 points
      • Filter size = 40 taps, FFT size = 256 points
      • Filter size = 80 taps, FFT size = 256 points
    • Filter coefficients:
      • Sample bandpass filter coefficients have been generated and provided to you. The file h_bp40.mat and the file h_bp80.mat contain the 40-point bandpass filter coefficient array and the 80-point bandpass filter coefficient array, respectively. You can load these into MATLAB using the load command. For example, load h_bp40 will load the variable, h_bp, contained in h_bp40.mat. These coefficients have unity bandpass spectrum. To use these coefficients in a DSP code, use the vector2assembly.m script.
      • The sample bandpass filters are generated using Kaiser windows and have the following characteristics:
        • 40-point:
          • passband = 1000 - 2500 Hz
          • passband ripple = 0.01%
          • stopband ripple = 0.1%
          • Transition width = 450 Hz assuming Fs = 8000 Hz
        • 80-point:
          • passband = 1000 - 2500 Hz
          • passband ripple = 0.002%
          • stopband ripple = 0.003%
          • Width = 322 Hz assuming Fs = 8000 Hz
    • Verifying your design: 
      • if your frequency-domain filtering code is working correctly, then the output it produces should be equivalent to the result of performing linear convolution of the input data and the filter impulse response. Use Matlab's filter(h,1,x) to compare, where h is your filter and x is your data input. This will truncate the last P-1 values of the full linear convolution, which is what you want.
    • Estimating Performance:
      • You are looking at two tradeoffs, performance in terms of number of operations per data sample and quality of filtered result in terms of number of filter taps. How does doubling the number of taps affect the performance in terms of CPU time? How does doubling the FFT size affect the performance in terms of CPU time?
      • Use the tic and toc commands to calculate the relative cpu_time of filtering 1,000,000 samples with your overlap-add or overlap-save implementation (type "help tic" for info in MATLAB). This will give you an idea of the relative amount of cpu time needed to perform the filtering operations with different parameter settings (filter length, FFT size, etc.).
        • For best results, put tic right before your main loop and toc right after your main loop.
        • Note: the actual number of samples is not critical. The idea is to get the tic/toc times to be on the order of seconds so that we can observe changes. 
        • If your code is taking more than 10 seconds to process 1,000,000 samples, feel free to reduce the number of samples until the tic/toc times reported are in the 1-10 second range.
        • (In the past, MATLAB provided a FLOPS command that was more precise for this purpose, but it is now obsolete, hence the need for this cruder measurement with a large data size.)
      • MATLAB Speed-up: Depending on how you implement your MATLAB code, the majority of your algorithm's running time may be used for memory allocation.  For example, the common practice of concatenating two vectors ( yy = [yy  next_frame] ) forces MATLAB to perform memory allocation each time the size of the vector grows.  This is OK, but it makes the running time of your MATLAB script dominated by this memory allocation instead of the FFTs involved.  This can be prevented with a little bit of extra work:
        • Since we know the final size of all of our vectors, we can  pre-allocate the vectors to be their final size:
          • yy = zeros(1, final_size);
        • The new data can be placed into this vector by using indexing:
          • yy(1:frame_size) = next_frame;
        • In general, this can be done in a for-loop with something like this, where ii is a loop index starting at 1: 
          • yy( (ii-1)*frame_size + 1: ii*frame_size) = next_frame;
        • This can be very important if we are trying to estimate the effect that the FFT size has on the performance of the algorithm.  Otherwise, our running time is dominated simply by the number of vector concatenations that must be done and the effect of the FFT calculation will not be noticed directly. 
      • Your numbers probably won't match the expected results. For example, you may get that P=40, N=256 is twice as fast as P=40, N=128, and the speedup should theoretically be something else. Try calling fftw('wisdom',[]); after every fft/ifft call. Matlab likes to somehow cache its fft/ifft calculations, so this command clears the fftw database (basically its fft cache) so that subsequent fft calculations take as long as they're supposed to.

 

B. FFT and Intro to the DSP Library

In this section, you will implement a 64-point FFT using the C55x DSP Library. The DSP Library contains many useful functions for DSP algorithms.  Examples of such functions are FFT routines and trigonometric functions.  TI has made this library available so that programmers do not need to implement these functions themselves and can use these functions as building blocks for implementing more complex DSP algorithms.   The following exercise will help you become familiar with using the DSP Library in a project.  For information on the DSP Library, refer to the DSP Library Reference Guide. (On-line Programming Reference->C55x DSP Library)

  • Coding Requirements:
    Implement a 64-point FFT followed by a 64-point Inverse FFT on the EVM using the DSP library.
      
    • Open the fft64 folder .
    • Open the project fft.pjt and resolve any compiler/linker errors. 
      • We use two libraries; runtime support library, which is standard C library, and dsp library.
      • NOTE: CCS may complain that it cannot find the library file 55xdspx.lib. This is because the project file was created on a machine whose CCS installation was on a particular drive letter (C:\). If you are on a machine for which CCS was intalled on a different drive letter, then you may have to change the drive letter for the location of the actual DSP library .lib file. The .lib file should be located at either C:\CCStudio_v3.1\c5500\dsplib\55xdspx.lib or D:\CCStudio_v3.1\c5500\dsplib\55xdspx.lib, depending on the machine that you are using.
      • 55xdsp.lib is for small memory model project, whereas 55xdspx.lib is for large memory model project. Note that fft.pjt is configured as large memory model project.
      • Similarly, CCS may complain that it cannot find a header (.h) file.  In this case, you will have to change the drive letter for the location of the DSP library "include" directory.  Under the Compiler tab under Build Options, (Project->Build Options), change the drive letter of the include path from C:\CCStudio_v3.1\c5500\dsplib\include to D:\CCStudio_v3.1\c5500\include or vice versa to ensure that the compiler knows where to find the proper header (.h) files to include.
      • Runtime support library and header files can be found in C:\CCStudio_v3.1\c5500\cgtools\lib and C:\CCStudio_v3.1\c5500\cgtools\include.
      • Similarly, rts55.lib is for small memory model, and rts55x.lib is for large memory model.
    • Familiarize yourself with this project template. You will be using it to integrate DSP library with your real-time audio code later on so that you can hear the output of your DSP code.  ( Refer to the DSP Library Reference (On-line Programming Reference->C55x DSP Library) for information on available functions/macros.)  There are several noteworthy points about this template: 
      • It serves as an example of running C programs without the use of DSP/BIOS.
      • Note that the library file 55xdspx.lib and rts55x.lib have been included in the Libraries folder of the project.  This allows the linker to access the DSP Library and  runtime support functions.
      • Also note that the header files for the DSP Library and runtime support functions have been included in the Compiler tab under Build Options (Project->Build Options).  The include path is specified with the "-i" option (as either C:\CCStudio_v3.1\c5500\dsplib\include or D:\CCStudio_v3.1\c5500\dsplib\include, and C:\CCStudio_v3.1\c5500\cgtools\include or D:\CCStudio_v3.1\c5500\cgtools\include). 
    • Edit main.c by adding the appropriate DSP library functions with the appropriate arguments.  All the DSP library setup (i.e. memory sections, variables, etc.) are already implemented in this template.  
      • You should either implement a cfft followed by a cifft, or implement an rfft followed by rifft.  
      • See below for a description of how to use the DSP Library properly.
    • Profile the 64-point FFT routine.  Compare the Profiled result from the DSP code and the documentation on the fft function in the DSP library. What are the discrepencies between the actual EVM code and the DSP library performance table? Would you have easily been able to write a 64-point FFT code that out performs the DSP library routine?
  • Proper use of the DSP library:  
    • As specified in the DSP library user manual, you need to include the .twiddle section in your linker command file. In addition, you must allocate this section onto the internal memory of DSP with the alignment of 2048. Use .twiddle: {} > MEMORY_SECTION PAGE 0, align (2048) in linker command file (this is done for you in fft.cmd).
    • Most FFT functions (cfft, rfft) requires the elements of their input arrays to be in bit-reversed order. cbrev is a complex bit reversal routine that will perform this re-ordering for complex data (with a real word and an imaginary word). cbrev function expects that the input array of this function to be aligned with even word boundary. 
    • Pay attention to the scale factor presented in the DSP library. If scale == 1, scale factor = nx, else scale factor == 1.  Scaling determines whether or not the result will be divided by N (the FFT size).
    • Specifically _cfft_NOSCALE and _cifft_NOSCALE function don't take advantages of DSP's automatic saturation feature. Test cfft / cifft pair with full range sine wave. If you want to enable saturation when using these functions, use asm("\tBSET SATD"); prior to calling a function. Be sure to reset the processor status after that function by using asm("\tBCLR SATD"); Note that these functions are called when using cfft, cifft, rfft, rifft functions with NOSCALE option.
    • Several known issues with the rfft/rifft function (DO NOT IGNORE THESE!)

1.      rfft and rifft are, in fact, macros. See dsplib.h file for the definitions; RFFT is composed of cfft / cbrev / unpack, and RIFFT comprises unpacki / cifft / cbrev.

2.      More RIFFT Issues:  The rifft function also suffers from an undocumented shift of two bits (factor of 4) such that taking the rfft followed by an rifft will result in an attenuation by 4 (approximately, not exactly 4). This affects the quality of waveform slightly, and it seems that the accuracy at the edge of block is degraded quite a bit. You may or may not need to compensate for this attenuation. It depends on the required precision of your application. 

3.      RFFT: The documentation for the rfft has some slight typos in the description of the packing scheme used for the frequency domain output. First of all, the second word of the output array is the Nyquist term, which should be purely real for real input values. The documentation mistakenly refers to this as an imaginary term (y(nx/2)Im instead of y(nx/2)Re). Secondly, the last two words of the output array will be the real and imaginary terms for the frequency bin before the Nyquist frequency, so the corresponding indices should be listed as (nx/2-1) instead of (nx/2).

4.      Scaled function (cfft_SCALE, cifft_SCALE) introduces some noise, and if the fft spectrum, which is generated by cfft_SCALE, is inverse transformed, the the time-domain response would have quite a larger error on the first two samples than on the other samples. This will be a critical problem with overlap-and-addition because this method doesn't throw away the first two samples.

    • There may be inconsistencies in the DSP Library documentation. If you run into any strange behavior with the DSP Library you are strongly encouraged to isolate and test each library call in your code. MATLAB can be very useful for this; writing a script to inspect the actual input and output data from DSP routines can save you a lot of debugging time.
    • In order to suppress noise level sufficiently small in 16bit audio processing, it is strongly recommended to use 32bit version of functions such as cfft32, cifft32, rfft32, and rifft32. Put your 16bit input data on the upper 16bit of 32bit input data to transform, then do the internal processing, whatever it may be, maintaining data width to 32bit accuracy. After you do the inverse transform, the upper 16bit data of the final 32bit data can be extracted.
  • FFT Scaling:
    • One of the arguments to the FFT/IFFT routines is scale, which specifies whether or not the DFT summation will be divided by N, the FFT size. 
    • Traditionally, the (forward) DFT does not scale by 1/N, while the (inverse) IDFT does scale by 1/N.  (Take a look at the definitions).  
    • However, on the DSP, we typically scale by 1/N in the forward direction to avoid overflow.  Here is an example where overflow will occur: 
      • If you have an input array of all 1.0's (fractional), the DC term of an N-point FFT should then be N. Since this cannot be properly stored as a 16-bit fraction (Q15 format) the result will overflow into the guard bits of the accumulator, and the 16-bit result (taken from AH or BH) will be wrong. 
      • If we scale by 1/N for this same input, our output is guaranteed to be no greater than 1.0, so it will not be affected by overflow.
    • Note that if we scale by 1/N in the forward direction, we do not need to scale by N in the reverse direction, so the two operations will still be inverses of each other.
    • Scaling by 1/N in the forward direction can cause interesting problems, since the forward FFT is not normally scaled by 1/N.  For example:
      • Say we take the forward FFT of two signals, x and y, and scale by 1/N while taking the FFT.
      • If we now multiply the frequency domain signals together, we get a result that is scaled by 1/(N*N).  
      • This may cause problems, depending on what you are doing, so you will want to keep this in mind.
  • MATLAB Verification:  
    You will be expected to write a script to verify your code.  This will prove that your routines work as expected and it will also help you fully understand the DSP Library routines that you will be using.  In particular, it will force you to look at the scaling done by the routines and the output format of the FFT routines.  The work you do here should make your job easier for the second part of this lab, as well as for the final project.  
    Here are the requirements.
    • Verify your FFT routine (either cfft32 or rfft32).  This should involve several things:
      • Overwrite the input to the FFT with some data before performing the FFT. Using random data proves that the routine works in the general case, but using a signal such as a sine wave may give you more insight.
      • Perform the FFT (and other necessary routines such as cbrev32) and verify that your routines produce the proper output for this input.  
      • Use MATLAB to perform the FFT on the same input data.
      • Compare your DSP result to the result generated by MATLAB.  There are a couple of considerations here:
        • The output of your FFT routine (cfft32 or rfft32) will be in some packed form (such as {real, imaginary, real, imaginary, ...} or something more complicated).  You can deal with this in one of two ways: 
          1. Either expand the packed data out to an array of complex numbers (the form MATLAB uses), or
          2. Take the MATLAB result and pack it into the same form as the DSP's FFT output
        • You will have to account for scaling differences due to the use of "fractional" integers, the scaling parameters passed to the FFT routine, and also any scaling inherent in the FFT routines themselves (rifft32).  By going through this, you will be forced to understand how the scaling works before moving on to debug larger problems.
    • Verify your Inverse FFT (IFFT) routine (either cifft32 or rifft32).  This should involve similar issues to the FFT verification described above.  In addition: 
      • You will now be providing a complex, frequency-domain  input to the IFFT routine.  This data is in some packed form (the same as the output from the cifft32 or rifft32).
      • Use the output from your FFT routine as the input to the IFFT routine.  However, note the following:
        • This does not effectively isolate the routines (the IFFT output will depend on the FFT routine, and we would like to verify the IFFT separately).  
        • If your FFT is already verified, then passing its output to the IFFT should be OK and it will allow you to see the result of a signal passing through the FFT and IFFT in succession.  This will expose you to any possible scaling issues.
        • To test the IFFT in isolation, you would have to generate some input data and make sure that it is packed into the expected form.  Once again, when verifying the result in MATLAB, you will need a single array of complex numbers (for taking the IFFT), so the array of data that goes into your DSP IFFT will not be the same as the array that MATLAB uses. This can be resolved in a couple of ways:
          1. Either create random 32-bit data to be passed to the DSP and then un-pack this into a single complex array for use by MATLAB, or
          2. Create a single array of random complex numbers for MATLAB and then pack this into the proper form before writing to the DSP
        • Once again, you will need to be aware of scaling issues and get a full understanding of the scaling caused by IFFT routines. As mentioned above, rifft has a scaling quirk that you may want to be aware of. 
    •  Recommendation: Create MATLAB functions
      • It is recommended that you write some MATLAB functions to convert the packed output from DSP FFT routines to unpacked arrays for use by MATLAB.  This will allow you to make meaningful MATLAB plots of the frequency-domain data produced by your FFT routines and also to use this data in MATLAB computations. 
      • It would also be very useful to write a function that does the reverse (converts from a complex array to a packed array).
      • rfft consideration: If you are writing a routine to expand the output of rfft32 you will have to remember that only half of the spectrum is represented in the array and the other half will have to be recreated as the complex conjugate of the first half.
      • Writing these functions now will make your MATLAB code cleaner and will allow you to reuse them later on (this lab, and the final project).
      • Unpacking example:  Here is a simple example of converting a "packed" cfft32 output to a complex MATLAB vector:
        • Assume that we have some input that is packed as (re, im, re, im, ...).  We will use the numbers 1 through 8 for this example. We can extract the "real" components using indexing starting at index 1, jumping by 2, and going to the end of x.  Similarly, we can extract the "imaginary" components starting at index 2. These real and imaginary vectors can be combined into a complex vector in MATLAB as follows :
          1. x = 1:8;  (this is the same as x = [1 2 3 4 5 6 7 8])
          2. real_x = x(1:2:end);  (this gives us real_x = [1 3 5 7] )
          3. imag_x = x(2:2:end);  (this gives us imag_x = [2 4 6 8] )
          4. unpacked_x = real_x + j * imag_x;  (this gives us unpacked_x = [(1 + j*2) (3+j*4) (5+j*6) (7+j*8)] which is what we wanted.)
        • The rfft output can be unpacked using something slightly more complicated.  This may require flipping a vector, which can be done by specifying a decreasing index range or by using the flip left-right or up-down  functions (fliplr/flipud):
          1. x_flipped  = x(end:-1:1);  (this gives us x_flipped = [8 7 6 5 4 3 2 1] )
          2. x_flipped2 = fliplr(x);      (this gives us x_flipped2 = [8 7 6 5 4 3 2 1] )
    • Caution: Overflow
      • It is now possible to experience overflow problems in your code.  Here is an example:
        • If you have an input array of all 1.0's (fractional) and the DC term of an N-point FFT should then be N.  Since this cannot be properly stored as a fraction (Q15 or Q31 format) the result will overflow.   
      • This means that if you have random data that is large (uses up the full range of the 16-bits), you may get overflow after taking the FFT.  
      • You can check for overflow by using MATLAB to determine if the ideal result is greater than 32,767 (after accounting for all other scaling: fractional, and otherwise).  This may take some thinking, but it is important to be aware of it.

 

 

 


C. Filtering in the Frequency Domain (DSP Implementation)

In a previous section, you have gone through the frequency-domain filtering algorithm in MATLAB. Here, you will port it to the DSP. You will use the DSP library to perform the FFT portion of this algorithm. Your job is to integrate this algorithm into the real-time processing environment that was used in earlier labs so that you will be able to hear the output of your frequency domain filter.

1. DSP Implementation

  • For this section, there are two things you need to do. Implement frequency-domain filtering algorithm using the DSP library and integrate this algorithm into a real-time code. There are two approaches depending on what code template you start with. Choose one of these two approaches for implementation:
    • Recommended approach: If you started with a real-time code, then you need to integrate DSP library into it. This will ensure that once you are successful, you are done. However, you may encounter real-time issues that can sidetrack you in implementing the algorithm. If you decide to implement this way, then:
      • Copy the source codes from a working sinewave generator or audio recorder that you did in earlier labs to a new directory.
      • Modify the project to include the DSP Library .lib file and the proper include directory for the library function header files (as in the earlier part of this lab).
        (DON'T include rts55x.lib. That's not needed for DSP/BIOS application)
      • Implement the frequency-domain filter using DSP library routines directly into the real-time code.
    • You can start by testing the algorithm using a code that you know works with DSP library. Once you have verified your algorithm, you can then try to integrate this into a working real-time code. In this case, the real-time debugging would be done during the integration instead of during algorithm porting. If you decide to implement this way, then:
      • Copy the source codes from lab4/fft64/ (the working code that uses the DSP library) that you have completed in Section III.B. to a new directory.
      • Implement the frequency-domain filter using DSP library routines and make sure it works in this code first.
      • When you have verified that your frequency-domain filter works, integrate it into a real-time code. Copy the source codes from a working sinewave generator or audio recorder that you did in earlier labs to your new directory.
  • Specifications:
    • Filter size = 40 taps. You may choose to use the sample filter coefficients provided to you in h_bp40.mat. However, you will need to convert the filter coefficients into the appropriate format.
    • FFT size = 128 points.
    • From the MATLAB code, determine the number of new input samples that can be processed for each pass of the algorithm. Remember that this is a function of the filter size and FFT size.
    • For real-time integration:
      • Set the sampling rate to 8 KHz in the real-time code.
      • You are to choose the size of your audio buffer.
    • You will be graded on the functionality and performance of this code. (Note that this does not suggest hand-optimizing the DSP library functions yourself.)
    • You will also be required to perform verification with MATLAB, as described below.  The MATLAB verification capabilities can be very useful for verifying individual parts of your code (complex multiplication functions, etc.) so it is recommended that you use this during your coding process. (More below...).
    • Be sure to adjust the buffer size appropriately in audio.h ( #define BUFFSIZE N )
    • Also, you should filter both channels of audio
  • Algorithmic Comparison:
    Profile the core filtering code and compare the performance to the time domain filter in Lab 3 Section II.B.1. In order to to make a fair comparison, both implementations should be based on the same number of filter taps. You will need to estimate the execution time that your lab 3 implementation would have taken for a 40 tap filter.
    • Based on the MATLAB exercise in section B.1, can you determine an estimate of the size of the FFT used for the frequency-domain filter such that the frequency-domain filter is more efficient than the time-domain filter?  
    • Does such a size exist?  If not, can you explain why?
    • Here are the factors involved in comparing the two algorithms:
      • The parameters: Filter length is P, Number of input samples is L, FFT size is N
      • In terms of algorithmic complexity, linear convolution that takes in L input samples and produces L new output samples is on the order of  L*P operations: O(L*P).  This means that the number of operations/cycles can be estimated as  k1*L*P, for some constant k1.
      • For the frequency-domain filter, the algorithmic complexity is on the order of  N*log2(N) operations O(N*log2(N)). This means that the number of operations can be estimated as  k2*N*log2(N), for some constant k2.
      • In order to directly compare the two methods, we must relate the parameters.  This is typically done by fixing some of the parameters to be constants or by making one parameter a function of another one.  Here are a couple possibilities:
        • We could fix P and make N a function of the input size L and the constant P (since N will be related to the filter length and the number of input samples)
        • We could let P vary as a function of L or N and then reduce the problem down to a single variable.  Example: let P = N/2 and L = ? (constrained by N and P).  This gives us everything in terms of a single parameter N.
      • If we relate both filtering methods by way of a single parameter, we still cannot directly compare the two until we know the constants used (k1 and k2).  We really only need an estimate of these constants, and we can get these estimates from our knowledge of the DSP.  For example, for linear convolution, we know that k1 will be approximately 1 if k1*L*P represents the number of cycles to perform linear convolution (as we saw in Lab 3).  The constant k2 can be estimated from the cycle count required for the FFT functions in the DSP library.
      • Once we have an estimate of the constants involved (and both equations are related by a single parameter), then we can estimate which algorithm will be more efficient as a function of the single parameter.  The result will depend heavily on the choices made (fixed P or P=N/2, etc.) so any conclusion should be qualified by stating these assumptions.

2. MATLAB Verification

As in Lab 3, you will be expected to write a script to verify your code.  Here are the requirements.

  • At the very least, you will be required to verify that the output of your frequency domain algorithm is equivalent to the ideal (MATLAB) filtered output, as described in Lab 3.  This should have already been done by your Lab 3 script, so this shouldn't require too much work.
  • Debugging Suggestion:
    Although you are only required to verify the output of the entire algorithm, you are strongly encouraged to use the MATLAB verification capabilities to test individual components of your design.  A lot of time can be saved by debugging smaller pieces of a design, instead of attempting to debug the entire design.
    • For example, your frequency-domain filtering algorithm will need to have some form of complex multiplication routine for frequency-domain samples.  MATLAB can be used to isolate and test this routine, since it can overwrite the input before performing the operation and can then verify that the result of the operation was correct for the given input. 
    • In this lab and in future labs, the algorithms we implement will typically consist of several different stages that are performed sequentially (example: FFT -> complex multiply ->  inverse FFT).  Each of these stages has an input and an output (which is usually fed to the input of the next stage).  MATLAB verification can be used to debug each of these stages separately, instead of treating the completed system as a black box and trying to verify its output from its input.  

 

3. "Ideal" Filter

In order to better understand filter design, you will now filter the frequency response of the input by multiplying with an ideal filter. This ideal filter has a unity passband and zero stopband (as specified in the frequency domain).

  • Copy the working frequency-domain filter code from the working directory of the previous section into this working directory.
  • Plot the magnitude and phase of the 128-point FFT of the filter coefficient stored in h_bp40.mat. Note that the phase is linear but the magnitude has small ripples.
  • What would happen if rather than dealing with non-flat magnitude response in the frequency domain, you create an ideal filter by simply setting the passband filter frequency coefficients to unity (zero phase) and the stopband filter frequency coefficients to zero?
  • To see the effect of the above question, replace the filter coefficients in your DSP implementation with the filter coefficients of an ideal filter. Set the passband to be 1 KHz to 2.5 KHz (same as the sample filter coefficients) (assuming an 8kHz sampling rate).
  • For the purposes of your DSP code, you can specify the frequency-domain coefficients directly, without computing the time-domain sequence. (Note: we will eventually ask you to compute the time-domain impulse response, so it may be easier to compute this first.) If you do specify the frequency domain values for the DSP directly, make sure that the format of these samples is compatible with your code (like the output of a cfft32 or rfft32 call, most likely).
  • Qualitatively compare the output of this ideal filter to the output of the 40-tap sample filter.
  • Can you explain why this ideal filter does not work? There are three distinct reasons, and the following should help you see why:
    • Provide a MATLAB plot of the time-domain impulse response of this filter.  Note the length, and comment on how this effects the algorithm.
    • Also provide a plot of the actual magnitude spectrum of this filter (using zero-padding to get higher resolution in the frequency domain). Observe the behavior of the filter for frequencies in-between the frequency-domain samples that were forced to 0 or 1).  
    • How well does this filter satisfy the requirements of your filtering algorithm?

Last modified: 9:00 am 3/07/2007