CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

interpolation method

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Blob



Joined: 02 Jan 2006
Posts: 75
Location: Neeroeteren, Limburg, Belgium

View user's profile Send private message

interpolation method
PostPosted: Tue Mar 15, 2011 2:42 am     Reply with quote

Hello All,

I am looking for a simple and fast interpolation method.

I use a 18f2620 at 40MHz (H4 enabled)

I use an analogue input (0-255)

according to this input value I will read a value from memory.
At the moment I have divided the input range in 12 steps which can be set up by the user. He can change the input level and the value stored at that level.

Now I would like to interpolate for values between 2 input levels.

At the moment I use the common 2D math function:


Code:
Y = Y1+ (X-X1)*(Y2-Y1)/(X2-X1)


But this takes a long time to calculate, too long...

Now I was thinking of an other interpolation method

Code:

Analogue lower level = ANA_L (f.e. 50)
Analogue higher level = ANA_H (f.e. 150)
Actual Analogue level = ANA (f.e. 95)
Output lower level = OUT_L (f.e. 2000)
Output_higher level = OUT_H (f.e. 4000)
Desired Output level = OUT

I calculate the ANA range: ANA_H - ANA-L =  100
and I calculate the difference between ANA and ANA_L = 45
and the OUT range OUT_H - OUT-L = 2000

now I keep deviding the ANA range by 2
if the result is bigger than ANA_L I will subtract it from the previous result, else I will add it.

The same is done for the OUT range


ANA/2   ANAresult   OUT/2   OUTresult
100      100      2000      2000
50       50       1000      1000
25       25        500      500
13       38        250      750
6        44        125      875
3        47         63      938
2        45         31      906
1        45         16      891
0        45          8      898


last step is to add the result to OUT_L

This gives about the same result as the formula above (2900 ~ 2898)


Is this a wise (read "fast") way to do it?
Or are there other methods?

I can not use lookup tables because the user can set the ANA_L, ANA_H, OUT_L and OUT_H himself...

Thanks in advance,
Blob
epitalon



Joined: 11 Mar 2010
Posts: 11
Location: france

View user's profile Send private message

PostPosted: Tue Mar 15, 2011 12:47 pm     Reply with quote

Quote:

I can not use lookup tables because the user can set the ANA_L, ANA_H, OUT_L and OUT_H himself...


You can compute the look up table each time the user changes one value. Would it be too costly ?
temtronic



Joined: 01 Jul 2010
Posts: 9245
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Tue Mar 15, 2011 1:20 pm     Reply with quote

If you have enough RAM, the lookup table is only 256 bytes, create it once, after the user inputs the in-lo,in-hi,out-lo,out-hi values.Then it's a simple,fast...read A2D,call lookup table, carryon...
Blob



Joined: 02 Jan 2006
Posts: 75
Location: Neeroeteren, Limburg, Belgium

View user's profile Send private message

PostPosted: Wed Mar 16, 2011 2:51 am     Reply with quote

Hello,

Thanks for your reply.

The lookup table is not really an option.

In fact the user can set 12 input levels for the ADC with corresponding OUT value.

depending on other inputs there are 4 sets of ANA_L, ANA_H, OUT_L and OUT_H.

OUT_L and OUT_H also depend on the rotation speed of my project.
there are 309 possible rpm values so I have 4*309*12 = 14832 different values for OUT_L and OUT_H.

So a formula or loop is the best option I guess...

Thanks,
Blob
Ttelmah



Joined: 11 Mar 2010
Posts: 19548

View user's profile Send private message

PostPosted: Wed Mar 16, 2011 3:10 am     Reply with quote

There may be lots of different output possibilities, but if you are only using the 8bit ADC, there are only 256 possibilities here.
The point being made was that once you have the set points from the user, you could 'precalculate' all the possible outputs for the 256 possible inputs (so only 256 possibilities), which may take a second or so to do, but save this as a 256 element table, and in use, you have near instantaneous solution for every possible ADC value.

On the calculation though, time will depend massively on the maths type being used. You might want to retry your arithmetic being careful about using 'lower' maths types when possible, and see if the time becomes more acceptable.

Best Wishes
Blob



Joined: 02 Jan 2006
Posts: 75
Location: Neeroeteren, Limburg, Belgium

View user's profile Send private message

PostPosted: Wed Mar 16, 2011 4:35 am     Reply with quote

The project I am building is monitoring a rotating disk.

