Table Interpolation

Hello all, been hanging out for sometime here.
Finally went and got my self an arduino. I have been having fun learning quit a bit on some of the stuff it can do. Over the last few days i have started making a controller for a little project.

I got stuck trying to figure out how to interpolate values in a table.

I am using Flash.h as i find progmem to be a big pain.

FLASH_TABLE(int, font_table, 8 /* width of table */, 
    {100, 100, 100, 100,  90,   80,   80,  70},  // 100 %
    {100, 100, 100, 100, 100,  90,   80,  80},  // 50 %
    {100, 100, 100, 100, 100, 100, 100, 100},  // 40 %
    {100, 100, 100, 100, 100, 100, 100, 100},  // 30 %
    {100, 100, 100, 100, 100, 100, 100, 100},  // 20 %
    {100, 100, 100, 100, 100, 100, 100, 100}); // 10 %
/*     2    3    4    5    6    7    8    9
       0    0    0    0    0    0    0    0
       0    0    0    0    0    0    0    0
       0    0    0    0    0    0    0    0
*/

I can get the value of an intersection very easily but doing

font_table[tps][rpm]) ;

The value will work out duty cycle for an output.
But i am having a hard time trying to interpolate between them.
Dos anyone have a word of wisdom.

You're table is full of 100's in blocks, that's a good sign of waste.

From your code it looks like tps are values between 0 and 5 and rpm between 0 and 7. What would you like to interpolate? Either you code misses some critical part or I just fail to get the problem you're trying to solve.

Korman

The project is a big one, if i can get this sorted that is. It is a fresh air based antilag controller with built in boost control.

The table will be a fair it bigger, but i am using it for proof of concept for my self. There will also be more than one.

I have the rpm working already, as it is a 0 - 5v output from the main ecu it self it was not very hard, there are no crank wheels to decode.

The 100's are not a waste at 100% duty it will hold the wastegate shut, offering very fast response from the turbo, it is done like this because it is a fail safe, if the controller fails or the pwm valve fails the turbo will only give low boost, thus saving the engine from a big failure.

The problem is the data in there is just numbers out of my head, you can have a drop as sudden as 100% to 50%, but for it not to interpolate between the rpm and load sites you will feel it in the car and the boost will drop suddenly insted of smoothly.

Yes i could buy an ecu for 5,000usd or more but then that would cost more than the car and we will not be aloud to race as it is a grass roots budget class..

I hope this explains a bit more.

I'm even more confused after your last message.

Ok, I get it, the production table will not be filled with the lower triangle filled with 100, so optimising them away isn't of interest.

Now what confuses me is whether you want interpolate additional values in case the rpm or tps have values between those present in the table coordinates (eg rpm=2.7 or tps=3.1415) or if you just want to get a smooth transition from one value in the table to another one (eg old value = 50, new value = 100, the valve or whatever takes 35s to change gradually and not open up like a barn door)

The second requirement I would leave the table as it is and use the values read just as a target. The code presented in your previous message seems rather irrelevant to the problem.

If you really want to interpolate the settings, just take the average weighted based on the distance to closest 4 entries. Assuming a linear interpolation between point you would get: [tps: 0.3, rpm: 6.5] => 76.5

Which one did you mean?

Korman

Forgive me, Coding is not my first language, i am a gear head. Trying to bridge the gap now by learning programing and electronic hardware.

I want to interpolate the values in the table if for instances in between what is there as you said below "(eg rpm=2.7 or tps=3.1415)"

I think if i include a better table and use values from it i might explain better.

FLASH_TABLE(int, font_table, 8 /* width of table */,
    {100, 100, 100,  10,   0,   0,   0,   0},  // 100 %
    {100, 100, 100,  60,  40,  30,  20,  10},  // 90 %
    {100, 100, 100,  80,  70,  50,  30,  30},  // 80 %
    {100, 100, 100,  90,  80,  80,  70,  60},  // 70 %
    {100, 100, 100, 100,  90,  80,  80,  70},  // 60 %
    {100, 100, 100, 100, 100,  90,  80,  80},  // 50 %
    {100, 100, 100, 100, 100, 100, 100, 100},  // 40 %
    {100, 100, 100, 100, 100, 100, 100, 100},  // 30 %
    {100, 100, 100, 100, 100, 100, 100, 100},  // 20 %
    {100, 100, 100, 100, 100, 100, 100, 100}); // 10 %
