views:

82

answers:

5

I'm working on a microcontroller DDS project in C and am having some trouble figuring out how to compute a linear interpolation to smooth the output values. The program as it stands now
uses the top 8 bits of a 24 bit accumulator as an index to an array of 8 bit output values. I need to come up with a function that will take the middle and lower byte of the accumulator and generate a value in between the "previous" and "next" value in the array. This would be straightforward enough on fast hardware, but since I'm using a microcontroller I really need to avoid doing any floating point operations or divisions!

With those restrictions, I'm not certain of a way to go about getting an 8 bit interpolated value from my two 8 bit input numbers and the lower 2 byes of the accumulator, which represents the "distance" between the two input values. Thanks in advance for any advice!

CLARIFICATION

DDS = Direct Digital Synthesis

in DDS a waveform is generated from a lookup table using a phase accumulator. The phase accumulator usually contains an integer component and a fractional component. The integer component is used as an index into the lookup table. In simple DDS implementations the fractional part is ignored, but for higher quality output the fractional component is used to interpolate (usually just linear interpolation) between adjacent lookup table values. For the above question we are looking at how to efficiently perform this linear interpolation between two lookup table values for a given fraction, f, where 0 <= f < 1.

A: 

Linear interpolation between two values, a and b is (a+b)/2.

This is easy in simple hardware and involves no division or floating point.

Divide-by-2 == shift right one bit.

S.Lott
That only interpolates the mid-point - for the above question you probably need to interpolate an arbitrary point.
Paul R
"Arbitrary"? The mid-point seems as arbitrary as any other point.
S.Lott
@S.Lott: if you read the question he's doing DDS with a phase accumulator - the fractional part of the phase accumulator determines the point between two values at which you need to interpolate. As for "arbitrary", my dictionary says: *2. Mathematics (of a constant or other quantity) of unspecified value.*
Paul R
@Paul R: "generate a value in between the "previous" and "next" value in the array" Somehow doesn't mean "midpoint"? Interesting. Do you think the question should be updated to supply this missing piece of the puzzle?
S.Lott
@S.Lott: I think the OP is probably assuming (wrongly) that most people on SO will be familiar with DDS and phase accumulators.
Paul R
@Paul R: But you see no value in fixing the question? No suggestion to the author? What about other people new to this topic who might be helped, but don't share the in-depth assumptions you're claiming are somehow obvious.
S.Lott
S.Lott: I think all the required information is actually present in the question if it's read carefully, but if you have some ideas on how to make it clearer then I'm sure they would be welcome.
Paul R
@Paul R: "I think all the required information is actually present in the question". Obviously. Since at least one person missed the required information, then the question is not perfect is it? "how to make it clearer"? Are you serious? What you said in these comments is **obviously** missing from the question. Why are you asking for ideas? If at least one person didn't get the question, then it can -- obviously -- be improved. Right? What am I missing? What are you asking for?
S.Lott
@S.Lott: I'm not asking for anything - as it happens the question was reasonably clear to me, since I come from the relevant background. In order to clarify the question you'd need to ask someone who *didn't* understand it what part they would like to be made more detailed/explicit.
Paul R
@Paul R: And this thread of comments is somehow **not** "someone who didn't understand it". Who could you find that didn't understand it? What thread of comments could you read to get ideas for what was missing? Any clue? Any suggestions where someone might find a string of comments that clearly shows some confusion? Any hint at all?
S.Lott
@S.Lott: you've lost me now.
Paul R
@Paul R: Let's recap. This thread of comments shows that the question is not "perfect". It clearly shows how the question can be improved by adding information to the question. This thread of comments. I asked if you -- since you understand this thread of comments -- could suggest something. Is that clear? This thread of comments obviously to contains additional useful information. This thread of comments. I'm the person who didn't understand the question. You provided clarification. Is that clear? What suggestions could you make to improve the question based on your comments to me?
S.Lott
@S.Lott: You want me to edit the question to make it clearer ? Or you want me to make suggestions to the OP to make it clearer ? I'm happy to help - I'm just not sure what you want me to do, exactly.
Paul R
@Paul R: "Do you think the question should be updated to supply this missing piece of the puzzle?" If so, could you suggest fixes to the author? Could you just think about people -- like me specifically -- who did not understand specific parts of the question? After thinking about other people (me specifically, if that helps you think) could you suggest changes to the author? Could you help all the other people who have the same problems I had? Could you just think about improving the question? Perhaps make a suggestion to the author? Just consider the confused masses for a moment?
S.Lott
@S.Lott: OK - I've added some clarification to the question - HTH.
Paul R
Thanks for taking the time to add the clarification.
Bitrex
A: 

