From 554f58eb2837a1dfcb9cdc5ab89f412462558120 Mon Sep 17 00:00:00 2001 From: Josh Stewart Date: Tue, 12 Nov 2019 15:18:32 +1100 Subject: [PATCH] Add better 3D table caching --- speeduino/comms.ino | 15 +++- speeduino/table.h | 7 +- speeduino/table.ino | 179 +++++--------------------------------------- 3 files changed, 36 insertions(+), 165 deletions(-) diff --git a/speeduino/comms.ino b/speeduino/comms.ino index cb0bc366..2cb488c9 100644 --- a/speeduino/comms.ino +++ b/speeduino/comms.ino @@ -772,6 +772,7 @@ void receiveValue(uint16_t valueOffset, byte newValue) //This should never happen. It means there's an invalid offset value coming through } } + fuelTable.cacheIsValid = false; //Invalid the tables cache to ensure a lookup of new values break; case veSetPage: @@ -794,7 +795,7 @@ void receiveValue(uint16_t valueOffset, byte newValue) if (valueOffset < 272) { //X Axis - ignitionTable.axisX[(valueOffset - 256)] = (int)(newValue) * TABLE_RPM_MULTIPLIER; //The RPM values sent by megasquirt are divided by 100, need to multiple it back by 100 to make it correct + ignitionTable.axisX[(valueOffset - 256)] = (int)(newValue) * TABLE_RPM_MULTIPLIER; //The RPM values sent by TunerStudio are divided by 100, need to multiple it back by 100 to make it correct } else if(valueOffset < 288) { @@ -803,6 +804,7 @@ void receiveValue(uint16_t valueOffset, byte newValue) ignitionTable.axisY[tempOffset] = (int)(newValue) * TABLE_LOAD_MULTIPLIER; } } + ignitionTable.cacheIsValid = false; //Invalid the tables cache to ensure a lookup of new values break; case ignSetPage: @@ -825,7 +827,7 @@ void receiveValue(uint16_t valueOffset, byte newValue) if (valueOffset < 272) { //X Axis - afrTable.axisX[(valueOffset - 256)] = int(newValue) * TABLE_RPM_MULTIPLIER; //The RPM values sent by megasquirt are divided by 100, need to multiply it back by 100 to make it correct (TABLE_RPM_MULTIPLIER) + afrTable.axisX[(valueOffset - 256)] = int(newValue) * TABLE_RPM_MULTIPLIER; //The RPM values sent by TunerStudio are divided by 100, need to multiply it back by 100 to make it correct (TABLE_RPM_MULTIPLIER) } else { @@ -835,6 +837,7 @@ void receiveValue(uint16_t valueOffset, byte newValue) } } + afrTable.cacheIsValid = false; //Invalid the tables cache to ensure a lookup of new values break; case afrSetPage: @@ -891,6 +894,9 @@ void receiveValue(uint16_t valueOffset, byte newValue) tempOffset = valueOffset - 232; stagingTable.axisY[(7 - tempOffset)] = int(newValue) * TABLE_LOAD_MULTIPLIER; } + boostTable.cacheIsValid = false; //Invalid the tables cache to ensure a lookup of new values + vvtTable.cacheIsValid = false; //Invalid the tables cache to ensure a lookup of new values + stagingTable.cacheIsValid = false; //Invalid the tables cache to ensure a lookup of new values break; case seqFuelPage: @@ -910,6 +916,10 @@ void receiveValue(uint16_t valueOffset, byte newValue) else if (valueOffset < 186) { tempOffset = valueOffset - 180; trim4Table.axisX[tempOffset] = int(newValue) * TABLE_RPM_MULTIPLIER; } //New value is on the X (RPM) axis of the table. The RPM values sent by TunerStudio are divided by 100, need to multiply it back by 100 to make it correct (TABLE_RPM_MULTIPLIER) else if (valueOffset < 192) { tempOffset = valueOffset - 186; trim4Table.axisY[(5 - tempOffset)] = int(newValue) * TABLE_LOAD_MULTIPLIER; } //New value is on the Y (Load) axis of the table + trim1Table.cacheIsValid = false; //Invalid the tables cache to ensure a lookup of new values + trim2Table.cacheIsValid = false; //Invalid the tables cache to ensure a lookup of new values + trim3Table.cacheIsValid = false; //Invalid the tables cache to ensure a lookup of new values + trim4Table.cacheIsValid = false; //Invalid the tables cache to ensure a lookup of new values break; case canbusPage: @@ -954,6 +964,7 @@ void receiveValue(uint16_t valueOffset, byte newValue) //This should never happen. It means there's an invalid offset value coming through } } + fuelTable2.cacheIsValid = false; //Invalid the tables cache to ensure a lookup of new values break; default: diff --git a/speeduino/table.h b/speeduino/table.h index 52ffabac..64c7dde4 100644 --- a/speeduino/table.h +++ b/speeduino/table.h @@ -30,7 +30,7 @@ struct table2D { //Store the last input and output for caching int16_t lastInput; int16_t lastOutput; - byte cacheTime; //TRacks when the last cache value was set so it can expire after x seconds. A timeout is required to pickup when a tuning value is changed, otherwise the old cached value will continue to be returned as the X value isn't changing. + byte cacheTime; //Tracks when the last cache value was set so it can expire after x seconds. A timeout is required to pickup when a tuning value is changed, otherwise the old cached value will continue to be returned as the X value isn't changing. }; //void table2D_setSize(struct table2D targetTable, byte newSize); @@ -52,6 +52,11 @@ struct table3D { //Store the last X and Y coordinates in the table. This is used to make the next check faster byte lastXMax, lastXMin; byte lastYMax, lastYMin; + + //Store the last input and output values, again for caching purposes + int16_t lastXInput, lastYInput; + byte lastOutput; //This will need changing if we ever have 16-bit table values + bool cacheIsValid; ///< This tracks whether the tables cache should be used. Ordinarily this is true, but is set to false whenever TunerStudio sends a new value for the table }; //void table3D_setSize(struct table3D *targetTable, byte); diff --git a/speeduino/table.ino b/speeduino/table.ino index 9c8a45e0..8d0889b3 100644 --- a/speeduino/table.ino +++ b/speeduino/table.ino @@ -48,177 +48,17 @@ void table3D_setSize(struct table3D *targetTable, byte newSize) targetTable->axisY = (int16_t *)malloc(newSize * sizeof(int16_t)); targetTable->xSize = newSize; targetTable->ySize = newSize; + targetTable->cacheIsValid = false; //Invalid the tables cache to ensure a lookup of new values } //initialisationComplete } /* -This function simply pulls a 1D linear interpolated (ie averaged) value from a 2D table +This function pulls a 1D linear interpolated (ie averaged) value from a 2D table ie: Given a value on the X axis, it returns a Y value that coresponds to the point on the curve between the nearest two defined X values This function must take into account whether a table contains 8-bit or 16-bit values. Unfortunately this means many of the lines are duplicated depending on this */ -/* -int table2D_getValue_orig(struct table2D *fromTable, int X_in) -{ - int returnValue = 0; - bool valueFound = false; - - int X = X_in; - int xMinValue, xMaxValue; - int xMin = 0; - int xMax = 0; - - //Check whether the X input is the same as last time this ran - if( (X_in == fromTable->lastInput) && (fromTable->cacheTime == currentStatus.secl) ) - { - returnValue = fromTable->lastOutput; - valueFound = true; - } - else - { - fromTable->cacheTime = currentStatus.secl; //As we're not using the cache value, set the current secl value to track when this new value was calc'd - - if (fromTable->valueSize == SIZE_BYTE) - { - //Byte version - xMinValue = fromTable->axisX[0]; - xMaxValue = fromTable->axisX[fromTable->xSize-1]; - } - else - { - //int version - xMinValue = fromTable->axisX16[0]; - xMaxValue = fromTable->axisX16[fromTable->xSize-1]; - } - - //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; } - - - if (fromTable->valueSize == SIZE_BYTE) - { - //**** Byte version **** - - //1st check is whether we're still in the same X bin as last time - if ( (X <= fromTable->axisX[fromTable->lastXMax]) && (X > fromTable->axisX[fromTable->lastXMin]) ) - { - xMaxValue = fromTable->axisX[fromTable->lastXMax]; - xMinValue = fromTable->axisX[fromTable->lastXMin]; - xMax = fromTable->lastXMax; - xMin = fromTable->lastXMin; - } - else - { - //If we're not in the same bin, loop through to find where we are - 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) ) - { - returnValue = fromTable->values[x]; //Simply return the coresponding value - valueFound = true; - break; - } - else - { - //Normal case - if ( (X <= fromTable->axisX[x]) && (X > fromTable->axisX[x-1]) ) - { - xMaxValue = fromTable->axisX[x]; - xMinValue = fromTable->axisX[x-1]; - xMax = x; - fromTable->lastXMax = xMax; - xMin = x-1; - fromTable->lastXMin = xMin; - break; - } - } - } - } - } - else - { - // **** INT VERSION **** - - //1st check is whether we're still in the same X bin as last time - if ( (X <= fromTable->axisX16[fromTable->lastXMax]) && (X > fromTable->axisX16[fromTable->lastXMin]) ) - { - xMaxValue = fromTable->axisX16[fromTable->lastXMax]; - xMinValue = fromTable->axisX16[fromTable->lastXMin]; - xMax = fromTable->lastXMax; - xMin = fromTable->lastXMin; - } - else - { - //If we're not in the same bin, loop through to find where we are - for (int x = fromTable->xSize-1; x >= 0; x--) - { - //Checks the case where the X value is exactly what was requested - if ( (X == fromTable->axisX16[x]) || (x == 0) ) - { - returnValue = fromTable->values16[x]; //Simply return the coresponding value - valueFound = true; - break; - } - else - { - //Normal case - if ( (X <= fromTable->axisX16[x]) && (X > fromTable->axisX16[x-1]) ) - { - xMaxValue = fromTable->axisX16[x]; - xMinValue = fromTable->axisX16[x-1]; - xMax = x; - fromTable->lastXMax = xMax; - xMin = x-1; - fromTable->lastXMin = xMin; - break; - } //Found in loop - } //Exact hit - } //For loop - } //In cache or not - } //byte or int values - } //X_in same as last time - - if (valueFound == false) - { - 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 - uint16_t yVal; - if (fromTable->valueSize == SIZE_BYTE) - { - //Byte version - yVal = ((long)(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; } - } - else - { - //int version - yVal = ((long)(m << 6) / n) * (abs(fromTable->values16[xMax] - fromTable->values16[xMin])); - yVal = (yVal >> 6); - - if (fromTable->values[xMax] > fromTable->values16[xMin]) { yVal = fromTable->values16[xMin] + yVal; } - else { yVal = fromTable->values16[xMin] - yVal; } - } - returnValue = yVal; - } - - fromTable->lastInput = X_in; - fromTable->lastOutput = returnValue; - - return returnValue; -} -*/ - int table2D_getValue(struct table2D *fromTable, int X_in) { //Orig memory usage = 5414 @@ -374,6 +214,14 @@ int get3DTableValue(struct table3D *fromTable, int Y_in, int X_in) if(X > xMaxValue) { X = xMaxValue; } if(X < xMinValue) { X = xMinValue; } + //0th check is whether the same X and Y values are being sent as last time. If they are, this not only prevents a lookup of the axis, but prevents the interpolation calcs being performed + if( (X_in == fromTable->lastXInput) && (Y_in == fromTable->lastYInput) && (fromTable->cacheIsValid == true)) + { + return fromTable->lastOutput; + } + + //Commence the lookups on the X and Y axis + //1st check is whether we're still in the same X bin as last time if ( (X <= fromTable->axisX[fromTable->lastXMax]) && (X > fromTable->axisX[fromTable->lastXMin]) ) { @@ -558,5 +406,12 @@ int get3DTableValue(struct table3D *fromTable, int Y_in, int X_in) int r = (p * q) >> 8; tableResult = ( (A * m) + (B * n) + (C * o) + (D * r) ) >> 8; } + + //Update the tables cache data + fromTable->lastXInput = X_in; + fromTable->lastYInput = Y_in; + fromTable->lastOutput = tableResult; + fromTable->cacheIsValid = true; + return tableResult; }