/*     2    3    4    5    6    7    8    9
       0    0    0    0    0    0    0    0
       0    0    0    0    0    0    0    0
       0    0    0    0    0    0    0    0

If engine speed is 7000, and if tps is 90% the out put, the value would be 30

How ever if rpm is 7750 and tps was 88% i need to figure out how to get it to use

25% of the 7000rpm value and 75% of the 8000 value and similar with the tps value.

i am sorry for confusing you.

What about using linear equations of the type y=mx+b. You can compute the m and b values using excell and your table and just use the linear equation, just input x to it and you are done.

Senso, if you look at the data (eg the 70% line: 100-90-80-80-70-60), you'll see that the progression in that table isn't linear. Thus, using a linear approximation will fail big time.

Jason, I'll post a function to do that later.

Korman

The first as linear.. :-[

Senso, i did think of that, the problem comes down to the point where it will never be linear. the other table for the EGR will be even worst aagain.

Korman thanks :) will look for it.

Here’s a try. As usual, not tested at all.

const int rpmSteps = 6; // Number of entries for rpm
const long rpmFactor = 1000; // Difference between entries for rpm
const int rpmMin = 2000; // Value of the first entry for rpm
const int rpmMax = rpmMin + (rpmSteps - 1) * rpmFactor; // Value of the last entry for rpm
const int tpsSteps = 7; // Number of entries for tps
const long tpsFactor = 10; //  Difference between entries for tps
const int tpsMin = 0; //  Value of the first entry for tps
const int tpsMax = tpsMin + (tpsSteps - 1) * tpsFactor; // Value of the last entry for tps

FLASH_TABLE(int, font_table, rpmSteps /* width of table */,
    {100,  10,   0,   0,   0,   0},  // 100 %
    {100,  60,  40,  30,  20,  10},  // 90 %
    {100,  80,  70,  50,  30,  30},  // 80 %
    {100,  90,  80,  80,  70,  60},  // 70 %
    {100, 100,  90,  80,  80,  70},  // 60 %
    {100, 100, 100,  90,  80,  80},  // 50 %
    {100, 100, 100, 100, 100, 100}); // 40 %

int magicnumber (int rpm, int tps) {
    int rpmIL, rpmIH;
    int tpsIL, tpsIH;
    long rpmOffset, tpsOffset;

    // Find right table indices to work on
    if (rpm < rpmMin) {
        rpmIL = rpmIH = 0;
        rpmOffset = 0;
    }
    else if (rpm > rpmMax) {
        rpmIL = rpmIH = rpmSteps - 1;
        rpmOffset = 0;
    }
    else {
        rpmIL = (rpm - rpmMin) / rpmFactor;
        rpmIH = rpmIL + 1;
        rpmOffset = (rpm - rpmMin) % rpmFactor;
    }

    if (tps < tpsMin) {
        tpsIL = tpsIH = 0;
        tpsOffset = 0;
    }
    else if (tps > tpsMax) {
        tpsIL = tpsIH = tpsSteps - 1;
        tpsOffset = 0;
    }
    else {
        tpsIL = (tps - tpsMin) / tpsFactor;
        tpsIH = tpsIL + 1;
        tpsOffset = (tps - tpsMin) % tpsFactor;
    }

    // Interpolate the values
    return (((font_table[tpsIL][rpmIL] * (rpmFactor - rpmOffset)
        + font_table[tpsIL][rpmIH] * rpmOffset) * (tpsFactor - tpsOffset))
        + ((font_table[tpsIH][rpmIL] * (rpmFactor - rpmOffset)
        + font_table[tpsIH][rpmIH] * rpmOffset) * tpsOffset))
        / (rpmFactor * tpsFactor);
}

To cut down on identical rows and columns at the beginning and the end of the table, I introduced the rpmMin and tpsMin. This is the value the first data row in the table represents. Anything smaller will be expected to use the same values as the first row / column. In the same vein, rpmMax and tpsMax present the value of the last row/column and all identical rows afterwards have been stripped.

rpmFactor and tpsFactor is the step size between two entries. For rpm I assumed 1000 (if one entry in the table is 4000, the next one will be 5000) for tps 10 ( from 40% to 50%)

Last, rpmSteps and tpsSteps is the number of entries for each dimension.

The first big part of the functions just makes sure we’re inside sane values and figures out what 4 values in the table are of interest. In the return statement, the average weighted by remainder from the index calculation is calculated in both dimensions. The formula looks ugly but is in principle simple.

The whole thing could be written more compactly, but then it woudl get more cryptic too. Perhaps that helps.

Korman

Hay thanks, i just tried it quickly, it dos not seem to work quite right. It is still returning hole figures from the table., but i understand what you are doing in the function.