The value of the 8 bit analogue input will result in a start address of a table. The user can define 12 set points => 12 tables.

The rpm of the disk going from 550 rpm up to 16000.
Every 50 rpm refers to a memory location so I have 309 rpm addresses.

Then there is an other digital input which tells me what dataset I have to use.
I have 4 sets.

Together with the start address and the data set, the rpm address will point to an output value.


It is correct that I have only 255 input possibilities, but the rpm and the digital input is not constant, so it will be very hard to use a lookup table.

I am writing a small loop with only divisions by 2.
I assume /2 equals x >> 1? and takes not long to perform...

I will post it when ready.

Best regards,
Blob
SherpaDoug



Joined: 07 Sep 2003
Posts: 1640
Location: Cape Cod Mass USA

View user's profile Send private message

PostPosted: Wed Mar 16, 2011 6:29 am     Reply with quote

Maybe you could pre-calculate all your divisors 1/(x2-x1) and store them in a table. Then when doing the real math you can multiply by the pre-calculated number, rather than actually doing the division which takes a lot of time.
_________________
The search for better is endless. Instead simply find very good and get the job done.
sseidman



Joined: 14 Mar 2005
Posts: 159

View user's profile Send private message

PostPosted: Wed Mar 16, 2011 8:03 am     Reply with quote

Can you multiply all your numbers up high enough such that integer division would be accurate enough?? I do all I can to avoid floats.
Blob



Joined: 02 Jan 2006
Posts: 75
Location: Neeroeteren, Limburg, Belgium

View user's profile Send private message

PostPosted: Wed Mar 16, 2011 8:55 am     Reply with quote

Hello,

My interpolator works in the the available time

for those who are interested here is the code

Code:

//read ADC and compare with set points of user to determine x in start_adr[x]
//setpoint ADC_L < ADC_in < ADC_H
//according to digital inputs select dataset => start_adr[x] += dataset
//measure rpm value to obtain the adr value

OUT_H =  read_program_eeprom(start_adr[x]+adr);
OUT_L =  read_program_eeprom(start_adr[x-1]+adr);

if(OUT_H > OUT_L)      //rising
{
   bl_rising = true;
   OUT_DIFF = OUT_H-OUT_L;
}
else
{
   bl_rising = false;
   OUT_DIFF = OUT_L-OUT_H;
}

ADC_in = ADC_in - ADC_L;   //relative position of ADC_in
ADC_diff = ADC_H - ADC_L;   //width of interval
ADC_HELP = ADC_diff;      //help variable
OUT = OUT_diff;            //set out


for(i=0;i<4;i++)            //do 4 times (more accurate result when higher)
{
   if(ADC_HELP > ADC_in)   //if greater than
   {
      ADC_HELP -= ADC_diff/2;   //subtract half of the input step
      OUT -= OUT_diff/2;      //subtract half of the output step
   }
   else if(ADC_HELP < ADC_in)
   {
      ADC_HELP += ADC_diff/2;   //add half of the input step
      OUT += OUT_diff/2;      //add half of the output step
   }

   OUT_diff /= 2;      //refine output step
   ADC_diff /= 2;      //refine input step
}

OUT += OUT_L;


Best regards,
Blob
epitalon



Joined: 11 Mar 2010
Posts: 11
Location: france

View user's profile Send private message

PostPosted: Thu Mar 17, 2011 3:20 am     Reply with quote

Quote:

My interpolator works in the the available time


That is clever. But achieved precision is limited.
I wonder if you could achieve better precision and similar or better execution time by precalculating (Y2-Y1)/(X2-X1) in fixed point format :

example :
Code:

  // Precalculation
  // Note 65535 = 2 at power 16 =  1 << 16
  unsigned int32 ratio = (Y2 - Y1) * 65536 / (X2 - X1);

  // Be carefull of not overflowing int32
  // There is no such concern if (Y2 - Y1) is contained in 16 bit integer.

  // Interpolation
  unsigned int32 ratio = read_program_eeprom(start_adr_ratio[x]+adr);
  OUT_diff = (unsigned int32) (ADC_in - ADC_L) * ratio / 65536;
 


That suggestion is in accordance with what suggested sseidman and SherpaDoug.

Note that any power of 2 is a good multiplicative factor because in that case, dividing by such factor involves only shifting bits.

But it is even better to use 2 raised at power 16 or 8 if you are using an 8 bit microcontroller because in that case, dividing by such factor does not involve shifting bits but shifting bytes.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group