Go Down

Topic: Interpolation: 12-bit DAC with 8-bit tables (Read 1 time) previous topic - next topic

bendedavis

I'm working on an LFO which has it's waveforms stored in 8-bit arrays. What would be the best way to interpolate the values in those tables to output to a 12-bit DAC?

AWOL

That probably depends on how long you have got to do the arithmetic.
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

CrossRoads

Multiply them all by 1.5 before sending them?
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

bendedavis


Multiply them all by 1.5 before sending them?


If that was the case, I'd just use map(). I want as much resolution from the 12-bit DAC as possible while still using arrays with only 256 values. I need some code to fill in values in between.

Tom Carpenter

surely all it would need would be something along the lines of:

Code: [Select]
byte eightBitValue = ...
int twelveBitValue = (int)eightBitValue << 4;
~Tom~

bendedavis


surely all it would need would be something along the lines of:

Code: [Select]
byte eightBitValue = ...
int twelveBitValue = (int)eightBitValue << 4;



That would still only give 256 values. I need 4096 values, filling in all the spaces left out from the 8-bit table. Need a way of interpolating between each value in the table.

Docedison

Majenko??? Why 1.5?, the data was literally converted to an uint_8 value when it was 'clipped' to fit into 8 bits. The additional resolution simply isn't there anymore.
Or did I just make a BIG assumption?. Or is it just to make the data 10 bits long again? I really don't understand this at all.

Doc
--> WA7EMS <--
"The solution of every problem is another problem." -Johann Wolfgang von Goethe
I do answer technical questions PM'd to me with whatever is in my clipboard

PeterH


I'm working on an LFO which has it's waveforms stored in 8-bit arrays. What would be the best way to interpolate the values in those tables to output to a 12-bit DAC?


I'm not sure what you're trying to achieve. Do you have a waveform sampled at discrete points (using 8 bit resolution) and you're trying to output it as smoothly as possible using 12-bit values? In that case you would use an algorithm to interpolate between the sample values. For example you could use a linear interpolation as the first stab. Depending how fussed you are about the exact shape of the wave form there may be better interpolation algorithms based on curve fitting. But linear interpolation is quick and easy.
I only provide help via the forum - please do not contact me for private consultancy.

CrossRoads

#8
Jun 28, 2012, 12:56 am Last Edit: Jun 28, 2012, 01:02 am by CrossRoads Reason: 1
The interpolation requires some "look-ahead" capability to fill in the gaps.
If the output frequency is slow,  then the code needs to know the time for sample[ x ], the value of sample[ x+1 ] at time 2. If the time between sample is always the same, then the number of values to interpolate between time1, sample[ x ] and time2, sample[ x+1 ] can be generated I think in a predictable manner.

For example if the time between was to be split into 4 groups, then the math might be:
delta = sample[ x+1 ] - sample[ x ]
sample = delta/5 ( can be positive or negative )
at time x.1, output = sample[ x ] + sample
time x.2,  output = sample[ x ] + 2*sample
time x.3, output = sample[ x ] + 3*sample
time x.4, output = sample[ x ] + 4*sample
time2, output = sample[ x+1 ]  ( or sample[ x ] + delta, or 5*sample )
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

olikraus

Assumption:
You have an arrray "uint8_t a[10]", which contains values of a signal at t = 0ms, 100ms, 200ms, 300ms ... 900ms.
Assumed problem:
You want to know the signal value (12 bit) at u = 123ms (or any other value between 0 and 1000)

Algorithm:
1) Search for the index values, where t1 <= u <= t2
For x = 123 this is t1=100 (with index i1=1 and t2=200 with index i2=i1+1)

2) do a linear interpolation between a[i1] and a[i2]
in general this is:
a[i1]+(a[i2]-a[i2])*(u-t1)/(t2-t1)
to get the increased 12 bit resolution, do a shift by 4 bit.. at the right places  ;)
(a[i1]<<4)+((a[i2]-a[i2])<<4)*(u-t1)/(t2-t1)
Still, types are not assigned. Probably the simplest solution is to use int32_t for the multiplication:
(((int16_t)a[i1])<<4)+(int16_t)((((int32_t)a[i2]-(int32_t)a[i2])<<4)*(int32_t)(u-t1)/(int32_t)(t2-t1))