I have to catch a plain right now. So i will be looking at it again on Sunday or Monday.

When I try those two calls:

Serial.println (magicnumber(6700, 17));
Serial.println (magicnumber(3100, 33));

I get 24 and 92. Given that [6000,10]=20 and [3000,30]=90 those numbers look reasonable, don't they?

Korman

#include <Flash.h>

const int rpmSteps = 6; // Number of entries for rpm
const long rpmFactor = 1000; // Difference between entries for rpm
const int rpmMin = 2000; // Value of the first entry for rpm
const int rpmMax = rpmMin + (rpmSteps - 1) * rpmFactor; // Value of the last entry for rpm
const int tpsSteps = 7; // Number of entries for tps
const long tpsFactor = 10; //  Difference between entries for tps
const int tpsMin = 0; //  Value of the first entry for tps
const int tpsMax = tpsMin + (tpsSteps - 1) * tpsFactor; // Value of the last entry for tps

FLASH_TABLE(int, font_table, rpmSteps /* width of table */,
    {100,  10,   0,   0,   0,   0},  // 100 %
    {100,  60,  40,  30,  20,  10},  // 90 %
    {100,  80,  70,  50,  30,  30},  // 80 %
    {100,  90,  80,  80,  70,  60},  // 70 %
    {100, 100,  90,  80,  80,  70},  // 60 %
    {100, 100, 100,  90,  80,  80},  // 50 %
    {100, 100, 100, 100, 100, 100}); // 40 %

void setup(void) {
    Serial.begin(115200);
}

void loop() {
  Serial.println (magicnumber(6700, 17));
  Serial.println (magicnumber(3100, 33)); 
}

int magicnumber (int rpm, int tps) {
    int rpmIL, rpmIH;
    int tpsIL, tpsIH;
    long rpmOffset, tpsOffset;

    // Find right table indices to work on
    if (rpm < rpmMin) {
        rpmIL = rpmIH = 0;
        rpmOffset = 0;
    }
    else if (rpm > rpmMax) {
        rpmIL = rpmIH = rpmSteps - 1;
        rpmOffset = 0;
    }
    else {
        rpmIL = (rpm - rpmMin) / rpmFactor;
        rpmIH = rpmIL + 1;
        rpmOffset = (rpm - rpmMin) % rpmFactor;
    }

    if (tps < tpsMin) {
        tpsIL = tpsIH = 0;
        tpsOffset = 0;
    }
    else if (tps > tpsMax) {
        tpsIL = tpsIH = tpsSteps - 1;
        tpsOffset = 0;
    }
    else {
        tpsIL = (tps - tpsMin) / tpsFactor;
        tpsIH = tpsIL + 1;
        tpsOffset = (tps - tpsMin) % tpsFactor;
    }

    // Interpolate the values
    return (((font_table[tpsIL][rpmIL] * (rpmFactor - rpmOffset)
        + font_table[tpsIL][rpmIH] * rpmOffset) * (tpsFactor - tpsOffset))
        + ((font_table[tpsIH][rpmIL] * (rpmFactor - rpmOffset)
        + font_table[tpsIH][rpmIH] * rpmOffset) * tpsOffset))
        / (rpmFactor * tpsFactor);
}

The result i get from this is 20 and 90.

I am having a hell of a time figuring out why it is working for you and not me.

My Arduino is a Duemilanove 328
My IDE is 21.

Will keep playing untill i here from you :slight_smile:

My Arduino is a Duemilanove 328
My IDE is 21.

Strange. I have the same IDE version (on Linux though) and also some ATmega328 Arduino, but I run all on Linux. When I’m at home, I’m going to try the exact program you posted.

Korman

Mine is windows. i do have a linux one setup, i will ahve to try but i do not think there will be much anything different.

I played with it a bit, but i think all i figured out was what not to do..

thanks for all the help so far.

This was a really nasty problem. When I tried the code, I didn’t bother downloading the Flash library and put the table into RAM. Then it worked. As soon as the table was moved to the flash memory, it stopped. The reason for it was, that the access to the table gave 4 times the same values.

Now in the repaired code, I read the values into temporary variables and wait in between for a moment. I guess, the fact alone to call any function is enough to get the correct values from the flash array. And I simply couldn’t resist that delayMicroseconds (0); command. Anyone not knowing the reason would think that code stupid.

#include <Flash.h>

