From 91206ac90e5cb25a27397bd7f3426bf6863a0599 Mon Sep 17 00:00:00 2001 From: Josh Stewart Date: Tue, 29 Oct 2019 22:50:46 +1100 Subject: [PATCH] SUPER experimental new version of 2D table lookup. See #270 --- speeduino/corrections.ino | 12 +-- speeduino/idle.ino | 2 +- speeduino/init.ino | 21 ++++- speeduino/table.h | 12 ++- speeduino/table.ino | 163 +++++++++++++++++++++++++++++++++++++- 5 files changed, 193 insertions(+), 17 deletions(-) diff --git a/speeduino/corrections.ino b/speeduino/corrections.ino index 9539f227..796ba0a3 100644 --- a/speeduino/corrections.ino +++ b/speeduino/corrections.ino @@ -97,11 +97,13 @@ static inline byte correctionWUE() { byte WUEValue; //Possibly reduce the frequency this runs at (Costs about 50 loops per second) - if (currentStatus.coolant > (WUETable.axisX[9] - CALIBRATION_TEMPERATURE_OFFSET)) + //if (currentStatus.coolant > (WUETable.axisX[9] - CALIBRATION_TEMPERATURE_OFFSET)) + if (currentStatus.coolant > (table2D_getAxisValue(&WUETable, 9) - CALIBRATION_TEMPERATURE_OFFSET)) { //This prevents us doing the 2D lookup if we're already up to temp BIT_CLEAR(currentStatus.engine, BIT_ENGINE_WARMUP); - WUEValue = WUETable.values[9]; //Set the current value to be whatever the final value on the curve is. + //WUEValue = WUETable.values[9]; //Set the current value to be whatever the final value on the curve is. + WUEValue = table2D_getAxisValue(&WUETable, 9); } else { @@ -298,8 +300,7 @@ Uses a 2D enrichment table (WUETable) where the X axis is engine temp and the Y static inline byte correctionBatVoltage() { byte batValue = 100; - if (currentStatus.battery10 > (injectorVCorrectionTable.axisX[5])) { batValue = injectorVCorrectionTable.values[injectorVCorrectionTable.xSize-1]; } //This prevents us doing the 2D lookup if the voltage is above maximum - else { batValue = table2D_getValue(&injectorVCorrectionTable, currentStatus.battery10); } + batValue = table2D_getValue(&injectorVCorrectionTable, currentStatus.battery10); return batValue; } @@ -311,8 +312,7 @@ This corrects for changes in air density from movement of the temperature static inline byte correctionIATDensity() { byte IATValue = 100; - if ( (currentStatus.IAT + CALIBRATION_TEMPERATURE_OFFSET) > (IATDensityCorrectionTable.axisX[8])) { IATValue = IATDensityCorrectionTable.values[IATDensityCorrectionTable.xSize-1]; } //This prevents us doing the 2D lookup if the intake temp is above maximum - else { IATValue = table2D_getValue(&IATDensityCorrectionTable, currentStatus.IAT + CALIBRATION_TEMPERATURE_OFFSET); }//currentStatus.IAT is the actual temperature, values in IATDensityCorrectionTable.axisX are temp+offset + IATValue = table2D_getValue(&IATDensityCorrectionTable, currentStatus.IAT + CALIBRATION_TEMPERATURE_OFFSET); //currentStatus.IAT is the actual temperature, values in IATDensityCorrectionTable.axisX are temp+offset return IATValue; } diff --git a/speeduino/idle.ino b/speeduino/idle.ino index 17da2275..831cf7d4 100644 --- a/speeduino/idle.ino +++ b/speeduino/idle.ino @@ -263,7 +263,7 @@ void idleControl() doStep(); } - else if( (currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET) < iacStepTable.axisX[IDLE_TABLE_SIZE-1]) + else if( (currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET) < table2D_getAxisValue(&iacStepTable, (IDLE_TABLE_SIZE-1)) ) { //Standard running //We must also have more than zero RPM for the running state diff --git a/speeduino/init.ino b/speeduino/init.ino index e4e8676e..39e0f63f 100644 --- a/speeduino/init.ino +++ b/speeduino/init.ino @@ -57,78 +57,95 @@ void initialiseAll() //Repoint the 2D table structs to the config pages that were just loaded taeTable.valueSize = SIZE_BYTE; //Set this table to use byte values + taeTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins taeTable.xSize = 4; taeTable.values = configPage4.taeValues; taeTable.axisX = configPage4.taeBins; maeTable.valueSize = SIZE_BYTE; //Set this table to use byte values + maeTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins maeTable.xSize = 4; maeTable.values = configPage4.maeRates; maeTable.axisX = configPage4.maeBins; WUETable.valueSize = SIZE_BYTE; //Set this table to use byte values + WUETable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins WUETable.xSize = 10; WUETable.values = configPage2.wueValues; WUETable.axisX = configPage4.wueBins; ASETable.valueSize = SIZE_BYTE; + ASETable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins ASETable.xSize = 4; ASETable.values = configPage2.asePct; ASETable.axisX = configPage2.aseBins; ASECountTable.valueSize = SIZE_BYTE; + ASECountTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins ASECountTable.xSize = 4; ASECountTable.values = configPage2.aseCount; ASECountTable.axisX = configPage2.aseBins; PrimingPulseTable.valueSize = SIZE_BYTE; + PrimingPulseTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins PrimingPulseTable.xSize = 4; PrimingPulseTable.values = configPage2.primePulse; PrimingPulseTable.axisX = configPage2.primeBins; crankingEnrichTable.valueSize = SIZE_BYTE; - crankingEnrichTable.valueSize = SIZE_BYTE; + crankingEnrichTable.axisSize = SIZE_BYTE; crankingEnrichTable.xSize = 4; crankingEnrichTable.values = configPage10.crankingEnrichValues; crankingEnrichTable.axisX = configPage10.crankingEnrichBins; dwellVCorrectionTable.valueSize = SIZE_BYTE; + dwellVCorrectionTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins dwellVCorrectionTable.xSize = 6; dwellVCorrectionTable.values = configPage4.dwellCorrectionValues; dwellVCorrectionTable.axisX = configPage6.voltageCorrectionBins; injectorVCorrectionTable.valueSize = SIZE_BYTE; + injectorVCorrectionTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins injectorVCorrectionTable.xSize = 6; injectorVCorrectionTable.values = configPage6.injVoltageCorrectionValues; injectorVCorrectionTable.axisX = configPage6.voltageCorrectionBins; IATDensityCorrectionTable.valueSize = SIZE_BYTE; + IATDensityCorrectionTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins IATDensityCorrectionTable.xSize = 9; IATDensityCorrectionTable.values = configPage6.airDenRates; IATDensityCorrectionTable.axisX = configPage6.airDenBins; IATRetardTable.valueSize = SIZE_BYTE; + IATRetardTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins IATRetardTable.xSize = 6; IATRetardTable.values = configPage4.iatRetValues; IATRetardTable.axisX = configPage4.iatRetBins; CLTAdvanceTable.valueSize = SIZE_BYTE; + CLTAdvanceTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins CLTAdvanceTable.xSize = 6; CLTAdvanceTable.values = (byte*)configPage4.cltAdvValues; CLTAdvanceTable.axisX = configPage4.cltAdvBins; rotarySplitTable.valueSize = SIZE_BYTE; + rotarySplitTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins rotarySplitTable.xSize = 8; rotarySplitTable.values = configPage10.rotarySplitValues; rotarySplitTable.axisX = configPage10.rotarySplitBins; flexFuelTable.valueSize = SIZE_BYTE; + flexFuelTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins flexFuelTable.xSize = 6; flexFuelTable.values = configPage10.flexFuelAdj; flexFuelTable.axisX = configPage10.flexFuelBins; flexAdvTable.valueSize = SIZE_BYTE; + flexAdvTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins flexAdvTable.xSize = 6; flexAdvTable.values = configPage10.flexAdvAdj; flexAdvTable.axisX = configPage10.flexAdvBins; flexBoostTable.valueSize = SIZE_INT; + flexBoostTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins (NOTE THIS IS DIFFERENT TO THE VALUES!!) flexBoostTable.xSize = 6; - flexBoostTable.values16 = configPage10.flexBoostAdj; + flexBoostTable.values = configPage10.flexBoostAdj; flexBoostTable.axisX = configPage10.flexBoostBins; knockWindowStartTable.valueSize = SIZE_BYTE; + knockWindowStartTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins knockWindowStartTable.xSize = 6; knockWindowStartTable.values = configPage10.knock_window_angle; knockWindowStartTable.axisX = configPage10.knock_window_rpms; knockWindowDurationTable.valueSize = SIZE_BYTE; + knockWindowDurationTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins knockWindowDurationTable.xSize = 6; knockWindowDurationTable.values = configPage10.knock_window_dur; knockWindowDurationTable.axisX = configPage10.knock_window_rpms; diff --git a/speeduino/table.h b/speeduino/table.h index d6dc4223..787967d2 100644 --- a/speeduino/table.h +++ b/speeduino/table.h @@ -12,14 +12,16 @@ The 2D table can contain either 8-bit (byte) or 16-bit (int) values The valueSize variable should be set to either 8 or 16 to indicate this BEFORE the table is used */ struct table2D { + //Used 5414 RAM with original version byte valueSize; + byte axisSize; byte xSize; - byte *values; - byte *axisX; + void *values; + void *axisX; - int16_t *values16; - int16_t *axisX16; + //int16_t *values16; + //int16_t *axisX16; //Store the last X and Y coordinates in the table. This is used to make the next check faster int16_t lastXMax; @@ -33,6 +35,8 @@ struct table2D { //void table2D_setSize(struct table2D targetTable, byte newSize); void table2D_setSize(struct table2D, byte); +int16_t table2D_getAxisValue(struct table2D, byte); +int16_t table2D_getRawValue(struct table2D, byte); struct table3D { diff --git a/speeduino/table.ino b/speeduino/table.ino index 56615b2e..f82ccbc9 100644 --- a/speeduino/table.ino +++ b/speeduino/table.ino @@ -11,6 +11,7 @@ Note that this may clear some of the existing values of the table #include "table.h" #include "globals.h" +/* void table2D_setSize(struct table2D* targetTable, byte newSize) { //Table resize is ONLY permitted during system initialisation. @@ -33,6 +34,7 @@ void table2D_setSize(struct table2D* targetTable, byte newSize) } //Byte or int } //initialisationComplete } +*/ void table3D_setSize(struct table3D *targetTable, byte newSize) @@ -56,7 +58,8 @@ ie: Given a value on the X axis, it returns a Y value that coresponds to the poi 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(struct table2D *fromTable, int X_in) +/* +int table2D_getValue_orig(struct table2D *fromTable, int X_in) { int returnValue = 0; bool valueFound = false; @@ -184,9 +187,7 @@ int table2D_getValue(struct table2D *fromTable, int X_in) unsigned int n = xMaxValue - xMinValue; //Float version - /* - int yVal = (m / n) * (abs(fromTable.values[xMax] - fromTable.values[xMin])); - */ + //int yVal = (m / n) * (abs(fromTable.values[xMax] - fromTable.values[xMin])); //Non-Float version uint16_t yVal; @@ -216,6 +217,160 @@ int table2D_getValue(struct table2D *fromTable, int X_in) return returnValue; } +*/ + +int table2D_getValue(struct table2D *fromTable, int X_in) +{ + //Orig memory usage = 5414 + int returnValue = 0; + bool valueFound = false; + + //Copy the from table values (bytes or ints) into common int temp arrays + int tempAxisX[fromTable->xSize]; + int tempValues[fromTable->xSize]; + for(byte x=0; x< fromTable->xSize; x++) + { + if(fromTable->valueSize == SIZE_INT) { tempValues[x] = ((int16_t*)((*fromTable).values))[x]; } + else if(fromTable->valueSize == SIZE_BYTE) { tempValues[x] = ((uint8_t*)((*fromTable).values))[x]; } + + if(fromTable->axisSize == SIZE_INT) { tempAxisX[x] = ((int16_t*)((*fromTable).axisX))[x]; } + else if(fromTable->axisSize == SIZE_BYTE) { tempAxisX[x] = ((uint8_t*)((*fromTable).axisX))[x]; } + } + + int X = X_in; + int xMinValue, xMaxValue; + int xMin = 0; + int xMax = 0; + + xMinValue = tempAxisX[0]; + xMaxValue = tempAxisX[fromTable->xSize-1]; + + //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; + } + //If the requested X value is greater/small than the maximum/minimum bin, reset X to be that value + else if(X > xMaxValue) + { + returnValue = tempValues[fromTable->xSize-1]; + valueFound = true; + } + else if(X < xMinValue) + { + returnValue = tempValues[0]; + valueFound = true; + } + //Finally if none of that is found + 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 the requested X value is greater/small than the maximum/minimum bin, reset X to be that value + //Failsafe, this should've been handled above + if(X > xMaxValue) { X = xMaxValue; } + if(X < xMinValue) { X = xMinValue; } + + + //1st check is whether we're still in the same X bin as last time + if ( (X <= tempAxisX[fromTable->lastXMax]) && (X > tempAxisX[fromTable->lastXMin]) ) + { + xMaxValue = tempAxisX[fromTable->lastXMax]; + xMinValue = tempAxisX[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 == tempAxisX[x]) || (x == 0) ) + { + returnValue = tempValues[x]; //Simply return the coresponding value + valueFound = true; + break; + } + else + { + //Normal case + if ( (X <= tempAxisX[x]) && (X > tempAxisX[x-1]) ) + { + xMaxValue = tempAxisX[x]; + xMinValue = tempAxisX[x-1]; + xMax = x; + fromTable->lastXMax = xMax; + xMin = x-1; + fromTable->lastXMin = xMin; + break; + } + } + } + } + } //X_in same as last time + + if (valueFound == false) + { + int16_t m = X - xMinValue; + int16_t n = xMaxValue - xMinValue; + + //Float version + /* + int yVal = (m / n) * (abs(fromTable.values[xMax] - fromTable.values[xMin])); + */ + + //Non-Float version + int16_t yVal; + yVal = ((long)(m << 6) / n) * (abs(tempValues[xMax] - tempValues[xMin])); + yVal = (yVal >> 6); + + if (tempValues[xMax] > tempValues[xMin]) { yVal = tempValues[xMin] + yVal; } + else { yVal = tempValues[xMin] - yVal; } + + returnValue = yVal; + } + + fromTable->lastInput = X_in; + fromTable->lastOutput = returnValue; + + return returnValue; +} + +/** + * @brief Returns an axis (bin) value from the 2D table. This works regardless of whether that axis is bytes or int16_ts + * + * @param fromTable + * @param X_in + * @return int16_t + */ +int16_t table2D_getAxisValue(struct table2D *fromTable, byte X_in) +{ + int returnValue = 0; + + if(fromTable->axisSize == SIZE_INT) { returnValue = ((int16_t*)((*fromTable).axisX))[X_in]; } + else if(fromTable->axisSize == SIZE_BYTE) { returnValue = ((uint8_t*)((*fromTable).axisX))[X_in]; } + + return returnValue; +} + +/** + * @brief Returns an value from the 2D table given an index value. No interpolation is performed + * + * @param fromTable + * @param X_in + * @return int16_t + */ +int16_t table2D_getRawValue(struct table2D *fromTable, byte X_index) +{ + int returnValue = 0; + + if(fromTable->valueSize == SIZE_INT) { returnValue = ((int16_t*)((*fromTable).values))[X_index]; } + else if(fromTable->valueSize == SIZE_BYTE) { returnValue = ((uint8_t*)((*fromTable).values))[X_index]; } + + return returnValue; +} //This function pulls a value from a 3D table given a target for X and Y coordinates.