Merge pull request #1631 from mck1117/pull-out-maf
Refactor out MAF as the first AirmassModel
This commit is contained in:
commit
1abc6b000a
|
@ -0,0 +1,10 @@
|
||||||
|
#include "airmass.h"
|
||||||
|
|
||||||
|
AirmassModelBase::AirmassModelBase(const ValueProvider3D& veTable) : m_veTable(&veTable) {}
|
||||||
|
|
||||||
|
float AirmassModelBase::getVe(int rpm, float load) const {
|
||||||
|
efiAssert(OBD_PCM_Processor_Fault, m_veTable != nullptr, "VE table null", 0);
|
||||||
|
|
||||||
|
// TODO: allow override of the Y axis value based on a config field
|
||||||
|
return m_veTable->getValue(rpm, load);
|
||||||
|
}
|
|
@ -1,6 +1,22 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "engine.h"
|
||||||
|
|
||||||
struct AirmassResult {
|
struct AirmassResult {
|
||||||
float CylinderAirmass = 0;
|
float CylinderAirmass = 0;
|
||||||
float EngineLoadPercent = 100;
|
float EngineLoadPercent = 100;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AirmassModelBase {
|
||||||
|
DECLARE_ENGINE_PTR;
|
||||||
|
|
||||||
|
AirmassModelBase(const ValueProvider3D& veTable);
|
||||||
|
virtual AirmassResult getAirmass(int rpm) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Retrieve the user-calibrated volumetric efficiency from the table
|
||||||
|
float getVe(int rpm, percent_t load) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const ValueProvider3D* const m_veTable;
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
#include "global.h"
|
||||||
|
#include "engine.h"
|
||||||
|
#include "maf_airmass.h"
|
||||||
|
#include "maf.h"
|
||||||
|
|
||||||
|
EXTERN_ENGINE;
|
||||||
|
|
||||||
|
AirmassResult MafAirmass::getAirmass(int rpm) {
|
||||||
|
float maf = getRealMaf(PASS_ENGINE_PARAMETER_SIGNATURE) + engine->engineLoadAccelEnrichment.getEngineLoadEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE);
|
||||||
|
return getAirmassImpl(maf, rpm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
AirmassResult MafAirmass::getAirmassImpl(float massAirFlow, int rpm) const {
|
||||||
|
// If the engine is stopped, MAF is meaningless
|
||||||
|
if (rpm == 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// kg/hr -> g/s
|
||||||
|
float gramPerSecond = massAirFlow * 1000 / 3600;
|
||||||
|
|
||||||
|
// 1/min -> 1/s
|
||||||
|
float revsPerSecond = rpm / 60.0f;
|
||||||
|
float airPerRevolution = gramPerSecond / revsPerSecond;
|
||||||
|
|
||||||
|
// Now we have to divide among cylinders - on a 4 stroke, half of the cylinders happen every rev
|
||||||
|
// This math is floating point to work properly on engines with odd cyl count
|
||||||
|
float halfCylCount = CONFIG(specs.cylindersCount) / 2.0f;
|
||||||
|
|
||||||
|
float cylinderAirmass = airPerRevolution / halfCylCount;
|
||||||
|
|
||||||
|
//Create % load for fuel table using relative naturally aspiratedcylinder filling
|
||||||
|
float airChargeLoad = 100 * cylinderAirmass / ENGINE(standardAirCharge);
|
||||||
|
|
||||||
|
//Correct air mass by VE table
|
||||||
|
float correctedAirmass = cylinderAirmass * getVe(rpm, airChargeLoad) / 100;
|
||||||
|
|
||||||
|
return {
|
||||||
|
correctedAirmass,
|
||||||
|
airChargeLoad, // AFR/VE/ignition table Y axis
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "airmass.h"
|
||||||
|
|
||||||
|
class MafAirmass final : public AirmassModelBase {
|
||||||
|
public:
|
||||||
|
MafAirmass(const ValueProvider3D& veTable) : AirmassModelBase(veTable) {}
|
||||||
|
|
||||||
|
AirmassResult getAirmass(int rpm) override;
|
||||||
|
|
||||||
|
// Compute airmass based on flow & engine speed
|
||||||
|
AirmassResult getAirmassImpl(float massAirFlow, int rpm) const;
|
||||||
|
};
|
|
@ -11,4 +11,6 @@ CONTROLLERS_ALGO_SRC_CPP = $(PROJECT_DIR)/controllers/algo/advance_map.cpp \
|
||||||
$(PROJECT_DIR)/controllers/algo/engine2.cpp \
|
$(PROJECT_DIR)/controllers/algo/engine2.cpp \
|
||||||
$(PROJECT_DIR)/controllers/gauges/lcd_menu_tree.cpp \
|
$(PROJECT_DIR)/controllers/gauges/lcd_menu_tree.cpp \
|
||||||
$(PROJECT_DIR)/controllers/algo/event_registry.cpp \
|
$(PROJECT_DIR)/controllers/algo/event_registry.cpp \
|
||||||
|
$(PROJECT_DIR)/controllers/algo/airmass/airmass.cpp \
|
||||||
|
$(PROJECT_DIR)/controllers/algo/airmass/maf_airmass.cpp \
|
||||||
$(PROJECT_DIR)/controllers/algo/airmass/speed_density_base.cpp \
|
$(PROJECT_DIR)/controllers/algo/airmass/speed_density_base.cpp \
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
#include "airmass.h"
|
#include "airmass.h"
|
||||||
|
#include "maf_airmass.h"
|
||||||
#include "fuel_math.h"
|
#include "fuel_math.h"
|
||||||
#include "interpolation.h"
|
#include "interpolation.h"
|
||||||
#include "engine_configuration.h"
|
#include "engine_configuration.h"
|
||||||
|
@ -153,41 +154,6 @@ floatms_t getRunningFuel(floatms_t baseFuel DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
||||||
|
|
||||||
/* DISPLAY_ENDIF */
|
/* DISPLAY_ENDIF */
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
AirmassResult getRealMafAirmass(float airSpeed, int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
|
||||||
// If the engine is stopped, MAF is meaningless
|
|
||||||
if (rpm == 0) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// kg/hr -> g/s
|
|
||||||
float gramPerSecond = airSpeed * 1000 / 3600;
|
|
||||||
|
|
||||||
// 1/min -> 1/s
|
|
||||||
float revsPerSecond = rpm / 60.0f;
|
|
||||||
float airPerRevolution = gramPerSecond / revsPerSecond;
|
|
||||||
|
|
||||||
// Now we have to divide among cylinders - on a 4 stroke, half of the cylinders happen every rev
|
|
||||||
// This math is floating point to work properly on engines with odd cyl count
|
|
||||||
float halfCylCount = CONFIG(specs.cylindersCount) / 2.0f;
|
|
||||||
|
|
||||||
float cylinderAirmass = airPerRevolution / halfCylCount;
|
|
||||||
|
|
||||||
//Create % load for fuel table using relative naturally aspiratedcylinder filling
|
|
||||||
float airChargeLoad = 100 * cylinderAirmass / ENGINE(standardAirCharge);
|
|
||||||
|
|
||||||
//Correct air mass by VE table
|
|
||||||
float correctedAirmass = cylinderAirmass * veMap.getValue(rpm, airChargeLoad) / 100;
|
|
||||||
|
|
||||||
return {
|
|
||||||
correctedAirmass,
|
|
||||||
airChargeLoad, // AFR/VE table Y axis
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr float convertToGramsPerSecond(float ccPerMinute) {
|
constexpr float convertToGramsPerSecond(float ccPerMinute) {
|
||||||
float ccPerSecond = ccPerMinute / 60;
|
float ccPerSecond = ccPerMinute / 60;
|
||||||
return ccPerSecond * 0.72f; // 0.72g/cc fuel density
|
return ccPerSecond * 0.72f; // 0.72g/cc fuel density
|
||||||
|
@ -202,13 +168,14 @@ float getInjectionDurationForAirmass(float airMass, float afr DECLARE_ENGINE_PAR
|
||||||
return airMass / (afr * gPerSec);
|
return airMass / (afr * gPerSec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static MafAirmass mafAirmass(veMap);
|
||||||
|
|
||||||
AirmassResult getAirmass(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
AirmassResult getAirmass(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
||||||
switch (CONFIG(fuelAlgorithm)) {
|
switch (CONFIG(fuelAlgorithm)) {
|
||||||
case LM_SPEED_DENSITY:
|
case LM_SPEED_DENSITY:
|
||||||
return getSpeedDensityAirmass(PASS_ENGINE_PARAMETER_SIGNATURE);
|
return getSpeedDensityAirmass(PASS_ENGINE_PARAMETER_SIGNATURE);
|
||||||
case LM_REAL_MAF: {
|
case LM_REAL_MAF: {
|
||||||
float maf = getRealMaf(PASS_ENGINE_PARAMETER_SIGNATURE) + engine->engineLoadAccelEnrichment.getEngineLoadEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE);
|
return mafAirmass.getAirmass(rpm);
|
||||||
return getRealMafAirmass(maf, rpm PASS_ENGINE_PARAMETER_SUFFIX);
|
|
||||||
} default:
|
} default:
|
||||||
firmwareError(CUSTOM_ERR_ASSERT, "Fuel mode %d is not airmass mode", CONFIG(fuelAlgorithm));
|
firmwareError(CUSTOM_ERR_ASSERT, "Fuel mode %d is not airmass mode", CONFIG(fuelAlgorithm));
|
||||||
return {};
|
return {};
|
||||||
|
@ -384,6 +351,8 @@ floatms_t getInjectorLag(float vBatt DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
||||||
* is to prepare the fuel map data structure for 3d interpolation
|
* is to prepare the fuel map data structure for 3d interpolation
|
||||||
*/
|
*/
|
||||||
void initFuelMap(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
void initFuelMap(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
||||||
|
INJECT_ENGINE_REFERENCE(&mafAirmass);
|
||||||
|
|
||||||
fuelMap.init(config->fuelTable, config->fuelLoadBins, config->fuelRpmBins);
|
fuelMap.init(config->fuelTable, config->fuelLoadBins, config->fuelRpmBins);
|
||||||
#if (IGN_LOAD_COUNT == FUEL_LOAD_COUNT) && (IGN_RPM_COUNT == FUEL_RPM_COUNT)
|
#if (IGN_LOAD_COUNT == FUEL_LOAD_COUNT) && (IGN_RPM_COUNT == FUEL_RPM_COUNT)
|
||||||
fuelPhaseMap.init(config->injectionPhase, config->injPhaseLoadBins, config->injPhaseRpmBins);
|
fuelPhaseMap.init(config->injectionPhase, config->injPhaseLoadBins, config->injPhaseRpmBins);
|
||||||
|
|
|
@ -22,8 +22,6 @@ floatms_t getBaseFuel(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX);
|
||||||
*/
|
*/
|
||||||
floatms_t getRunningFuel(floatms_t baseFuel DECLARE_ENGINE_PARAMETER_SUFFIX);
|
floatms_t getRunningFuel(floatms_t baseFuel DECLARE_ENGINE_PARAMETER_SUFFIX);
|
||||||
|
|
||||||
AirmassResult getRealMafAirmass(float airMass, int rpm DECLARE_ENGINE_PARAMETER_SUFFIX);
|
|
||||||
|
|
||||||
floatms_t getBaseTableFuel(int rpm, float engineLoad);
|
floatms_t getBaseTableFuel(int rpm, float engineLoad);
|
||||||
float getBaroCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE);
|
float getBaroCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE);
|
||||||
int getNumberOfInjections(injection_mode_e mode DECLARE_ENGINE_PARAMETER_SUFFIX);
|
int getNumberOfInjections(injection_mode_e mode DECLARE_ENGINE_PARAMETER_SUFFIX);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "fuel_math.h"
|
#include "fuel_math.h"
|
||||||
|
#include "maf_airmass.h"
|
||||||
#include "trigger_structure.h"
|
#include "trigger_structure.h"
|
||||||
#include "allsensors.h"
|
#include "allsensors.h"
|
||||||
#include "engine_math.h"
|
#include "engine_math.h"
|
||||||
|
@ -14,18 +15,25 @@
|
||||||
#include "efi_gpio.h"
|
#include "efi_gpio.h"
|
||||||
#include "advance_map.h"
|
#include "advance_map.h"
|
||||||
#include "sensor.h"
|
#include "sensor.h"
|
||||||
|
#include "mocks.h"
|
||||||
|
|
||||||
|
using ::testing::FloatNear;
|
||||||
|
|
||||||
TEST(misc, testMafFuelMath) {
|
TEST(misc, testMafFuelMath) {
|
||||||
WITH_ENGINE_TEST_HELPER(FORD_ASPIRE_1996);
|
WITH_ENGINE_TEST_HELPER(FORD_ASPIRE_1996);
|
||||||
extern fuel_Map3D_t veMap;
|
|
||||||
veMap.setAll(75);
|
|
||||||
|
|
||||||
engineConfiguration->fuelAlgorithm = LM_REAL_MAF;
|
engineConfiguration->fuelAlgorithm = LM_REAL_MAF;
|
||||||
engineConfiguration->injector.flow = 200;
|
engineConfiguration->injector.flow = 200;
|
||||||
|
|
||||||
setAfrMap(config->afrTable, 13);
|
setAfrMap(config->afrTable, 13);
|
||||||
|
|
||||||
auto airmass = getRealMafAirmass(200, 6000 PASS_ENGINE_PARAMETER_SUFFIX);
|
MockVp3d veTable;
|
||||||
|
// Ensure that the correct cell is read from the VE table
|
||||||
|
EXPECT_CALL(veTable, getValue(6000, FloatNear(70.9814f, EPS4D)))
|
||||||
|
.WillOnce(Return(75.0f));
|
||||||
|
|
||||||
|
MafAirmass dut(veTable);
|
||||||
|
INJECT_ENGINE_REFERENCE(&dut);
|
||||||
|
|
||||||
|
auto airmass = dut.getAirmassImpl(200, 6000);
|
||||||
|
|
||||||
// Check results
|
// Check results
|
||||||
EXPECT_NEAR(0.277777f * 0.75f, airmass.CylinderAirmass, EPS4D);
|
EXPECT_NEAR(0.277777f * 0.75f, airmass.CylinderAirmass, EPS4D);
|
||||||
|
|
Loading…
Reference in New Issue