Addition of 2D table functions

This commit is contained in:
Josh Stewart 2014-01-07 16:01:14 +08:00
parent 26e23865e1
commit ac463dadf4
2 changed files with 204 additions and 136 deletions

155
table.h
View File

@ -2,7 +2,22 @@
This file is used for everything related to maps/tables including their definition, functions etc
*/
#include <Arduino.h>
struct table {
struct table2Dx4 {
const static byte xSize = 4;
byte values[xSize];
int axisX[xSize];
};
struct table2Dx10 {
const static byte xSize = 10;
byte values[xSize];
int axisX[xSize];
};
struct table3D {
//All tables must be the same size for simplicity
const static byte xSize = 8;
const static byte ySize = 8;
@ -14,7 +29,7 @@ struct table {
};
/*
Tables have an origin (0,0) in the top left hand corner. Vertical axis is expressed first.
3D Tables have an origin (0,0) in the top left hand corner. Vertical axis is expressed first.
Eg: 2x2 table
-----
|2 7|
@ -26,137 +41,5 @@ Eg: 2x2 table
(1,0) = 1
*/
//This function pulls a value from a table given a target for X and Y coordinates.
//It performs a 2D linear interpolation as descibred in: http://www.megamanual.com/v22manual/ve_tuner.pdf
int getTableValue(struct table fromTable, int Y, int X)
{
//Loop through the X axis bins for the min/max pair
//Note: For the X axis specifically, rather than looping from tableAxisX[0] up to tableAxisX[max], we start at tableAxisX[Max] and go down.
// This is because the important tables (fuel and injection) will have the highest RPM at the top of the X axis, so starting there will mean the best case occurs when the RPM is highest (And hence the CPU is needed most)
int xMinValue = fromTable.axisX[0];
int xMaxValue = fromTable.axisX[fromTable.xSize-1];
int xMin = 0;
int xMax = 0;
//If the requested X value is greater/small than the maximum/minimum bin, reset X to be that value
if(X > xMaxValue) { X = xMaxValue; }
if(X < xMinValue) { X = xMinValue; }
for (int x = fromTable.xSize-1; x >= 0; x--)
{
//Checks the case where the X value is exactly what was requested
if ( (X == fromTable.axisX[x]) || (x == 0) )
{
xMaxValue = fromTable.axisX[x];
xMinValue = fromTable.axisX[x];
xMax = x;
xMin = x;
break;
}
//Normal case
if ( (X <= fromTable.axisX[x]) && (X > fromTable.axisX[x-1]) )
{
xMaxValue = fromTable.axisX[x];
xMinValue = fromTable.axisX[x-1];
xMax = x;
xMin = x-1;
break;
}
}
//Loop through the Y axis bins for the min/max pair
int yMaxValue = fromTable.axisY[0];
int yMinValue = fromTable.axisY[fromTable.ySize-1];
int yMin = 0;
int yMax = 0;
//If the requested Y value is greater/small than the maximum/minimum bin, reset Y to be that value
if(Y > yMaxValue) { Y = yMaxValue; }
if(Y < yMinValue) { Y = yMinValue; }
for (int y = fromTable.ySize-1; y >= 0; y--)
{
//Checks the case where the Y value is exactly what was requested
if ( (Y == fromTable.axisY[y]) || (y==0) )
{
yMaxValue = fromTable.axisY[y];
yMinValue = fromTable.axisY[y];
yMax = y;
yMin = y;
break;
}
//Normal case
if ( (Y >= fromTable.axisY[y]) && (Y < fromTable.axisY[y-1]) )
{
yMaxValue = fromTable.axisY[y];
yMinValue = fromTable.axisY[y-1];
yMax = y;
yMin = y-1;
break;
}
}
/*
At this point we have the 4 corners of the map where the interpolated value will fall in
Eg: (yMin,xMin) (yMin,xMax)
(yMax,xMin) (yMax,xMax)
In the following calculation the table values are referred to by the following variables:
A B
C D
*/
int A = fromTable.values[yMin][xMin];
int B = fromTable.values[yMin][xMax];
int C = fromTable.values[yMax][xMin];
int D = fromTable.values[yMax][xMax];
//Create some normalised position values
//These are essentially percentages (between 0 and 1) of where the desired value falls between the nearest bins on each axis
// Float version
/*
float p, q;
if (xMaxValue == xMinValue)
{ p = (float)(X-xMinValue); }
else { p = ((float)(X - xMinValue)) / (float)(xMaxValue - xMinValue); }
if (yMaxValue == yMinValue)
{ q = (float)(Y - yMinValue); }
else { q = ((float)(Y - yMaxValue)) / (float)(yMinValue - yMaxValue); }
float m = (1.0-p) * (1.0-q);
float n = p * (1-q);
float o = (1-p) * q;
float r = p * q;
return ( (A * m) + (B * n) + (C * o) + (D * r) );
*/
// Non-Float version:
//Initial check incase the values were hit straight on
long p;
if (xMaxValue == xMinValue)
{ p = ((long)(X - xMinValue) << 8); } //This only occurs if the requested X value was equal to one of the X axis bins
else
{
p = ((long)(X - xMinValue) << 8) / (xMaxValue - xMinValue); } //This is the standard case
long q;
if (yMaxValue == yMinValue)
{ q = ((long)(Y - yMinValue) << 8); }
else
{ q = ((long)(Y - yMaxValue) << 8) / (yMinValue - yMaxValue); }
int m = ((257-p) * (257-q)) >> 8;
int n = (p * (257-q)) >> 8;
int o = ((257-p) * q) >> 8;
int r = (p * q) >> 8;
return ( (A * m) + (B * n) + (C * o) + (D * r) ) >> 8;
}
int get3DTableValue(struct table3D, int, int);
int get2DTableValue(struct table2D, int);

185
table.ino Normal file
View File

@ -0,0 +1,185 @@
//This function simply pulls a 1D linear interpolated (ie averaged) value from a 2D table
int get2DTableValue(struct table2Dx4 fromTable, int X)
{
int xMinValue = fromTable.axisX[0];
int xMaxValue = fromTable.axisX[fromTable.xSize-1];
int xMin = 0;
int xMax = 0;
//If the requested X value is greater/small than the maximum/minimum bin, reset X to be that value
if(X > xMaxValue) { X = xMaxValue; }
if(X < xMinValue) { X = xMinValue; }
for (int x = fromTable.xSize-1; x >= 0; x--)
{
//Checks the case where the X value is exactly what was requested
if ( (X == fromTable.axisX[x]) || (x == 0) )
{
return fromTable.values[x]; //Simply return the coresponding value
}
//Normal case
if ( (X <= fromTable.axisX[x]) && (X > fromTable.axisX[x-1]) )
{
xMaxValue = fromTable.axisX[x];
xMinValue = fromTable.axisX[x-1];
xMax = x;
xMin = x-1;
break;
}
}
unsigned int m = X - xMinValue;
unsigned int n = xMaxValue - xMinValue;
//Float version
/*
int yVal = (m / n) * (abs(fromTable.values[xMax] - fromTable.values[xMin]));
*/
//Non-Float version
int yVal;
yVal = ((m << 6) / n) * (abs(fromTable.values[xMax] - fromTable.values[xMin]));
yVal = (yVal >> 6);
if (fromTable.values[xMax] > fromTable.values[xMin]) { yVal = fromTable.values[xMin] + yVal; }
else { yVal = fromTable.values[xMin] - yVal; }
return yVal;
}
//This function pulls a value from a 3D table given a target for X and Y coordinates.
//It performs a 2D linear interpolation as descibred in: http://www.megamanual.com/v22manual/ve_tuner.pdf
int get3DTableValue(struct table3D fromTable, int Y, int X)
{
//Loop through the X axis bins for the min/max pair
//Note: For the X axis specifically, rather than looping from tableAxisX[0] up to tableAxisX[max], we start at tableAxisX[Max] and go down.
// This is because the important tables (fuel and injection) will have the highest RPM at the top of the X axis, so starting there will mean the best case occurs when the RPM is highest (And hence the CPU is needed most)
int xMinValue = fromTable.axisX[0];
int xMaxValue = fromTable.axisX[fromTable.xSize-1];
int xMin = 0;
int xMax = 0;
//If the requested X value is greater/small than the maximum/minimum bin, reset X to be that value
if(X > xMaxValue) { X = xMaxValue; }
if(X < xMinValue) { X = xMinValue; }
for (int x = fromTable.xSize-1; x >= 0; x--)
{
//Checks the case where the X value is exactly what was requested
if ( (X == fromTable.axisX[x]) || (x == 0) )
{
xMaxValue = fromTable.axisX[x];
xMinValue = fromTable.axisX[x];
xMax = x;
xMin = x;
break;
}
//Normal case
if ( (X <= fromTable.axisX[x]) && (X > fromTable.axisX[x-1]) )
{
xMaxValue = fromTable.axisX[x];
xMinValue = fromTable.axisX[x-1];
xMax = x;
xMin = x-1;
break;
}
}
//Loop through the Y axis bins for the min/max pair
int yMaxValue = fromTable.axisY[0];
int yMinValue = fromTable.axisY[fromTable.ySize-1];
int yMin = 0;
int yMax = 0;
//If the requested Y value is greater/small than the maximum/minimum bin, reset Y to be that value
if(Y > yMaxValue) { Y = yMaxValue; }
if(Y < yMinValue) { Y = yMinValue; }
for (int y = fromTable.ySize-1; y >= 0; y--)
{
//Checks the case where the Y value is exactly what was requested
if ( (Y == fromTable.axisY[y]) || (y==0) )
{
yMaxValue = fromTable.axisY[y];
yMinValue = fromTable.axisY[y];
yMax = y;
yMin = y;
break;
}
//Normal case
if ( (Y >= fromTable.axisY[y]) && (Y < fromTable.axisY[y-1]) )
{
yMaxValue = fromTable.axisY[y];
yMinValue = fromTable.axisY[y-1];
yMax = y;
yMin = y-1;
break;
}
}
/*
At this point we have the 4 corners of the map where the interpolated value will fall in
Eg: (yMin,xMin) (yMin,xMax)
(yMax,xMin) (yMax,xMax)
In the following calculation the table values are referred to by the following variables:
A B
C D
*/
int A = fromTable.values[yMin][xMin];
int B = fromTable.values[yMin][xMax];
int C = fromTable.values[yMax][xMin];
int D = fromTable.values[yMax][xMax];
//Create some normalised position values
//These are essentially percentages (between 0 and 1) of where the desired value falls between the nearest bins on each axis
// Float version
/*
float p, q;
if (xMaxValue == xMinValue)
{ p = (float)(X-xMinValue); }
else { p = ((float)(X - xMinValue)) / (float)(xMaxValue - xMinValue); }
if (yMaxValue == yMinValue)
{ q = (float)(Y - yMinValue); }
else { q = ((float)(Y - yMaxValue)) / (float)(yMinValue - yMaxValue); }
float m = (1.0-p) * (1.0-q);
float n = p * (1-q);
float o = (1-p) * q;
float r = p * q;
return ( (A * m) + (B * n) + (C * o) + (D * r) );
*/
// Non-Float version:
//Initial check incase the values were hit straight on
long p;
if (xMaxValue == xMinValue)
{ p = ((long)(X - xMinValue) << 8); } //This only occurs if the requested X value was equal to one of the X axis bins
else
{
p = ((long)(X - xMinValue) << 8) / (xMaxValue - xMinValue); } //This is the standard case
long q;
if (yMaxValue == yMinValue)
{ q = ((long)(Y - yMinValue) << 8); }
else
{ q = ((long)(Y - yMaxValue) << 8) / (yMinValue - yMaxValue); }
int m = ((257-p) * (257-q)) >> 8;
int n = (p * (257-q)) >> 8;
int o = ((257-p) * q) >> 8;
int r = (p * q) >> 8;
return ( (A * m) + (B * n) + (C * o) + (D * r) ) >> 8;
}