Factor out airmass logic (#1483)
* refactor * extract more airmass * rename, fix tests
This commit is contained in:
parent
830a3ee4dd
commit
76745412c6
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
struct AirmassResult {
|
||||
float CylinderAirmass = 0;
|
||||
float EngineLoadPercent = 100;
|
||||
};
|
|
@ -202,7 +202,6 @@ void EngineState::periodicFastCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
|||
currentRawVE = interpolateClamped(0.0f, idleVe, CONFIG(idlePidDeactivationTpsThreshold), currentRawVE, tps.Value);
|
||||
}
|
||||
currentBaroCorrectedVE = baroCorrection * currentRawVE * PERCENT_DIV;
|
||||
targetAFR = afrMap.getValue(rpm, map);
|
||||
} else {
|
||||
baseTableFuel = getBaseTableFuel(rpm, engineLoad);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
*/
|
||||
|
||||
#include "global.h"
|
||||
#include "airmass.h"
|
||||
#include "fuel_math.h"
|
||||
#include "interpolation.h"
|
||||
#include "engine_configuration.h"
|
||||
|
@ -145,10 +146,10 @@ floatms_t getRunningFuel(floatms_t baseFuel DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
|||
* Function block now works to create a standardised load from the cylinder filling as well as tune fuel via VE table.
|
||||
* @return total duration of fuel injection per engine cycle, in milliseconds
|
||||
*/
|
||||
float getRealMafFuel(float airSpeed, int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
||||
AirmassResult getRealMafAirmass(float airSpeed, int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
||||
// If the engine is stopped, MAF is meaningless
|
||||
if (rpm == 0) {
|
||||
return 0;
|
||||
return {};
|
||||
}
|
||||
|
||||
// kg/hr -> g/s
|
||||
|
@ -167,13 +168,13 @@ float getRealMafFuel(float airSpeed, int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
|||
//Create % load for fuel table using relative naturally aspiratedcylinder filling
|
||||
float airChargeLoad = 100 * cylinderAirmass / ENGINE(standardAirCharge);
|
||||
|
||||
//Correct air mass by VE table
|
||||
float corrCylAirmass = cylinderAirmass * veMap.getValue(rpm, airChargeLoad) / 100;
|
||||
float afr = afrMap.getValue(rpm, airChargeLoad);
|
||||
float pulseWidthSeconds = getInjectionDurationForAirmass(corrCylAirmass, afr PASS_ENGINE_PARAMETER_SUFFIX);
|
||||
//Correct air mass by VE table
|
||||
float correctedAirmass = cylinderAirmass * veMap.getValue(rpm, airChargeLoad) / 100;
|
||||
|
||||
// Convert to ms
|
||||
return 1000 * pulseWidthSeconds;
|
||||
return {
|
||||
correctedAirmass,
|
||||
airChargeLoad, // AFR/VE table Y axis
|
||||
};
|
||||
}
|
||||
|
||||
constexpr float convertToGramsPerSecond(float ccPerMinute) {
|
||||
|
@ -190,6 +191,19 @@ float getInjectionDurationForAirmass(float airMass, float afr DECLARE_ENGINE_PAR
|
|||
return airMass / (afr * gPerSec);
|
||||
}
|
||||
|
||||
AirmassResult getAirmass(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
||||
switch (CONFIG(fuelAlgorithm)) {
|
||||
case LM_SPEED_DENSITY:
|
||||
return getSpeedDensityAirmass(getMap(PASS_ENGINE_PARAMETER_SIGNATURE) PASS_ENGINE_PARAMETER_SUFFIX);
|
||||
case LM_REAL_MAF: {
|
||||
float maf = getRealMaf(PASS_ENGINE_PARAMETER_SIGNATURE) + engine->engineLoadAccelEnrichment.getEngineLoadEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE);
|
||||
return getRealMafAirmass(maf, rpm PASS_ENGINE_PARAMETER_SUFFIX);
|
||||
} default:
|
||||
firmwareError(CUSTOM_ERR_ASSERT, "Fuel mode %d is not airmass mode", CONFIG(fuelAlgorithm));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* per-cylinder fuel amount
|
||||
* todo: rename this method since it's now base+TPSaccel
|
||||
|
@ -202,17 +216,27 @@ floatms_t getBaseFuel(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
|||
ENGINE(engineState.tpsAccelEnrich) = tpsAccelEnrich;
|
||||
|
||||
floatms_t baseFuel;
|
||||
if (CONFIG(fuelAlgorithm) == LM_SPEED_DENSITY) {
|
||||
baseFuel = getSpeedDensityFuel(getMap(PASS_ENGINE_PARAMETER_SIGNATURE) PASS_ENGINE_PARAMETER_SUFFIX);
|
||||
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(baseFuel), "NaN sd baseFuel", 0);
|
||||
} else if (engineConfiguration->fuelAlgorithm == LM_REAL_MAF) {
|
||||
float maf = getRealMaf(PASS_ENGINE_PARAMETER_SIGNATURE) + engine->engineLoadAccelEnrichment.getEngineLoadEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE);
|
||||
baseFuel = getRealMafFuel(maf, rpm PASS_ENGINE_PARAMETER_SUFFIX);
|
||||
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(baseFuel), "NaN rm baseFuel", 0);
|
||||
|
||||
if ((CONFIG(fuelAlgorithm) == LM_SPEED_DENSITY) || (engineConfiguration->fuelAlgorithm == LM_REAL_MAF)) {
|
||||
// airmass modes - get airmass first, then convert to fuel
|
||||
auto airmass = getAirmass(rpm PASS_ENGINE_PARAMETER_SUFFIX);
|
||||
|
||||
// The airmass mode will tell us how to look up AFR - use the provided Y axis value
|
||||
float targetAfr = afrMap.getValue(rpm, airmass.EngineLoadPercent);
|
||||
|
||||
// TODO: surface airmass.EngineLoadPercent to tunerstudio for proper display
|
||||
|
||||
// Plop some state for others to read
|
||||
ENGINE(engineState.targetAFR) = targetAfr;
|
||||
ENGINE(engineState.sd.airMassInOneCylinder) = airmass.CylinderAirmass;
|
||||
|
||||
baseFuel = getInjectionDurationForAirmass(airmass.CylinderAirmass, targetAfr PASS_ENGINE_PARAMETER_SUFFIX) * 1000;
|
||||
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(baseFuel), "NaN baseFuel", 0);
|
||||
} else {
|
||||
baseFuel = engine->engineState.baseTableFuel;
|
||||
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(baseFuel), "NaN bt baseFuel", 0);
|
||||
}
|
||||
|
||||
engine->engineState.baseFuel = baseFuel;
|
||||
|
||||
return tpsAccelEnrich + baseFuel;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "engine.h"
|
||||
#include "airmass.h"
|
||||
|
||||
void initFuelMap(DECLARE_ENGINE_PARAMETER_SIGNATURE);
|
||||
|
||||
|
@ -21,7 +22,7 @@ floatms_t getBaseFuel(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX);
|
|||
*/
|
||||
floatms_t getRunningFuel(floatms_t baseFuel DECLARE_ENGINE_PARAMETER_SUFFIX);
|
||||
|
||||
floatms_t getRealMafFuel(float airMass, int rpm DECLARE_ENGINE_PARAMETER_SUFFIX);
|
||||
AirmassResult getRealMafAirmass(float airMass, int rpm DECLARE_ENGINE_PARAMETER_SUFFIX);
|
||||
|
||||
floatms_t getBaseTableFuel(int rpm, float engineLoad);
|
||||
float getBaroCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE);
|
||||
|
|
|
@ -54,6 +54,7 @@ CONTROLLERS_INC=\
|
|||
$(CONTROLLERS_DIR)/system \
|
||||
$(CONTROLLERS_DIR)/system/timer \
|
||||
$(CONTROLLERS_DIR)/algo \
|
||||
$(CONTROLLERS_DIR)/algo/airmass \
|
||||
$(CONTROLLERS_DIR)/engine_cycle \
|
||||
$(CONTROLLERS_DIR)/trigger/decoders \
|
||||
$(CONTROLLERS_DIR)/trigger \
|
||||
|
|
|
@ -131,7 +131,7 @@ float getCylinderAirMass(float volumetricEfficiency, float MAP, float tempK DECL
|
|||
/**
|
||||
* @return per cylinder injection time, in Milliseconds
|
||||
*/
|
||||
floatms_t getSpeedDensityFuel(float map DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
||||
AirmassResult getSpeedDensityAirmass(float map DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
||||
ScopePerf perf(PE::GetSpeedDensityFuel);
|
||||
|
||||
/**
|
||||
|
@ -140,27 +140,29 @@ floatms_t getSpeedDensityFuel(float map DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
|||
float tChargeK = ENGINE(engineState.sd.tChargeK);
|
||||
if (cisnan(tChargeK)) {
|
||||
warning(CUSTOM_ERR_TCHARGE_NOT_READY2, "tChargeK not ready"); // this would happen before we have CLT reading for example
|
||||
return 0;
|
||||
return {};
|
||||
}
|
||||
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(map), "NaN map", 0);
|
||||
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(map), "NaN map", {});
|
||||
|
||||
engine->engineState.sd.manifoldAirPressureAccelerationAdjustment = engine->engineLoadAccelEnrichment.getEngineLoadEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE);
|
||||
|
||||
float adjustedMap = engine->engineState.sd.adjustedManifoldAirPressure = map + engine->engineState.sd.manifoldAirPressureAccelerationAdjustment;
|
||||
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(adjustedMap), "NaN adjustedMap", 0);
|
||||
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(adjustedMap), "NaN adjustedMap", {});
|
||||
|
||||
float airMass = getCylinderAirMass(ENGINE(engineState.currentBaroCorrectedVE), adjustedMap, tChargeK PASS_ENGINE_PARAMETER_SUFFIX);
|
||||
if (cisnan(airMass)) {
|
||||
warning(CUSTOM_ERR_6685, "NaN airMass");
|
||||
return 0;
|
||||
return {};
|
||||
}
|
||||
#if EFI_PRINTF_FUEL_DETAILS
|
||||
printf("map=%.2f adjustedMap=%.2f airMass=%.2f\t\n",
|
||||
map, adjustedMap, engine->engineState.sd.adjustedManifoldAirPressure);
|
||||
#endif /*EFI_PRINTF_FUEL_DETAILS */
|
||||
|
||||
engine->engineState.sd.airMassInOneCylinder = airMass;
|
||||
return getInjectionDurationForAirmass(airMass, ENGINE(engineState.targetAFR) PASS_ENGINE_PARAMETER_SUFFIX) * 1000;
|
||||
return {
|
||||
airMass,
|
||||
map, // AFR/VE table Y axis
|
||||
};
|
||||
}
|
||||
|
||||
void setDefaultVETable(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "engine.h"
|
||||
#include "airmass.h"
|
||||
|
||||
#define gramm_second_to_cc_minute(gs) ((gs) / 0.0119997981)
|
||||
#define cc_minute_to_gramm_second(ccm) ((ccm) * 0.0119997981)
|
||||
|
@ -17,4 +18,4 @@ float getCylinderAirMass(float volumetricEfficiency, float MAP, float tempK DECL
|
|||
|
||||
void setDefaultVETable(DECLARE_ENGINE_PARAMETER_SIGNATURE);
|
||||
void initSpeedDensity(DECLARE_ENGINE_PARAMETER_SIGNATURE);
|
||||
floatms_t getSpeedDensityFuel(float map DECLARE_ENGINE_PARAMETER_SUFFIX);
|
||||
AirmassResult getSpeedDensityAirmass(float map DECLARE_ENGINE_PARAMETER_SUFFIX);
|
||||
|
|
|
@ -25,7 +25,11 @@ TEST(misc, testMafFuelMath) {
|
|||
|
||||
setAfrMap(config->afrTable, 13);
|
||||
|
||||
EXPECT_NEAR(0.75 * 13.3547, getRealMafFuel(300, 6000 PASS_ENGINE_PARAMETER_SUFFIX), EPS4D);
|
||||
auto airmass = getRealMafAirmass(200, 6000 PASS_ENGINE_PARAMETER_SUFFIX);
|
||||
|
||||
// Check results
|
||||
EXPECT_NEAR(0.277777f * 0.75f, airmass.CylinderAirmass, EPS4D);
|
||||
EXPECT_NEAR(70.9884, airmass.EngineLoadPercent, EPS4D);
|
||||
}
|
||||
|
||||
TEST(misc, testFuelMap) {
|
||||
|
|
Loading…
Reference in New Issue