Generating a high SNR sinusoide with lut on a DSP

I'm generating a sinus by using a lut method on a DSPic33f. My sample rate is 48 kHz, so I saved 12000 of the first value (unsigned int, 16 bits) and use trigonometric formulas to calculate the other parts of the sinusoid. I can change the frequency of this sine by turning a rotary encoder. Every 2 ms, a timer interrupt read the value of the rotary encoder, and the value (frequency in Hz) will be the new index to read my lut.

A bit of the code for you to understand :

ANGLE += FREQUENCY_VALUE;
if(ANGLE < SINE_TABLE_LEN)
{
index  = ANGLE;
sign = 1;
}
else if((ANGLE >= SINE_TABLE_LEN) && (ANGLE < SINE_TABLE_LEN2))
{
index = SINE_TABLE_LEN2 - ANGLE;
sign = 1;
}
else if((ANGLE >= SINE_TABLE_LEN2) && (ANGLE < SINE_TABLE_LEN3))
{
index = ANGLE - SINE_TABLE_LEN2;
sign = -1;
}
else if((ANGLE >= SINE_TABLE_LEN3) && (ANGLE < SINE_TABLE_LEN4))
{
index = SINE_TABLE_LEN4 - ANGLE;
sign = -1;
}
else
{
ANGLE -= SINE_TABLE_LEN4;
index = ANGLE;
sign = 1;
}
audio_sample_l = sign * sine_lut[index-1];
audio_sample_r = audio_sample_l;


I initialize my variable ANGLE at 0 and FREQUENCY_VALUE at 1. When a plot the fft of my sinus I get some odd harmonic noise that I can not explain. When I compare the output samples with the one from my lut, they are the same (no index error). The problem may come from the fact that I do not start at the beginning of my lut but at the previous sample to avoid phase changes. There may be some rounded error also. My third lead is the initialization values and the index calculation (test in the loop also). What do you think ?

Anyone have some ideas to change that ?

• You can easily check if this is a rounding problem by plotting the spectrum of your LUT data (after extension to a full period) with a program like matlab. Where does the spectrum plot come from? Could it be distortion from the DAC/output amp? – pichenettes Jul 29 '14 at 12:24
• how many points are in your LUT? and why do this silly sign-fiddling thing? – robert bristow-johnson Jul 29 '14 at 13:28
• There are 12000 points ; so I guess storing a quarter period and extending it through symmetry helps saving space. – pichenettes Jul 29 '14 at 13:30
• Your "else" catch-all will only work with FREQUENCY_VALUE=1 – Mark Borgerding Jul 29 '14 at 15:00

Trace your logic when ANGLE is incremented to SINE_TABLE_LEN4 -- you'll find you are accessing below the buffer. You should really adopt a zero-based index scheme when coding in C.

• I have change my index scheme because I have seen that i got some differences between my samples values and a pure sine generated by Audition. I also add some initialization flag to start from index 0 at the beginning of the program and when I change my frequency value. It breaks my the continuity of the signal but it improves my "SNR" from 20 dB ! So thanks for the tip :) – Nessy W. Jul 30 '14 at 7:21

You are generating a stair-stepped sine wave, which is why you get so many harmonics. Since you are using 12000 points for your LUT, the stair steps are small so the harmonics are already fairly low - your picture says that your generated signal (100Hz) is 80dB above the strongest harmonic. Any frequency that you try to generate that doesn't divide evenly into 48000 will cause the harmonics to be worse to some extent because your FREQUECY_VALUE won't be an integer in those cases, causing the the value that you look up to be slightly off the correct value.

This is pretty much what you can expect for the method you are using.See the examples here for comparison. You could follow the suggestions on that linked page and get the same performance from a smaller LUT, or get much better performance from your monster LUT.

How clean do you really need your generated signal to be?

• Actually FREQUENCY_VALUE is always an integer. When my frequency is a divider a 48000, my noise floor are around -190 dB and my harmonic around -100. Otherwise, my noise floor is around -120 dB and harmonics are drown in it. But I do understand that I cannot do better than 100 dB of difference because of my lut size, my register size (16bits) and my sampling frequency. Thanks for the link ! – Nessy W. Jul 30 '14 at 7:40
• Yeah, I figured FREQUENY_VALUE would be an integer. I wasn't very clear in my answer. What I should have said was "FREQUNECY_VALUE would need to have digits after the decimal point, but can't since it is an integer." – JRE Jul 30 '14 at 8:16
• I don't understand when you say ""FREQUNECY_VALUE would need to have digits after the decimal point". FREQUENCY_VALUE is an int because I don't need frequency such as 75.9 etc. But if I define ANGLE as a float, how should I calculate it as a function of FREQUENCY_VALUE ? – Nessy W. Jul 30 '14 at 12:02
• FREQUENCY_VALUE and ANGLE are int because you are using ANGLE as an index into an array without converting from float to int. What I meant about needing decimals is this: Assume you want to generate the frequency 666Hz using your LUT. That works out to 72.072072072 repeating as the correct number of samples for each cycle. This also means that FREQUENCY_VALUE will have to be fractional. Since it is an int, your synthesis will be inaccurate in frequency, and will also have more harmonics than when generating a frequency such that FREQUENCY_VALUE is really an integer value. – JRE Jul 30 '14 at 12:42