Forum Discussion
Altera_Forum
Honored Contributor
17 years agoAltera FFT - output scaling. How to do this correctly?
Hi,
I do not get correct scaled results if I do everything the FFT user guide recommends. I have instantiated a 1024 points FFT, with single output and burst architecture. The data width and twiddle factor precision is 16bits. I input 1024 times 16bit signed integers from the ADC to the 16bit real-input of the FFT module. (The ADC has a +/-2.5V input) To the imaginary input of the FFT module I apply 16bit zeros. This should be correct, shouldn’t it? The FFT module outputs 1024 pairs of 16bit real and 16bit imaginary part. First question: Are the real and imaginary 16bit values the mantissa in the sense of a floating point mantissa (first bit = sign, second bit = 1.digit in front of the radix point, remaining 14bits = decimal places) OR do I have to interpret these values as fixed point values. I have a sample frequency of 3kHz and used a very low frequency of 1Hz and 9Hz to verify the scaling to not have additional falsifications, caused by the rectangular window I used for the first shot. I input samples of an ideal sine waveform with different amplitudes and the same frequency, BUT whether I scale the output back with the given exponent value or not, the calculated magnitude is not the expected one. For example: For a 9Hz sine with an amplitude of +/-2,3V which is equal to +30146/-30147 decimal (+/-2.5V ADC input) I got the following real and imaginary parts for the corresponding frequency. Real: 3384 (0x0D38) Imaginary: -14391 (0xC7C9) I would expect a magnitude in the range of 30146 decimal, but using these values the magnitude is 14783. Using the exponent makes the results too big and I see no relation to input signal with such big numbers. How can such a large output (31bits in my case, max exponent = 15bits) provide unity gain, if the input is much smaller (16bits). Just choosing the lower 16bits of the shifted/scaled values can not be correct, because of the sign. I am a bit confused. Can somebody give me a hint? An example calcualtion would also be a great help.22 Replies
- Altera_Forum
Honored Contributor
First, your imaginary value being set to zero is ok. It simply means you are dealing with real input and so expect two lines in the spectrum for each input frequency.
second, you need to think of fixed point only(unless your fft is not). third, you should get two symmetrical bins at nearly your amplitude value. Your figure suggest a multiplication by 2(2 x 14783 =~ 30146). About scaling: (why you say input is 16 bits, output is 31 ?) If altera says divide output by 2^exp i.e. multiply by 2^-exp then: division: use shift towards LSB extending sign bit and discarding those LSBs that drop off as you shift. for mult:shift magnitude bits towards sign bit inserting leading zeros. - Altera_Forum
Honored Contributor
I get two symmetrical lines in the spectrum. Fine, so far. The frequency values are the expected ones.
second, you need to think of fixed point only(unless your fft is not). Since I'm using burst architecture, I can not chose fixed or floating point representation like with the variable streaming architecture. I assume the output is fixed point by default. Another issue is, that referring to Alteras documentation, it seems to me that Altera does not differ between integer and fixed-point data format. Theortically that is not the same, but if +0,9999999 (fixed-point) or +32767 (integer) is just a question of interpretation, istn't it? The digital number I give the FFT module is the same, in this case 0x7FFF (16bit). third, you should get two symmetrical bins at nearly your amplitude value. I do not get neraly identical bins at neraly my amplitude value. I monitored the direct output signals of the FFT Megacore instance with the SignaltapII and I got 0x0D38 (real) and 0xC7C9 (imag) at the output for the frequency part I'm interested in. The exponent output shows 0x36 for the entire block. So this is not a bug in the wrapper I wrote around the FFT instance. That's why I asked for the input format, too. Maybe the input is not correct. Input example: For a 9Hz sine signal, sampled with 3kHz and an amplitude of 2,3V the maximum input amplitude integer (16bit signed) is 30147 decimal. This should be correct. about scaling: (why you say input is 16 bits, output is 31 ?)if altera says divide output by 2^exp i.e. multiply by 2^-exp
then:
I said the output is 31 bit, because I referred to the FFT user guide, where it is mentioned that you have to extend your output by the maximum number of shifts, that is possible... in my case 15bits. Since I'am using the NiosII processor to process the results I can use the entire 31 bits for a high resolution, if I want. But where is the difference between these: --output unscaled 0x7FFF -- output scaled: shifting e.g. four times left (mult) 0x7FFF0 --> 0x7FFF when taking the upper 16bits I win no new information with this multiplication, do I? for mult:shift magnitude bits towards sign bit inserting leading zeros. Should I scale the magnitude? I think you mean I should scale real and imginary parts. We should not mix the terms here. This is confusing. By the way the spectrum is always mirrored at half the sample rate. The information above fs/2 is redundant and obviously not helpful. Why is this output? I could save half the memory by dropping the data above fs/2. Is this correct?
- Altera_Forum
Honored Contributor
--- Quote Start --- By the way the spectrum is always mirrored at half the sample rate. The information above fs/2 is redundant and obviously not helpful. Why is this output? I could save half the memory by dropping the data above fs/2. Is this correct? --- Quote End --- yes and no. If you don't want phase information then yes. amplitude is symmetrical around dc(and around Fs/2 as well) but phase may not be. The issue becomes relevant if you frequency shift your sinusoid to higher point e.g. move from dc to a carrier at 1MHz then you get two side bands, one from each side of your baseband (around dc or each side of Fs/2). --- Quote Start --- Another issue is, that referring to Alteras documentation, it seems to me that Altera does not differ between integer and fixed-point data format. Theortically that is not the same, but if +0,9999999 (fixed-point) or +32767 (integer) is just a question of interpretation, istn't it? The digital number I give the FFT module is the same, in this case 0x7FFF (16bit). --- Quote End --- integers are represented as fixed point. If you think of .1, .2 ...etc then they are first scaled to integers to be represented on the binary ladder of 16bits. Scaling is up to your flavour,possible signal range and some other factors. --- Quote Start --- --output unscaled 0x7FFF -- output scaled: shifting e.g. four times left (mult) 0x7FFF0 --> 0x7FFF when taking the upper 16bits I win no new information with this multiplication, do I? --- Quote End --- The issue of scaling makes sense only if there is room to scale, otherwise altera got it wrong ! Frankly I don't understand why altera left scaling for the field engineer. --- Quote Start --- for mult:shift magnitude bits towards sign bit inserting leading zeros. Should I scale the magnitude? I think you mean I should scale real and imginary parts. We should not mix the terms here. This is confusing. --- Quote End --- magnitude bits as opposed to sign bit, context obvious --- Quote Start --- I do not get neraly identical bins at neraly my amplitude value. I monitored the direct output signals of the FFT Megacore instance with the SignaltapII and I got 0x0D38 (real) and 0xC7C9 (imag) at the output for the frequency part I'm interested in. The exponent output shows 0x36 for the entire block. So this is not a bug in the wrapper I wrote around the FFT instance. That's why I asked for the input format, too. Maybe the input is not correct. about scaling: (why you say input is 16 bits, output is 31 ?) if altera says divide output by 2^exp i.e. multiply by 2^-exp then: I said the output is 31 bit, because I referred to the FFT user guide, where it is mentioned that you have to extend your output by the maximum number of shifts, that is possible... in my case 15bits. Since I'am using the NiosII processor to process the results I can use the entire 31 bits for a high resolution, if I want. But where is the difference between these: --- Quote End --- The exponent has to be a small value and having 15 bits is unreasonable. Do you mean maximum exponent value can reach 2^15 -1 = 32767 Do you then mean you shift 32767 bits. Altera IP gurus must limit the exponent to a realistic value in harmony with output bit width. - Altera_Forum
Honored Contributor
I mean the exponent is 6bits signed and can range from 1 to -15, referring to page A-2 of the FFT user guide.
The user guide tells to scale the output with the exponent value of the processed block. Doesn't that mean that I have scale every block of 1024 points I process? But if not, what is the exponent output for? The question is: Do I have to use the exponent value? And how do I have to apply it exactly. The goal is to get the amplitude spectrum. Right now I calculate the magnitude this way: mag = sqrt(16bitreal^2 + 16bitimag^2) ...(the result is too small) I also tried: mag = sqrt((16bitreal^2 * 2^(-exponent)) + (16bitimag^2 * 2^(-exponent)) ) ... the result is too large, compared to the input range. Is this correct? - Altera_Forum
Honored Contributor
A further note on shifting:
imagine your data is 15bits if data = Ox00ff (255) shift 4 bits towards sign bit to become Ox0ff0 (or Ox0ff&four internal bits if available). = 4080 = 16 x 255 or shift 4 bits towards LSB to become Ox000f = 15 =~ 255/16 if your data = Ox7FFF (your example) = -1 shift 4 bits towards sign bit to get Ox7FF0 = -16 or 4 bits towards LSB to get Ox7FFF = -1 (this is the odd case, woudn't divide) - Altera_Forum
Honored Contributor
--- Quote Start --- I mean the exponent is 6bits signed and can range from 1 to -15, referring to page A-2 of the FFT user guide. The user guide tells to scale the output with the exponent value of the processed block. Doesn't that mean that I have scale every block of 1024 points I process? But if not, what is the exponent output for? The question is: Do I have to use the exponent value? And how do I have to apply it exactly. The goal is to get the amplitude spectrum. Right now I calculate the magnitude this way: mag = sqrt(16bitreal^2 + 16bitimag^2) ...(the result is too small) I also tried: mag = sqrt((16bitreal^2 * 2^(-exponent)) + (16bitimag^2 * 2^(-exponent)) ) ... the result is too large, compared to the input range. Is this correct? --- Quote End --- yes you apply exponent per block as it varies. Your scaling formula is correct but how do you implement in hardware. do you apply shift or use multipliers in hardware? or do you get results wrong on paper anyway? - Altera_Forum
Honored Contributor
When you say exponent is Ox36 then make sure this is not used as 36 but as -10 in 2's complement...
- Altera_Forum
Honored Contributor
Now it seems to work. But I don't know the correct reason why. Let me describe.
First of all I use a Nios II processor to handle the data of the FFT. I don't scale anything in hardware. Performance is not a problem right know. I wrote a wrapper around the FFT instance so that the real and imaginary outputs are merged to a 32bit bus and the exponent can be read separatetly. here my C-code for the Scaling: samples_u32arr[] is an static array holding all 1024 32bit values (16bit real+16bit imag) i read from the fft module. number_of_samples = 1024. for(samplecounter_u16 = 0; samplecounter_u16 < number_of_samples; samplecounter_u16++) { real_16arr[samplecounter_u16] = (int16_t)samples_u32arr[samplecounter_u16] & 0xffff; imag_16arr[samplecounter_u16] = (int16_t)((samples_u32arr[samplecounter_u16] >> 16) & 0xffff); real_32arr[samplecounter_u16] = ((int32_t)real_16arr[samplecounter_u16]) << -exponent_s8; imag_32arr[samplecounter_u16] = ((int32_t)imag_16arr[samplecounter_u16]) << -exponent_s8; real_16 = (int16_t)((real_32arr[samplecounter_u16] >> 9) & 0xffff); imag_16 = (int16_t)((imag_32arr[samplecounter_u16] >> 9) & 0xffff); result_u16arr[samplecounter_u16] = (uint16_t)sqrt(real_16*real_16 + imag_16*imag_16); } The strange thing is, that I have to shift the data right 9 times, after I shifted it left with the value of the exponent, which is changing with the input data. I don't know why exactly the number 9!!! But if I do shift 9 times right and choose the lower 16bit the output amplitude corresponds to the input amplitude, with small deviations. I tested the scaling with a sine and a rectangular waveform with different amplitudes at the same frequency of 9Hz (3kHz sample rate). So there are no additional deviations caused by the window function (in this case just a rectangle). - Altera_Forum
Honored Contributor
I am not sure about the 9 bits shift but here is my suspicion:
If your fft output bitwidth is 25 bits while your input bitwidth is 16 bits then I guess the IP may introduce 2^9 scaling to fill up all output bits. Thus you need to divide back by 2^9. You can just take the 16 MSBs instead of shifting right then taking lower 16 bits. - Altera_Forum
Honored Contributor
--- Quote Start --- I am not sure about the 9 bits shift but here is my suspicion: If your fft output bitwidth is 25 bits while your input bitwidth is 16 bits then I guess the IP may introduce 2^9 scaling to fill up all output bits. Thus you need to divide back by 2^9. You can just take the 16 MSBs instead of shifting right then taking lower 16 bits. --- Quote End --- My output is not 25bits wide. It is 16bits + a maximum of 15bits. So chosing the MSBs only is not a good idea