From bd40691e677f519bd47c47f55d29183c6352931c Mon Sep 17 00:00:00 2001 From: andreika-git Date: Sat, 29 Sep 2018 19:16:36 +0300 Subject: [PATCH] AirFlow-interpolated tCharge Mode (#612) * Add vars & enums * updateTChargeK() * limitRateOfChange() * Impl. AirFlow-interpolated tCharge mode * Update tsOutputChannels: tCharge & airFlow * Set default config params * Unit-tests * Fix tChargeMode_e --- firmware/console/status_loop.cpp | 6 ++-- firmware/controllers/algo/advance_map.cpp | 2 ++ firmware/controllers/algo/engine.cpp | 22 ++++++++++--- firmware/controllers/algo/engine.h | 10 +++++- .../controllers/algo/engine_configuration.cpp | 6 ++++ firmware/controllers/algo/rusefi_enums.h | 11 ++++--- firmware/controllers/math/speed_density.cpp | 31 +++++++++++++++---- firmware/tunerstudio/rusefi.input | 1 + firmware/util/efilib.cpp | 5 +++ firmware/util/efilib.h | 3 ++ unit_tests/test_engine_math.cpp | 13 +++++++- 11 files changed, 91 insertions(+), 19 deletions(-) diff --git a/firmware/console/status_loop.cpp b/firmware/console/status_loop.cpp index d07f56c3cd..bd14dce88e 100644 --- a/firmware/console/status_loop.cpp +++ b/firmware/console/status_loop.cpp @@ -686,7 +686,8 @@ void updateTunerStudioState(TunerStudioOutputChannels *tsOutputChannels DECLARE_ tsOutputChannels->intakeAirTemperature = intake; tsOutputChannels->throttlePositon = tps; tsOutputChannels->massAirFlowVoltage = hasMafSensor() ? getMaf(PASS_ENGINE_PARAMETER_SIGNATURE) : 0; - tsOutputChannels->massAirFlow = hasMafSensor() ? getRealMaf(PASS_ENGINE_PARAMETER_SIGNATURE) : 0; + // For air-interpolated tCharge mode, we calculate a decent massAirFlow approximation, so we can show it to users even without MAF sensor! + tsOutputChannels->massAirFlow = hasMafSensor() ? getRealMaf(PASS_ENGINE_PARAMETER_SIGNATURE) : engine->engineState.airFlow; tsOutputChannels->oilPressure = engine->sensors.oilPressure; tsOutputChannels->injectionOffset = engine->engineState.injectionOffset; @@ -914,7 +915,8 @@ void updateTunerStudioState(TunerStudioOutputChannels *tsOutputChannels DECLARE_ tsOutputChannels->clutchDownState = engine->clutchDownState; tsOutputChannels->brakePedalState = engine->brakePedalState; - tsOutputChannels->tCharge = getTCharge(rpm, tps, coolant, intake PASS_ENGINE_PARAMETER_SUFFIX); + // tCharge depends on the previous state, so we should use the stored value. + tsOutputChannels->tCharge = ENGINE(engineState.tCharge); float timing = engine->engineState.timingAdvance; tsOutputChannels->ignitionAdvance = timing > 360 ? timing - 720 : timing; tsOutputChannels->sparkDwell = ENGINE(engineState.sparkDwell); diff --git a/firmware/controllers/algo/advance_map.cpp b/firmware/controllers/algo/advance_map.cpp index 15542086f2..54e863ea90 100644 --- a/firmware/controllers/algo/advance_map.cpp +++ b/firmware/controllers/algo/advance_map.cpp @@ -32,6 +32,7 @@ extern TunerStudioOutputChannels tsOutputChannels; #endif static ign_Map3D_t advanceMap("advance"); +// This coeff in ctor parameter is sufficient for int16<->float conversion! static ign_tps_Map3D_t advanceTpsMap("advanceTps", 1.0 / ADVANCE_TPS_STORAGE_MULT); static ign_Map3D_t iatAdvanceCorrectionMap("iat corr"); @@ -178,6 +179,7 @@ void setDefaultIatTimingCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE) { } void prepareTimingMap(DECLARE_ENGINE_PARAMETER_SIGNATURE) { + // We init both tables in RAM because here we're at a very early stage, with no config settings loaded. advanceMap.init(config->ignitionTable, config->ignitionLoadBins, config->ignitionRpmBins); advanceTpsMap.init(CONFIG(ignitionTpsTable), CONFIG(ignitionTpsBins), diff --git a/firmware/controllers/algo/engine.cpp b/firmware/controllers/algo/engine.cpp index 010d92c891..badf3a930e 100644 --- a/firmware/controllers/algo/engine.cpp +++ b/firmware/controllers/algo/engine.cpp @@ -195,7 +195,9 @@ EngineState::EngineState() { vssEventCounter = 0; targetAFR = 0; tpsAccelEnrich = 0; - tChargeK = 0; + tCharge = tChargeK = 0; + timeSinceLastTChargeK = getTimeNowNt(); + airFlow = 0; cltTimingCorrection = 0; runningFuel = baseFuel = currentVE = 0; timeOfPreviousWarning = -10; @@ -288,10 +290,8 @@ void EngineState::periodicFastCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) { timingAdvance = getAdvance(rpm, engineLoad PASS_ENGINE_PARAMETER_SUFFIX); if (engineConfiguration->fuelAlgorithm == LM_SPEED_DENSITY) { - float coolantC = ENGINE(sensors.clt); - float intakeC = ENGINE(sensors.iat); float tps = getTPS(PASS_ENGINE_PARAMETER_SIGNATURE); - tChargeK = convertCelsiusToKelvin(getTCharge(rpm, tps, coolantC, intakeC PASS_ENGINE_PARAMETER_SUFFIX)); + updateTChargeK(rpm, tps PASS_ENGINE_PARAMETER_SUFFIX); float map = getMap(); /** @@ -311,6 +311,20 @@ void EngineState::periodicFastCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) { } } +void EngineState::updateTChargeK(int rpm, float tps DECLARE_ENGINE_PARAMETER_SUFFIX) { + float coolantC = ENGINE(sensors.clt); + float intakeC = ENGINE(sensors.iat); + float newTCharge = getTCharge(rpm, tps, coolantC, intakeC PASS_ENGINE_PARAMETER_SUFFIX); + // convert to microsecs and then to seconds + efitick_t curTime = getTimeNowNt(); + float secsPassed = (float)NT2US(curTime - timeSinceLastTChargeK) / 1000000.0f; + if (!cisnan(newTCharge)) { + // control the rate of change or just fill with the initial value + tCharge = (tChargeK == 0) ? newTCharge : limitRateOfChange(newTCharge, tCharge, CONFIG(tChargeAirIncrLimit), CONFIG(tChargeAirDecrLimit), secsPassed); + tChargeK = convertCelsiusToKelvin(tCharge); + timeSinceLastTChargeK = curTime; + } +} /** * Here we have a bunch of stuff which should invoked after configuration change diff --git a/firmware/controllers/algo/engine.h b/firmware/controllers/algo/engine.h index b0aa826fe9..d17d2a8cc5 100644 --- a/firmware/controllers/algo/engine.h +++ b/firmware/controllers/algo/engine.h @@ -130,6 +130,7 @@ public: EngineState(); void periodicFastCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE); void updateSlowSensors(DECLARE_ENGINE_PARAMETER_SIGNATURE); + void updateTChargeK(int rpm, float tps DECLARE_ENGINE_PARAMETER_SUFFIX); FuelConsumptionState fuelConsumption; @@ -144,6 +145,10 @@ public: * speed-density logic, calculated air mass in grams */ float airMass; + /** + * speed-density logic, calculated air flow in kg/h for tCharge Air-Interp. method + */ + float airFlow; float engineNoiseHipLevel; @@ -194,7 +199,10 @@ public: float baroCorrection; // speed density - float tChargeK; + // Rate-of-change limiter is applied to degrees, so we store both Kelvin and degrees. + float tCharge, tChargeK; + efitick_t timeSinceLastTChargeK; + float currentVE; float targetAFR; diff --git a/firmware/controllers/algo/engine_configuration.cpp b/firmware/controllers/algo/engine_configuration.cpp index 1b847ccd37..7ae1f42a30 100644 --- a/firmware/controllers/algo/engine_configuration.cpp +++ b/firmware/controllers/algo/engine_configuration.cpp @@ -756,6 +756,12 @@ void setDefaultConfiguration(DECLARE_ENGINE_PARAMETER_SIGNATURE) { engineConfiguration->tChargeMinRpmMaxTps = 0.25; engineConfiguration->tChargeMaxRpmMinTps = 0.25; engineConfiguration->tChargeMaxRpmMaxTps = 0.9; + engineConfiguration->tChargeMode = TCHARGE_MODE_RPM_TPS; + engineConfiguration->tChargeAirCoefMin = 0.098f; + engineConfiguration->tChargeAirCoefMax = 0.902f; + engineConfiguration->tChargeAirFlowMax = 153.6f; + engineConfiguration->tChargeAirIncrLimit = 1.0f; + engineConfiguration->tChargeAirDecrLimit = 12.5f; engineConfiguration->noAccelAfterHardLimitPeriodSecs = 3; diff --git a/firmware/controllers/algo/rusefi_enums.h b/firmware/controllers/algo/rusefi_enums.h index 4c819ccbc2..812119158a 100644 --- a/firmware/controllers/algo/rusefi_enums.h +++ b/firmware/controllers/algo/rusefi_enums.h @@ -521,11 +521,6 @@ typedef enum { Force_4_bytes_size_spi_device = ENUM_32_BITS, } spi_device_e; -typedef enum { - TC_ZERO = 0, - Force_4_bytes_size_tChargeMode_e = ENUM_32_BITS, // we need to force persistent value size -} tChargeMode_e; - /** * Frankenso analog #1 PC2 ADC12 * Frankenso analog #2 PC1 ADC11 @@ -930,4 +925,10 @@ typedef enum { IS_SENDING_SPI_COMMAND, } hip_state_e; +typedef enum { + TCHARGE_MODE_RPM_TPS = 0, + TCHARGE_MODE_AIR_INTERP = 1, + Force_4bytes_size_tChargeMode_e = ENUM_32_BITS, +} tChargeMode_e; + #endif /* RUSEFI_ENUMS_H_ */ diff --git a/firmware/controllers/math/speed_density.cpp b/firmware/controllers/math/speed_density.cpp index d8b132ccdf..7c17d67780 100644 --- a/firmware/controllers/math/speed_density.cpp +++ b/firmware/controllers/math/speed_density.cpp @@ -32,18 +32,37 @@ float getTCharge(int rpm, float tps, float coolantTemp, float airTemp DECLARE_EN warning(CUSTOM_ERR_START_STACK, "t-getTCharge NaN"); return coolantTemp; } - float minRpmKcurrentTPS = interpolateMsg("minRpm", tpMin, engineConfiguration->tChargeMinRpmMinTps, tpMax, - engineConfiguration->tChargeMinRpmMaxTps, tps); - float maxRpmKcurrentTPS = interpolateMsg("maxRpm", tpMin, engineConfiguration->tChargeMaxRpmMinTps, tpMax, - engineConfiguration->tChargeMaxRpmMaxTps, tps); - float Tcharge_coff = interpolateMsg("Kcurr", rpmMin, minRpmKcurrentTPS, rpmMax, maxRpmKcurrentTPS, rpm); + float Tcharge_coff; + + if (CONFIG(tChargeMode) == TCHARGE_MODE_AIR_INTERP) { + const floatms_t gramsPerMsToKgPerHour = (3600.0f * 1000.0f) / 1000.0f; + // We're actually using an 'old' airMass calculated for the previous cycle, but it's ok, we're not having any self-excitaton issues + floatms_t airMassForEngine = engine->engineState.airMass * engineConfiguration->specs.cylindersCount; + // airMass is in grams per 1 cycle for 1 cyl. Convert it to airFlow in kg/h for the engine. + // And if the engine is stopped (0 rpm), then airFlow is also zero (avoiding NaN division) + floatms_t airFlow = (rpm == 0) ? 0 : airMassForEngine * gramsPerMsToKgPerHour / getEngineCycleDuration(rpm PASS_ENGINE_PARAMETER_SUFFIX); + // just interpolate between user-specified min and max coefs, based on the max airFlow value + Tcharge_coff = interpolateClamped(0.0, CONFIG(tChargeAirCoefMin), CONFIG(tChargeAirFlowMax), CONFIG(tChargeAirCoefMax), airFlow); + // save it for console output (instead of MAF massAirFlow) + engine->engineState.airFlow = airFlow; + } else { + // TCHARGE_MODE_RPM_TPS + float minRpmKcurrentTPS = interpolateMsg("minRpm", tpMin, engineConfiguration->tChargeMinRpmMinTps, tpMax, + engineConfiguration->tChargeMinRpmMaxTps, tps); + float maxRpmKcurrentTPS = interpolateMsg("maxRpm", tpMin, engineConfiguration->tChargeMaxRpmMinTps, tpMax, + engineConfiguration->tChargeMaxRpmMaxTps, tps); + + Tcharge_coff = interpolateMsg("Kcurr", rpmMin, minRpmKcurrentTPS, rpmMax, maxRpmKcurrentTPS, rpm); + } + if (cisnan(Tcharge_coff)) { warning(CUSTOM_ERR_T2_CHARGE, "t2-getTCharge NaN"); return coolantTemp; } - float Tcharge = coolantTemp * (1 - Tcharge_coff) + airTemp * Tcharge_coff; + // We use a robust interp. function for proper tcharge_coff clamping. + float Tcharge = interpolateClamped(0.0f, coolantTemp, 1.0f, airTemp, Tcharge_coff); if (cisnan(Tcharge)) { // we can probably end up here while resetting engine state - interpolation would fail diff --git a/firmware/tunerstudio/rusefi.input b/firmware/tunerstudio/rusefi.input index 0f65eda0e7..d800ca8883 100644 --- a/firmware/tunerstudio/rusefi.input +++ b/firmware/tunerstudio/rusefi.input @@ -638,6 +638,7 @@ fileVersion = { 20171101 } table = ignitionTpsTableTbl, ignitionTableMap, "Ignition TPS Table", 1 ; constant, variable + ; Currently we share ignitionRpmBins between two advance tables... Is it ok? xBins = ignitionRpmBins, RPMValue yBins = ignitionTpsBins, TPSValue diff --git a/firmware/util/efilib.cpp b/firmware/util/efilib.cpp index 3fad609592..9b942356aa 100644 --- a/firmware/util/efilib.cpp +++ b/firmware/util/efilib.cpp @@ -306,3 +306,8 @@ void printHistogram(Logging *logging, histogram_s *histogram) { #endif /* EFI_HISTOGRAMS */ } +float limitRateOfChange(float newValue, float oldValue, float incrLimitPerSec, float decrLimitPerSec, float secsPassed) { + if (newValue >= oldValue) + return (incrLimitPerSec <= 0.0f) ? newValue : oldValue + minF(newValue - oldValue, incrLimitPerSec * secsPassed); + return (decrLimitPerSec <= 0.0f) ? newValue : oldValue - minF(oldValue - newValue, decrLimitPerSec * secsPassed); +} diff --git a/firmware/util/efilib.h b/firmware/util/efilib.h index de8223f4ac..525ca455a9 100644 --- a/firmware/util/efilib.h +++ b/firmware/util/efilib.h @@ -66,6 +66,9 @@ bool isSameF(float v1, float v2); bool strEqualCaseInsensitive(const char *str1, const char *str2); bool strEqual(const char *str1, const char *str2); +// Currently used by air-interp. tCharge mode (see EngineState::updateTChargeK()). +float limitRateOfChange(float newValue, float oldValue, float incrLimitPerSec, float decrLimitPerSec, float secsPassed); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/unit_tests/test_engine_math.cpp b/unit_tests/test_engine_math.cpp index fecafd996e..502c29d43b 100644 --- a/unit_tests/test_engine_math.cpp +++ b/unit_tests/test_engine_math.cpp @@ -37,7 +37,6 @@ void testEngineMath(void) { assertEqualsM("600 RPM", 50, getOneDegreeTimeMs(600) * 180); assertEqualsM("6000 RPM", 5, getOneDegreeTimeMs(6000) * 180); - assertEquals(312.5, getTCharge(1000, 0, 300, 350 PASS_ENGINE_PARAMETER_SUFFIX)); assertEquals(313.5833, getTCharge(1000, 50, 300, 350 PASS_ENGINE_PARAMETER_SUFFIX)); assertEquals(314.6667, getTCharge(1000, 100, 300, 350 PASS_ENGINE_PARAMETER_SUFFIX)); @@ -46,6 +45,18 @@ void testEngineMath(void) { assertEquals(312.5, getTCharge(4000, 0, 300, 350 PASS_ENGINE_PARAMETER_SUFFIX)); assertEquals(320.0833, getTCharge(4000, 50, 300, 350 PASS_ENGINE_PARAMETER_SUFFIX)); assertEquals(327.6667, getTCharge(4000, 100, 300, 350 PASS_ENGINE_PARAMETER_SUFFIX)); + + // test Air Interpolation mode + engineConfiguration->tChargeMode = TCHARGE_MODE_AIR_INTERP; + engineConfiguration->tChargeAirCoefMin = 0.098f; + engineConfiguration->tChargeAirCoefMax = 0.902f; + engineConfiguration->tChargeAirFlowMax = 153.6f; + // calc. some airMass given the engine displacement=1.839 and 4 cylinders (FORD_ESCORT_GT) + engine->engineState.airMass = getCylinderAirMass(engineConfiguration, /*VE*/1.0f, /*MAP*/100.0f, /*tChargeK*/273.15f + 20.0f); + assertEquals(0.5464f, engine->engineState.airMass); + // calc. airFlow using airMass, and find tCharge + assertEquals(59.1175f, getTCharge(/*RPM*/1000, /*TPS*/0, /*CLT*/90.0f, /*IAT*/20.0f PASS_ENGINE_PARAMETER_SUFFIX)); + assertEquals(65.5625f/*kg/h*/, engine->engineState.airFlow); } void testIgnitionMapGenerator(void) {