This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

aliased bins in rfft() results?

Other Parts Discussed in Thread: TMS320VC5506

I realize that I could be making some mistakes in my code, or missing something fundamental, but I seem to have found some errors in rfft().  I have included a link to a zipped project for Code Composer Studio 3 so that you can see what I'm doing and determine whether the problem is in my code or the DSPLib.

http://www.sounds.wa.com/K1/alias.zip

I am focused on 128-point real FFT results, and so I am using the sine table in the TMS320VC5506 to generate sine waves that are multiples of the sample rate.  These are intended as test signals so I can understand the proper operation - from input to output - of the rfft() function provided as source with CCS3.  Ignoring DC, there are 127 distinct frequencies which would fall into exactly one bin of the FFT results, and I am focusing on the 63 frequencies below Nyquist.  My code steps through most of these, and then scans the results to summarize.

What I have noticed is that only a few frequencies produce pure results, falling into only one FFT result bin.  More often than not, I get a complementary bin which looks like a negative frequency, but the problem is that I would only expect this to happen with frequencies above Nyquist.  As a small side note, most bins seem to have a magnitude of at least 1, which I blame on my rectangular-to-polar conversion code or rounding errors.

Can anyone explain the ghosting?  What's strange is that the power (magnitude) seems to be shared between the expected bin and the mirror.  When only one bin has a magnitude (what I expect), the amplitude is near full (also expected), but when there is a second bin, the amplitude is shared so that the sum of the two would be approximately full.  I'm guessing that this means there is some distortion in the sine waves that I create, but I cannot explain it myself, and have reviewed the code.

Take a look, and tell me what's up.  Hopefully the source will explain everything I'm doing.  The printf() output tries to skip over frequencies where only one bin has magnitude, but whenever more than one bin has something significant, the DSP code prints a report to CCS3 with the amplitude as a 4-digit hex value, followed by the bin number in hex.  The last number on each line is the frequency number for the test signal, so you should expect the bin with the same number to be the only one with any amplitude.  Follow up here if running the project results in further questions.

P.S.  I realize that the code before and after rfft() is far from optimal.  If this were production code, then I would optimize and potentially write many of those loops using assembly intrinsics.  At the moment, though, I'm focused on the results from rfft() and not on full utilization of the DSP abilities.

