diff --git a/speeduino/corrections.cpp b/speeduino/corrections.cpp index 806c973edd..0a8c703f32 100644 --- a/speeduino/corrections.cpp +++ b/speeduino/corrections.cpp @@ -57,7 +57,16 @@ uint8_t dfcoTaper; */ void initialiseCorrections(void) { - egoPID.SetMode(AUTOMATIC); //Turn O2 PID on + PID_output = 0L; + PID_O2 = 0L; + PID_AFRTarget = 0L; + // Toggling between modes resets the PID internal state + // This is required by the unit tests + // TODO: modify PID code to provide a method to reset it. + egoPID.SetMode(AUTOMATIC); + egoPID.SetMode(MANUAL); + egoPID.SetMode(AUTOMATIC); + currentStatus.flexIgnCorrection = 0; currentStatus.egoCorrection = 100; //Default value of no adjustment must be set to avoid randomness on first correction cycle after startup AFRnextCycle = 0; @@ -584,6 +593,26 @@ byte correctionFuelTemp(void) return fuelTempValue; } + +// ============================= Air Fuel Ratio (AFR) correction ============================= + +uint8_t calculateAfrTarget(table3d16RpmLoad &afrLookUpTable, const statuses ¤t, const config2 &page2, const config6 &page6) { + //afrTarget value lookup must be done if O2 sensor is enabled, and always if incorporateAFR is enabled + if (page2.incorporateAFR == true) { + return get3DTableValue(&afrLookUpTable, current.fuelLoad, current.RPM); + } + if (page6.egoType!=EGO_TYPE_OFF) + { + //Determine whether the Y axis of the AFR target table tshould be MAP (Speed-Density) or TPS (Alpha-N) + //Note that this should only run after the sensor warmup delay when using Include AFR option, + if( current.runSecs > page6.ego_sdelay) { + return get3DTableValue(&afrLookUpTable, current.fuelLoad, current.RPM); + } + return current.O2; //Catch all + } + return current.afrTarget; +} + /** Lookup the AFR target table and perform either a simple or PID adjustment based on this. Simple (Best suited to narrowband sensors): @@ -599,17 +628,8 @@ PID (Best suited to wideband sensors): */ byte correctionAFRClosedLoop(void) { - byte AFRValue = 100; - - if( (configPage6.egoType > 0) || (configPage2.incorporateAFR == true) ) //afrTarget value lookup must be done if O2 sensor is enabled, and always if incorporateAFR is enabled - { - currentStatus.afrTarget = currentStatus.O2; //Catch all in case the below doesn't run. This prevents the Include AFR option from doing crazy things if the AFR target conditions aren't met. This value is changed again below if all conditions are met. + byte AFRValue = 100U; - //Determine whether the Y axis of the AFR target table tshould be MAP (Speed-Density) or TPS (Alpha-N) - //Note that this should only run after the sensor warmup delay when using Include AFR option, but on Incorporate AFR option it needs to be done at all times - if( (currentStatus.runSecs > configPage6.ego_sdelay) || (configPage2.incorporateAFR == true) ) { currentStatus.afrTarget = get3DTableValue(&afrTable, currentStatus.fuelLoad, currentStatus.RPM); } //Perform the target lookup - } - if((configPage6.egoType > 0) && (BIT_CHECK(currentStatus.status1, BIT_STATUS1_DFCO) != 1 ) ) //egoType of 0 means no O2 sensor. If DFCO is active do not run the ego controllers to prevent iterator wind-up. { AFRValue = currentStatus.egoCorrection; //Need to record this here, just to make sure the correction stays 'on' even if the nextCycle count isn't ready diff --git a/speeduino/corrections.h b/speeduino/corrections.h index cd95922dba..e748a350f8 100644 --- a/speeduino/corrections.h +++ b/speeduino/corrections.h @@ -9,6 +9,7 @@ All functions in the gamma file return void initialiseCorrections(void); uint16_t correctionsFuel(void); +uint8_t calculateAfrTarget(table3d16RpmLoad &afrLookUpTable, const statuses ¤t, const config2 &page2, const config6 &page6); byte correctionWUE(void); //Warmup enrichment uint16_t correctionCranking(void); //Cranking enrichment byte correctionASE(void); //After Start Enrichment @@ -24,7 +25,6 @@ byte correctionLaunch(void); //Launch control correction byte correctionDFCOfuel(void); //DFCO taper correction bool correctionDFCO(void); //Decelleration fuel cutoff - int8_t correctionsIgn(int8_t advance); int8_t correctionFixedTiming(int8_t advance); int8_t correctionCrankingFixedTiming(int8_t advance); diff --git a/speeduino/globals.h b/speeduino/globals.h index 4d951f2296..662ef9dfe8 100644 --- a/speeduino/globals.h +++ b/speeduino/globals.h @@ -276,8 +276,10 @@ #define EVEN_FIRE 0 #define ODD_FIRE 1 -#define EGO_ALGORITHM_SIMPLE 0 -#define EGO_ALGORITHM_PID 2 +#define EGO_ALGORITHM_SIMPLE 0U +#define EGO_ALGORITHM_INVALID1 1U +#define EGO_ALGORITHM_PID 2U +#define EGO_ALGORITHM_NONE 3U #define STAGING_MODE_TABLE 0 #define STAGING_MODE_AUTO 1 @@ -873,6 +875,13 @@ struct config2 { } __attribute__((__packed__)); //The 32 bit systems require all structs to be fully packed #endif +#define IDLEADVANCE_MODE_OFF 0U +#define IDLEADVANCE_MODE_ADDED 1U +#define IDLEADVANCE_MODE_SWITCHED 2U + +#define IDLEADVANCE_ALGO_TPS 0U +#define IDLEADVANCE_ALGO_CTPS 1U + /** Page 4 of the config - variables required for ignition and rpm/crank phase /cam phase decoding. * See the ini file for further reference. */ diff --git a/speeduino/init.cpp b/speeduino/init.cpp index 340e695325..02fecdd9da 100644 --- a/speeduino/init.cpp +++ b/speeduino/init.cpp @@ -29,6 +29,44 @@ #include "rtc_common.h" #endif +#if !defined(UNIT_TEST) +static inline +#endif +void construct2dTables(void) { + //Repoint the 2D table structs to the config pages that were just loaded + construct2dTable(taeTable, _countof(configPage4.taeValues), configPage4.taeValues, configPage4.taeBins); + construct2dTable(maeTable, _countof(configPage4.maeRates), configPage4.maeRates, configPage4.maeBins); + construct2dTable(WUETable, _countof(configPage2.wueValues), configPage2.wueValues, configPage4.wueBins); + construct2dTable(ASETable, _countof(configPage2.asePct), configPage2.asePct, configPage2.aseBins); + construct2dTable(ASECountTable, _countof(configPage2.aseCount), configPage2.aseCount, configPage2.aseBins); + construct2dTable(PrimingPulseTable, _countof(configPage2.primePulse), configPage2.primePulse, configPage2.primeBins); + construct2dTable(crankingEnrichTable, _countof(configPage10.crankingEnrichValues), configPage10.crankingEnrichValues, configPage10.crankingEnrichBins); + construct2dTable(dwellVCorrectionTable, _countof(configPage4.dwellCorrectionValues), configPage4.dwellCorrectionValues, configPage6.voltageCorrectionBins); + construct2dTable(injectorVCorrectionTable, _countof(configPage6.injVoltageCorrectionValues), configPage6.injVoltageCorrectionValues, configPage6.voltageCorrectionBins); + construct2dTable(IATDensityCorrectionTable, _countof(configPage6.airDenRates), configPage6.airDenRates, configPage6.airDenBins); + construct2dTable(baroFuelTable, _countof(configPage4.baroFuelValues), configPage4.baroFuelValues, configPage4.baroFuelBins); + construct2dTable(IATRetardTable, _countof(configPage4.iatRetValues), configPage4.iatRetValues, configPage4.iatRetBins); + construct2dTable(CLTAdvanceTable, _countof(configPage4.cltAdvValues), configPage4.cltAdvValues, configPage4.cltAdvBins); + construct2dTable(idleTargetTable, _countof(configPage6.iacCLValues), configPage6.iacCLValues, configPage6.iacBins); + construct2dTable(idleAdvanceTable, _countof(configPage4.idleAdvValues), configPage4.idleAdvValues, configPage4.idleAdvBins); + construct2dTable(rotarySplitTable, _countof(configPage10.rotarySplitValues), configPage10.rotarySplitValues, configPage10.rotarySplitBins); + construct2dTable(flexFuelTable, _countof(configPage10.flexFuelAdj), configPage10.flexFuelAdj, configPage10.flexFuelBins); + construct2dTable(flexAdvTable, _countof(configPage10.flexAdvAdj), configPage10.flexAdvAdj, configPage10.flexAdvBins); + construct2dTable(fuelTempTable, _countof(configPage10.fuelTempValues), configPage10.fuelTempValues, configPage10.fuelTempBins); + construct2dTable(oilPressureProtectTable, _countof(configPage10.oilPressureProtMins), configPage10.oilPressureProtMins, configPage10.oilPressureProtRPM); + construct2dTable(coolantProtectTable, _countof(configPage9.coolantProtRPM), configPage9.coolantProtRPM, configPage9.coolantProtTemp); + construct2dTable(fanPWMTable, _countof(configPage9.PWMFanDuty), configPage9.PWMFanDuty, configPage6.fanPWMBins); + construct2dTable(wmiAdvTable, _countof(configPage10.wmiAdvAdj), configPage10.wmiAdvAdj, configPage10.wmiAdvBins); + construct2dTable(rollingCutTable, _countof(configPage15.rollingProtCutPercent), configPage15.rollingProtCutPercent, configPage15.rollingProtRPMDelta); + construct2dTable(injectorAngleTable, _countof(configPage2.injAng), configPage2.injAng, configPage2.injAngRPM); + construct2dTable(flexBoostTable, _countof(configPage10.flexBoostAdj), configPage10.flexBoostAdj, configPage10.flexBoostBins); + construct2dTable(knockWindowStartTable, _countof(configPage10.knock_window_angle), configPage10.knock_window_angle, configPage10.knock_window_rpms); + construct2dTable(knockWindowDurationTable, _countof(configPage10.knock_window_dur), configPage10.knock_window_dur, configPage10.knock_window_rpms); + construct2dTable(cltCalibrationTable, _countof(cltCalibration_values), cltCalibration_values, cltCalibration_bins); + construct2dTable(iatCalibrationTable, _countof(iatCalibration_values), iatCalibration_values, iatCalibration_bins); + construct2dTable(o2CalibrationTable, _countof(o2Calibration_values), o2Calibration_values, o2Calibration_bins); +} + /** Initialise Speeduino for the main loop. * Top level init entry point for all initialisations: * - Initialise and set sizes of 3D tables @@ -121,178 +159,10 @@ void initialiseAll(void) BIT_SET(currentStatus.status4, BIT_STATUS4_ALLOW_LEGACY_COMMS); //Flag legacy comms as being allowed on startup //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.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; - injectorAngleTable.valueSize = SIZE_INT; - injectorAngleTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins - injectorAngleTable.xSize = 4; - injectorAngleTable.values = configPage2.injAng; - injectorAngleTable.axisX = configPage2.injAngRPM; - 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; - baroFuelTable.valueSize = SIZE_BYTE; - baroFuelTable.axisSize = SIZE_BYTE; - baroFuelTable.xSize = 8; - baroFuelTable.values = configPage4.baroFuelValues; - baroFuelTable.axisX = configPage4.baroFuelBins; - 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; - idleTargetTable.valueSize = SIZE_BYTE; - idleTargetTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins - idleTargetTable.xSize = 10; - idleTargetTable.values = configPage6.iacCLValues; - idleTargetTable.axisX = configPage6.iacBins; - idleAdvanceTable.valueSize = SIZE_BYTE; - idleAdvanceTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins - idleAdvanceTable.xSize = 6; - idleAdvanceTable.values = (byte*)configPage4.idleAdvValues; - idleAdvanceTable.axisX = configPage4.idleAdvBins; - 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.values = configPage10.flexBoostAdj; - flexBoostTable.axisX = configPage10.flexBoostBins; - fuelTempTable.valueSize = SIZE_BYTE; - fuelTempTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins - fuelTempTable.xSize = 6; - fuelTempTable.values = configPage10.fuelTempValues; - fuelTempTable.axisX = configPage10.fuelTempBins; - - 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; - - oilPressureProtectTable.valueSize = SIZE_BYTE; - oilPressureProtectTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins - oilPressureProtectTable.xSize = 4; - oilPressureProtectTable.values = configPage10.oilPressureProtMins; - oilPressureProtectTable.axisX = configPage10.oilPressureProtRPM; - - coolantProtectTable.valueSize = SIZE_BYTE; - coolantProtectTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins - coolantProtectTable.xSize = 6; - coolantProtectTable.values = configPage9.coolantProtRPM; - coolantProtectTable.axisX = configPage9.coolantProtTemp; - - - fanPWMTable.valueSize = SIZE_BYTE; - fanPWMTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins - fanPWMTable.xSize = 4; - fanPWMTable.values = configPage9.PWMFanDuty; - fanPWMTable.axisX = configPage6.fanPWMBins; - - rollingCutTable.valueSize = SIZE_BYTE; - rollingCutTable.axisSize = SIZE_SIGNED_BYTE; //X axis is SIGNED for this table. - rollingCutTable.xSize = 4; - rollingCutTable.values = configPage15.rollingProtCutPercent; - rollingCutTable.axisX = configPage15.rollingProtRPMDelta; - - wmiAdvTable.valueSize = SIZE_BYTE; - wmiAdvTable.axisSize = SIZE_BYTE; //Set this table to use byte axis bins - wmiAdvTable.xSize = 6; - wmiAdvTable.values = configPage10.wmiAdvAdj; - wmiAdvTable.axisX = configPage10.wmiAdvBins; - - cltCalibrationTable.valueSize = SIZE_INT; - cltCalibrationTable.axisSize = SIZE_INT; - cltCalibrationTable.xSize = 32; - cltCalibrationTable.values = cltCalibration_values; - cltCalibrationTable.axisX = cltCalibration_bins; - - iatCalibrationTable.valueSize = SIZE_INT; - iatCalibrationTable.axisSize = SIZE_INT; - iatCalibrationTable.xSize = 32; - iatCalibrationTable.values = iatCalibration_values; - iatCalibrationTable.axisX = iatCalibration_bins; - - o2CalibrationTable.valueSize = SIZE_BYTE; - o2CalibrationTable.axisSize = SIZE_INT; - o2CalibrationTable.xSize = 32; - o2CalibrationTable.values = o2Calibration_values; - o2CalibrationTable.axisX = o2Calibration_bins; + construct2dTables(); //Setup the calibration tables - loadCalibration(); - - + loadCalibration(); //Set the pin mappings if((configPage2.pinMapping == 255) || (configPage2.pinMapping == 0)) //255 = EEPROM value in a blank AVR; 0 = EEPROM value in new FRAM diff --git a/speeduino/speeduino.ino b/speeduino/speeduino.ino index 5e340852ba..55ddd640dc 100644 --- a/speeduino/speeduino.ino +++ b/speeduino/speeduino.ino @@ -434,6 +434,7 @@ void loop(void) //Begin the fuel calculation //Calculate an injector pulsewidth from the VE + currentStatus.afrTarget = calculateAfrTarget(afrTable, currentStatus, configPage2, configPage6); currentStatus.corrections = correctionsFuel(); currentStatus.PW1 = PW(req_fuel_uS, currentStatus.VE, currentStatus.MAP, currentStatus.corrections, inj_opentime_uS); diff --git a/speeduino/table2d.cpp b/speeduino/table2d.cpp index 3f874656cc..c36011be56 100644 --- a/speeduino/table2d.cpp +++ b/speeduino/table2d.cpp @@ -13,6 +13,35 @@ Note that this may clear some of the existing values of the table #include "globals.h" #endif +static void construct2dTable(table2D &table, uint8_t valueSize, uint8_t axisSize, uint8_t length, void *values, void *bins) { + table.valueSize = valueSize; + table.axisSize = axisSize; + table.xSize = length; + table.values = values; + table.axisX = bins; + table.lastInput = INT16_MAX; + table.lastXMax = INT16_MAX; + table.lastXMin = INT16_MAX; +} + +void construct2dTable(table2D &table, uint8_t length, uint8_t *values, uint8_t *bins) { + construct2dTable(table, SIZE_BYTE, SIZE_BYTE, length, values, bins); +} +void construct2dTable(table2D &table, uint8_t length, uint8_t *values, int8_t *bins) { + construct2dTable(table, SIZE_BYTE, SIZE_SIGNED_BYTE, length, values, bins); +} +void construct2dTable(table2D &table, uint8_t length, uint16_t *values, uint16_t *bins) { + construct2dTable(table, SIZE_INT, SIZE_INT, length, values, bins); +} +void construct2dTable(table2D &table, uint8_t length, uint8_t *values, uint16_t *bins) { + construct2dTable(table, SIZE_BYTE, SIZE_INT, length, values, bins); +} +void construct2dTable(table2D &table, uint8_t length, uint16_t *values, uint8_t *bins) { + construct2dTable(table, SIZE_INT, SIZE_BYTE, length, values, bins); +} +void construct2dTable(table2D &table, uint8_t length, int16_t *values, uint8_t *bins) { + construct2dTable(table, SIZE_INT, SIZE_BYTE, length, values, bins); +} static inline uint8_t getCacheTime(void) { #if !defined(UNIT_TEST) diff --git a/speeduino/table2d.h b/speeduino/table2d.h index b3f00f3b3d..faa1864626 100644 --- a/speeduino/table2d.h +++ b/speeduino/table2d.h @@ -36,6 +36,13 @@ struct table2D { 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 construct2dTable(table2D &table, uint8_t length, uint8_t *values, uint8_t *bins); +void construct2dTable(table2D &table, uint8_t length, uint8_t *values, int8_t *bins); +void construct2dTable(table2D &table, uint8_t length, uint16_t *values, uint16_t *bins); +void construct2dTable(table2D &table, uint8_t length, uint8_t *values, uint16_t *bins); +void construct2dTable(table2D &table, uint8_t length, uint16_t *values, uint8_t *bins); +void construct2dTable(table2D &table, uint8_t length, int16_t *values, uint8_t *bins); + int16_t table2D_getAxisValue(struct table2D *fromTable, byte X_in); int16_t table2D_getRawValue(struct table2D *fromTable, byte X_index); diff --git a/test/test_fuel/test_PW.cpp b/test/test_fuel/test_PW.cpp index 5d4c155905..625657c1aa 100644 --- a/test/test_fuel/test_PW.cpp +++ b/test/test_fuel/test_PW.cpp @@ -2,6 +2,7 @@ #include #include #include "test_PW.h" +// #include "init.h" #include "../test_utils.h" #define PW_ALLOWED_ERROR 30 @@ -9,16 +10,15 @@ void testPW(void) { SET_UNITY_FILENAME() { - - RUN_TEST(test_PW_No_Multiply); - RUN_TEST(test_PW_MAP_Multiply); - RUN_TEST(test_PW_MAP_Multiply_Compatibility); - RUN_TEST(test_PW_AFR_Multiply); - RUN_TEST(test_PW_Large_Correction); - RUN_TEST(test_PW_Very_Large_Correction); - RUN_TEST(test_PW_4Cyl_PW0); - RUN_TEST(test_PW_Limit_Long_Revolution); - RUN_TEST(test_PW_Limit_90pct); + RUN_TEST_P(test_PW_No_Multiply); + RUN_TEST_P(test_PW_MAP_Multiply); + RUN_TEST_P(test_PW_MAP_Multiply_Compatibility); + RUN_TEST_P(test_PW_AFR_Multiply); + RUN_TEST_P(test_PW_Large_Correction); + RUN_TEST_P(test_PW_Very_Large_Correction); + RUN_TEST_P(test_PW_4Cyl_PW0); + RUN_TEST_P(test_PW_Limit_Long_Revolution); + RUN_TEST_P(test_PW_Limit_90pct); } } @@ -30,6 +30,7 @@ int injOpen; void test_PW_setCommon() { + // initialiseAll(); REQ_FUEL = 1060; VE = 130; MAP = 94; @@ -152,6 +153,8 @@ void test_PW_4Cyl_PW0(void) //Tests the PW Limit calculation for a normal scenario void test_PW_Limit_90pct(void) { + test_PW_setCommon(); + revolutionTime = 10000UL; //6000 rpm configPage2.dutyLim = 90; @@ -163,6 +166,8 @@ void test_PW_Limit_90pct(void) //Occurs at approx. 915rpm void test_PW_Limit_Long_Revolution(void) { + test_PW_setCommon(); + revolutionTime = 100000UL; //600 rpm, below 915rpm cutover point configPage2.dutyLim = 90; configPage2.strokes = TWO_STROKE; diff --git a/test/test_fuel/test_corrections.cpp b/test/test_fuel/test_corrections.cpp index f5356b4b55..b5bcc67128 100644 --- a/test/test_fuel/test_corrections.cpp +++ b/test/test_fuel/test_corrections.cpp @@ -1,81 +1,72 @@ -#include -#include #include +#include "globals.h" +#include "corrections.h" #include "test_corrections.h" #include "../test_utils.h" +#include "init.h" +#include "sensors.h" +#include "speeduino.h" +#include "../test_utils.h" +extern void construct2dTables(void); -void testCorrections() -{ - SET_UNITY_FILENAME() { +extern byte correctionWUE(void); - test_corrections_WUE(); - test_corrections_dfco(); - test_corrections_TAE(); //TPS based accel enrichment corrections - /* - RUN_TEST(test_corrections_cranking); //Not written yet - RUN_TEST(test_corrections_ASE); //Not written yet - RUN_TEST(test_corrections_floodclear); //Not written yet - RUN_TEST(test_corrections_closedloop); //Not written yet - RUN_TEST(test_corrections_flex); //Not written yet - RUN_TEST(test_corrections_bat); //Not written yet - RUN_TEST(test_corrections_iatdensity); //Not written yet - RUN_TEST(test_corrections_baro); //Not written yet - RUN_TEST(test_corrections_launch); //Not written yet - RUN_TEST(test_corrections_dfco); //Not written yet - */ - } +static void setup_wue_table(void) { + construct2dTables(); + initialiseCorrections(); + + //Set some fake values in the table axis. Target value will fall between points 6 and 7 + TEST_DATA_P uint8_t bins[] = { + 0, 0, 0, 0, 0, 0, + 70 + CALIBRATION_TEMPERATURE_OFFSET, + 90 + CALIBRATION_TEMPERATURE_OFFSET, + 100 + CALIBRATION_TEMPERATURE_OFFSET, + 120 + CALIBRATION_TEMPERATURE_OFFSET + }; + TEST_DATA_P uint8_t values[] = { 0, 0, 0, 0, 0, 0, 120, 130, 130, 130 }; + populate_2dtable_P(&WUETable, values, bins); } -void test_corrections_WUE_active(void) +static void test_corrections_WUE_active(void) { + setup_wue_table(); + //Check for WUE being active currentStatus.coolant = 0; - ((uint8_t*)WUETable.axisX)[9] = 120 + CALIBRATION_TEMPERATURE_OFFSET; //Set a WUE end value of 120 + correctionWUE(); TEST_ASSERT_BIT_HIGH(BIT_ENGINE_WARMUP, currentStatus.engine); } -void test_corrections_WUE_inactive(void) +static void test_corrections_WUE_inactive(void) { + setup_wue_table(); + //Check for WUE being inactive due to the temp being too high currentStatus.coolant = 200; - ((uint8_t*)WUETable.axisX)[9] = 120 + CALIBRATION_TEMPERATURE_OFFSET; //Set a WUE end value of 120 correctionWUE(); TEST_ASSERT_BIT_LOW(BIT_ENGINE_WARMUP, currentStatus.engine); } -void test_corrections_WUE_inactive_value(void) +static void test_corrections_WUE_inactive_value(void) { + setup_wue_table(); + configPage4.wueBins[9] = 100; + configPage2.wueValues[9] = 123; //Use a value other than 100 here to ensure we are using the non-default value + //Check for WUE being set to the final row of the WUE curve if the coolant is above the max WUE temp currentStatus.coolant = 200; - ((uint8_t*)WUETable.axisX)[9] = 100; - ((uint8_t*)WUETable.values)[9] = 123; //Use a value other than 100 here to ensure we are using the non-default value - - //Force invalidate the cache - WUETable.cacheTime = currentStatus.secl - 1; TEST_ASSERT_EQUAL(123, correctionWUE() ); } -void test_corrections_WUE_active_value(void) +static void test_corrections_WUE_active_value(void) { //Check for WUE being made active and returning a correct interpolated value currentStatus.coolant = 80; - //Set some fake values in the table axis. Target value will fall between points 6 and 7 - ((uint8_t*)WUETable.axisX)[0] = 0; - ((uint8_t*)WUETable.axisX)[1] = 0; - ((uint8_t*)WUETable.axisX)[2] = 0; - ((uint8_t*)WUETable.axisX)[3] = 0; - ((uint8_t*)WUETable.axisX)[4] = 0; - ((uint8_t*)WUETable.axisX)[5] = 0; - ((uint8_t*)WUETable.axisX)[6] = 70 + CALIBRATION_TEMPERATURE_OFFSET; - ((uint8_t*)WUETable.axisX)[7] = 90 + CALIBRATION_TEMPERATURE_OFFSET; - ((uint8_t*)WUETable.axisX)[8] = 100 + CALIBRATION_TEMPERATURE_OFFSET; - ((uint8_t*)WUETable.axisX)[9] = 120 + CALIBRATION_TEMPERATURE_OFFSET; - - ((uint8_t*)WUETable.values)[6] = 120; - ((uint8_t*)WUETable.values)[7] = 130; + + setup_wue_table(); //Force invalidate the cache WUETable.cacheTime = currentStatus.secl - 1; @@ -84,52 +75,676 @@ void test_corrections_WUE_active_value(void) TEST_ASSERT_EQUAL(125, correctionWUE() ); } -void test_corrections_WUE(void) +static void test_corrections_WUE(void) { - RUN_TEST(test_corrections_WUE_active); - RUN_TEST(test_corrections_WUE_inactive); - RUN_TEST(test_corrections_WUE_active_value); - RUN_TEST(test_corrections_WUE_inactive_value); + RUN_TEST_P(test_corrections_WUE_active); + RUN_TEST_P(test_corrections_WUE_inactive); + RUN_TEST_P(test_corrections_WUE_active_value); + RUN_TEST_P(test_corrections_WUE_inactive_value); } -void test_corrections_cranking(void) -{ +extern uint16_t correctionCranking(void); + +static void setup_correctionCranking_table(void) { + construct2dTables(); + initialiseCorrections(); + + uint8_t values[] = { 120U / 5U, 130U / 5U, 140U / 5U, 150U / 5U }; + uint8_t bins[] = { + (uint8_t)(currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET - 10U), + (uint8_t)(currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET + 10U), + (uint8_t)(currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET + 20U), + (uint8_t)(currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET + 30U) + }; + populate_2dtable(&crankingEnrichTable, values, bins); } -void test_corrections_ASE(void) -{ +static void test_corrections_cranking_inactive(void) { + construct2dTables(); + initialiseCorrections(); + BIT_CLEAR(currentStatus.engine, BIT_ENGINE_CRANK); + BIT_CLEAR(currentStatus.engine, BIT_ENGINE_ASE); + configPage10.crankingEnrichTaper = 0U; + + TEST_ASSERT_EQUAL(100, correctionCranking() ); +} + +static void test_corrections_cranking_cranking(void) { + BIT_SET(currentStatus.engine, BIT_ENGINE_CRANK); + BIT_CLEAR(currentStatus.engine, BIT_ENGINE_ASE); + configPage10.crankingEnrichTaper = 0U; + currentStatus.coolant = 150 - CALIBRATION_TEMPERATURE_OFFSET; + setup_correctionCranking_table(); + + // Should be half way between the 2 table values. + TEST_ASSERT_EQUAL(125, correctionCranking() ); +} + +static void test_corrections_cranking_taper_noase(void) { + BIT_CLEAR(currentStatus.engine, BIT_ENGINE_ASE); + BIT_SET(LOOP_TIMER, BIT_TIMER_10HZ); + configPage10.crankingEnrichTaper = 100U; + currentStatus.ASEValue = 100U; + + currentStatus.coolant = 150 - CALIBRATION_TEMPERATURE_OFFSET; + setup_correctionCranking_table(); + + // Reset taper + BIT_SET(currentStatus.engine, BIT_ENGINE_CRANK); + (void)correctionCranking(); + + // Advance taper to halfway + BIT_CLEAR(currentStatus.engine, BIT_ENGINE_CRANK); + for (uint8_t index=0; index(100U-configPage6.egoLimit)) { + setup_valid_ego_cycle(); + currentStatus.egoCorrection = 100U - counter; + correction = correctionAFRClosedLoop(); + TEST_ASSERT_LESS_THAN(100U, correction); + ++counter; + } + setup_valid_ego_cycle(); + TEST_ASSERT_EQUAL(100U-configPage6.egoLimit, correctionAFRClosedLoop()); + setup_valid_ego_cycle(); + TEST_ASSERT_EQUAL(100U-configPage6.egoLimit, correctionAFRClosedLoop()); + setup_valid_ego_cycle(); + TEST_ASSERT_EQUAL(100U-configPage6.egoLimit, correctionAFRClosedLoop()); +} + +static void test_corrections_closedloop_simple_rich_maxcorrection(void) { + setup_ego_simple(); + + test_rich_max_correction(); +} + +static void setup_ego_pid(void) { + setup_ego_simple(); + configPage6.egoType = EGO_TYPE_WIDE; + configPage6.egoAlgorithm = EGO_ALGORITHM_PID; + configPage6.egoKP = 50U; + configPage6.egoKI = 20U; + configPage6.egoKD = 10U; + + // Initial PID controller setup + correctionAFRClosedLoop(); + setup_valid_ego_cycle(); +} + +// PID is time based and may need multiple cycles to move +static uint8_t run_pid(uint8_t cycles, uint8_t delayMillis) { + for (uint8_t index=0; indexUINT16_MAX) time period + reset_AE(); + MAP_time = MAPlast_time + UINT16_MAX*2; + MAPlast = 40; + currentStatus.MAP = 41; + accelValue = correctionAccel(); //Run the AE calcs + TEST_ASSERT_EQUAL(15, currentStatus.mapDOT); + TEST_ASSERT_EQUAL(100+72, accelValue); + TEST_ASSERT_BIT_HIGH(BIT_ENGINE_ACC, currentStatus.engine); //Confirm AE is flagged on + TEST_ASSERT_BIT_LOW(BIT_ENGINE_DCC, currentStatus.engine); //Confirm AE is flagged on + + // Large change over small time period + reset_AE(); + MAP_time = MAPlast_time + 1000UL; + MAPlast = 10; + currentStatus.MAP = 1000; + accelValue = correctionAccel(); //Run the AE calcs + TEST_ASSERT_EQUAL(6960, currentStatus.mapDOT); + TEST_ASSERT_EQUAL((100+136), accelValue); + TEST_ASSERT_BIT_HIGH(BIT_ENGINE_ACC, currentStatus.engine); //Confirm AE is flagged on + TEST_ASSERT_BIT_LOW(BIT_ENGINE_DCC, currentStatus.engine); //Confirm AE is flagged on + + // Large change over long (>UINT16_MAX) time period + reset_AE(); + MAP_time = MAPlast_time + UINT16_MAX*2; + MAPlast = 10; + currentStatus.MAP = 1000; + accelValue = correctionAccel(); //Run the AE calcs + TEST_ASSERT_EQUAL(14850, currentStatus.mapDOT); + TEST_ASSERT_EQUAL(100+136, accelValue); + TEST_ASSERT_BIT_HIGH(BIT_ENGINE_ACC, currentStatus.engine); //Confirm AE is flagged pn + TEST_ASSERT_BIT_LOW(BIT_ENGINE_DCC, currentStatus.engine); //Confirm AE is flagged on +} + +static void test_corrections_MAE_50pc_rpm_taper() +{ + setup_MAE(); + + //RPM is 50% of the way through the taper range + currentStatus.RPM = 3000; + configPage2.aeTaperMin = 10; //1000 + configPage2.aeTaperMax = 50; //5000 + + MAPlast_time = UINT16_MAX*2UL; + MAP_time = MAPlast_time + 25000UL; + MAPlast = 40; + currentStatus.MAP = 50; + + uint16_t accelValue = correctionAccel(); //Run the AE calcs + + TEST_ASSERT_EQUAL(400, currentStatus.mapDOT); + TEST_ASSERT_EQUAL((100+66), accelValue); + TEST_ASSERT_BIT_HIGH(BIT_ENGINE_ACC, currentStatus.engine); //Confirm AE is flagged on +} + +static void test_corrections_MAE_110pc_rpm_taper() +{ + setup_MAE(); + + //RPM is 110% of the way through the taper range, which should result in no additional AE + currentStatus.RPM = 5400; + configPage2.aeTaperMin = 10; //1000 + configPage2.aeTaperMax = 50; //5000 + + MAPlast_time = UINT16_MAX*2UL; + MAP_time = MAPlast_time + 25000UL; + MAPlast = 40; + currentStatus.MAP = 50; + + uint16_t accelValue = correctionAccel(); //Run the AE calcs + + TEST_ASSERT_EQUAL(400, currentStatus.mapDOT); + TEST_ASSERT_EQUAL(100, accelValue); //Should be no AE as we're above the RPM taper end point + TEST_ASSERT_BIT_HIGH(BIT_ENGINE_ACC, currentStatus.engine); //Confirm AE is flagged on +} + +static void test_corrections_MAE_under_threshold() +{ + setup_MAE(); + + //RPM is 50% of the way through the taper range, but TPS value will be below threshold + currentStatus.RPM = 3000; + configPage2.aeTaperMin = 10; //1000 + configPage2.aeTaperMax = 50; //5000 + + MAPlast_time = UINT16_MAX*2UL; + MAP_time = MAPlast_time + 25000UL; + MAPlast = 0; + currentStatus.MAP = 6; + configPage2.maeThresh = 241; //Above the reading of 240%/s + + uint16_t accelValue = correctionAccel(); //Run the AE calcs + + TEST_ASSERT_EQUAL(240, currentStatus.mapDOT); + TEST_ASSERT_EQUAL(100, accelValue); //Should be no AE as we're above the RPM taper end point + TEST_ASSERT_BIT_LOW(BIT_ENGINE_ACC, currentStatus.engine); //Confirm AE is flagged off +} + +static void test_corrections_MAE_50pc_warmup_taper() +{ + setup_MAE(); + disable_AE_taper(); + + MAPlast_time = UINT16_MAX*2UL; + MAP_time = MAPlast_time + 25000UL; + MAPlast = 40; + currentStatus.MAP = 50; + + //Set a cold % of 50% increase + configPage2.aeColdPct = 150; + configPage2.aeColdTaperMax = 60 + CALIBRATION_TEMPERATURE_OFFSET; + configPage2.aeColdTaperMin = 0 + CALIBRATION_TEMPERATURE_OFFSET; + //Set the coolant to be 50% of the way through the warmup range + currentStatus.coolant = 30; + + uint16_t accelValue = correctionAccel(); //Run the AE calcs + + TEST_ASSERT_EQUAL(400, currentStatus.mapDOT); + TEST_ASSERT_EQUAL((100+165), accelValue); + TEST_ASSERT_BIT_HIGH(BIT_ENGINE_ACC, currentStatus.engine); //Confirm AE is flagged on +} + +static void test_corrections_MAE_timout() +{ + setup_MAE(); + disable_AE_taper(); + + // Confirm MAE is on + MAPlast_time = UINT16_MAX*2UL; + MAP_time = MAPlast_time + 25000UL; + MAPlast = 40; + currentStatus.MAP = 50; + configPage2.aeTime = 0; // This should cause the current cycle to expire & the next one to not occur. + TEST_ASSERT_EQUAL((100+132), correctionAccel()); + TEST_ASSERT_EQUAL(400, currentStatus.mapDOT); + TEST_ASSERT_BIT_HIGH(BIT_ENGINE_ACC, currentStatus.engine); //Confirm AE is flagged on + TEST_ASSERT_BIT_LOW(BIT_ENGINE_DCC, currentStatus.engine); //Confirm AE is flagged on + + // Timeout TAE + TEST_ASSERT_EQUAL(100, correctionAccel()); + TEST_ASSERT_EQUAL(0, currentStatus.mapDOT); + TEST_ASSERT_BIT_LOW(BIT_ENGINE_ACC, currentStatus.engine); //Confirm AE is flagged on + TEST_ASSERT_BIT_LOW(BIT_ENGINE_DCC, currentStatus.engine); //Confirm AE is flagged on + + // But we haven't changed MAP readings so another cycle should begin + TEST_ASSERT_EQUAL((100+132), correctionAccel()); + TEST_ASSERT_EQUAL(400, currentStatus.mapDOT); + TEST_ASSERT_BIT_HIGH(BIT_ENGINE_ACC, currentStatus.engine); //Confirm AE is flagged on + TEST_ASSERT_BIT_LOW(BIT_ENGINE_DCC, currentStatus.engine); //Confirm AE is flagged on +} + + +static void test_corrections_MAE() +{ + RUN_TEST_P(test_corrections_MAE_negative_mapdot); + RUN_TEST_P(test_corrections_MAE_no_rpm_taper); + RUN_TEST_P(test_corrections_MAE_50pc_rpm_taper); + RUN_TEST_P(test_corrections_MAE_110pc_rpm_taper); + RUN_TEST_P(test_corrections_MAE_under_threshold); + RUN_TEST_P(test_corrections_MAE_50pc_warmup_taper); + RUN_TEST_P(test_corrections_MAE_timout); +} + +static void setup_afrtarget(table3d16RpmLoad &afrLookUpTable, + statuses ¤t, + config2 &page2, + config6 &page6) { + TEST_DATA_P table3d_value_t values[] = { + //0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, + 34, 35, 36, 37, 39, 41, 42, 43, 43, 44, 44, 44, 44, 44, 44, 44, + 35, 36, 38, 41, 44, 46, 47, 48, 48, 49, 49, 49, 49, 49, 49, 49, + 36, 39, 42, 46, 50, 51, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 38, 43, 48, 52, 55, 56, 57, 58, 58, 58, 58, 58, 58, 58, 58, 58, + 42, 49, 54, 58, 61, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 63, + 48, 56, 60, 64, 66, 66, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, + 54, 62, 66, 69, 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 61, 69, 72, 74, 76, 76, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, + 68, 75, 78, 79, 81, 81, 81, 82, 82, 82, 82, 82, 82, 82, 82, 82, + 74, 80, 83, 84, 85, 86, 86, 86, 87, 87, 87, 87, 87, 87, 87, 87, + 81, 86, 88, 89, 90, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 93, 96, 98, 99, 99, 100, 100, 101, 101, 101, 101, 101, 101, 101, 101, 101, + 98, 101, 103, 103, 104, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 104, 106, 107, 108, 109, 109, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 109, 111, 112, 113, 114, 114, 114, 115, 115, 115, 114, 114, 114, 114, 114, 114, + }; + TEST_DATA_P table3d_axis_t xAxis[] = {500, 700, 900, 1200, 1600, 2000, 2500, 3100, 3500, 4100, 4700, 5300, 5900, 6500, 6750, 7000}; + TEST_DATA_P table3d_axis_t yAxis[] = { 16, 26, 30, 36, 40, 46, 50, 56, 60, 66, 70, 76, 86, 90, 96, 100}; + populate_table_P(afrLookUpTable, xAxis, yAxis, values); + + memset(&page2, 0, sizeof(page2)); + page2.incorporateAFR = true; + + memset(&page6, 0, sizeof(page6)); + page6.egoType = EGO_TYPE_NARROW; + page6.ego_sdelay = 10; + + memset(¤t, 0, sizeof(current)); + current.runSecs = page6.ego_sdelay + 2U; + current.fuelLoad = 60; + current.RPM = 3100; + current.O2 = 75U; +} + + +static void test_corrections_afrtarget_no_compute(void) { + table3d16RpmLoad afrLookUpTable; + statuses current; + config2 page2; + config6 page6; + setup_afrtarget(afrLookUpTable, current, page2, page6); + + page2.incorporateAFR = false; + page6.egoType = EGO_TYPE_OFF; + current.afrTarget = 111; + + TEST_ASSERT_EQUAL(current.afrTarget, calculateAfrTarget(afrLookUpTable, current, page2, page6)); +} + +static void test_corrections_afrtarget_no_compute_egodelay(void) { + table3d16RpmLoad afrLookUpTable; + statuses current; + config2 page2; + config6 page6; + setup_afrtarget(afrLookUpTable, current, page2, page6); + + page2.incorporateAFR = false; + current.runSecs = page6.ego_sdelay - 2U; + current.afrTarget = current.O2/2U; + + TEST_ASSERT_EQUAL(current.O2, calculateAfrTarget(afrLookUpTable, current, page2, page6)); +} + +static void test_corrections_afrtarget_incorporteafr(void) { + table3d16RpmLoad afrLookUpTable; + statuses current; + config2 page2; + config6 page6; + setup_afrtarget(afrLookUpTable, current, page2, page6); + + page2.incorporateAFR = true; + page6.egoType = EGO_TYPE_OFF; + + TEST_ASSERT_EQUAL(77U, calculateAfrTarget(afrLookUpTable, current, page2, page6)); +} + +static void test_corrections_afrtarget_ego(void) { + table3d16RpmLoad afrLookUpTable; + statuses current; + config2 page2; + config6 page6; + setup_afrtarget(afrLookUpTable, current, page2, page6); + + page2.incorporateAFR = false; + page6.egoType = EGO_TYPE_NARROW; + + TEST_ASSERT_EQUAL(77U, calculateAfrTarget(afrLookUpTable, current, page2, page6)); +} + +static void test_corrections_afrtarget(void) { + RUN_TEST_P(test_corrections_afrtarget_no_compute); + RUN_TEST_P(test_corrections_afrtarget_no_compute_egodelay); + RUN_TEST_P(test_corrections_afrtarget_incorporteafr); + RUN_TEST_P(test_corrections_afrtarget_ego); +} + +extern byte correctionIATDensity(void); + +#if !defined(_countof) +#define _countof(x) (sizeof(x) / sizeof (x[0])) +#endif + +extern byte correctionBaro(void); + +static void test_corrections_correctionsFuel_ae_modes(void) { + setup_TAE(); + populate_2dtable(&injectorVCorrectionTable, 100, 100); + populate_2dtable(&baroFuelTable, 100, 100); + populate_2dtable(&IATDensityCorrectionTable, 100, 100); + populate_2dtable(&flexFuelTable, 100, 100); + populate_2dtable(&fuelTempTable, 100, 100); + + //Disable the taper + currentStatus.RPM = 2000; + configPage2.aeTaperMin = 50; //5000 + configPage2.aeTaperMax = 60; //6000 + configPage2.decelAmount = 33U; + + currentStatus.TPSlast = 0; + currentStatus.TPS = 50; //25% actual value + currentStatus.coolant = 212; + currentStatus.runSecs = 255; + currentStatus.battery10 = 90; + currentStatus.IAT = 100; + currentStatus.launchingHard = false; + currentStatus.launchingSoft = false; + BIT_CLEAR(currentStatus.status1, BIT_STATUS1_DFCO); + BIT_CLEAR(currentStatus.engine, BIT_ENGINE_CRANK); + + configPage2.battVCorMode = BATTV_COR_MODE_WHOLE; + configPage2.dfcoEnabled = 0; + + configPage4.dfcoRPM = 100; + configPage4.wueBins[9] = 100; + configPage2.wueValues[9] = 100; //Use a value other than 100 here to ensure we are using the non-default value + WUETable.cacheTime = currentStatus.secl - 1; + + configPage4.floodClear = 100; + + configPage6.egoType = 0; + configPage6.egoAlgorithm = EGO_ALGORITHM_SIMPLE; + + TEST_ASSERT_EQUAL_MESSAGE(100, correctionWUE(), "correctionWUE"); + TEST_ASSERT_EQUAL_MESSAGE(100, correctionASE(), "correctionASE"); + TEST_ASSERT_EQUAL_MESSAGE(100, correctionCranking(), "correctionCranking"); + TEST_ASSERT_EQUAL_MESSAGE(100, correctionFloodClear(), "correctionFloodClear"); + TEST_ASSERT_EQUAL_MESSAGE(100, correctionAFRClosedLoop(), "correctionAFRClosedLoop"); + TEST_ASSERT_EQUAL_MESSAGE(100, correctionBatVoltage(), "correctionBatVoltage"); + TEST_ASSERT_EQUAL_MESSAGE(100, correctionIATDensity(), "correctionIATDensity"); + TEST_ASSERT_EQUAL_MESSAGE(100, correctionBaro(), "correctionBaro"); + TEST_ASSERT_EQUAL_MESSAGE(100, correctionFlex(), "correctionFlex"); + TEST_ASSERT_EQUAL_MESSAGE(100, correctionFuelTemp(), "correctionFuelTemp"); + TEST_ASSERT_EQUAL_MESSAGE(100, correctionLaunch(), "correctionLaunch"); + TEST_ASSERT_FALSE(correctionDFCO()); + TEST_ASSERT_EQUAL_MESSAGE(100, correctionDFCOfuel(), "correctionDFCOfuel"); + + // Acceeleration + configPage2.aeApplyMode = AE_MODE_MULTIPLIER; + currentStatus.TPSlast = 0; + currentStatus.TPS = 50; //25% actual value + + reset_AE(); + TEST_ASSERT_EQUAL(232U, correctionsFuel()); + + configPage2.aeApplyMode = AE_MODE_ADDER; + currentStatus.TPSlast = 0; + currentStatus.TPS = 50; + reset_AE(); + TEST_ASSERT_EQUAL(100U, correctionsFuel()); + + // Deceeleration + configPage2.aeApplyMode = AE_MODE_MULTIPLIER; + currentStatus.TPSlast = 50; + currentStatus.TPS = 45; + reset_AE(); + TEST_ASSERT_EQUAL(configPage2.decelAmount, correctionsFuel()); + TEST_ASSERT_LESS_THAN(0U, currentStatus.tpsDOT); + + configPage2.aeApplyMode = AE_MODE_ADDER; + currentStatus.TPSlast = 50; + currentStatus.TPS = 45; + reset_AE(); + TEST_ASSERT_EQUAL(configPage2.decelAmount, correctionsFuel()); + TEST_ASSERT_LESS_THAN(0U, currentStatus.tpsDOT); +} + +static void test_corrections_correctionsFuel_clip_limit(void) { + construct2dTables(); + initialiseCorrections(); + + populate_2dtable(&injectorVCorrectionTable, 255, 100); + populate_2dtable(&baroFuelTable, 255, 100); + populate_2dtable(&IATDensityCorrectionTable, 255, 100); + populate_2dtable(&flexFuelTable, 255, 100); + populate_2dtable(&fuelTempTable, 255, 100); + + configPage2.flexEnabled = 1; + configPage2.battVCorMode = BATTV_COR_MODE_WHOLE; + configPage2.dfcoEnabled = 0; + currentStatus.coolant = 212; + currentStatus.runSecs = 255; + currentStatus.battery10 = 100; + currentStatus.IAT = 100 - CALIBRATION_TEMPERATURE_OFFSET; + currentStatus.baro = 100; + currentStatus.ethanolPct = 100; + currentStatus.launchingHard = false; + currentStatus.launchingSoft = false; + currentStatus.AEamount = 100; + + configPage4.wueBins[9] = 100; + configPage2.wueValues[9] = 100; //Use a value other than 100 here to ensure we are using the non-default value + + TEST_ASSERT_EQUAL_MESSAGE(100, correctionWUE(), "correctionWUE"); + TEST_ASSERT_EQUAL_MESSAGE(100, correctionASE(), "correctionASE"); + TEST_ASSERT_EQUAL_MESSAGE(100, correctionCranking(), "correctionCranking"); + TEST_ASSERT_EQUAL_MESSAGE(100, correctionAccel(), "correctionAccel"); + TEST_ASSERT_EQUAL_MESSAGE(100, correctionFloodClear(), "correctionFloodClear"); + TEST_ASSERT_EQUAL_MESSAGE(100, correctionAFRClosedLoop(), "correctionAFRClosedLoop"); + TEST_ASSERT_EQUAL_MESSAGE(255, correctionBatVoltage(), "correctionBatVoltage"); + TEST_ASSERT_EQUAL_MESSAGE(255, correctionIATDensity(), "correctionIATDensity"); + TEST_ASSERT_EQUAL_MESSAGE(255, correctionBaro(), "correctionBaro"); + TEST_ASSERT_EQUAL_MESSAGE(255, correctionFlex(), "correctionFlex"); + TEST_ASSERT_EQUAL_MESSAGE(255, correctionFuelTemp(), "correctionFuelTemp"); + TEST_ASSERT_EQUAL_MESSAGE(100, correctionLaunch(), "correctionLaunch"); + TEST_ASSERT_FALSE(correctionDFCO()); + TEST_ASSERT_EQUAL_MESSAGE(100, correctionDFCOfuel(), "correctionDFCOfuel"); + + TEST_ASSERT_EQUAL(1500U, correctionsFuel()); +} + +static void test_corrections_correctionsFuel(void) { + RUN_TEST_P(test_corrections_correctionsFuel_ae_modes); + RUN_TEST_P(test_corrections_correctionsFuel_clip_limit); +} + +void testCorrections() +{ + SET_UNITY_FILENAME() { + test_corrections_WUE(); + test_corrections_dfco(); + test_corrections_TAE(); //TPS based accel enrichment corrections + test_corrections_MAE(); //MAP based accel enrichment corrections + test_corrections_cranking(); + test_corrections_ASE(); + test_corrections_floodclear(); + test_corrections_bat(); + test_corrections_launch(); + test_corrections_flex(); + test_corrections_afrtarget(); + test_corrections_closedloop(); + test_corrections_correctionsFuel(); + } } \ No newline at end of file diff --git a/test/test_fuel/test_corrections.h b/test/test_fuel/test_corrections.h index d9ef1bb1c3..9b0bb17429 100644 --- a/test/test_fuel/test_corrections.h +++ b/test/test_fuel/test_corrections.h @@ -1,13 +1 @@ -void testCorrections(); -void test_corrections_WUE(void); -void test_corrections_cranking(void); -void test_corrections_ASE(void); -void test_corrections_floodclear(void); -void test_corrections_closedloop(void); -void test_corrections_flex(void); -void test_corrections_bat(void); -void test_corrections_iatdensity(void); -void test_corrections_baro(void); -void test_corrections_launch(void); -void test_corrections_dfco(void); -void test_corrections_TAE(void); \ No newline at end of file +void testCorrections(); \ No newline at end of file diff --git a/test/test_fuel/test_fuel.cpp b/test/test_fuel/test_fuel.cpp index cb5f2b4ff2..321fe85a72 100644 --- a/test/test_fuel/test_fuel.cpp +++ b/test/test_fuel/test_fuel.cpp @@ -19,7 +19,6 @@ void setup() UNITY_BEGIN(); // IMPORTANT LINE! - initialiseAll(); //Run the main initialise function testCorrections(); testPW(); testStaging(); diff --git a/test/test_fuel/test_staging.cpp b/test/test_fuel/test_staging.cpp index 88d91d1cd9..a9fda2e4ad 100644 --- a/test/test_fuel/test_staging.cpp +++ b/test/test_fuel/test_staging.cpp @@ -3,22 +3,24 @@ #include #include "test_staging.h" #include "../test_utils.h" +// #include "init.h" void testStaging(void) { SET_UNITY_FILENAME() { - - RUN_TEST(test_Staging_Off); - RUN_TEST(test_Staging_4cyl_Auto_Inactive); - RUN_TEST(test_Staging_4cyl_Table_Inactive); - RUN_TEST(test_Staging_4cyl_Auto_50pct); - RUN_TEST(test_Staging_4cyl_Auto_33pct); - RUN_TEST(test_Staging_4cyl_Table_50pct); + RUN_TEST(test_Staging_Off); + RUN_TEST(test_Staging_4cyl_Auto_Inactive); + RUN_TEST(test_Staging_4cyl_Table_Inactive); + RUN_TEST(test_Staging_4cyl_Auto_50pct); + RUN_TEST(test_Staging_4cyl_Auto_33pct); + RUN_TEST(test_Staging_4cyl_Table_50pct); } } void test_Staging_setCommon() { + // initialiseAll(); + configPage2.nCylinders = 4; currentStatus.RPM = 3000; currentStatus.fuelLoad = 50; @@ -51,7 +53,7 @@ void test_Staging_Off(void) uint32_t pwLimit = 9000; //90% duty cycle at 6000rpm calculateStaging(pwLimit); - TEST_ASSERT_FALSE(BIT_CHECK(currentStatus.status4, BIT_STATUS4_STAGING_ACTIVE)); + TEST_ASSERT_BIT_LOW(BIT_STATUS4_STAGING_ACTIVE, currentStatus.status4); } void test_Staging_4cyl_Auto_Inactive(void) @@ -74,7 +76,7 @@ void test_Staging_4cyl_Auto_Inactive(void) TEST_ASSERT_EQUAL(7000, currentStatus.PW2); TEST_ASSERT_EQUAL(0, currentStatus.PW3); TEST_ASSERT_EQUAL(0, currentStatus.PW4); - TEST_ASSERT_FALSE(BIT_CHECK(currentStatus.status4, BIT_STATUS4_STAGING_ACTIVE)); + TEST_ASSERT_BIT_LOW(BIT_STATUS4_STAGING_ACTIVE, currentStatus.status4); } void test_Staging_4cyl_Table_Inactive(void) @@ -101,7 +103,7 @@ void test_Staging_4cyl_Table_Inactive(void) TEST_ASSERT_EQUAL(7000, currentStatus.PW2); TEST_ASSERT_EQUAL(0, currentStatus.PW3); TEST_ASSERT_EQUAL(0, currentStatus.PW4); - TEST_ASSERT_FALSE(BIT_CHECK(currentStatus.status4, BIT_STATUS4_STAGING_ACTIVE)); + TEST_ASSERT_BIT_LOW(BIT_STATUS4_STAGING_ACTIVE, currentStatus.status4); } void test_Staging_4cyl_Auto_50pct(void) @@ -123,7 +125,7 @@ void test_Staging_4cyl_Auto_50pct(void) TEST_ASSERT_EQUAL(pwLimit, currentStatus.PW2); TEST_ASSERT_EQUAL(9000, currentStatus.PW3); TEST_ASSERT_EQUAL(9000, currentStatus.PW4); - TEST_ASSERT_TRUE(BIT_CHECK(currentStatus.status4, BIT_STATUS4_STAGING_ACTIVE)); + TEST_ASSERT_BIT_HIGH(BIT_STATUS4_STAGING_ACTIVE, currentStatus.status4); } void test_Staging_4cyl_Auto_33pct(void) @@ -145,7 +147,7 @@ void test_Staging_4cyl_Auto_33pct(void) TEST_ASSERT_EQUAL(pwLimit, currentStatus.PW2); TEST_ASSERT_EQUAL(6000, currentStatus.PW3); TEST_ASSERT_EQUAL(6000, currentStatus.PW4); - TEST_ASSERT_TRUE(BIT_CHECK(currentStatus.status4, BIT_STATUS4_STAGING_ACTIVE)); + TEST_ASSERT_BIT_HIGH(BIT_STATUS4_STAGING_ACTIVE, currentStatus.status4); } void test_Staging_4cyl_Table_50pct(void) @@ -175,5 +177,5 @@ void test_Staging_4cyl_Table_50pct(void) TEST_ASSERT_EQUAL(4000, currentStatus.PW2); TEST_ASSERT_EQUAL(2500, currentStatus.PW3); TEST_ASSERT_EQUAL(2500, currentStatus.PW4); - TEST_ASSERT_TRUE(BIT_CHECK(currentStatus.status4, BIT_STATUS4_STAGING_ACTIVE)); + TEST_ASSERT_BIT_HIGH(BIT_STATUS4_STAGING_ACTIVE, currentStatus.status4); } \ No newline at end of file diff --git a/test/test_ign/main.cpp b/test/test_ign/main.cpp new file mode 100644 index 0000000000..69f9a3b665 --- /dev/null +++ b/test/test_ign/main.cpp @@ -0,0 +1,30 @@ +#include +#include + +void testIgnCorrections(void); + +#define UNITY_EXCLUDE_DETAILS + +void setup() +{ + pinMode(LED_BUILTIN, OUTPUT); + + // NOTE!!! Wait for >2 secs + // if board doesn't support software reset via Serial.DTR/RTS + delay(2000); + + UNITY_BEGIN(); // IMPORTANT LINE! + + testIgnCorrections(); + + UNITY_END(); // stop unit testing +} + +void loop() +{ + // Blink to indicate end of test + digitalWrite(LED_BUILTIN, HIGH); + delay(250); + digitalWrite(LED_BUILTIN, LOW); + delay(250); +} \ No newline at end of file diff --git a/test/test_ign/test_corrections.cpp b/test/test_ign/test_corrections.cpp new file mode 100644 index 0000000000..5de3d5ad1a --- /dev/null +++ b/test/test_ign/test_corrections.cpp @@ -0,0 +1,873 @@ +#include +#include "globals.h" +#include "corrections.h" +// #include "init.h" +#include "idle.h" +#include "../test_utils.h" +#include "sensors.h" + +extern void construct2dTables(void); + +extern int8_t correctionFixedTiming(int8_t advance); + +static void test_correctionFixedTiming_inactive(void) { + configPage2.fixAngEnable = 0; + configPage4.FixAng = 13; + + TEST_ASSERT_EQUAL(8, correctionFixedTiming(8)); + TEST_ASSERT_EQUAL(-3, correctionFixedTiming(-3)); +} + +static void test_correctionFixedTiming_active(void) { + configPage2.fixAngEnable = 1; + configPage4.FixAng = 13; + + TEST_ASSERT_EQUAL(configPage4.FixAng, correctionFixedTiming(8)); + TEST_ASSERT_EQUAL(configPage4.FixAng, correctionFixedTiming(-3)); +} + +static void test_correctionFixedTiming(void) { + RUN_TEST_P(test_correctionFixedTiming_inactive); + RUN_TEST_P(test_correctionFixedTiming_active); +} + +extern int8_t correctionCLTadvance(int8_t advance); + +static void setup_clt_advance_table(void) { + construct2dTables(); + initialiseCorrections(); + TEST_DATA_P uint8_t bins[] = { 60, 70, 80, 90, 100, 110 }; + TEST_DATA_P uint8_t values[] = { 30, 25, 20, 15, 10, 5 }; + populate_2dtable_P(&CLTAdvanceTable, values, bins); +} + +static void test_correctionCLTadvance_lookup(void) { + setup_clt_advance_table(); + + currentStatus.coolant = 105 - CALIBRATION_TEMPERATURE_OFFSET; + TEST_ASSERT_EQUAL(8 + 8 - 15, correctionCLTadvance(8)); + + currentStatus.coolant = 65 - CALIBRATION_TEMPERATURE_OFFSET; + TEST_ASSERT_EQUAL(1 + 28 - 15, correctionCLTadvance(1)); + + currentStatus.coolant = 105 - CALIBRATION_TEMPERATURE_OFFSET; + TEST_ASSERT_EQUAL(-3 + 8 - 15, correctionCLTadvance(-3)); +} + +static void test_correctionCLTadvance(void) { + RUN_TEST_P(test_correctionCLTadvance_lookup); +} + +static void test_correctionCrankingFixedTiming_nocrank_inactive(void) { + setup_clt_advance_table(); + BIT_CLEAR(currentStatus.engine, BIT_ENGINE_CRANK); + configPage2.crkngAddCLTAdv = 0; + configPage4.CrankAng = 8; + + TEST_ASSERT_EQUAL(-7, correctionCrankingFixedTiming(-7)); +} + +static void test_correctionCrankingFixedTiming_crank_fixed(void) { + setup_clt_advance_table(); + BIT_SET(currentStatus.engine, BIT_ENGINE_CRANK); + configPage2.crkngAddCLTAdv = 0; + + configPage4.CrankAng = 8; + TEST_ASSERT_EQUAL(configPage4.CrankAng, correctionCrankingFixedTiming(-7)); + + configPage4.CrankAng = -8; + TEST_ASSERT_EQUAL(configPage4.CrankAng, correctionCrankingFixedTiming(-7)); +} + +static void test_correctionCrankingFixedTiming_crank_coolant(void) { + setup_clt_advance_table(); + BIT_SET(currentStatus.engine, BIT_ENGINE_CRANK); + configPage2.crkngAddCLTAdv = 1; + + configPage4.CrankAng = 8; + + currentStatus.coolant = 65 - CALIBRATION_TEMPERATURE_OFFSET; + TEST_ASSERT_EQUAL(1 + 28 - 15, correctionCLTadvance(1)); +} + +static void test_correctionCrankingFixedTiming(void) { + RUN_TEST_P(test_correctionCrankingFixedTiming_nocrank_inactive); + RUN_TEST_P(test_correctionCrankingFixedTiming_crank_fixed); + RUN_TEST_P(test_correctionCrankingFixedTiming_crank_coolant); +} + +extern int8_t correctionFlexTiming(int8_t advance); + +static void setup_flexAdv(void) { + construct2dTables(); + initialiseCorrections(); + TEST_DATA_P uint8_t bins[] = { 30, 40, 50, 60, 70, 80 }; + TEST_DATA_P uint8_t values[] = { 30, 25, 20, 15, 10, 5 }; + populate_2dtable_P(&flexAdvTable, values, bins); + + configPage2.flexEnabled = 1; + currentStatus.ethanolPct = 55; +} + +static void test_correctionFlexTiming_inactive(void) { + setup_flexAdv(); + configPage2.flexEnabled = 0; + + TEST_ASSERT_EQUAL(-7, correctionFlexTiming(-7)); + TEST_ASSERT_EQUAL(3, correctionFlexTiming(3)); +} + +static void test_correctionFlexTiming_table_lookup(void) { + setup_flexAdv(); + + TEST_ASSERT_EQUAL(8 + 18 - OFFSET_IGNITION, correctionFlexTiming(8)); + TEST_ASSERT_EQUAL(18 - OFFSET_IGNITION, currentStatus.flexIgnCorrection); + + currentStatus.ethanolPct = 35; + TEST_ASSERT_EQUAL(-4 + 28 - OFFSET_IGNITION, correctionFlexTiming(-4)); + TEST_ASSERT_EQUAL(28 - OFFSET_IGNITION, currentStatus.flexIgnCorrection); +} + +static void test_correctionFlexTiming(void) { + RUN_TEST_P(test_correctionFlexTiming_inactive); + RUN_TEST_P(test_correctionFlexTiming_table_lookup); +} + +extern int8_t correctionWMITiming(int8_t advance); + +static void setup_WMIAdv(void) { + construct2dTables(); + initialiseCorrections(); + + configPage10.wmiEnabled= 1; + configPage10.wmiAdvEnabled = 1; + BIT_CLEAR(currentStatus.status4, BIT_STATUS4_WMI_EMPTY); + configPage10.wmiTPS = 50; + currentStatus.TPS = configPage10.wmiTPS + 1; + configPage10.wmiRPM = 30; + currentStatus.RPM = configPage10.wmiRPM + 1U; + configPage10.wmiMAP = 35; + currentStatus.MAP = (configPage10.wmiMAP*2L)+1L; + configPage10.wmiIAT = 155; + currentStatus.IAT = configPage10.wmiIAT - CALIBRATION_TEMPERATURE_OFFSET + 1; + + TEST_DATA_P uint8_t bins[] = { 30, 40, 50, 60, 70, 80 }; + TEST_DATA_P uint8_t values[] = { 30, 25, 20, 15, 10, 5 }; + populate_2dtable_P(&wmiAdvTable, values, bins); +} + +static void test_correctionWMITiming_table_lookup(void) { + setup_WMIAdv(); + + currentStatus.MAP = (55*2U)+1U; + TEST_ASSERT_EQUAL(8 + 18 - OFFSET_IGNITION, correctionWMITiming(8)); + + currentStatus.MAP = (35*2U)+1U; + TEST_ASSERT_EQUAL(-4 + 28 - OFFSET_IGNITION, correctionWMITiming(-4)); +} + +static void test_correctionWMITiming_wmidisabled_inactive(void) { + setup_WMIAdv(); + configPage10.wmiEnabled= 0; + + TEST_ASSERT_EQUAL(8, correctionWMITiming(8)); + TEST_ASSERT_EQUAL(-3, correctionWMITiming(-3)); +} + + +static void test_correctionWMITiming_wmiadvdisabled_inactive(void) { + setup_WMIAdv(); + configPage10.wmiAdvEnabled = 0; + + TEST_ASSERT_EQUAL(8, correctionWMITiming(8)); + TEST_ASSERT_EQUAL(-3, correctionWMITiming(-3)); +} + +static void test_correctionWMITiming_empty_inactive(void) { + setup_WMIAdv(); + BIT_SET(currentStatus.status4, BIT_STATUS4_WMI_EMPTY); + + TEST_ASSERT_EQUAL(8, correctionWMITiming(8)); + TEST_ASSERT_EQUAL(-3, correctionWMITiming(-3)); +} + +static void test_correctionWMITiming_tpslow_inactive(void) { + setup_WMIAdv(); + currentStatus.TPS = configPage10.wmiTPS - 1; + + TEST_ASSERT_EQUAL(8, correctionWMITiming(8)); + TEST_ASSERT_EQUAL(-3, correctionWMITiming(-3)); +} + +static void test_correctionWMITiming_rpmlow_inactive(void) { + setup_WMIAdv(); + currentStatus.RPM = configPage10.wmiRPM - 1U; + + TEST_ASSERT_EQUAL(8, correctionWMITiming(8)); + TEST_ASSERT_EQUAL(-3, correctionWMITiming(-3)); +} + +static void test_correctionWMITiming_maplow_inactive(void) { + setup_WMIAdv(); + currentStatus.MAP = (configPage10.wmiMAP*2)-1; + + TEST_ASSERT_EQUAL(8, correctionWMITiming(8)); + TEST_ASSERT_EQUAL(-3, correctionWMITiming(-3)); +} + +static void test_correctionWMITiming_iatlow_inactive(void) { + setup_WMIAdv(); + currentStatus.IAT = configPage10.wmiIAT - CALIBRATION_TEMPERATURE_OFFSET - 1; + + TEST_ASSERT_EQUAL(8, correctionWMITiming(8)); + TEST_ASSERT_EQUAL(-3, correctionWMITiming(-3)); +} + +static void test_correctionWMITiming(void) { + RUN_TEST_P(test_correctionWMITiming_table_lookup); + RUN_TEST_P(test_correctionWMITiming_tpslow_inactive); + RUN_TEST_P(test_correctionWMITiming_wmidisabled_inactive); + RUN_TEST_P(test_correctionWMITiming_wmiadvdisabled_inactive); + RUN_TEST_P(test_correctionWMITiming_empty_inactive); + RUN_TEST_P(test_correctionWMITiming_tpslow_inactive); + RUN_TEST_P(test_correctionWMITiming_rpmlow_inactive); + RUN_TEST_P(test_correctionWMITiming_maplow_inactive); + RUN_TEST_P(test_correctionWMITiming_iatlow_inactive); +} + +extern int8_t correctionIATretard(int8_t advance); + +static void setup_IATRetard(void) { + construct2dTables(); + initialiseCorrections(); + + TEST_DATA_P uint8_t bins[] = { 30, 40, 50, 60, 70, 80 }; + TEST_DATA_P uint8_t values[] = { 30, 25, 20, 15, 10, 5 }; + populate_2dtable_P(&IATRetardTable, values, bins); + + currentStatus.IAT = 75; +} + +static void test_correctionIATretard_table_lookup(void) { + setup_IATRetard(); + + TEST_ASSERT_EQUAL(-11-8, correctionIATretard(-11)); + + currentStatus.IAT = 35; + TEST_ASSERT_EQUAL(11-28, correctionIATretard(11)); +} + +static void test_correctionIATretard(void) { + RUN_TEST_P(test_correctionIATretard_table_lookup); +} + +extern int8_t correctionIdleAdvance(int8_t advance); + +static void setup_idleadv_tps(void) { + configPage2.idleAdvAlgorithm = IDLEADVANCE_ALGO_TPS; + configPage2.idleAdvTPS = 30; + currentStatus.TPS = configPage2.idleAdvTPS - 1U; +} + +static void setup_idleadv_ctps(void) { + configPage2.idleAdvAlgorithm = IDLEADVANCE_ALGO_CTPS; + currentStatus.CTPSActive = 1; +} + +static void setup_correctionIdleAdvance(void) { + construct2dTables(); + initialiseCorrections(); + + TEST_DATA_P uint8_t bins[] = { 30, 40, 50, 60, 70, 80 }; + TEST_DATA_P uint8_t values[] = { 30, 25, 20, 15, 10, 5 }; + populate_2dtable_P(&idleAdvanceTable, values, bins); + + configPage2.idleAdvEnabled = IDLEADVANCE_MODE_ADDED; + configPage2.idleAdvDelay = 5; + configPage2.idleAdvRPM = 20; + configPage2.vssMode = 0; + configPage6.iacAlgorithm = IAC_ALGORITHM_NONE; + configPage9.idleAdvStartDelay = 0U; + + runSecsX10 = configPage2.idleAdvDelay * 5; + BIT_SET(currentStatus.engine, BIT_ENGINE_RUN); + // int idleRPMdelta = (currentStatus.CLIdleTarget - (currentStatus.RPM / 10) ) + 50; + currentStatus.CLIdleTarget = 100; + currentStatus.RPM = (configPage2.idleAdvRPM * 100) - 1U; + + setup_idleadv_tps(); + // Run once to initialise internal state + correctionIdleAdvance(8); +} + +static void assert_correctionIdleAdvance(int8_t advance, uint8_t expectedLookupValue) { + configPage2.idleAdvEnabled = IDLEADVANCE_MODE_ADDED; + TEST_ASSERT_EQUAL(advance + expectedLookupValue - 15, correctionIdleAdvance(advance)); + + configPage2.idleAdvEnabled = IDLEADVANCE_MODE_SWITCHED; + TEST_ASSERT_EQUAL(expectedLookupValue - 15, correctionIdleAdvance(advance)); +} + +static void test_correctionIdleAdvance_tps_lookup_nodelay(void) { + setup_correctionIdleAdvance(); + + setup_idleadv_tps(); + + currentStatus.RPM = (currentStatus.CLIdleTarget * 10) + 100; + assert_correctionIdleAdvance(8, 25); + + currentStatus.RPM = (currentStatus.CLIdleTarget * 10) - 100; + assert_correctionIdleAdvance(-3, 15); +} + +static void test_correctionIdleAdvance_ctps_lookup_nodelay(void) { + setup_correctionIdleAdvance(); + + setup_idleadv_ctps(); + + currentStatus.RPM = (currentStatus.CLIdleTarget * 10) + 100; + assert_correctionIdleAdvance(8, 25); + + currentStatus.RPM = (currentStatus.CLIdleTarget * 10) - 100; + assert_correctionIdleAdvance(-3, 15); +} + +static void test_correctionIdleAdvance_inactive_notrunning(void) { + setup_correctionIdleAdvance(); + TEST_ASSERT_EQUAL(23, correctionIdleAdvance(8)); + BIT_CLEAR(currentStatus.engine, BIT_ENGINE_RUN); + TEST_ASSERT_EQUAL(23, correctionIdleAdvance(8)); + TEST_ASSERT_EQUAL(8, correctionIdleAdvance(8)); +} + +static void test_correctionIdleAdvance_noadvance_modeoff(void) { + setup_correctionIdleAdvance(); + TEST_ASSERT_EQUAL(23, correctionIdleAdvance(8)); + configPage2.idleAdvEnabled = IDLEADVANCE_MODE_OFF; + TEST_ASSERT_EQUAL(8, correctionIdleAdvance(8)); +} + +static void test_correctionIdleAdvance_noadvance_rpmtoohigh(void) { + setup_correctionIdleAdvance(); + TEST_ASSERT_EQUAL(23, correctionIdleAdvance(8)); + currentStatus.RPM = (configPage2.idleAdvRPM * 100)+1; + TEST_ASSERT_EQUAL(8, correctionIdleAdvance(8)); +} + +static void test_correctionIdleAdvance_noadvance_vsslimit(void) { + setup_correctionIdleAdvance(); + TEST_ASSERT_EQUAL(23, correctionIdleAdvance(8)); + configPage2.vssMode = 1; + configPage2.idleAdvVss = 15; + currentStatus.vss = configPage2.idleAdvVss + 1; + TEST_ASSERT_EQUAL(8, correctionIdleAdvance(8)); +} + +static void test_correctionIdleAdvance_noadvance_tpslimit(void) { + setup_correctionIdleAdvance(); + setup_idleadv_tps(); + TEST_ASSERT_EQUAL(23, correctionIdleAdvance(8)); + currentStatus.TPS = configPage2.idleAdvTPS + 1U; + TEST_ASSERT_EQUAL(8, correctionIdleAdvance(8)); +} + +static void test_correctionIdleAdvance_noadvance_ctpsinactive(void) { + setup_correctionIdleAdvance(); + setup_idleadv_ctps(); + TEST_ASSERT_EQUAL(23, correctionIdleAdvance(8)); + currentStatus.CTPSActive = 0; + TEST_ASSERT_EQUAL(8, correctionIdleAdvance(8)); +} + +static void test_correctionIdleAdvance_noadvance_rundelay(void) { + setup_correctionIdleAdvance(); + TEST_ASSERT_EQUAL(23, correctionIdleAdvance(8)); + runSecsX10 = (configPage2.idleAdvDelay * 5)-1; + TEST_ASSERT_EQUAL(8, correctionIdleAdvance(8)); +} + +static void test_correctionIdleAdvance_delay(void) { + setup_correctionIdleAdvance(); + configPage9.idleAdvStartDelay = 3; + BIT_SET(LOOP_TIMER, BIT_TIMER_10HZ); + TEST_ASSERT_EQUAL(8, correctionIdleAdvance(8)); + TEST_ASSERT_EQUAL(8, correctionIdleAdvance(8)); + TEST_ASSERT_EQUAL(8, correctionIdleAdvance(8)); + TEST_ASSERT_EQUAL(23, correctionIdleAdvance(8)); +} + +static void test_correctionIdleAdvance(void) { + RUN_TEST_P(test_correctionIdleAdvance_tps_lookup_nodelay); + RUN_TEST_P(test_correctionIdleAdvance_ctps_lookup_nodelay); + RUN_TEST_P(test_correctionIdleAdvance_inactive_notrunning); + RUN_TEST_P(test_correctionIdleAdvance_noadvance_modeoff); + RUN_TEST_P(test_correctionIdleAdvance_noadvance_rpmtoohigh); + RUN_TEST_P(test_correctionIdleAdvance_noadvance_vsslimit); + RUN_TEST_P(test_correctionIdleAdvance_noadvance_tpslimit); + RUN_TEST_P(test_correctionIdleAdvance_noadvance_ctpsinactive); + RUN_TEST_P(test_correctionIdleAdvance_noadvance_rundelay); + RUN_TEST_P(test_correctionIdleAdvance_delay); +} + +extern int8_t correctionSoftRevLimit(int8_t advance); + +static void setup_correctionSoftRevLimit(void) { + construct2dTables(); + initialiseCorrections(); + + configPage6.engineProtectType = PROTECT_CUT_IGN; + configPage4.SoftRevLim = 50; + configPage4.SoftLimMax = 1; + configPage4.SoftLimRetard = 5; + configPage2.SoftLimitMode = SOFT_LIMIT_FIXED; + + currentStatus.RPMdiv100 = configPage4.SoftRevLim + 1; + softLimitTime = 0; + + BIT_CLEAR(LOOP_TIMER, BIT_TIMER_10HZ); +} + +static void assert_correctionSoftRevLimit(int8_t advance) { + configPage2.SoftLimitMode = SOFT_LIMIT_FIXED; + TEST_ASSERT_EQUAL(configPage4.SoftLimRetard, correctionSoftRevLimit(advance)); + TEST_ASSERT_BIT_HIGH(BIT_STATUS2_SFTLIM , currentStatus.status2); + + BIT_CLEAR(currentStatus.status2, BIT_STATUS2_SFTLIM); + configPage2.SoftLimitMode = SOFT_LIMIT_RELATIVE; + TEST_ASSERT_EQUAL(advance-configPage4.SoftLimRetard, correctionSoftRevLimit(advance)); + TEST_ASSERT_BIT_HIGH(BIT_STATUS2_SFTLIM , currentStatus.status2); +} + +static void test_correctionSoftRevLimit_modes(void) { + setup_correctionSoftRevLimit(); + + assert_correctionSoftRevLimit(8); + assert_correctionSoftRevLimit(-3); +} + +static void test_correctionSoftRevLimit_inactive_protecttype(void) { + setup_correctionSoftRevLimit(); + + configPage6.engineProtectType = PROTECT_CUT_OFF; + BIT_SET(currentStatus.status2, BIT_STATUS2_SFTLIM); + TEST_ASSERT_EQUAL(8, correctionSoftRevLimit(8)); + TEST_ASSERT_BIT_LOW(BIT_STATUS2_SFTLIM , currentStatus.status2); + + configPage6.engineProtectType = PROTECT_CUT_FUEL; + BIT_SET(currentStatus.status2, BIT_STATUS2_SFTLIM); + TEST_ASSERT_EQUAL(8, correctionSoftRevLimit(8)); + TEST_ASSERT_BIT_LOW(BIT_STATUS2_SFTLIM , currentStatus.status2); +} + +static void test_correctionSoftRevLimit_inactive_rpmtoohigh(void) { + setup_correctionSoftRevLimit(); + assert_correctionSoftRevLimit(8); + + currentStatus.RPMdiv100 = configPage4.SoftRevLim-1; + BIT_SET(currentStatus.status2, BIT_STATUS2_SFTLIM); + TEST_ASSERT_EQUAL(8, correctionSoftRevLimit(8)); + TEST_ASSERT_BIT_LOW(BIT_STATUS2_SFTLIM , currentStatus.status2); +} + +static void test_correctionSoftRevLimit_timeout(void) { + setup_correctionSoftRevLimit(); + + configPage4.SoftLimMax = 3; + configPage2.SoftLimitMode = SOFT_LIMIT_RELATIVE; + BIT_SET(LOOP_TIMER, BIT_TIMER_10HZ); + TEST_ASSERT_EQUAL(8-configPage4.SoftLimRetard, correctionSoftRevLimit(8)); + TEST_ASSERT_EQUAL(-5-configPage4.SoftLimRetard, correctionSoftRevLimit(-5)); + TEST_ASSERT_EQUAL(23-configPage4.SoftLimRetard, correctionSoftRevLimit(23)); + TEST_ASSERT_EQUAL(-21, correctionSoftRevLimit(-21)); + TEST_ASSERT_EQUAL(8, correctionSoftRevLimit(8)); + TEST_ASSERT_EQUAL(0, correctionSoftRevLimit(0)); +} + +static void test_correctionSoftRevLimit(void) { + RUN_TEST_P(test_correctionSoftRevLimit_modes); + RUN_TEST_P(test_correctionSoftRevLimit_inactive_protecttype); + RUN_TEST_P(test_correctionSoftRevLimit_inactive_rpmtoohigh); + RUN_TEST_P(test_correctionSoftRevLimit_timeout); +} + +extern int8_t correctionNitrous(int8_t advance); + +static void test_correctionNitrous_disabled(void) { + configPage10.n2o_enable = 0; + TEST_ASSERT_EQUAL(13, correctionNitrous(13)); + TEST_ASSERT_EQUAL(-13, correctionNitrous(-13)); +} + +static void test_correctionNitrous_stage1(void) { + configPage10.n2o_enable = 1; + configPage10.n2o_stage1_retard = 5; + configPage10.n2o_stage2_retard = 0; + + currentStatus.nitrous_status = NITROUS_STAGE1; + TEST_ASSERT_EQUAL(8, correctionNitrous(13)); + TEST_ASSERT_EQUAL(-18, correctionNitrous(-13)); + + currentStatus.nitrous_status = NITROUS_BOTH; + TEST_ASSERT_EQUAL(8, correctionNitrous(13)); + TEST_ASSERT_EQUAL(-18, correctionNitrous(-13)); +} + +static void test_correctionNitrous_stage2(void) { + configPage10.n2o_enable = 1; + configPage10.n2o_stage1_retard = 0; + configPage10.n2o_stage2_retard = 5; + + currentStatus.nitrous_status = NITROUS_STAGE2; + TEST_ASSERT_EQUAL(8, correctionNitrous(13)); + TEST_ASSERT_EQUAL(-18, correctionNitrous(-13)); + + currentStatus.nitrous_status = NITROUS_BOTH; + TEST_ASSERT_EQUAL(8, correctionNitrous(13)); + TEST_ASSERT_EQUAL(-18, correctionNitrous(-13)); +} + +static void test_correctionNitrous_stageboth(void) { + configPage10.n2o_enable = 1; + configPage10.n2o_stage1_retard = 3; + configPage10.n2o_stage2_retard = 5; + + currentStatus.nitrous_status = NITROUS_BOTH; + TEST_ASSERT_EQUAL(5, correctionNitrous(13)); + TEST_ASSERT_EQUAL(-21, correctionNitrous(-13)); +} + +static void test_correctionNitrous(void) { + RUN_TEST_P(test_correctionNitrous_disabled); + RUN_TEST_P(test_correctionNitrous_stage1); + RUN_TEST_P(test_correctionNitrous_stage2); + RUN_TEST_P(test_correctionNitrous_stageboth); +} + +extern int8_t correctionSoftLaunch(int8_t advance); + +static void setup_correctionSoftLaunch(void) { + configPage6.launchEnabled = 1; + configPage6.flatSArm = 20; + configPage6.lnchSoftLim = 40; + configPage10.lnchCtrlTPS = 80; + + currentStatus.clutchTrigger = 1; + currentStatus.clutchEngagedRPM = ((configPage6.flatSArm) * 100) - 100; + currentStatus.RPM = ((configPage6.lnchSoftLim) * 100) + 100; + currentStatus.TPS = configPage10.lnchCtrlTPS + 1; +} + +static void test_correctionSoftLaunch_on(void) { + setup_correctionSoftLaunch(); + + configPage6.lnchRetard = -3; + TEST_ASSERT_EQUAL(configPage6.lnchRetard, correctionSoftLaunch(-8)); + TEST_ASSERT_TRUE(currentStatus.launchingSoft); + TEST_ASSERT_BIT_HIGH(BIT_STATUS2_SLAUNCH, currentStatus.status2); + + configPage6.lnchRetard = 3; + currentStatus.launchingSoft = false; + BIT_CLEAR(currentStatus.status2, BIT_STATUS2_SLAUNCH); + TEST_ASSERT_EQUAL(configPage6.lnchRetard, correctionSoftLaunch(8)); + TEST_ASSERT_TRUE(currentStatus.launchingSoft); + TEST_ASSERT_BIT_HIGH(BIT_STATUS2_SLAUNCH, currentStatus.status2); +} + +static void test_correctionSoftLaunch_off_disabled(void) { + setup_correctionSoftLaunch(); + configPage6.launchEnabled = 0; + configPage6.lnchRetard = -3; + + TEST_ASSERT_EQUAL(-8, correctionSoftLaunch(-8)); + TEST_ASSERT_FALSE(currentStatus.launchingSoft); + TEST_ASSERT_BIT_LOW(BIT_STATUS2_SLAUNCH, currentStatus.status2); +} + +static void test_correctionSoftLaunch_off_noclutchtrigger(void) { + setup_correctionSoftLaunch(); + currentStatus.clutchTrigger = 0; + configPage6.lnchRetard = -3; + + TEST_ASSERT_EQUAL(-8, correctionSoftLaunch(-8)); + TEST_ASSERT_FALSE(currentStatus.launchingSoft); + TEST_ASSERT_BIT_LOW(BIT_STATUS2_SLAUNCH, currentStatus.status2); +} + +static void test_correctionSoftLaunch_off_clutchrpmlow(void) { + setup_correctionSoftLaunch(); + currentStatus.clutchEngagedRPM = (configPage6.flatSArm * 100) + 1; + configPage6.lnchRetard = -3; + + TEST_ASSERT_EQUAL(-8, correctionSoftLaunch(-8)); + TEST_ASSERT_FALSE(currentStatus.launchingSoft); + TEST_ASSERT_BIT_LOW(BIT_STATUS2_SLAUNCH, currentStatus.status2); +} + +static void test_correctionSoftLaunch_off_rpmlimit(void) { + setup_correctionSoftLaunch(); + currentStatus.RPM = (configPage6.lnchSoftLim * 100) - 1; + configPage6.lnchRetard = -3; + + TEST_ASSERT_EQUAL(-8, correctionSoftLaunch(-8)); + TEST_ASSERT_FALSE(currentStatus.launchingSoft); + TEST_ASSERT_BIT_LOW(BIT_STATUS2_SLAUNCH, currentStatus.status2); +} + +static void test_correctionSoftLaunch_off_tpslow(void) { + setup_correctionSoftLaunch(); + currentStatus.TPS = configPage10.lnchCtrlTPS - 1; + configPage6.lnchRetard = -3; + + TEST_ASSERT_EQUAL(-8, correctionSoftLaunch(-8)); + TEST_ASSERT_FALSE(currentStatus.launchingSoft); + TEST_ASSERT_BIT_LOW(BIT_STATUS2_SLAUNCH, currentStatus.status2); +} + +static void test_correctionSoftLaunch(void) { + RUN_TEST_P(test_correctionSoftLaunch_on); + RUN_TEST_P(test_correctionSoftLaunch_off_disabled); + RUN_TEST_P(test_correctionSoftLaunch_off_noclutchtrigger); + RUN_TEST_P(test_correctionSoftLaunch_off_clutchrpmlow); + RUN_TEST_P(test_correctionSoftLaunch_off_rpmlimit); + RUN_TEST_P(test_correctionSoftLaunch_off_tpslow); +} + +extern int8_t correctionSoftFlatShift(int8_t advance); + +static void setup_correctionSoftFlatShift(void) { + configPage6.flatSEnable = 1; + configPage6.flatSArm = 10; + configPage6.flatSSoftWin = 10; + + currentStatus.clutchTrigger = 1; + currentStatus.clutchEngagedRPM = ((configPage6.flatSArm) * 100) + 500; + currentStatus.RPM = currentStatus.clutchEngagedRPM + 600; + + BIT_CLEAR(currentStatus.status5, BIT_STATUS5_FLATSS); +} + +static void test_correctionSoftFlatShift_on(void) { + setup_correctionSoftFlatShift(); + configPage6.flatSRetard = -3; + + TEST_ASSERT_EQUAL(configPage6.flatSRetard, correctionSoftFlatShift(-8)); + TEST_ASSERT_BIT_HIGH(BIT_STATUS5_FLATSS, currentStatus.status5); + + BIT_CLEAR(currentStatus.status5, BIT_STATUS5_FLATSS); + TEST_ASSERT_EQUAL(configPage6.flatSRetard, correctionSoftFlatShift(3)); + TEST_ASSERT_BIT_HIGH(BIT_STATUS5_FLATSS, currentStatus.status5); +} + +static void test_correctionSoftFlatShift_off_disabled(void) { + setup_correctionSoftFlatShift(); + configPage6.flatSRetard = -3; + configPage6.flatSEnable = 0; + + BIT_SET(currentStatus.status5, BIT_STATUS5_FLATSS); + TEST_ASSERT_EQUAL(-8, correctionSoftFlatShift(-8)); + TEST_ASSERT_BIT_LOW(BIT_STATUS5_FLATSS, currentStatus.status5); +} + +static void test_correctionSoftFlatShift_off_noclutchtrigger(void) { + setup_correctionSoftFlatShift(); + configPage6.flatSRetard = -3; + currentStatus.clutchTrigger = 0; + + BIT_SET(currentStatus.status5, BIT_STATUS5_FLATSS); + TEST_ASSERT_EQUAL(-8, correctionSoftFlatShift(-8)); + TEST_ASSERT_BIT_LOW(BIT_STATUS5_FLATSS, currentStatus.status5); +} + +static void test_correctionSoftFlatShift_off_clutchrpmtoolow(void) { + setup_correctionSoftFlatShift(); + configPage6.flatSRetard = -3; + currentStatus.clutchEngagedRPM = ((configPage6.flatSArm) * 100) - 500; + + BIT_SET(currentStatus.status5, BIT_STATUS5_FLATSS); + TEST_ASSERT_EQUAL(-8, correctionSoftFlatShift(-8)); + TEST_ASSERT_BIT_LOW(BIT_STATUS5_FLATSS, currentStatus.status5); +} + +static void test_correctionSoftFlatShift_off_rpmnotinwindow(void) { + setup_correctionSoftFlatShift(); + configPage6.flatSRetard = -3; + currentStatus.RPM = (currentStatus.clutchEngagedRPM - (configPage6.flatSSoftWin * 100) ) - 100; + + BIT_SET(currentStatus.status5, BIT_STATUS5_FLATSS); + TEST_ASSERT_EQUAL(-8, correctionSoftFlatShift(-8)); + TEST_ASSERT_BIT_LOW(BIT_STATUS5_FLATSS, currentStatus.status5); +} + +static void test_correctionSoftFlatShift(void) { + RUN_TEST_P(test_correctionSoftFlatShift_on); + RUN_TEST_P(test_correctionSoftFlatShift_off_disabled); + RUN_TEST_P(test_correctionSoftFlatShift_off_noclutchtrigger); + RUN_TEST_P(test_correctionSoftFlatShift_off_clutchrpmtoolow); + RUN_TEST_P(test_correctionSoftFlatShift_off_rpmnotinwindow); +} + +#if 0 // Wait until Noisymime is done with knock implementation +extern int8_t correctionKnock(int8_t advance); + +static void setup_correctionKnock(void) { + configPage10.knock_mode = KNOCK_MODE_DIGITAL; + configPage10.knock_count = 5U; + configPage10.knock_firstStep = 3U; + // knockCounter = configPage10.knock_count + 1; +// TEST_DATA_P uint8_t startBins[] = { 30, 40, 50, 60, 70, 80 }; +// TEST_DATA_P uint8_t startValues[] = { 30, 25, 20, 15, 10, 5 }; +// populate_2dtable_P(&knockWindowStartTable, startValues, startBins); + +// TEST_DATA_P uint8_t durationBins[] = { 30, 40, 50, 60, 70, 80 }; +// TEST_DATA_P uint8_t durationValues[] = { 30, 25, 20, 15, 10, 5 }; +// populate_2dtable_P(&knockWindowDurationTable, durationValues, durationBins); +} + +static void test_correctionKnock_firstStep(void) { + setup_correctionKnock(); + + TEST_ASSERT_EQUAL(-11, correctionKnock(-8)); +} + +static void test_correctionKnock_disabled_modeoff(void) { + setup_correctionKnock(); + configPage10.knock_mode = KNOCK_MODE_OFF; + TEST_ASSERT_EQUAL(-8, correctionKnock(-8)); +} + +static void test_correctionKnock_disabled_counttoolow(void) { + setup_correctionKnock(); + knockCounter = configPage10.knock_count - 1; + TEST_ASSERT_EQUAL(-8, correctionKnock(-8)); +} + +static void test_correctionKnock_disabled_knockactive(void) { + setup_correctionKnock(); + currentStatus.knockActive = true; + TEST_ASSERT_EQUAL(-8, correctionKnock(-8)); +} +#endif + +static void test_correctionKnock(void) { +} + +static void setup_correctionsDwell(void) { + construct2dTables(); + initialiseCorrections(); + + configPage4.sparkDur = 10; + configPage2.perToothIgn = false; + configPage4.dwellErrCorrect = 0; + configPage4.sparkMode = IGN_MODE_WASTED; + + currentStatus.actualDwell = 770; + currentStatus.battery10 = 95; + + revolutionTime = 666; + + TEST_DATA_P uint8_t bins[] = { 60, 70, 80, 90, 100, 110 }; + TEST_DATA_P uint8_t values[] = { 130, 125, 120, 115, 110, 90 }; + populate_2dtable_P(&dwellVCorrectionTable, values, bins); +} + +static void test_correctionsDwell_nopertooth(void) { + setup_correctionsDwell(); + + currentStatus.battery10 = 105; + configPage2.nCylinders = 8; + + configPage4.sparkMode = IGN_MODE_WASTED; + TEST_ASSERT_EQUAL(296, correctionsDwell(800)); + + configPage4.sparkMode = IGN_MODE_SINGLE; + TEST_ASSERT_EQUAL(74, correctionsDwell(800)); + + configPage4.sparkMode = IGN_MODE_ROTARY; + configPage10.rotaryType = ROTARY_IGN_RX8; + TEST_ASSERT_EQUAL(296, correctionsDwell(800)); + + configPage4.sparkMode = IGN_MODE_ROTARY; + configPage10.rotaryType = ROTARY_IGN_FC; + TEST_ASSERT_EQUAL(74, correctionsDwell(800)); +} + +static void test_correctionsDwell_pertooth(void) { + setup_correctionsDwell(); + + currentStatus.battery10 = 105; + configPage2.perToothIgn = true; + configPage4.dwellErrCorrect = 1; + configPage4.sparkMode = IGN_MODE_WASTED; + + currentStatus.actualDwell = 200; + TEST_ASSERT_EQUAL(444, correctionsDwell(800)); + + currentStatus.actualDwell = 1400; + TEST_ASSERT_EQUAL(296, correctionsDwell(800)); +} + +static void test_correctionsDwell_wasted_nopertooth_largerevolutiontime(void) { + setup_correctionsDwell(); + + currentStatus.dwellCorrection = 55; + currentStatus.battery10 = 105; + revolutionTime = 5000; + TEST_ASSERT_EQUAL(800, correctionsDwell(800)); +} + +static void test_correctionsDwell_initialises_current_actualDwell(void) { + setup_correctionsDwell(); + + currentStatus.actualDwell = 0; + correctionsDwell(777); + TEST_ASSERT_EQUAL(777, currentStatus.actualDwell); +} + +static void test_correctionsDwell_sets_dwellCorrection(void) { + setup_correctionsDwell(); + + currentStatus.dwellCorrection = UINT8_MAX; + currentStatus.battery10 = 90; + correctionsDwell(777); + TEST_ASSERT_EQUAL(115, currentStatus.dwellCorrection); +} + +static void test_correctionsDwell_uses_batvcorrection(void) { + setup_correctionsDwell(); + configPage2.nCylinders = 8; + configPage4.sparkMode = IGN_MODE_WASTED; + + currentStatus.battery10 = 105; + TEST_ASSERT_EQUAL(296, correctionsDwell(800)); + + currentStatus.battery10 = 65; + TEST_ASSERT_EQUAL(337, correctionsDwell(800)); +} + +static void test_correctionsDwell(void) { + RUN_TEST_P(test_correctionsDwell_nopertooth); + RUN_TEST_P(test_correctionsDwell_pertooth); + RUN_TEST_P(test_correctionsDwell_wasted_nopertooth_largerevolutiontime); + RUN_TEST_P(test_correctionsDwell_initialises_current_actualDwell); + RUN_TEST_P(test_correctionsDwell_sets_dwellCorrection); + RUN_TEST_P(test_correctionsDwell_uses_batvcorrection); +} + +void testIgnCorrections(void) { + Unity.TestFile = __FILE__; + + test_correctionFixedTiming(); + test_correctionCLTadvance(); + test_correctionCrankingFixedTiming(); + test_correctionFlexTiming(); + test_correctionWMITiming(); + test_correctionIATretard(); + test_correctionIdleAdvance(); + test_correctionSoftRevLimit(); + test_correctionNitrous(); + test_correctionSoftLaunch(); + test_correctionSoftFlatShift(); + test_correctionKnock(); + // correctionDFCOignition() is tested in the fueling unit tests, since it is tightly coupled to fuel DFCO + test_correctionsDwell(); +} \ No newline at end of file diff --git a/test/test_math/test_division.cpp b/test/test_math/test_division.cpp index 6f5a32df4a..4354d256ee 100644 --- a/test/test_math/test_division.cpp +++ b/test/test_math/test_division.cpp @@ -168,7 +168,6 @@ void test_maths_udiv_32_16_perf(void) auto nativeTest = [] (uint16_t index, uint32_t &checkSum) { checkSum += (uint32_t)indexToDividend(index) / (uint32_t)index; }; auto optimizedTest = [] (uint16_t index, uint32_t &checkSum) { checkSum += udiv_32_16(indexToDividend(index), index); }; - TEST_MESSAGE("udiv_32_16"); auto comparison = compare_executiontime(iters, start_index, end_index, step, nativeTest, optimizedTest); // The checksums will be different due to rounding. This is only @@ -189,7 +188,6 @@ void test_maths_div100_s16_perf(void) auto nativeTest = [] (int16_t index, int32_t &checkSum) { checkSum += (int16_t)index / (int16_t)100; }; auto optimizedTest = [] (int16_t index, int32_t &checkSum) { checkSum += div100(index); }; - TEST_MESSAGE("div100_s16"); auto comparison = compare_executiontime(iters, start_index, end_index, step, nativeTest, optimizedTest); // The checksums will be different due to rounding. This is only @@ -200,6 +198,26 @@ void test_maths_div100_s16_perf(void) #endif } +void test_maths_div10_s16_perf(void) +{ + // Unit test to confirm using div100 to divide by 10 is quicker than straight division by 10. +#if defined(ARDUINO_ARCH_AVR) + constexpr int16_t iters = 1; + constexpr int16_t start_index = -3213; + constexpr int16_t end_index = 3213; + constexpr int16_t step = 17; + + auto nativeTest = [] (int16_t index, int32_t &checkSum) { checkSum += (int16_t)index / (int16_t)10; }; + auto optimizedTest = [] (int16_t index, int32_t &checkSum) { checkSum += div100((int16_t)(index * 10)); }; + auto comparison = compare_executiontime(iters, start_index, end_index, step, nativeTest, optimizedTest); + + // The checksums will be different due to rounding. This is only + // here to force the compiler to run the loops above + TEST_ASSERT_INT32_WITHIN(UINT32_MAX/2, comparison.timeA.result, comparison.timeB.result); + + TEST_ASSERT_LESS_THAN(comparison.timeA.durationMicros, comparison.timeB.durationMicros); +#endif +} void test_maths_div100_s32_perf(void) { @@ -211,7 +229,6 @@ void test_maths_div100_s32_perf(void) auto nativeTest = [] (int32_t index, int32_t &checkSum) { checkSum += (int32_t)index / (int32_t)100; }; auto optimizedTest = [] (int32_t index, int32_t &checkSum) { checkSum += div100(index); }; - TEST_MESSAGE("div100_s32"); auto comparison = compare_executiontime(iters, start_index, end_index, step, nativeTest, optimizedTest); // The checksums will be different due to rounding. This is only @@ -234,6 +251,7 @@ void testDivision(void) { RUN_TEST(test_maths_udiv_32_16_perf); RUN_TEST(test_maths_div360); RUN_TEST(test_maths_div100_s16_perf); + RUN_TEST(test_maths_div10_s16_perf); RUN_TEST(test_maths_div100_s32_perf); } } \ No newline at end of file diff --git a/test/test_tables/tests_tables.cpp b/test/test_tables/tests_tables.cpp index 8163bbc4a2..2fc1af3c7d 100644 --- a/test/test_tables/tests_tables.cpp +++ b/test/test_tables/tests_tables.cpp @@ -1,18 +1,11 @@ -//#include -#include // memcpy +// #include // memcpy #include #include #include "tests_tables.h" #include "table3d.h" #include "../test_utils.h" -#define _countof(x) (sizeof(x) / sizeof (x[0])) - -#if defined(PROGMEM) -const PROGMEM table3d_value_t values[] = { -#else -const table3d_value_t values[] = { -#endif +TEST_DATA_P table3d_value_t values[] = { //0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 34, 35, 36, 37, 39, 41, 42, 43, 43, 44, 44, 44, 44, 44, 44, 44, @@ -31,13 +24,12 @@ const table3d_value_t values[] = { 104, 106, 107, 108, 109, 109, 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, 109, 111, 112, 113, 114, 114, 114, 115, 115, 115, 114, 114, 114, 114, 114, 114, }; -static const table3d_axis_t tempXAxis[] = {500, 700, 900, 1200, 1600, 2000, 2500, 3100, 3500, 4100, 4700, 5300, 5900, 6500, 6750, 7000}; -static const table3d_axis_t xMin = tempXAxis[0]; -static const table3d_axis_t xMax = tempXAxis[_countof(tempXAxis)-1]; -static const table3d_axis_t tempYAxis[] = { 16, 26, 30, 36, 40, 46, 50, 56, 60, 66, 70, 76, 86, 90, 96, 100}; -static const table3d_axis_t yMin = tempYAxis[0]; -static const table3d_axis_t yMax = tempYAxis[_countof(tempYAxis)-1]; - +TEST_DATA_P table3d_axis_t tempXAxis[] = { 500, 700, 900, 1200, 1600, 2000, 2500, 3100, 3500, 4100, 4700, 5300, 5900, 6500, 6750, 7000}; +static constexpr table3d_axis_t xMin = tempXAxis[0]; +static constexpr table3d_axis_t xMax = tempXAxis[_countof(tempXAxis)-1]; +TEST_DATA_P table3d_axis_t tempYAxis[] = { 16, 26, 30, 36, 40, 46, 50, 56, 60, 66, 70, 76, 86, 90, 96, 100}; +static constexpr table3d_axis_t yMin = tempYAxis[0]; +static constexpr table3d_axis_t yMax = tempYAxis[_countof(tempYAxis)-1]; static table3d16RpmLoad testTable; @@ -65,51 +57,7 @@ void setup_TestTable(void) ---------------------------------------------------------------------------------------------------------------- 500 | 700 | 900 | 1200 | 1600 | 2000 | 2500 | 3100 | 3500 | 4100 | 4700 | 5300 | 5900 | 6500 | 6750 | 7000 */ - - // - // NOTE: USE OF ITERATORS HERE IS DELIBERATE. IT INCLUDES THEM IN THE UNIT TESTS, giving - // them some coverage - // - { - table_axis_iterator itX = testTable.axisX.begin(); - const table3d_axis_t *pXValue = tempXAxis; - while (!itX.at_end()) - { - *itX = *pXValue; - ++pXValue; - ++itX; - } - } - { - table_axis_iterator itY = testTable.axisY.begin(); - const table3d_axis_t *pYValue = tempYAxis; - while (!itY.at_end()) - { - *itY = *pYValue; - ++pYValue; - ++itY; - } - } - - { - table_value_iterator itZ = testTable.values.begin(); - const table3d_value_t *pZValue = values; - while (!itZ.at_end()) - { - table_row_iterator itRow = *itZ; - while (!itRow.at_end()) - { -#if defined(PROGMEM) - *itRow = pgm_read_byte(pZValue); -#else - *itRow = *pZValue; -#endif - ++pZValue; - ++itRow; - } - ++itZ; - } - } + populate_table_P(testTable, tempXAxis, tempYAxis, values); } void testTables() diff --git a/test/test_utils.h b/test/test_utils.h index 552a78cd52..aadb3f8f9b 100644 --- a/test/test_utils.h +++ b/test/test_utils.h @@ -3,6 +3,20 @@ #include #include +#include +#include +#include "table2d.h" +#include "table3d.h" + +template +constexpr void STR_LEN_CHECK(char const (&)[N]) +{ + static_assert(N < MAX_LEN, "String overflow!"); +} + +#if !defined(_countof) +#define _countof(x) (sizeof(x) / sizeof (x[0])) +#endif // Unity macro to reduce memory usage (RAM, .bss) // @@ -16,24 +30,123 @@ #define RUN_TEST_P(func) \ { \ char funcName[128]; \ + constexpr size_t bufferLen = _countof(funcName); \ + STR_LEN_CHECK(#func); \ strcpy_P(funcName, PSTR(#func)); \ UnityDefaultTestRun(func, funcName, __LINE__); \ } -static __inline__ uint8_t ufname_set(const char *newFName) +// ============================ SET_UNITY_FILENAME ============================ + +static inline uint8_t ufname_set(const char *newFName) { Unity.TestFile = newFName; return 1; } -static __inline__ void ufname_szrestore(char** __s) + +static inline void ufname_szrestore(char** __s) { Unity.TestFile = *__s; __asm__ volatile ("" ::: "memory"); } + #define UNITY_FILENAME_RESTORE char* _ufname_saved \ __attribute__((__cleanup__(ufname_szrestore))) = (char*)Unity.TestFile #define SET_UNITY_FILENAME() \ for ( UNITY_FILENAME_RESTORE, _ufname_done = ufname_set(__FILE__); \ _ufname_done; _ufname_done = 0 ) + +// ============================ end SET_UNITY_FILENAME ============================ + +// Store test data in flash, if feasible. +#if defined(PROGMEM) +#define TEST_DATA_P static constexpr PROGMEM +#else +#define TEST_DATA_P static constexpr +#endif + +// Populate a 3d table (from PROGMEM if available) +// You wuld typically declare the 3 source arrays usin TEST_DATA_P +template +static inline void populate_table_P(table3d_t &table, + const table3d_axis_t *pXValues, // PROGMEM if available + const table3d_axis_t *pYValues, // PROGMEM if available + const table3d_value_t *pZValues) // PROGMEM if available +{ + { + table_axis_iterator itX = table.axisX.begin(); + while (!itX.at_end()) + { +#if defined(PROGMEM) + *itX = (table3d_axis_t)pgm_read_word(pXValues); +#else + *itX = *pXValues; +#endif + ++pXValues; + ++itX; + } + } + { + table_axis_iterator itY = table.axisY.begin(); + while (!itY.at_end()) + { +#if defined(PROGMEM) + *itY = (table3d_axis_t)pgm_read_word(pYValues); +#else + *itY = *pYValues; +#endif + ++pYValues; + ++itY; + } + } + { + table_value_iterator itZ = table.values.begin(); + while (!itZ.at_end()) + { + table_row_iterator itRow = *itZ; + while (!itRow.at_end()) + { +#if defined(PROGMEM) + *itRow = pgm_read_byte(pZValues); +#else + *itRow = *pZValues; +#endif + ++pZValues; + ++itRow; + } + ++itZ; + } + } +} + + +// Populate a 2d table with constant values +static inline void populate_2dtable(table2D *pTable, uint8_t value, uint8_t bin) { + for (uint8_t index=0; indexxSize; ++index) { + ((uint8_t*)pTable->values)[index] = value; + ((uint8_t*)pTable->axisX)[index] = bin; + } + pTable->cacheTime = UINT8_MAX; +} + +template +static inline void populate_2dtable(table2D *pTable, const TValue values[], const TBin bins[]) { + memcpy(pTable->axisX, bins, pTable->xSize * sizeof(TBin)); + memcpy(pTable->values, values, pTable->xSize * sizeof(TValue)); + pTable->cacheTime = UINT8_MAX; +} + +// Populate a 2d table (from PROGMEM if available) +// You would typically declare the 2 source arrays using TEST_DATA_P +template +static inline void populate_2dtable_P(table2D *pTable, const TValue values[], const TBin bins[]) { +#if defined(PROGMEM) + memcpy_P(pTable->axisX, bins, pTable->xSize * sizeof(TBin)); + memcpy_P(pTable->values, values, pTable->xSize * sizeof(TValue)); + pTable->cacheTime = UINT8_MAX; +#else + populate_2dtable(pTable, values, bins) +#endif +} \ No newline at end of file