Pages: [1]   Go Down
Author Topic: Interpolation: 12-bit DAC with 8-bit tables  (Read 1236 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?
Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 285
Posts: 25632
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That probably depends on how long you have got to do the arithmetic.
Logged

"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.

Global Moderator
Boston area, metrowest
Online Online
Brattain Member
*****
Karma: 514
Posts: 26258
Author of "Arduino for Teens". Available for Design & Build services. Now with Unlimited Eagle board sizes!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Multiply them all by 1.5 before sending them?
Logged

Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Leeds, UK
Offline Offline
Edison Member
*
Karma: 78
Posts: 1719
Once the magic blue smoke is released, it won't go back in!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Code:
byte eightBitValue = ...
int twelveBitValue = (int)eightBitValue << 4;
Logged

~Tom~

Offline Offline
Newbie
*
Karma: 0
Posts: 3
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Code:
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.
Logged

Anaheim CA.
Offline Offline
Faraday Member
**
Karma: 46
Posts: 2865
...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

--> 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

UK
Offline Offline
Shannon Member
****
Karma: 222
Posts: 12520
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

I only provide help via the forum - please do not contact me for private consultancy.

Global Moderator
Boston area, metrowest
Online Online
Brattain Member
*****
Karma: 514
Posts: 26258
Author of "Arduino for Teens". Available for Design & Build services. Now with Unlimited Eagle board sizes!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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 )
« Last Edit: June 27, 2012, 06:02:26 pm by CrossRoads » Logged

Designing & building electrical circuits for over 25 years. Check out the ATMega1284P based Bobuino and other '328P & '1284P creations & offerings at  www.crossroadsfencing.com/BobuinoRev17.
Arduino for Teens available at Amazon.com.

Germany
Offline Offline
Edison Member
*
Karma: 130
Posts: 1438
If you believe something is right, you won't see what's wrong (David Straker).
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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  smiley-wink
(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
Logged

Offline Offline
Edison Member
*
Karma: 32
Posts: 1282
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.

  
Logged

UK
Offline Offline
Faraday Member
**
Karma: 99
Posts: 4153
Where is your SSCCE?!?!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Get 10% off all 4D Systems TFT screens this month: use discount code MAJENKO10

USA
Offline Offline
Sr. Member
****
Karma: 13
Posts: 360
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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?
Logged

Pages: [1]   Go Up
Jump to: