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
This commit is contained in:
parent
ea9bac7bb5
commit
bd40691e67
|
@ -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);
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue