Linear interpolation using 2D lookup table

Hi all,

This is my first post, I'm trying to perform linear interpolation from a lookup as shown below.

//lookup table for forward current (mA) vs. normalized radiant flux. Key-value pair: {current, flux}
double current_vs_flux[][2] = {{50, 0},
  {100, 0.13},
  {150, 0.19},
  {200, 0.25},
  {250, 0.31},
  {300, 0.36},
  {350, 0.42},
  {400, 0.47},
  {450, 0.52},
  {500, 0.57},
  {550, 0.61},
  {600, 0.66},
  {650, 0.7},
  {700, 0.75},
  {750, 0.79},
  {800, 0.83},
  {850, 0.87},
  {900, 0.91},
  {950, 0.95},
  {1000, 1}
};

I have made the following function which performs linear interpolation which approximates the output current(x) for a given input flux(y) value from the formula,

x = x0 + (x1-x0)*((y-y0)/y1-y0)

where,
x0 --> lower bound of x
x1 --> upper bound of x
y0 --> lower bound of y
y1 --> upper bound of y

double linearInterpolate(double y, double data[][2])
{
  double x0, x1, y0, y1;
  for (int i = 0; i < sizeof(data) / (sizeof(data[0][0]) * 2); i++)
  {
    if (y > data[i][1] && y < data[i + 1][1])
    {
      y0 = data[i][1];  //lower bound
      y1 = data[i + 1][1]; //upper bound
      x0 = data[i][0];
      x1 = data[i + 1][0];
      return (x0 + ((x1 - x0) * ((y - y0) / (y1 - y0))));
    }
  }
}

Next, I run the following code by calling the interpolate function and I get the following output

void setup()
{
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop()
{
  double y = 0.44;
  x = linearInterpolate(y, current_vs_flux);
  Serial.println("Drive Current : ");
  Serial.println(x);
  delay(1000);
}

OUTPUT:

Drive Current :
0.00

Now, instead of calling the function explicitly I perform the linear interpolation approximation in the loop function itself and get the following output

void setup()
{
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop()
{
  double y = 0.44;
  double x, x0, x1, y0, y1;
  for (int i = 0; i < sizeof(current_vs_flux) / (sizeof(current_vs_flux[0][0]) * 2); i++)
  {
    if (y >= current_vs_flux[i][1] && y <= current_vs_flux[i + 1][1])
    {
      y0 = current_vs_flux[i][1];  //lower bound
      y1 = current_vs_flux[i + 1][1]; //upper bound
      x0 = current_vs_flux[i][0];
      x1 = current_vs_flux[i + 1][0];
      x = x0 + ((x1 - x0) * ((y - y0) / (y1 - y0)));
    }
  }
  Serial.println("Drive Current : ");
  Serial.println(x);
  delay(1000);
}

OUTPUT

Drive Current :
370.00

I cannot seem to figure out why I'm always getting zero when calling the function explicitly. But when I do a linear interpolation without using the function in the loop function, it approximates proper.

Could someone please explain why this is happening? Could I have gone wrong somewhere? I would need to perform interpolation in different parts of a larger code and it would seem silly to do interpolation every time without a function.

Any help is much appreciated, looking forward to your reply.

Regards
Sid

Next, I run the following code

The following code cannot compile, therefore cannot run.

My bad, I will post the entire code here. Basically the first time I'm calling the linearInterpolate() function and it gives the output as zero.

The second time I run it without calling linearInterpolate(), I perform the interpolation in the loop function.

1st time

//lookup table for forward current (mA) vs. normalized radiant flux. Key-value pair: {current, flux}
double current_vs_flux[][2] = {{50, 0},
  {100, 0.13},
  {150, 0.19},
  {200, 0.25},
  {250, 0.31},
  {300, 0.36},
  {350, 0.42},
  {400, 0.47},
  {450, 0.52},
  {500, 0.57},
  {550, 0.61},
  {600, 0.66},
  {650, 0.7},
  {700, 0.75},
  {750, 0.79},
  {800, 0.83},
  {850, 0.87},
  {900, 0.91},
  {950, 0.95},
  {1000, 1}
};

void setup()
{
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop()
{
  double y = 0.44;
  double x, x0, x1, y0, y1;
  x = linearInterpolate(y, current_vs_flux);
  Serial.println("Drive Current : ");
  Serial.println(x);
  delay(1000);
}

double linearInterpolate(double y, double data[][2])
{
  double x, x0, x1, y0, y1;
  for (int i = 0; i < sizeof(data) / (sizeof(data[0][0]) * 2); i++)
  {
    if (y > data[i][1] && y < data[i + 1][1])
    {
      y0 = data[i][1];  //lower bound
      y1 = data[i + 1][1]; //upper bound
      x0 = data[i][0];
      x1 = data[i + 1][0];
      x = x0 + ((x1 - x0) * ((y - y0) / (y1 - y0)));     
    }
  }
  return x;
}

OUTPUT

Drive Current :
0.00

2nd time

void setup()
{
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop()
{
  double y = 0.44;
  double x, x0, x1, y0, y1;
  for (int i = 0; i < sizeof(current_vs_flux) / (sizeof(current_vs_flux[0][0]) * 2); i++)
  {
    if (y >= current_vs_flux[i][1] && y <= current_vs_flux[i + 1][1])
    {
      y0 = current_vs_flux[i][1];  //lower bound
      y1 = current_vs_flux[i + 1][1]; //upper bound
      x0 = current_vs_flux[i][0];
      x1 = current_vs_flux[i + 1][0];
      x = x0 + ((x1 - x0) * ((y - y0) / (y1 - y0)));
    }
  }
  //x = linearInterpolate(y, current_vs_flux);
  Serial.println("Drive Current : ");
  Serial.println(x);
  delay(1000);
}

OUTPUT

Drive Current :
370.00

I'm using a Arduino Mega 2560.

double linearInterpolate(double y, double data[][2])

This declares data to be a pointer to an array of double of unknown length.

In this statement:

 for (int i = 0; i < sizeof(data) / (sizeof(data[0][0]) * 2); i++)

sizeof(data) is 2 because data is a pointer.
sizeof(data[0][0]) is 4 because data[0][0] is a double, which is actually a float on a Mega and is therefore 4 bytes and *2 gives 8.
Your for loop executes while i < 2/8 which is zero (because 2/8 is an integer division) and thus loop is never executed.

You should also pass the length of the array to the linearInterpolate function because it has no way of knowing the length of the data array.

There are some assumptions in that code which will also eventually bite you.

  • Your code assumes that it will always find a value. If it doesn't, it will return the value of x which has not been initialized and will be a random value on the stack. It happens to be zero in your test code but you can't count on it. Initialize it to -1 and test for that return value.
  • If you do find a value, you don't return it immediately. Put return(x) after x has been calculated inside the if statement.

Pete

Sizeof() is compile-time only - no use for values you pass around at runtime.

double is a synonym for float on most Arduinos

Your array wastes half the space as your x points are regularly spaced and don't need to be stored

You could have stored your array in PROGMEM to save RAM.

Quadratic interpolation isn't much harder and much better unless its a straight line in the first place.

Thank you for your input, I can specify the length of the table in the for loop since its size does not change. It works properly now.

Cheers
Sid