If you really need the speed, check out AVR assembler.

Amigable Clark Kant
+1  A: 

If you want better precision, i'd suggest checking the lower bits of the accumulator first. For example, if we want 4 output values instead of 1:

Acc += 0x2000;
uint lower_bits = Acc & 0xffff;
int rl = LUT[ Acc >> 16];
int rh = LUT[(Acc >> 16) + 1];
if (lower_bits < 0x4000)
    return rl;
if (lower_bits < 0x8000)
    return (rl * 3 + rh) >> 2;
if (lower_bits < 0xC000)
    return (rl + rh) >> 1;
return (rl + rh * 3) >> 2;
ruslik
+5  A: 

Assuming you have a table of waveform values (either one quadrant or four quadrants, it doesn't matter) then one possible optimisation is to store the delta values betwen successive table values. I.e. if you have e.g. N = 256 and a waveform table LUT[N] then you also have a table of delta values, LUT_delta[N]. The relationship between the two precomputed tables is LUT_delta[i] = LUT[i+1] - LUT[i]. So instead of looking up two consecutive table values, LUT[i] and LUT[i+1], subtracting these to get the delta, then doing the interpolation, you just look up the first table value, LUT[i], and the delta, LUT_delta[i] and then calculate the interpolated value. This requires the same number of table lookups, but fewer math operations. You should be able to do the interpolation with a single multiply-accumulate instruction if you're using a DSP, otherwise it's a multiply + scale + add on a general purpose CPU. Also if you interleave the LUT and LUT_delta values you may be able to look up LUT[i] and LUT_delta[i] with a single read and then unpack the two values.

Pseudo-code:

extract integer LUT index, i, from accumulator // just need a shift for this
extract fractional part of accumulator, f // mask or subtract to get f
get p = LUT[i] // lookup waveform value
get delta = LUT_delta[i] // lookup delta
calculate p_interp = p + p_delta * f // single multiply-accumulate instruction on most DSPs - need scaling on general purpose CPUs
Paul R
I'm probably not understanding you correctly - but with a 24 bit accumulator wouldn't that mean I'd have to store a delta value for all 2^16 increments between each point in my table? If I had that kind of space I would've just stored the table at higher resolution and not bothered with interpolation! :)
Bitrex
@Bitrex - no if you have an N point table (e.g. N = 256) then you just need a second N point table for the deltas, where `LUT_delta[i] = LUT[i+1] - LUT[i]`.
Paul R
Ah, I see. I think I have enough room for a table that size!
Bitrex
@Bitrex: you might want to benchmark first, without the second table, and then only consider this optimisation if you really need it. Use powers of 2 for table size etc, as another poster said. Also note that the delta values need to be signed (unless you're just using a single quadrant table).
Paul R
+2  A: 

To make linear interpolation without doing division, you should make sure that your denominator is a power of 2.

value(x) = previous,
value(x+1) = next value(x + dx) = previous + (next - previous) * dx

Your question is, how do I compute dx ? The trick is to have your interpolation index (the 16 low bit of your accumulator) computed so that the maximum value (dx = 1) is a power of two :

value(x + dx) = previous + ((next - previous) * index) / 1024

Here, you have computed your step value, so that the maximum step is 1024 and corresônds to dx=1. Index = 512 is for dx=0.5 etc...

shodanex
I see, so in my case with a 24 bit accumulator using the top 8 bits as an index (2^16 step value) the division should be by 2^16 or a 16 bit right shift.
Bitrex
Yes, the problem being that you must be able to store (next - previous)*2^16 in your computation.
shodanex