Thanks in advance.

  • Hi Brian,

    The format of the DATA *x array is different for the RFFT() dsplib routine.

    rfft(DATA *x, ushort nx, type) accepts input in the following format, where DATA *x points to an array of 16-bit signed, real numbers (no complex parts) and nx is the length of the array.

    x[0] = x(0)Re,
    x[1] = x(1)Re,
    x[2] = x(2)Re,
    x[3] = x(3)Re,
    x[4] = x(4)Re,

    x[nx-2] = x(nx-2)Re,
    x[nx-1] = x(nx-1)Re


    “On output, vector x contains the first half (nx/2 complex elements) of the FFT output in the following order. Real FFT is a symmetric function around the Nyquist point, and for this reason only half of the FFT(x) elements are required.” (See the TMS320C55x DSP Library Programmer’s Reference)

    The RFFT function call leaves the DATA *x array in the following format after completing the RFFT, where y = RFFT(x):

    x[0] = y(0)Re,
    x[1] = y(N/2)Re,
    x[2] = y(1)Re,
    x[3] = y(1)Im,
    x[4] = y(2)Re,
    x[5] = y(2)Im,

    x[N-2] = y(nx/2-1)Re,
    x[N-1] = y(nx/2-1)Im

    Please make sure you are correctly reading the results of the RFFT routine.

    Also check out the data formatting of the dsplib examples and test vectors (located in <DSPLIB PATH>\ EXAMPLES\...)

    Thanks for your continued contribution throughout this forum.

    Hope this helps,
    Mark

  • Thanks for the reply, Mark.  I am aware that rfft() takes Real data and produces complex data (as Real and Imaginary pairs).  This is one reason why I provided a link to my small project source code.

    I will take a look at the DSPLib examples again to see if I can find any discrepancies.  Unfortunately, those examples are a bit too simple to show much.

    If you or someone could look at my code, then I think you'd find it interesting.  While I started out trying to printf() the results, which may not be readable for most, I have subsequently used the graphing features of CCS to display the array with the complex results.  The graph makes it clear that there are sometimes as many as four frequency bin results with significant magnitudes.  In any event, if I'm doing something wrong, it should be evident in my code.  Sorry to ask that someone look at this, but I've looked at this every way I can think of.

    About the only test I have not run yet is to use cfft() instead.  That might prove useful.  I could modify my test source to produce a complex data representation of a Real sine wave, then run the cfft() and see whether I get expected or unexpected results.  Meanwhile, if someone could at least take a quick look at my code, then perhaps we can figure out whether this is a bug or my error.

  • Hi Brian,

    I have the same problem as you. The difference is that it only appears with a 2048 FFT size.

    For rfft() with less then 2048 points you can solve this problem putting DATA, TWIDDLE and code in different DARAM blocks.

    Unfortunatelly, it doesn´t work for a 2048 points.

    I need an urgent solution to this ( I´m using TMS320C5502 ).

    Regards

  • Thanks for the hint, Joel. I double-checked my project cmd file, and I already have data and twiddle in different DARAM blocks. The code is in a SARAM block, but I assume that code should not need to be in DARAM.

  • I am still experiencing errors in the DSPlib rfft() routine, and I am close to shipping my C5506 product!  The way that the expected magnitude of a pure sine wave is shared across 1, 2, or 4 bins makes it impossible to utilize the rfft() routine.

    I would appreciate it if someone from Texas Instruments would look at the CCS project that I uploaded, and tell me whether the errors are expected, or if I have made some kind of error.  If you build my project, you should see the exact problem, and you'll have my .cmd files and everything so you can see how I have everything aligned.  The only files missing are cbrev.asm, cfft_scale.asm, mul32.asm, sqrtv.asm, twiddle.asm, and unpack.asm - all of which come from the latest DSPlib distribution.

    @Mark, I am using rfft() exactly as specified.  In fact, the documentation is exactly what I would expect compared to other DSP libraries.  The only thing unexpected are the errors in the output data.

    @Joel, thanks for confirming that you found the same or similar problem.  However, I started out with DATA, twiddle, and code in different DARAM blocks.  Actually, all of these details are shown in my sample CCS project, which is why I bothered to upload it in the very first installment of this topic.

    Please, can anyone help out here?

  • You could always work around it by using cfft() and just passing in zeros for the imaginary parts of the complex input.

    Jason

  • I too, am seeing this issue. I'm using the C5509A, dsplib 3.3, CCSv4, 2048 FFT size. I get clear Ghosting around my 20Mhz sampling rate. what's odd is that I had it working  (lower speeds) with CCS3.3 and the C5502.

    Any movement on this front today?

    Perry

     

  • My provided test code which reproduces this problem generates pure sine waves which should align with a single frequency bin on output (of a 128-point FFT).

    rfft() only works correctly with 12 of the 64 possible frequencies, meaning that over 50 frequencies have spurious results.  The unexpected outputs with the highest amplitudes seem to correlate to aliases of the input frequency.

    cfft() improves on this by only getting 12 wrong, and the remaining 52 frequencies produce the expected results.  Strangely, though, the unexpected outputs seem completely unrelated to the input frequency, so I need to do some more research to find an explanation for this.  Some look like typical smearing, some look aliased, and some seem random.  With a complex input of 32 frequencies, these unexpected results would add up and reduce the accuracy of the results significantly.

    At this point, my theory is that the fft is measuring the difference between the two sin data sources as noise, and that "noise" is what produces the spurious results.  I'm using the sin table in the C5506 ROM to generate pure frequencies, but the values do not match the twiddle table.  My intention is to rewrite my test code to either use the twiddle table to generate my input frequencies, or perhaps to rewrite cfft() to use the ROM tables (the latter is obviously a riskier endeavor).  I expect that if I use the exact same sin data for twiddle and input waveform, then the frequency-domain output should be pure.

    P.S.  It seems that the cfft() routine could be rewritten to use the ROM sin table for up to 256-point FFT, while the existing twiddle tables must be used for larger FFT calculations up to 1024-point, 2048-point, or 4096-point.  Has anyone tried that?  Would using the ROM slow down the performance?

    Sorry for the sidetrack.  I will try to improve my test code by removing its use of the ROM sin tables and see where that gets me.  Every other FFT library that I have used will produce expected results in only a single frequency bin when I create a pure frequency sine wave as input.  In this particular product, I am using the C5506 to measure pure sine frequencies that are also generated by the C5506 (via DAC), so I really need the results to be accurate.  I could avoid those 12 "bad" frequencies in my design, but I would really like to understand the problems with cfft() before moving on.

  • @Perry: Where did you obtain dsplib 3.3?  Is it appropriate for the C5509A?

    I am working with dsplib v2.40, which is where I discovered and am reproducing these errors.  The following page implies that v2.40 is for all C55x processors.

    TMS320C55x DSP Library - SPRC100 - TI Software Folder

  • Brian, my apologies. I'm using 2.40. I was referring to the CCS install, sorry. This problem is persisting, it appears to be a data alignment issue entirely. I can change and repeat the ghosting of a single sinusoid based on the sectional placement of the .input, twiddle and .fftcode. At times the ghost is larger than the fundamental, sometimes equal and others it looks like a standard harmonic in roll off. All repeatable and based on a single sinusoid.

    Sorry for the bad info, hopefully I can solve this tonight.

    Perry

  • I have similar problems with 2048 points FFT. I contact TI support, receive the following answer and solve the problem.

    " It wasn't clear from your email if you have adjusted the twiddle table size to handle the larger FFT size.  The default lib is configured for 8 to 1024 point FFTs.  If you want to make a larger FFT, then you need to make some modifications and rebuild the lib for your application to use.  Details are at this Embedded Processor Wiki site: http://wiki.davincidsp.com/index.php/DSPLIB."

     

  • Brian, Joel etal,

    I was able to get past the issue. In the end, the 2048 FFT using rfft() and the twiddle2048.asm table needed properl aligned in the appropriate sections. Here is my cmd file, hopefully that sheds some light on the particulars.

    MEMORY {

    PAGE 0: /* ---- Unified Program/Data Address Space ---- */

    MMR (RWIX) : origin = 0x000000, len = 0x0000c0 /* MMRs */

    /*VECT (RIX) : origin = 0x000100, len = 0x000100*/

    /*DARAM (RWIX) : origin = 0x000200, len = 0x00dd00*/

    VECT (RIX) : origin = 0x000200, len = 0x000100

    DARAM (RWIX) : origin = 0x000300, len = 0x00ec00

     

    DMAdata (RWIX) : origin = 0x00ef00, len = 0x001000

    DAParms (RWIX) : origin = 0x00ff00, len = 0x000100

    SARAM0 (RWIX) : origin = 0x010000, len = 0x010000 /* 64KB */

    SARAM1 (RWIX) : origin = 0x020000, len = 0x020000 /* 128KB */

    FLASH_CE0 (RWIX) : origin = 0x040000, len = 0x200000 /* 2MB */

    FLASH_CE1 (RWIX) : origin = 0x400000, len = 0x200000 /* 2MB */

    /*VECT (RIX) : origin = 0xffff00, len = 0x100*/

    /*PDROM (RIX): origin = 0xff8000, length = 0x008000 /* 32KB */

     

    PAGE 2: /* -------- 64K-word I/O Address Space -------- */

     

    IOPORT (RWI) : origin = 0x000000, length = 0x020000

    }

     

    SECTIONS{

    DMA_data : {} > DMAdata, align(4) /* uninitialized section */

    DAparms_sect : {} > DAParms /* working parameter table */

    .text : {} > DARAM

    .cinit : {} > DARAM

    .stack : {} > DARAM

    .sysstack : {} > DARAM

    .bss : {} > DARAM

    .sysmem : {} > DARAM

    .cio : {} > DARAM

    .data : {} > DARAM

    .const : {} > DARAM

    .csldata : {} > DARAM

    .vectors : {} > VECT

    .fftcode : {} > SARAM0

    .data:twiddle : > SARAM0, align(2048)

     

    }

    Note the DMAdata section is the same as ".input" in the examples. I used a pragma to place the inbuff in code.

     

     

     

     

     

     

     

    #pragma

     

     

    DATA_SECTION(InputBuffer1,"DMA_data");

    short

     

     

     InputBuffer1[SAMPLEBUFFERSIZE]; //2048

    I also pulled the twiddle2048.asm file into the workspace.

    Using:

    CCSv4, dsplib 2.40, C5509A, CSL(yes), DSP/Bios(no)

    hope this is helpful, I'm on to the next task....

     

  • @Perry, thanks for the effort, but if you downloaded my project you'd see that I already have the twiddle data in it's own memory buffer and it is aligned properly.  I've even double-checked the .map file to confirm.  Also, I'm using a 128-point FFT, so the issues have nothing to do with twid2048.asm or twid4096.asm

  • Brian, I downloaded your project. The cmd file places the the twiddle table in DARAM and it doesn't specifically align it on a double boundry. All I did was read the file so it may end up placed as you suggest.

    I do know this: If I place "my" twiddle table at:

    .data:twiddle : > SARAM0, align(2048) it works perfectly.

    If I place the table at:

    .data:twiddle : > DARAM, align(2048) I get alias ghosting that is absolutely repeatable, has frequency dependent characteristics.

    This suggests that the table needs to be in SARAM, and in a different block than the fftcode. the 5506 has 8 block to place in.

    It appears you've gone through most of this, my apologies if this doesn't help.

    Good luck,

    Perry

  • Thanks, Perry.  It turns out that alignment was the issue after all.  However, I think that it was the fft() source data that was misaligned, maybe not the twiddle table.

    When I started my firmware project, I believe that the dsplib was older than v2.40, or at least something was different.  I remember that CCS would not build happily until I created a section named ".twiddle" - and ever since then the linker was happy.  I believe that I probably switched from CCS3 to CCS4 in the interim.  I'm actually not sure why it ever built with the section name of ".twiddle" ...

    The way that I found the solution was that I wanted to access the twiddle table from C for testing purposes, and so I had to rename the array as _twiddle in assembly, to match the C name.  Once I renamed it, the linker complained again, and that's when I noticed that the twiddle table lives at ".data:twiddle" by default, and I swear that I never saw that before.  The important fact is that I never mentioned ".data:twiddle" in my .cmd file, so I clearly was not controlling the memory map.  In any event, I now have my twiddle table in a section named "twiddle" and it is aligned properly.

    As a safeguard, I used the C compiler pragma for alignment of my fft data so that hopefully the alignment will not be a problem in the future. e.g.:

    #pragma DATA_ALIGN(array, FFT_WINDOW);

    #pragma DATA_SECTION(array, "fftData");

    DATA array[FFT_WINDOW+2];

    I plan to follow up on this issue and see whether the assembler has an equivalent alignment directive, especially for those cases where I do not need to access the data from C.

    Thanks for being persistent, Perry, and for looking at my project, even if only briefly.  I had been staring at this for a while before I figured out what was wrong, and it really took the addition of a new feature (the C test code) to really make it clear.

  • I suggest that adding the '.align' keyword in twiddle.asm would serve to alleviate these sorts of problems in the future.  The signal data for the fft should probably be aligned, too, which is the responsibility of the programmer, but at least the dsplib sources could align their own data where necessary.