Oliver

DVDdoug

Quote
That would still only give 256 values. I need 4096 values, filling in all the spaces left out from the 8-bit table. Need a way of interpolating between each value in the table.
OK...  I guess you want to increase the bit-depth and the sample rate.    Of course, this doesn't give you more true resolution.   There are still only 256 different inputs and 256 different outputs, although you are apparently trying to "smooth" the signal digitally.        

So first you increase the bit depth, by multiplying by 16 (or bit-shifting 4 places).

Then after you figure-out how many more samples you want in the time-dimension, you can interpolate to get the in-between values.   i.e. If you double the number of samples, then taking the average between two samples to fill-in the missing data is linear interpolation.

 

majenko


Majenko??? Why 1.5?, the data was literally converted to an uint_8 value when it was 'clipped' to fit into 8 bits. The additional resolution simply isn't there anymore.
Or did I just make a BIG assumption?. Or is it just to make the data 10 bits long again? I really don't understand this at all.

Doc


Hello? Do I hear my name being taken in vain?  Why am I being dragged into this discussion?!

So yes, to add my ยข2 to the discussion, the only way you will increase "resolution" is by increasing both the bitwise values and the quantity of samples.

You cannot convert 2 numbers at 8 bit into 2 numbers at 12 bit and expect an increase in resolution - you just get an increase in step size.  You need to add extra samples in between those numbers which are basically an average of the samples either side.

Say you have samples of 4, 28, 209, 48, 184.

You would first multiply them by 16 (or shift left 4 times) to give:

64, 448, 3344, 768, 2944

Then you would do the interpolation.  Do as many passes as you have time (and memory) for:

64, 256, 448, 1896, 3344, 2056, 768, 1856, 2944

It's rough and ready, but it makes the wave look a bit smoother.

There's more tricky stuff you could do with vectors but that really takes a DSP to do it right.

tmd3

I think your intent is to reduce quantization noise in the DAC output, so your output low-pass filter can have a higher cutoff frequency, or disappear entirely.  I also think you want to calculate your interpolated DAC output values on the fly, rather than store them in memory, because you have more spare processor cycles than memory.

Everybody's talking about linear interpolation, and I will, too.  It might be that it's not satisfactory to you, and you want something smoother, but you didn't say so, and the Arduino doesn't lend itself to the complicated floating-point operations that more complex algorithms require.  If you're wondering about linear interpolation, the Wikipedia article is straightforward and short.

Somewhere in your program is a variable that points to which of 4096 phantom values you want to output to the DAC.  That might be a straightforward twelve bit number, or it might be something more complicated, like bits 18 through 29 of a 32-bit accumulator.  You'll want to derive a pointer into your array from that value, and then do linear interpolation.

Here's some pseudocode:
Code: [Select]

Pointer12Bit = Derive12BitPointer(Pointer)
Pointer8Bit = Derive8BitPointer(Pointer12Bit)
DeltaMagnitude = Array[(Pointer8Bit+1) & 0xFF] - Array[Pointer8Bit]           
DeltaPhase = Pointer12Bit & 0xF
DACOutputValue = (Array[Pointer8Bit] << 4) + DeltaMagnitude * DeltaPhase


Obviously, you don't need to write explicit "DeriveXBitPointer()" functions, and you might be able to get by without ever calculating the 12 bit pointer, as long as you can isolate its top 8 bits to index into your array, and its bottom 4 bits for interpolation.  You can grab the values you need with shift and bitwise-and operations.  Also, "DeltaMagnitude" can take on negative values as big as -255, so it needs to be a signed type bigger than 1 byte, probably int.  "DeltaPhase" is always positive, and less that 16, so it can be unsigned char.  You might be able to do without the "&& 0xFF" in the third line; I add it because I'm never confident that the compiler won't do all the intermediate results in integer, and eventually try to access Array[256].

If your arrays don't change during execution, consider putting them in program memory, if you haven't already.  There's a lot more flash in an Arduino than SRAM.  You might be able to make them bigger, or have more of them.

So, what are you cooking up, that needs multiple arrays of output samples?

Go Up