const int rpmSteps = 6; // Number of entries for rpm
const long rpmFactor = 1000; // Difference between entries for rpm
const int rpmMin = 2000; // Value of the first entry for rpm
const int rpmMax = rpmMin + (rpmSteps - 1) * rpmFactor; // Value of the last entry for rpm
const int tpsSteps = 7; // Number of entries for tps
const long tpsFactor = 10; //  Difference between entries for tps
const int tpsMin = 0; //  Value of the first entry for tps
const int tpsMax = tpsMin + (tpsSteps - 1) * tpsFactor; // Value of the last entry for tps

FLASH_TABLE(int, font_table, rpmSteps /* width of table */,
    {100,  10,   0,   0,   0,   0},  // 100 %
    {100,  60,  40,  30,  20,  10},  // 90 %
    {100,  80,  70,  50,  30,  30},  // 80 %
    {100,  90,  80,  80,  70,  60},  // 70 %
    {100, 100,  90,  80,  80,  70},  // 60 %
    {100, 100, 100,  90,  80,  80},  // 50 %
    {100, 100, 100, 100, 100, 100}); // 40 %

void setup(void) {
    Serial.begin(115200);
}

void loop() {
  Serial.println (magicnumber(6700, 17));
  Serial.println (magicnumber(3100, 33));
  delay (4000);
}

int magicnumber (int rpm, int tps) {
    int rpmIL, rpmIH;
    int tpsIL, tpsIH;
    long rpmOffset, tpsOffset;

    // Find right table indices to work on
    if (rpm < rpmMin) {
        rpmIL = rpmIH = 0;
        rpmOffset = 0;
    }
    else if (rpm > rpmMax) {
        rpmIL = rpmIH = rpmSteps - 1;
        rpmOffset = 0;
    }
    else {
        rpmIL = (rpm - rpmMin) / rpmFactor;
        rpmIH = rpmIL + 1;
        rpmOffset = (rpm - rpmMin) % rpmFactor;
    }

    if (tps < tpsMin) {
        tpsIL = tpsIH = 0;
        tpsOffset = 0;
    }
    else if (tps > tpsMax) {
        tpsIL = tpsIH = tpsSteps - 1;
        tpsOffset = 0;
    }
    else {
        tpsIL = (tps - tpsMin) / tpsFactor;
        tpsIH = tpsIL + 1;
        tpsOffset = (tps - tpsMin) % tpsFactor;
    }

    // Interpolate the values
      long fLL = font_table[tpsIL][rpmIL];
      delayMicroseconds (0);
      long fLH = font_table[tpsIL][rpmIH];
      delayMicroseconds (0);
      long fHL = font_table[tpsIH][rpmIL];
      delayMicroseconds (0);
      long fHH = font_table[tpsIH][rpmIH];

    return (((fLL * (rpmFactor - rpmOffset)
        + fLH * rpmOffset) * (tpsFactor - tpsOffset))
        + ((fHL * (rpmFactor - rpmOffset)
        + fHH * rpmOffset) * tpsOffset))
        / (rpmFactor * tpsFactor);
}

This one works.

Korman

Mate, you are a gentleman and a scholar.

I think it is safe to say that although i understand what you did now, in my new beginnings with the Arduino and micro controllers period. I would never of found this solution.

Can i ask what clude you into it.

Also is there a more elegant way of doing it. i tried PROGMEM at first but was not happy with it. But maybe i did something wrong also, humm…

Thanks again.

What clued me in? My version worked, when I loaded your version it didn't. So I tried to declare it directly with PROGMEM with the same result. Then I was wondering, if has something to do with type conversions - the answer was no - but I had the variables from the table extracted into their own variables. Next I started to wonder what I read from the table and when all 4 values where 20 (or 90 in the other case) I though: Curious, that's not what I expected but it explains the results.

At this point I had this vague recollection that there was something with access speed of an EEPROM. Well, that recollection was about as relevant as a vague recollection about that summer afternoon and a secluded little lake and freckled Lucy, just a lot less pleasant than the one with Lucy, but it made me try to add a delay in between the reads from flash. And suddenly the results were correct. After this it was just trying to figure out what are the minimum delays which will work.

To be honest, I don't understand the exactly what happens either, because what I remember about the EEPROM doesn't apply to flash I think, but the problem was solved. My guess is, it has to do with pipelines and caches, but I wouldn't know why. It seems to work after a function call in between reading values. I didn't bother to check the assembly code generated either, perhaps this would yield some clue if it was a compiler induced problem.

In the end, it was just plain and simple debugging. If the results are not what one expects, check if the intermediate steps are correct and thus try to localise the problem.

Korman

Hi,

I am trying to get this to run but during compile I get an error - font_table has not been declared. Any ideas?

Silly me, I did not copy the Flash.H library into the correct directory. Now it works...