mirror of https://github.com/FOME-Tech/fome-fw.git
implement staged injection (#331)
* output channel * duty cycle etc math for second stage * action_s utility * basic enable switch * staging fraction math * implement staging logic * wire up pins for second stage injectors * staging UI * Improve staged injection test, check scheduler arguments for other injection tests * Stage 2 last pulse output channel, correct fuel consumption logic * wall wet on the whole shot * int vs size_t * use a define instead of function so we get line numbers * fix batch injection * gauges * bad test merge * stub out secondary injector model
This commit is contained in:
parent
5ee53b6160
commit
9673ff01f3
|
@ -49,7 +49,7 @@ const antilag_system_state_s* getLiveData(size_t) {
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
const injector_model_s* getLiveData(size_t) {
|
const injector_model_s* getLiveData(size_t) {
|
||||||
return &engine->module<InjectorModel>().unmock();
|
return &engine->module<InjectorModelPrimary>().unmock();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
|
|
@ -363,5 +363,8 @@ float mapFast
|
||||||
uint16_t autoscale afrGasolineScale;@@GAUGE_NAME_AFR_GAS_SCALE@@;"AFR",{1/@@PACK_MULT_AFR@@}, 0, 0, 0, 2
|
uint16_t autoscale afrGasolineScale;@@GAUGE_NAME_AFR_GAS_SCALE@@;"AFR",{1/@@PACK_MULT_AFR@@}, 0, 0, 0, 2
|
||||||
uint16_t autoscale afr2GasolineScale;@@GAUGE_NAME_AFR2_GAS_SCALE@@;"AFR",{1/@@PACK_MULT_AFR@@}, 0, 0, 0, 2
|
uint16_t autoscale afr2GasolineScale;@@GAUGE_NAME_AFR2_GAS_SCALE@@;"AFR",{1/@@PACK_MULT_AFR@@}, 0, 0, 0, 2
|
||||||
|
|
||||||
uint8_t[120 iterate] unusedAtTheEnd;;"",1, 0, 0, 0, 0
|
uint16_t autoscale actualLastInjectionStage2;@@GAUGE_NAME_FUEL_LAST_INJECTION_STAGE_2@@;"ms",{1/@@PACK_MULT_MS@@}, 0, 0, 0, 3
|
||||||
|
uint8_t autoscale injectorDutyCycleStage2;@@GAUGE_NAME_FUEL_INJ_DUTY_STAGE_2@@;"%",{1/2}, 0, 0, 0, 0
|
||||||
|
|
||||||
|
uint8_t[117 iterate] unusedAtTheEnd;;"",1, 0, 0, 0, 0
|
||||||
end_struct
|
end_struct
|
||||||
|
|
|
@ -150,6 +150,7 @@ static void printEngineSnifferPinMappings() {
|
||||||
printOutPin(enginePins.coils[i].getShortName(), engineConfiguration->ignitionPins[i]);
|
printOutPin(enginePins.coils[i].getShortName(), engineConfiguration->ignitionPins[i]);
|
||||||
printOutPin(enginePins.trailingCoils[i].getShortName(), engineConfiguration->trailingCoilPins[i]);
|
printOutPin(enginePins.trailingCoils[i].getShortName(), engineConfiguration->trailingCoilPins[i]);
|
||||||
printOutPin(enginePins.injectors[i].getShortName(), engineConfiguration->injectionPins[i]);
|
printOutPin(enginePins.injectors[i].getShortName(), engineConfiguration->injectionPins[i]);
|
||||||
|
printOutPin(enginePins.injectorsStage2[i].getShortName(), engineConfiguration->injectionPinsStage2[i]);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT;i++) {
|
for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT;i++) {
|
||||||
printOutPin(enginePins.auxValve[i].getShortName(), engineConfiguration->auxValves[i]);
|
printOutPin(enginePins.auxValve[i].getShortName(), engineConfiguration->auxValves[i]);
|
||||||
|
@ -609,6 +610,7 @@ void updateTunerStudioState() {
|
||||||
// 140
|
// 140
|
||||||
#if EFI_ENGINE_CONTROL
|
#if EFI_ENGINE_CONTROL
|
||||||
tsOutputChannels->injectorDutyCycle = getInjectorDutyCycle(rpm);
|
tsOutputChannels->injectorDutyCycle = getInjectorDutyCycle(rpm);
|
||||||
|
tsOutputChannels->injectorDutyCycleStage2 = getInjectorDutyCycleStage2(rpm);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// 224
|
// 224
|
||||||
|
|
|
@ -127,7 +127,8 @@ public:
|
||||||
FuelComputer fuelComputer;
|
FuelComputer fuelComputer;
|
||||||
|
|
||||||
type_list<
|
type_list<
|
||||||
Mockable<InjectorModel>,
|
Mockable<InjectorModelPrimary>,
|
||||||
|
Mockable<InjectorModelSecondary>,
|
||||||
#if EFI_IDLE_CONTROL
|
#if EFI_IDLE_CONTROL
|
||||||
Mockable<IdleController>,
|
Mockable<IdleController>,
|
||||||
#endif // EFI_IDLE_CONTROL
|
#endif // EFI_IDLE_CONTROL
|
||||||
|
|
|
@ -130,10 +130,19 @@ void EngineState::periodicFastCallback() {
|
||||||
float untrimmedInjectionMass = getInjectionMass(rpm) * engine->engineState.lua.fuelMult + engine->engineState.lua.fuelAdd;
|
float untrimmedInjectionMass = getInjectionMass(rpm) * engine->engineState.lua.fuelMult + engine->engineState.lua.fuelAdd;
|
||||||
auto clResult = fuelClosedLoopCorrection();
|
auto clResult = fuelClosedLoopCorrection();
|
||||||
|
|
||||||
// Store the pre-wall wetting injection duration for scheduling purposes only, not the actual injection duration
|
|
||||||
engine->engineState.injectionDuration = engine->module<InjectorModel>()->getInjectionDuration(untrimmedInjectionMass);
|
|
||||||
|
|
||||||
float fuelLoad = getFuelingLoad();
|
float fuelLoad = getFuelingLoad();
|
||||||
|
|
||||||
|
injectionStage2Fraction = getStage2InjectionFraction(rpm, fuelLoad);
|
||||||
|
float stage2InjectionMass = untrimmedInjectionMass * injectionStage2Fraction;
|
||||||
|
float stage1InjectionMass = untrimmedInjectionMass - stage2InjectionMass;
|
||||||
|
|
||||||
|
// Store the pre-wall wetting injection duration for scheduling purposes only, not the actual injection duration
|
||||||
|
engine->engineState.injectionDuration = engine->module<InjectorModelPrimary>()->getInjectionDuration(stage1InjectionMass);
|
||||||
|
engine->engineState.injectionDurationStage2 =
|
||||||
|
engineConfiguration->enableStagedInjection
|
||||||
|
? engine->module<InjectorModelSecondary>()->getInjectionDuration(stage2InjectionMass)
|
||||||
|
: 0;
|
||||||
|
|
||||||
injectionOffset = getInjectionOffset(rpm, fuelLoad);
|
injectionOffset = getInjectionOffset(rpm, fuelLoad);
|
||||||
engine->lambdaMonitor.update(rpm, fuelLoad);
|
engine->lambdaMonitor.update(rpm, fuelLoad);
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,8 @@ public:
|
||||||
// Per-injection fuel mass, including TPS accel enrich
|
// Per-injection fuel mass, including TPS accel enrich
|
||||||
float injectionMass[MAX_CYLINDER_COUNT] = {0};
|
float injectionMass[MAX_CYLINDER_COUNT] = {0};
|
||||||
|
|
||||||
|
float injectionStage2Fraction = 0;
|
||||||
|
|
||||||
Timer crankingTimer;
|
Timer crankingTimer;
|
||||||
|
|
||||||
WarningCodeState warnings;
|
WarningCodeState warnings;
|
||||||
|
@ -76,6 +78,7 @@ public:
|
||||||
* @see getInjectionDuration()
|
* @see getInjectionDuration()
|
||||||
*/
|
*/
|
||||||
floatms_t injectionDuration = 0;
|
floatms_t injectionDuration = 0;
|
||||||
|
floatms_t injectionDurationStage2 = 0;
|
||||||
|
|
||||||
angle_t injectionOffset = 0;
|
angle_t injectionOffset = 0;
|
||||||
|
|
||||||
|
|
|
@ -62,3 +62,7 @@ public:
|
||||||
|
|
||||||
using interface_t = IInjectorModel; // Mock interface
|
using interface_t = IInjectorModel; // Mock interface
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: differentiate primary vs. secondary injector configuration
|
||||||
|
struct InjectorModelPrimary : public InjectorModel { };
|
||||||
|
struct InjectorModelSecondary : public InjectorModel { };
|
||||||
|
|
|
@ -280,6 +280,12 @@ percent_t getInjectorDutyCycle(int rpm) {
|
||||||
return 100 * totalInjectiorAmountPerCycle / engineCycleDuration;
|
return 100 * totalInjectiorAmountPerCycle / engineCycleDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
percent_t getInjectorDutyCycleStage2(int rpm) {
|
||||||
|
floatms_t totalInjectiorAmountPerCycle = engine->engineState.injectionDurationStage2 * getNumberOfInjections(engineConfiguration->injectionMode);
|
||||||
|
floatms_t engineCycleDuration = getEngineCycleDuration(rpm);
|
||||||
|
return 100 * totalInjectiorAmountPerCycle / engineCycleDuration;
|
||||||
|
}
|
||||||
|
|
||||||
static float getCycleFuelMass(bool isCranking, float baseFuelMass) {
|
static float getCycleFuelMass(bool isCranking, float baseFuelMass) {
|
||||||
if (isCranking) {
|
if (isCranking) {
|
||||||
return getCrankingFuel(baseFuelMass);
|
return getCrankingFuel(baseFuelMass);
|
||||||
|
@ -312,7 +318,11 @@ float getInjectionMass(int rpm) {
|
||||||
float injectionFuelMass = cycleFuelMass * durationMultiplier;
|
float injectionFuelMass = cycleFuelMass * durationMultiplier;
|
||||||
|
|
||||||
// Prepare injector flow rate & deadtime
|
// Prepare injector flow rate & deadtime
|
||||||
engine->module<InjectorModel>()->prepare();
|
engine->module<InjectorModelPrimary>()->prepare();
|
||||||
|
|
||||||
|
if (engineConfiguration->enableStagedInjection) {
|
||||||
|
engine->module<InjectorModelSecondary>()->prepare();
|
||||||
|
}
|
||||||
|
|
||||||
floatms_t tpsAccelEnrich = engine->tpsAccelEnrichment.getTpsEnrichment();
|
floatms_t tpsAccelEnrich = engine->tpsAccelEnrichment.getTpsEnrichment();
|
||||||
efiAssert(ObdCode::CUSTOM_ERR_ASSERT, !cisnan(tpsAccelEnrich), "NaN tpsAccelEnrich", 0);
|
efiAssert(ObdCode::CUSTOM_ERR_ASSERT, !cisnan(tpsAccelEnrich), "NaN tpsAccelEnrich", 0);
|
||||||
|
@ -321,7 +331,7 @@ float getInjectionMass(int rpm) {
|
||||||
// For legacy reasons, the TPS accel table is in units of milliseconds, so we have to convert BACK to mass
|
// For legacy reasons, the TPS accel table is in units of milliseconds, so we have to convert BACK to mass
|
||||||
float tpsAccelPerInjection = durationMultiplier * tpsAccelEnrich;
|
float tpsAccelPerInjection = durationMultiplier * tpsAccelEnrich;
|
||||||
|
|
||||||
float tpsFuelMass = engine->module<InjectorModel>()->getFuelMassForDuration(tpsAccelPerInjection);
|
float tpsFuelMass = engine->module<InjectorModelPrimary>()->getFuelMassForDuration(tpsAccelPerInjection);
|
||||||
|
|
||||||
return injectionFuelMass + tpsFuelMass;
|
return injectionFuelMass + tpsFuelMass;
|
||||||
#else
|
#else
|
||||||
|
@ -441,5 +451,31 @@ float getCylinderFuelTrim(size_t cylinderNumber, int rpm, float fuelLoad) {
|
||||||
return (100 + trimPercent) / 100;
|
return (100 + trimPercent) / 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Hysteresis stage2Hysteresis;
|
||||||
|
|
||||||
|
float getStage2InjectionFraction(int rpm, float load) {
|
||||||
|
if (!engineConfiguration->enableStagedInjection) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float frac = 0.01f * interpolate3d(
|
||||||
|
config->injectorStagingTable,
|
||||||
|
config->injectorStagingLoadBins, load,
|
||||||
|
config->injectorStagingRpmBins, rpm
|
||||||
|
);
|
||||||
|
|
||||||
|
// don't allow very small fraction, with some hysteresis
|
||||||
|
if (!stage2Hysteresis.test(frac, 0.1, 0.03)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clamp to 90%
|
||||||
|
if (frac > 0.9) {
|
||||||
|
frac = 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
return frac;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -28,6 +28,8 @@ float getCrankingFuel(float baseFuel);
|
||||||
float getCrankingFuel3(float baseFuel, uint32_t revolutionCounterSinceStart);
|
float getCrankingFuel3(float baseFuel, uint32_t revolutionCounterSinceStart);
|
||||||
float getInjectionMass(int rpm);
|
float getInjectionMass(int rpm);
|
||||||
percent_t getInjectorDutyCycle(int rpm);
|
percent_t getInjectorDutyCycle(int rpm);
|
||||||
|
percent_t getInjectorDutyCycleStage2(int rpm);
|
||||||
|
float getStage2InjectionFraction(int rpm, float fuelLoad);
|
||||||
|
|
||||||
float getStandardAirCharge();
|
float getStandardAirCharge();
|
||||||
float getCylinderFuelTrim(size_t cylinderNumber, int rpm, float fuelLoad);
|
float getCylinderFuelTrim(size_t cylinderNumber, int rpm, float fuelLoad);
|
||||||
|
|
|
@ -8,8 +8,15 @@
|
||||||
|
|
||||||
#if EFI_ENGINE_CONTROL
|
#if EFI_ENGINE_CONTROL
|
||||||
|
|
||||||
void turnInjectionPinHigh(InjectionEvent *event) {
|
void turnInjectionPinHigh(uintptr_t arg) {
|
||||||
efitick_t nowNt = getTimeNowNt();
|
efitick_t nowNt = getTimeNowNt();
|
||||||
|
|
||||||
|
// clear last bit to recover the pointer
|
||||||
|
InjectionEvent *event = reinterpret_cast<InjectionEvent*>(arg & ~(1UL));
|
||||||
|
|
||||||
|
// extract last bit
|
||||||
|
bool stage2Active = arg & 1;
|
||||||
|
|
||||||
for (size_t i = 0; i < efi::size(event->outputs); i++) {
|
for (size_t i = 0; i < efi::size(event->outputs); i++) {
|
||||||
InjectorOutputPin *output = event->outputs[i];
|
InjectorOutputPin *output = event->outputs[i];
|
||||||
|
|
||||||
|
@ -17,6 +24,16 @@ void turnInjectionPinHigh(InjectionEvent *event) {
|
||||||
output->open(nowNt);
|
output->open(nowNt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stage2Active) {
|
||||||
|
for (size_t i = 0; i < efi::size(event->outputsStage2); i++) {
|
||||||
|
InjectorOutputPin *output = event->outputsStage2[i];
|
||||||
|
|
||||||
|
if (output) {
|
||||||
|
output->open(nowNt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FuelSchedule::FuelSchedule() {
|
FuelSchedule::FuelSchedule() {
|
||||||
|
@ -140,7 +157,8 @@ bool InjectionEvent::update() {
|
||||||
// Single point only uses injector 1 (index 0)
|
// Single point only uses injector 1 (index 0)
|
||||||
int injectorIndex = mode == IM_SINGLE_POINT ? 0 : ID2INDEX(getCylinderId(ownIndex));
|
int injectorIndex = mode == IM_SINGLE_POINT ? 0 : ID2INDEX(getCylinderId(ownIndex));
|
||||||
|
|
||||||
InjectorOutputPin *secondOutput = nullptr;
|
InjectorOutputPin* secondOutput = nullptr;
|
||||||
|
InjectorOutputPin* secondOutputStage2 = nullptr;
|
||||||
|
|
||||||
if (mode == IM_BATCH) {
|
if (mode == IM_BATCH) {
|
||||||
/**
|
/**
|
||||||
|
@ -152,6 +170,7 @@ bool InjectionEvent::update() {
|
||||||
int secondOrder = (ownIndex + (engineConfiguration->cylindersCount / 2)) % engineConfiguration->cylindersCount;
|
int secondOrder = (ownIndex + (engineConfiguration->cylindersCount / 2)) % engineConfiguration->cylindersCount;
|
||||||
int secondIndex = ID2INDEX(getCylinderId(secondOrder));
|
int secondIndex = ID2INDEX(getCylinderId(secondOrder));
|
||||||
secondOutput = &enginePins.injectors[secondIndex];
|
secondOutput = &enginePins.injectors[secondIndex];
|
||||||
|
secondOutputStage2 = &enginePins.injectorsStage2[secondIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
outputs[0] = &enginePins.injectors[injectorIndex];
|
outputs[0] = &enginePins.injectors[injectorIndex];
|
||||||
|
@ -160,6 +179,9 @@ bool InjectionEvent::update() {
|
||||||
// Stash the cylinder number so we can select the correct fueling bank later
|
// Stash the cylinder number so we can select the correct fueling bank later
|
||||||
cylinderNumber = injectorIndex;
|
cylinderNumber = injectorIndex;
|
||||||
|
|
||||||
|
outputsStage2[0] = &enginePins.injectorsStage2[injectorIndex];
|
||||||
|
outputsStage2[1] = secondOutputStage2;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,10 +48,11 @@ private:
|
||||||
public:
|
public:
|
||||||
// TODO: this should be private
|
// TODO: this should be private
|
||||||
InjectorOutputPin *outputs[MAX_WIRES_COUNT];
|
InjectorOutputPin *outputs[MAX_WIRES_COUNT];
|
||||||
|
InjectorOutputPin *outputsStage2[MAX_WIRES_COUNT];
|
||||||
float injectionStartAngle = 0;
|
float injectionStartAngle = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
void turnInjectionPinHigh(InjectionEvent *event);
|
void turnInjectionPinHigh(uintptr_t arg);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -63,6 +63,17 @@ void turnInjectionPinLow(InjectionEvent *event) {
|
||||||
event->update();
|
event->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void turnInjectionPinLowStage2(InjectionEvent* event) {
|
||||||
|
efitick_t nowNt = getTimeNowNt();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < efi::size(event->outputsStage2); i++) {
|
||||||
|
InjectorOutputPin *output = event->outputsStage2[i];
|
||||||
|
if (output) {
|
||||||
|
output->close(nowNt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void InjectionEvent::onTriggerTooth(efitick_t nowNt, float currentPhase, float nextPhase) {
|
void InjectionEvent::onTriggerTooth(efitick_t nowNt, float currentPhase, float nextPhase) {
|
||||||
auto eventAngle = injectionStartAngle;
|
auto eventAngle = injectionStartAngle;
|
||||||
|
|
||||||
|
@ -76,36 +87,49 @@ void InjectionEvent::onTriggerTooth(efitick_t nowNt, float currentPhase, float n
|
||||||
|
|
||||||
// Perform wall wetting adjustment on fuel mass, not duration, so that
|
// Perform wall wetting adjustment on fuel mass, not duration, so that
|
||||||
// it's correct during fuel pressure (injector flow) or battery voltage (deadtime) transients
|
// it's correct during fuel pressure (injector flow) or battery voltage (deadtime) transients
|
||||||
|
// TODO: is it correct to wall wet on both pulses?
|
||||||
injectionMassGrams = wallFuel.adjust(injectionMassGrams);
|
injectionMassGrams = wallFuel.adjust(injectionMassGrams);
|
||||||
const floatms_t injectionDuration = engine->module<InjectorModel>()->getInjectionDuration(injectionMassGrams);
|
|
||||||
|
// Disable staging in simultaneous mode
|
||||||
|
float stage2Fraction = isSimultaneous ? 0 : getEngineState()->injectionStage2Fraction;
|
||||||
|
|
||||||
|
// Compute fraction of fuel on stage 2, remainder goes on stage 1
|
||||||
|
const float injectionMassStage2 = stage2Fraction * injectionMassGrams;
|
||||||
|
float injectionMassStage1 = injectionMassGrams - injectionMassStage2;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Log this fuel as consumed
|
||||||
|
|
||||||
|
bool isCranking = getEngineRotationState()->isCranking();
|
||||||
|
int numberOfInjections = isCranking ? getNumberOfInjections(engineConfiguration->crankingInjectionMode) : getNumberOfInjections(engineConfiguration->injectionMode);
|
||||||
|
|
||||||
|
float actualInjectedMass = numberOfInjections * (injectionMassStage1 + injectionMassStage2);
|
||||||
|
|
||||||
|
engine->module<TripOdometer>()->consumeFuel(actualInjectedMass, nowNt);
|
||||||
|
}
|
||||||
|
|
||||||
|
const floatms_t injectionDurationStage1 = engine->module<InjectorModelPrimary>()->getInjectionDuration(injectionMassStage1);
|
||||||
|
const floatms_t injectionDurationStage2 = injectionMassStage2 > 0 ? engine->module<InjectorModelSecondary>()->getInjectionDuration(injectionMassStage2) : 0;
|
||||||
|
|
||||||
#if EFI_PRINTF_FUEL_DETAILS
|
#if EFI_PRINTF_FUEL_DETAILS
|
||||||
if (printFuelDebug) {
|
if (printFuelDebug) {
|
||||||
printf("fuel injectionDuration=%.2fms adjusted=%.2fms\n",
|
printf("fuel injectionDuration=%.2fms adjusted=%.2fms\n",
|
||||||
getEngineState()->injectionDuration,
|
getEngineState()->injectionDuration,
|
||||||
injectionDuration);
|
injectionDurationStage1);
|
||||||
}
|
}
|
||||||
#endif /*EFI_PRINTF_FUEL_DETAILS */
|
#endif /*EFI_PRINTF_FUEL_DETAILS */
|
||||||
|
|
||||||
bool isCranking = getEngineRotationState()->isCranking();
|
|
||||||
/**
|
|
||||||
* todo: pre-calculate 'numberOfInjections'
|
|
||||||
* see also injectorDutyCycle
|
|
||||||
*/
|
|
||||||
int numberOfInjections = isCranking ? getNumberOfInjections(engineConfiguration->crankingInjectionMode) : getNumberOfInjections(engineConfiguration->injectionMode);
|
|
||||||
|
|
||||||
engine->module<TripOdometer>()->consumeFuel(injectionMassGrams * numberOfInjections, nowNt);
|
|
||||||
|
|
||||||
if (this->cylinderNumber == 0) {
|
if (this->cylinderNumber == 0) {
|
||||||
engine->outputChannels.actualLastInjection = injectionDuration;
|
engine->outputChannels.actualLastInjection = injectionDurationStage1;
|
||||||
|
engine->outputChannels.actualLastInjectionStage2 = injectionDurationStage2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cisnan(injectionDuration)) {
|
if (cisnan(injectionDurationStage1) || cisnan(injectionDurationStage2)) {
|
||||||
warning(ObdCode::CUSTOM_OBD_NAN_INJECTION, "NaN injection pulse");
|
warning(ObdCode::CUSTOM_OBD_NAN_INJECTION, "NaN injection pulse");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (injectionDuration < 0) {
|
if (injectionDurationStage1 < 0) {
|
||||||
warning(ObdCode::CUSTOM_OBD_NEG_INJECTION, "Negative injection pulse %.2f", injectionDuration);
|
warning(ObdCode::CUSTOM_OBD_NEG_INJECTION, "Negative injection pulse %.2f", injectionDurationStage1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,48 +137,70 @@ void InjectionEvent::onTriggerTooth(efitick_t nowNt, float currentPhase, float n
|
||||||
// Durations under 50us-ish aren't safe for the scheduler
|
// Durations under 50us-ish aren't safe for the scheduler
|
||||||
// as their order may be swapped, resulting in a stuck open injector
|
// as their order may be swapped, resulting in a stuck open injector
|
||||||
// see https://github.com/rusefi/rusefi/pull/596 for more details
|
// see https://github.com/rusefi/rusefi/pull/596 for more details
|
||||||
if (injectionDuration < 0.050f)
|
if (injectionDurationStage1 < 0.050f)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
floatus_t durationUs = MS2US(injectionDuration);
|
floatus_t durationUsStage1 = MS2US(injectionDurationStage1);
|
||||||
|
floatus_t durationUsStage2 = MS2US(injectionDurationStage2);
|
||||||
|
|
||||||
|
// Only bother with the second stage if it's long enough to be relevant
|
||||||
|
bool hasStage2Injection = durationUsStage2 > 50;
|
||||||
|
|
||||||
#if EFI_PRINTF_FUEL_DETAILS
|
#if EFI_PRINTF_FUEL_DETAILS
|
||||||
if (printFuelDebug) {
|
if (printFuelDebug) {
|
||||||
InjectorOutputPin *output = outputs[0];
|
InjectorOutputPin *output = outputs[0];
|
||||||
printf("handleFuelInjectionEvent fuelout %s injection_duration %dus engineCycleDuration=%.1fms\t\n", output->getName(), (int)durationUs,
|
printf("handleFuelInjectionEvent fuelout %s injection_duration %dus engineCycleDuration=%.1fms\t\n", output->getName(), (int)durationUsStage1,
|
||||||
(int)MS2US(getCrankshaftRevolutionTimeMs(Sensor::getOrZero(SensorType::Rpm))) / 1000.0);
|
(int)MS2US(getCrankshaftRevolutionTimeMs(Sensor::getOrZero(SensorType::Rpm))) / 1000.0);
|
||||||
}
|
}
|
||||||
#endif /*EFI_PRINTF_FUEL_DETAILS */
|
#endif /*EFI_PRINTF_FUEL_DETAILS */
|
||||||
|
|
||||||
action_s startAction, endAction;
|
action_s startAction, endActionStage1, endActionStage2;
|
||||||
// We use different callbacks based on whether we're running sequential mode or not - everything else is the same
|
// We use different callbacks based on whether we're running sequential mode or not - everything else is the same
|
||||||
if (isSimultaneous) {
|
if (isSimultaneous) {
|
||||||
startAction = startSimultaneousInjection;
|
startAction = startSimultaneousInjection;
|
||||||
endAction = { &endSimultaneousInjection, this };
|
endActionStage1 = { &endSimultaneousInjection, this };
|
||||||
} else {
|
} else {
|
||||||
// sequential or batch
|
uintptr_t startActionPtr = reinterpret_cast<uintptr_t>(this);
|
||||||
startAction = { &turnInjectionPinHigh, this };
|
|
||||||
endAction = { &turnInjectionPinLow, this };
|
if (hasStage2Injection) {
|
||||||
|
// Set the low bit in the arg if there's a secondary injection to start too
|
||||||
|
startActionPtr |= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sequential or batch
|
||||||
|
startAction = { &turnInjectionPinHigh, startActionPtr };
|
||||||
|
endActionStage1 = { &turnInjectionPinLow, this };
|
||||||
|
endActionStage2 = { &turnInjectionPinLowStage2, this };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Correctly wrap injection start angle
|
||||||
float angleFromNow = eventAngle - currentPhase;
|
float angleFromNow = eventAngle - currentPhase;
|
||||||
if (angleFromNow < 0) {
|
if (angleFromNow < 0) {
|
||||||
angleFromNow += getEngineState()->engineCycle;
|
angleFromNow += getEngineState()->engineCycle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Schedule opening (stage 1 + stage 2 open together)
|
||||||
efitick_t startTime = scheduleByAngle(nullptr, nowNt, angleFromNow, startAction);
|
efitick_t startTime = scheduleByAngle(nullptr, nowNt, angleFromNow, startAction);
|
||||||
efitick_t turnOffTime = startTime + US2NT((int)durationUs);
|
|
||||||
getExecutorInterface()->scheduleByTimestampNt("inj", nullptr, turnOffTime, endAction);
|
// Schedule closing stage 1
|
||||||
|
efitick_t turnOffTimeStage1 = startTime + US2NT((int)durationUsStage1);
|
||||||
|
getExecutorInterface()->scheduleByTimestampNt("inj", nullptr, turnOffTimeStage1, endActionStage1);
|
||||||
|
|
||||||
|
// Schedule closing stage 2 (if applicable)
|
||||||
|
if (hasStage2Injection && endActionStage2) {
|
||||||
|
efitick_t turnOffTimeStage2 = startTime + US2NT((int)durationUsStage2);
|
||||||
|
getExecutorInterface()->scheduleByTimestampNt("inj stage 2", nullptr, turnOffTimeStage2, endActionStage2);
|
||||||
|
}
|
||||||
|
|
||||||
#if EFI_UNIT_TEST
|
#if EFI_UNIT_TEST
|
||||||
printf("scheduling injection angle=%.2f/delay=%.2f injectionDuration=%.2f\r\n", angleFromNow, NT2US(startTime - nowNt), injectionDuration);
|
printf("scheduling injection angle=%.2f/delay=%d injectionDuration=%d %d\r\n", angleFromNow, (int)NT2US(startTime - nowNt), (int)durationUsStage1, (int)durationUsStage2);
|
||||||
#endif
|
#endif
|
||||||
#if EFI_DEFAILED_LOGGING
|
#if EFI_DEFAILED_LOGGING
|
||||||
efiPrintf("handleFuel pin=%s eventIndex %d duration=%.2fms %d", outputs[0]->name,
|
efiPrintf("handleFuel pin=%s eventIndex %d duration=%.2fms %d", outputs[0]->name,
|
||||||
injEventIndex,
|
injEventIndex,
|
||||||
injectionDuration,
|
injectionDurationStage1,
|
||||||
getRevolutionCounter());
|
getRevolutionCounter());
|
||||||
efiPrintf("handleFuel pin=%s delay=%.2f %d", outputs[0]->name, NT2US(startTime - nowNt),
|
efiPrintf("handleFuel pin=%s delay=%.2f %d", outputs[0]->name, NT2US(startTime - nowNt),
|
||||||
getRevolutionCounter());
|
getRevolutionCounter());
|
||||||
|
|
|
@ -15,3 +15,4 @@ void mainTriggerCallback(uint32_t trgEventIndex, efitick_t edgeTimestamp, angle_
|
||||||
|
|
||||||
void endSimultaneousInjection(InjectionEvent *event);
|
void endSimultaneousInjection(InjectionEvent *event);
|
||||||
void turnInjectionPinLow(InjectionEvent *event);
|
void turnInjectionPinLow(InjectionEvent *event);
|
||||||
|
void turnInjectionPinLowStage2(InjectionEvent* event);
|
||||||
|
|
|
@ -20,7 +20,7 @@ floatms_t PrimeController::getPrimeDuration() const {
|
||||||
0.001f * // convert milligram to gram
|
0.001f * // convert milligram to gram
|
||||||
interpolate2d(clt.Value, engineConfiguration->primeBins, engineConfiguration->primeValues);
|
interpolate2d(clt.Value, engineConfiguration->primeBins, engineConfiguration->primeValues);
|
||||||
|
|
||||||
return engine->module<InjectorModel>()->getInjectionDuration(primeMass);
|
return engine->module<InjectorModelPrimary>()->getInjectionDuration(primeMass);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the engine is not stopped or cylinder cleanup is activated
|
// Check if the engine is not stopped or cylinder cleanup is activated
|
||||||
|
|
|
@ -52,6 +52,12 @@ static const char* const injectorNames[] = { "Injector 1", "Injector 2", "Inject
|
||||||
static const char* const injectorShortNames[] = { PROTOCOL_INJ1_SHORT_NAME, "i2", "i3", "i4", "i5", "i6", "i7", "i8",
|
static const char* const injectorShortNames[] = { PROTOCOL_INJ1_SHORT_NAME, "i2", "i3", "i4", "i5", "i6", "i7", "i8",
|
||||||
"i9", "iA", "iB", "iC"};
|
"i9", "iA", "iB", "iC"};
|
||||||
|
|
||||||
|
static const char* const injectorStage2Names[] = { "Injector Second Stage 1", "Injector Second Stage 2", "Injector Second Stage 3", "Injector Second Stage 4", "Injector Second Stage 5", "Injector Second Stage 6",
|
||||||
|
"Injector Second Stage 7", "Injector Second Stage 8", "Injector Second Stage 9", "Injector Second Stage 10", "Injector Second Stage 11", "Injector Second Stage 12"};
|
||||||
|
|
||||||
|
static const char* const injectorStage2ShortNames[] = { PROTOCOL_INJ1_STAGE2_SHORT_NAME, "j2", "j3", "j4", "j5", "j6", "j7", "j8",
|
||||||
|
"j9", "jA", "jB", "jC"};
|
||||||
|
|
||||||
static const char* const auxValveShortNames[] = { "a1", "a2"};
|
static const char* const auxValveShortNames[] = { "a1", "a2"};
|
||||||
|
|
||||||
static RegisteredOutputPin * registeredOutputHead = nullptr;
|
static RegisteredOutputPin * registeredOutputHead = nullptr;
|
||||||
|
@ -167,6 +173,10 @@ EnginePins::EnginePins() :
|
||||||
enginePins.injectors[i].injectorIndex = i;
|
enginePins.injectors[i].injectorIndex = i;
|
||||||
enginePins.injectors[i].setName(injectorNames[i]);
|
enginePins.injectors[i].setName(injectorNames[i]);
|
||||||
enginePins.injectors[i].shortName = injectorShortNames[i];
|
enginePins.injectors[i].shortName = injectorShortNames[i];
|
||||||
|
|
||||||
|
enginePins.injectorsStage2[i].injectorIndex = i;
|
||||||
|
enginePins.injectorsStage2[i].setName(injectorStage2Names[i]);
|
||||||
|
enginePins.injectorsStage2[i].shortName = injectorStage2ShortNames[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
static_assert(efi::size(auxValveShortNames) >= AUX_DIGITAL_VALVE_COUNT, "Too many aux valve pins");
|
static_assert(efi::size(auxValveShortNames) >= AUX_DIGITAL_VALVE_COUNT, "Too many aux valve pins");
|
||||||
|
@ -196,6 +206,7 @@ bool EnginePins::stopPins() {
|
||||||
for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
|
for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
|
||||||
result |= coils[i].stop();
|
result |= coils[i].stop();
|
||||||
result |= injectors[i].stop();
|
result |= injectors[i].stop();
|
||||||
|
result |= injectorsStage2[i].stop();
|
||||||
result |= trailingCoils[i].stop();
|
result |= trailingCoils[i].stop();
|
||||||
}
|
}
|
||||||
for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT; i++) {
|
for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT; i++) {
|
||||||
|
@ -264,6 +275,7 @@ void EnginePins::stopIgnitionPins() {
|
||||||
void EnginePins::stopInjectionPins() {
|
void EnginePins::stopInjectionPins() {
|
||||||
for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
|
for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
|
||||||
unregisterOutputIfPinOrModeChanged(enginePins.injectors[i], injectionPins[i], injectionPinMode);
|
unregisterOutputIfPinOrModeChanged(enginePins.injectors[i], injectionPins[i], injectionPinMode);
|
||||||
|
unregisterOutputIfPinOrModeChanged(enginePins.injectorsStage2[i], injectionPinsStage2[i], injectionPinMode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,6 +326,12 @@ void EnginePins::startInjectionPins() {
|
||||||
output->initPin(output->getName(), engineConfiguration->injectionPins[i],
|
output->initPin(output->getName(), engineConfiguration->injectionPins[i],
|
||||||
engineConfiguration->injectionPinMode);
|
engineConfiguration->injectionPinMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output = &enginePins.injectorsStage2[i];
|
||||||
|
if (isPinOrModeChanged(injectionPinsStage2[i], injectionPinMode)) {
|
||||||
|
output->initPin(output->getName(), engineConfiguration->injectionPinsStage2[i],
|
||||||
|
engineConfiguration->injectionPinMode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif /* EFI_PROD_CODE */
|
#endif /* EFI_PROD_CODE */
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,6 +118,7 @@ public:
|
||||||
OutputPin accelerometerCs;
|
OutputPin accelerometerCs;
|
||||||
|
|
||||||
InjectorOutputPin injectors[MAX_CYLINDER_COUNT];
|
InjectorOutputPin injectors[MAX_CYLINDER_COUNT];
|
||||||
|
InjectorOutputPin injectorsStage2[MAX_CYLINDER_COUNT];
|
||||||
IgnitionOutputPin coils[MAX_CYLINDER_COUNT];
|
IgnitionOutputPin coils[MAX_CYLINDER_COUNT];
|
||||||
IgnitionOutputPin trailingCoils[MAX_CYLINDER_COUNT];
|
IgnitionOutputPin trailingCoils[MAX_CYLINDER_COUNT];
|
||||||
NamedOutputPin auxValve[AUX_DIGITAL_VALVE_COUNT];
|
NamedOutputPin auxValve[AUX_DIGITAL_VALVE_COUNT];
|
||||||
|
|
|
@ -8,6 +8,24 @@
|
||||||
|
|
||||||
typedef void (*schfunc_t)(void *);
|
typedef void (*schfunc_t)(void *);
|
||||||
|
|
||||||
|
template<class To, class From>
|
||||||
|
std::enable_if_t<
|
||||||
|
sizeof(To) == sizeof(From) &&
|
||||||
|
std::is_trivially_copyable_v<From> &&
|
||||||
|
std::is_trivially_copyable_v<To>,
|
||||||
|
To>
|
||||||
|
// constexpr support needs compiler magic
|
||||||
|
bit_cast(const From& src) noexcept
|
||||||
|
{
|
||||||
|
static_assert(std::is_trivially_constructible_v<To>,
|
||||||
|
"This implementation additionally requires "
|
||||||
|
"destination type to be trivially constructible");
|
||||||
|
|
||||||
|
To dst;
|
||||||
|
std::memcpy(&dst, &src, sizeof(To));
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
class action_s {
|
class action_s {
|
||||||
public:
|
public:
|
||||||
// Default constructor constructs null action (ie, implicit bool conversion returns false)
|
// Default constructor constructs null action (ie, implicit bool conversion returns false)
|
||||||
|
@ -16,13 +34,13 @@ public:
|
||||||
// Allow implicit conversion from schfunc_t to action_s
|
// Allow implicit conversion from schfunc_t to action_s
|
||||||
action_s(schfunc_t callback) : action_s(callback, nullptr) { }
|
action_s(schfunc_t callback) : action_s(callback, nullptr) { }
|
||||||
action_s(schfunc_t callback, void *param) : m_callback(callback), m_param(param) { }
|
action_s(schfunc_t callback, void *param) : m_callback(callback), m_param(param) { }
|
||||||
template <typename TParam>
|
|
||||||
action_s(schfunc_t callback, TParam& param) : m_callback(callback), m_param(¶m) { }
|
|
||||||
|
|
||||||
// Allow any function that takes a single pointer parameter, so long as param is also of the same pointer type.
|
// Allow any function that takes a single pointer parameter, so long as param is also of the same pointer type.
|
||||||
// This constructor means you shouldn't ever have to cast to schfunc_t on your own.
|
// This constructor means you shouldn't ever have to cast to schfunc_t on your own.
|
||||||
template <typename TArg>
|
template <typename TArg>
|
||||||
action_s(void (*callback)(TArg*), TArg* param) : m_callback((schfunc_t)callback), m_param(param) { }
|
action_s(void (*callback)(TArg*), TArg* param) : m_callback((schfunc_t)callback), m_param(param) { }
|
||||||
|
template <typename TArg>
|
||||||
|
action_s(void (*callback)(TArg), TArg param) : m_callback(bit_cast<schfunc_t>(callback)), m_param(reinterpret_cast<void*>(param)) { }
|
||||||
|
|
||||||
void execute();
|
void execute();
|
||||||
schfunc_t getCallback() const;
|
schfunc_t getCallback() const;
|
||||||
|
|
|
@ -415,7 +415,7 @@ bit disableFan2WhenStopped;Inhibit operation of this fan while the engine is not
|
||||||
bit enableTrailingSparks;Enable secondary spark outputs that fire after the primary (rotaries, twin plug engines).
|
bit enableTrailingSparks;Enable secondary spark outputs that fire after the primary (rotaries, twin plug engines).
|
||||||
bit etb_use_two_wires;TLE7209 uses two-wire mode. TLE9201 and VNH2SP30 do NOT use two wire mode.
|
bit etb_use_two_wires;TLE7209 uses two-wire mode. TLE9201 and VNH2SP30 do NOT use two wire mode.
|
||||||
bit isDoubleSolenoidIdle;Subaru/BMW style where default valve position is somewhere in the middle. First solenoid opens it more while second can close it more than default position.
|
bit isDoubleSolenoidIdle;Subaru/BMW style where default valve position is somewhere in the middle. First solenoid opens it more while second can close it more than default position.
|
||||||
bit unused88b11
|
bit enableStagedInjection
|
||||||
bit useTLE8888_cranking_hack;
|
bit useTLE8888_cranking_hack;
|
||||||
bit kickStartCranking
|
bit kickStartCranking
|
||||||
bit useSeparateIdleTablesForCrankingTaper;This uses separate ignition timing and VE tables not only for idle conditions, also during the postcranking-to-idle taper transition (See also afterCrankingIACtaperDuration).
|
bit useSeparateIdleTablesForCrankingTaper;This uses separate ignition timing and VE tables not only for idle conditions, also during the postcranking-to-idle taper transition (See also afterCrankingIACtaperDuration).
|
||||||
|
@ -620,6 +620,7 @@ engineSyncCam_e engineSyncCam;Select which cam is used for engine sync. Other ca
|
||||||
output_pin_e o2heaterPin;On-off O2 sensor heater control. 'ON' if engine is running, 'OFF' if stopped or cranking.
|
output_pin_e o2heaterPin;On-off O2 sensor heater control. 'ON' if engine is running, 'OFF' if stopped or cranking.
|
||||||
|
|
||||||
output_pin_e[MAX_CYLINDER_COUNT iterate] injectionPins;
|
output_pin_e[MAX_CYLINDER_COUNT iterate] injectionPins;
|
||||||
|
output_pin_e[MAX_CYLINDER_COUNT iterate] injectionPinsStage2;
|
||||||
output_pin_e[MAX_CYLINDER_COUNT iterate] ignitionPins;
|
output_pin_e[MAX_CYLINDER_COUNT iterate] ignitionPins;
|
||||||
|
|
||||||
pin_output_mode_e injectionPinMode;
|
pin_output_mode_e injectionPinMode;
|
||||||
|
@ -1727,6 +1728,10 @@ uint8_t[4 x 4] autoscale lambdaMaxDeviationTable;;"lambda", 0.01, 0, 0, 1, 2
|
||||||
uint16_t[4] lambdaMaxDeviationLoadBins;;"", 1, 0, 0, 1000, 0
|
uint16_t[4] lambdaMaxDeviationLoadBins;;"", 1, 0, 0, 1000, 0
|
||||||
uint16_t[4] lambdaMaxDeviationRpmBins;;"RPM", 1, 0, 0, 18000, 0
|
uint16_t[4] lambdaMaxDeviationRpmBins;;"RPM", 1, 0, 0, 18000, 0
|
||||||
|
|
||||||
|
uint8_t[6 x 6] injectorStagingTable;;"%", 1, 0, 0, 90, 0
|
||||||
|
uint16_t[6] injectorStagingLoadBins;;"", 1, 0, 0, 1000, 0
|
||||||
|
uint16_t[6] injectorStagingRpmBins;;"RPM", 1, 0, 0, 18000, 0
|
||||||
|
|
||||||
end_struct
|
end_struct
|
||||||
|
|
||||||
! Pedal Position Sensor
|
! Pedal Position Sensor
|
||||||
|
@ -1911,6 +1916,7 @@ end_struct
|
||||||
|
|
||||||
#define PROTOCOL_COIL1_SHORT_NAME "c1"
|
#define PROTOCOL_COIL1_SHORT_NAME "c1"
|
||||||
#define PROTOCOL_INJ1_SHORT_NAME "i1"
|
#define PROTOCOL_INJ1_SHORT_NAME "i1"
|
||||||
|
#define PROTOCOL_INJ1_STAGE2_SHORT_NAME "j1"
|
||||||
|
|
||||||
! some board files override this value using prepend file
|
! some board files override this value using prepend file
|
||||||
#define ts_show_vr_threshold_all true
|
#define ts_show_vr_threshold_all true
|
||||||
|
|
|
@ -139,6 +139,7 @@
|
||||||
#define GAUGE_NAME_FUEL_CRANKING "Fuel: cranking"
|
#define GAUGE_NAME_FUEL_CRANKING "Fuel: cranking"
|
||||||
#define GAUGE_NAME_FUEL_RUNNING "Fuel: running"
|
#define GAUGE_NAME_FUEL_RUNNING "Fuel: running"
|
||||||
#define GAUGE_NAME_FUEL_LAST_INJECTION "Fuel: Last inj pulse width"
|
#define GAUGE_NAME_FUEL_LAST_INJECTION "Fuel: Last inj pulse width"
|
||||||
|
#define GAUGE_NAME_FUEL_LAST_INJECTION_STAGE_2 "Fuel: Last inj pulse width stg 2"
|
||||||
#define GAUGE_NAME_FUEL_BASE "Fuel: base cycle mass"
|
#define GAUGE_NAME_FUEL_BASE "Fuel: base cycle mass"
|
||||||
#define GAUGE_NAME_FUEL_TRIM "Fuel: fuel trim"
|
#define GAUGE_NAME_FUEL_TRIM "Fuel: fuel trim"
|
||||||
#define GAUGE_NAME_FUEL_TRIM_2 "Fuel: fuel trim 2"
|
#define GAUGE_NAME_FUEL_TRIM_2 "Fuel: fuel trim 2"
|
||||||
|
@ -149,6 +150,7 @@
|
||||||
#define GAUGE_NAME_FUEL_FLOW "Fuel: Flow rate"
|
#define GAUGE_NAME_FUEL_FLOW "Fuel: Flow rate"
|
||||||
|
|
||||||
#define GAUGE_NAME_FUEL_INJ_DUTY "Fuel: injector duty cycle"
|
#define GAUGE_NAME_FUEL_INJ_DUTY "Fuel: injector duty cycle"
|
||||||
|
#define GAUGE_NAME_FUEL_INJ_DUTY_STAGE_2 "Fuel: injector duty cycle stage 2"
|
||||||
#define GAUGE_NAME_TCHARGE "Air: SD tCharge"
|
#define GAUGE_NAME_TCHARGE "Air: SD tCharge"
|
||||||
#define GAUGE_NAME_TARGET_AFR "Fuel: target AFR"
|
#define GAUGE_NAME_TARGET_AFR "Fuel: target AFR"
|
||||||
#define GAUGE_NAME_TARGET_LAMBDA "Fuel: target lambda"
|
#define GAUGE_NAME_TARGET_LAMBDA "Fuel: target lambda"
|
||||||
|
|
|
@ -1180,6 +1180,12 @@ curve = 32Curve, "3-2 Shift Solenoid Percent by Speed"
|
||||||
yBins = gppwm4_loadBins, gppwmYAxis4
|
yBins = gppwm4_loadBins, gppwmYAxis4
|
||||||
zBins = gppwm4_table
|
zBins = gppwm4_table
|
||||||
|
|
||||||
|
table = stagedInjectionTbl, stagedInjectionMap, "Staged Injection %", 1
|
||||||
|
xyLabels = "RPM", ""
|
||||||
|
xBins = injectorStagingRpmBins, RPMValue
|
||||||
|
yBins = injectorStagingLoadBins, fuelingLoad
|
||||||
|
zBins = injectorStagingTable
|
||||||
|
|
||||||
table = tcuSolenoidTableTbl, tcuSolenoidTableMap, "Solenoids Active By Gear", 1
|
table = tcuSolenoidTableTbl, tcuSolenoidTableMap, "Solenoids Active By Gear", 1
|
||||||
xBins = solenoidCountArray, tcuCurrentGear
|
xBins = solenoidCountArray, tcuCurrentGear
|
||||||
yBins = gearCountArray, tcuCurrentGear
|
yBins = gearCountArray, tcuCurrentGear
|
||||||
|
@ -1410,7 +1416,9 @@ gaugeCategory = Fueling
|
||||||
iatCorrectionGauge = running_intakeTemperatureCoefficient, @@GAUGE_NAME_FUEL_IAT_CORR@@, "mult", 0, 3, 0, 0, 3, 3, 2, 2
|
iatCorrectionGauge = running_intakeTemperatureCoefficient, @@GAUGE_NAME_FUEL_IAT_CORR@@, "mult", 0, 3, 0, 0, 3, 3, 2, 2
|
||||||
cltCorrectionGauge = running_coolantTemperatureCoefficient, @@GAUGE_NAME_FUEL_CLT_CORR@@, "mult", 0, 3, 0, 0, 3, 3, 2, 2
|
cltCorrectionGauge = running_coolantTemperatureCoefficient, @@GAUGE_NAME_FUEL_CLT_CORR@@, "mult", 0, 3, 0, 0, 3, 3, 2, 2
|
||||||
injectorDutyCycleGauge=injectorDutyCycle, @@GAUGE_NAME_FUEL_INJ_DUTY@@,"%", 0, 120, 10, 10, 100, 100, 1, 1
|
injectorDutyCycleGauge=injectorDutyCycle, @@GAUGE_NAME_FUEL_INJ_DUTY@@,"%", 0, 120, 10, 10, 100, 100, 1, 1
|
||||||
|
injectorDutyCycleStg2Gauge=injectorDutyCycleStage2, @@GAUGE_NAME_FUEL_INJ_DUTY_STAGE_2@@,"%", 0, 120, 10, 10, 100, 100, 1, 1
|
||||||
actualLastInjectionGauge = actualLastInjection, @@GAUGE_NAME_FUEL_LAST_INJECTION@@, "mSec", 0, 25.5, 1.0, 1.2, 20, 25, 3, 1
|
actualLastInjectionGauge = actualLastInjection, @@GAUGE_NAME_FUEL_LAST_INJECTION@@, "mSec", 0, 25.5, 1.0, 1.2, 20, 25, 3, 1
|
||||||
|
actualLastInjectionStg2Gauge = actualLastInjectionStage2, @@GAUGE_NAME_FUEL_LAST_INJECTION_STAGE_2@@, "mSec", 0, 25.5, 1.0, 1.2, 20, 25, 3, 1
|
||||||
veValueGauge = veValue, "fuel: VE", "", 0, 120, 10, 10, 100, 100, 1, 1
|
veValueGauge = veValue, "fuel: VE", "", 0, 120, 10, 10, 100, 100, 1, 1
|
||||||
|
|
||||||
injectorLagMsGauge = m_deadtime, @@GAUGE_NAME_INJECTOR_LAG@@, "mSec", 0, 10, 0, 0, 10, 10, 3, 1
|
injectorLagMsGauge = m_deadtime, @@GAUGE_NAME_INJECTOR_LAG@@, "mSec", 0, 10, 0, 0, 10, 10, 3, 1
|
||||||
|
@ -1650,6 +1658,7 @@ menuDialog = main
|
||||||
subMenu = injTest, "Injector test", 0, {isInjectionEnabled}
|
subMenu = injTest, "Injector test", 0, {isInjectionEnabled}
|
||||||
subMenu = cylinderBankSelect, "Cylinder bank selection", 0, {isInjectionEnabled == 1}
|
subMenu = cylinderBankSelect, "Cylinder bank selection", 0, {isInjectionEnabled == 1}
|
||||||
subMenu = injectorNonlinear, "Injector small-pulse correction", 0, {isInjectionEnabled == 1}
|
subMenu = injectorNonlinear, "Injector small-pulse correction", 0, {isInjectionEnabled == 1}
|
||||||
|
subMenu = stagedInjection, "Staged injection", 0, {isInjectionEnabled}
|
||||||
|
|
||||||
groupMenu = "Cylinder fuel trims"
|
groupMenu = "Cylinder fuel trims"
|
||||||
groupChildMenu = fuelTrimTbl1, "Fuel trim cyl 1"
|
groupChildMenu = fuelTrimTbl1, "Fuel trim cyl 1"
|
||||||
|
@ -2440,6 +2449,26 @@ cmd_set_engine_type_default = "@@TS_IO_TEST_COMMAND_char@@@@ts_command_e_TS_
|
||||||
field = "Offset cyl 11", timing_offset_cylinder11, {cylindersCount > 10}
|
field = "Offset cyl 11", timing_offset_cylinder11, {cylindersCount > 10}
|
||||||
field = "Offset cyl 12", timing_offset_cylinder12, {cylindersCount > 11}
|
field = "Offset cyl 12", timing_offset_cylinder12, {cylindersCount > 11}
|
||||||
|
|
||||||
|
dialog = stagedInjectionLeft, "", yAxis
|
||||||
|
field = "Enable", enableStagedInjection, {isInjectionEnabled}
|
||||||
|
field = ""
|
||||||
|
field = "Injection Stage 2 Output 1", injectionPinsStage21, {isInjectionEnabled && enableStagedInjection}
|
||||||
|
field = "Injection Stage 2 Output 2", injectionPinsStage22, {isInjectionEnabled && enableStagedInjection && injectionMode != 3 && cylindersCount > 1}
|
||||||
|
field = "Injection Stage 2 Output 3", injectionPinsStage23, {isInjectionEnabled && enableStagedInjection && injectionMode != @@injection_mode_e_IM_SINGLE_POINT@@ && cylindersCount > 2}
|
||||||
|
field = "Injection Stage 2 Output 4", injectionPinsStage24, {isInjectionEnabled && enableStagedInjection && injectionMode != 3 && cylindersCount > 3}
|
||||||
|
field = "Injection Stage 2 Output 5 ", injectionPinsStage25, {isInjectionEnabled && enableStagedInjection && injectionMode != @@injection_mode_e_IM_SINGLE_POINT@@ && cylindersCount > 4}
|
||||||
|
field = "Injection Stage 2 Output 6 ", injectionPinsStage26, {isInjectionEnabled && enableStagedInjection && injectionMode != @@injection_mode_e_IM_SINGLE_POINT@@ && cylindersCount > 5}
|
||||||
|
field = "Injection Stage 2 Output 7 ", injectionPinsStage27, {isInjectionEnabled && enableStagedInjection && injectionMode != 3 && cylindersCount > 6}
|
||||||
|
field = "Injection Stage 2 Output 8 ", injectionPinsStage28, {isInjectionEnabled && enableStagedInjection && injectionMode != 3 && cylindersCount > 7}
|
||||||
|
field = "Injection Stage 2 Output 9 ", injectionPinsStage29, {isInjectionEnabled && enableStagedInjection && cylindersCount > 8}
|
||||||
|
field = "Injection Stage 2 Output 10 ", injectionPinsStage210, {isInjectionEnabled && enableStagedInjection && cylindersCount > 9}
|
||||||
|
field = "Injection Stage 2 Output 11 ", injectionPinsStage211, {isInjectionEnabled && enableStagedInjection && cylindersCount > 10}
|
||||||
|
field = "Injection Stage 2 Output 12 ", injectionPinsStage212, {isInjectionEnabled && enableStagedInjection && cylindersCount > 11}
|
||||||
|
|
||||||
|
dialog = stagedInjection, "", xAxis
|
||||||
|
panel = stagedInjectionLeft
|
||||||
|
panel = stagedInjectionTbl, {isInjectionEnabled && enableStagedInjection}
|
||||||
|
|
||||||
dialog = multisparkDwellParams, "Delay & Dwell"
|
dialog = multisparkDwellParams, "Delay & Dwell"
|
||||||
field = "Spark duration", multisparkSparkDuration, {multisparkEnable}
|
field = "Spark duration", multisparkSparkDuration, {multisparkEnable}
|
||||||
field = "Subsequent spark dwell", multisparkDwell, {multisparkEnable}
|
field = "Subsequent spark dwell", multisparkDwell, {multisparkEnable}
|
||||||
|
|
|
@ -259,7 +259,7 @@ public class EngineSnifferPanel {
|
||||||
signalBody = Color.darkGray;
|
signalBody = Color.darkGray;
|
||||||
} else if (name.startsWith("HIP")) {
|
} else if (name.startsWith("HIP")) {
|
||||||
signalBody = Color.white;
|
signalBody = Color.white;
|
||||||
} else if (name.startsWith("i")) {
|
} else if (name.startsWith("i") || name.startsWith("j")) {
|
||||||
// injection
|
// injection
|
||||||
signalBody = Color.green;
|
signalBody = Color.green;
|
||||||
} else if (name.startsWith("map")) {
|
} else if (name.startsWith("map")) {
|
||||||
|
|
|
@ -20,6 +20,8 @@ public class NameUtil {
|
||||||
return "Coil #" + name.substring(1);
|
return "Coil #" + name.substring(1);
|
||||||
if (name.charAt(0) == Fields.PROTOCOL_INJ1_SHORT_NAME.charAt(0))
|
if (name.charAt(0) == Fields.PROTOCOL_INJ1_SHORT_NAME.charAt(0))
|
||||||
return "Injector #" + name.substring(1);
|
return "Injector #" + name.substring(1);
|
||||||
|
if (name.charAt(0) == Fields.PROTOCOL_INJ1_STAGE2_SHORT_NAME.charAt(0))
|
||||||
|
return "Injector Second Stage #" + name.substring(1);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -48,7 +48,7 @@ TEST(priming, duration) {
|
||||||
EngineTestHelper eth(engine_type_e::TEST_ENGINE);
|
EngineTestHelper eth(engine_type_e::TEST_ENGINE);
|
||||||
|
|
||||||
MockInjectorModel2 injectorModel;
|
MockInjectorModel2 injectorModel;
|
||||||
engine->module<InjectorModel>().set(&injectorModel);
|
engine->module<InjectorModelPrimary>().set(&injectorModel);
|
||||||
|
|
||||||
for (size_t i = 0; i < efi::size(engineConfiguration->primeBins); i++) {
|
for (size_t i = 0; i < efi::size(engineConfiguration->primeBins); i++) {
|
||||||
engineConfiguration->primeBins[i] = i * 10;
|
engineConfiguration->primeBins[i] = i * 10;
|
||||||
|
|
|
@ -6,6 +6,15 @@ using ::testing::_;
|
||||||
using ::testing::StrictMock;
|
using ::testing::StrictMock;
|
||||||
using ::testing::InSequence;
|
using ::testing::InSequence;
|
||||||
|
|
||||||
|
using ::testing::Eq;
|
||||||
|
using ::testing::Not;
|
||||||
|
using ::testing::Property;
|
||||||
|
using ::testing::Truly;
|
||||||
|
|
||||||
|
static bool ActionArgumentHasLowBitSet(const action_s& a) {
|
||||||
|
return (reinterpret_cast<uintptr_t>(a.getArgument()) & 1) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
TEST(injectionScheduling, InjectionIsScheduled) {
|
TEST(injectionScheduling, InjectionIsScheduled) {
|
||||||
StrictMock<MockExecutor> mockExec;
|
StrictMock<MockExecutor> mockExec;
|
||||||
|
|
||||||
|
@ -22,7 +31,7 @@ TEST(injectionScheduling, InjectionIsScheduled) {
|
||||||
// Injection duration of 20ms
|
// Injection duration of 20ms
|
||||||
MockInjectorModel2 im;
|
MockInjectorModel2 im;
|
||||||
EXPECT_CALL(im, getInjectionDuration(_)).WillOnce(Return(20.0f));
|
EXPECT_CALL(im, getInjectionDuration(_)).WillOnce(Return(20.0f));
|
||||||
engine->module<InjectorModel>().set(&im);
|
engine->module<InjectorModelPrimary>().set(&im);
|
||||||
|
|
||||||
engine->rpmCalculator.oneDegreeUs = 100;
|
engine->rpmCalculator.oneDegreeUs = 100;
|
||||||
|
|
||||||
|
@ -33,11 +42,61 @@ TEST(injectionScheduling, InjectionIsScheduled) {
|
||||||
// rising edge 5 degrees from now
|
// rising edge 5 degrees from now
|
||||||
float nt5deg = USF2NT(engine->rpmCalculator.oneDegreeUs * 5);
|
float nt5deg = USF2NT(engine->rpmCalculator.oneDegreeUs * 5);
|
||||||
efitick_t startTime = nowNt + nt5deg;
|
efitick_t startTime = nowNt + nt5deg;
|
||||||
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime, _));
|
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime, Not(Truly(ActionArgumentHasLowBitSet))));
|
||||||
// falling edge 20ms later
|
// falling edge 20ms later
|
||||||
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime + MS2NT(20), _));
|
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime + MS2NT(20), Property(&action_s::getArgument, Eq(&event))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Event scheduled at 125 degrees
|
||||||
|
event.injectionStartAngle = 125;
|
||||||
|
|
||||||
|
// We are at 120 degrees now, next tooth 130
|
||||||
|
event.onTriggerTooth(nowNt, 120, 130);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(injectionScheduling, InjectionIsScheduledDualStage) {
|
||||||
|
StrictMock<MockExecutor> mockExec;
|
||||||
|
StrictMock<MockInjectorModel2> im;
|
||||||
|
|
||||||
|
EngineTestHelper eth(engine_type_e::TEST_ENGINE);
|
||||||
|
engine->executor.setMockExecutor(&mockExec);
|
||||||
|
engine->module<InjectorModelPrimary>().set(&im);
|
||||||
|
engine->module<InjectorModelSecondary>().set(&im);
|
||||||
|
|
||||||
|
efitick_t nowNt = 1000000;
|
||||||
|
|
||||||
|
InjectionEvent event;
|
||||||
|
InjectorOutputPin pin;
|
||||||
|
pin.injectorIndex = 0;
|
||||||
|
event.outputs[0] = &pin;
|
||||||
|
|
||||||
|
engine->rpmCalculator.oneDegreeUs = 100;
|
||||||
|
|
||||||
|
// Some nonzero fuel quantity on both stages
|
||||||
|
engine->engineState.injectionMass[0] = 50;
|
||||||
|
engine->engineState.injectionStage2Fraction = 0.2;
|
||||||
|
|
||||||
|
{
|
||||||
|
InSequence is;
|
||||||
|
|
||||||
|
// Primary injection duration of 20ms, secondary 10ms
|
||||||
|
EXPECT_CALL(im, getInjectionDuration(40)).WillOnce(Return(20.0f));
|
||||||
|
EXPECT_CALL(im, getInjectionDuration(10)).WillOnce(Return(10.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
InSequence is;
|
||||||
|
|
||||||
|
// Should schedule one normal injection:
|
||||||
|
// rising edge 5 degrees from now
|
||||||
|
float nt5deg = USF2NT(engine->rpmCalculator.oneDegreeUs * 5);
|
||||||
|
efitick_t startTime = nowNt + nt5deg;
|
||||||
|
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime, Truly(ActionArgumentHasLowBitSet)));
|
||||||
|
// falling edge (primary) 20ms later
|
||||||
|
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime + MS2NT(20), Property(&action_s::getArgument, Eq(&event))));
|
||||||
|
// falling edge (secondary) 10ms later
|
||||||
|
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime + MS2NT(10), Property(&action_s::getArgument, Eq(&event))));
|
||||||
|
}
|
||||||
|
|
||||||
// Event scheduled at 125 degrees
|
// Event scheduled at 125 degrees
|
||||||
event.injectionStartAngle = 125;
|
event.injectionStartAngle = 125;
|
||||||
|
@ -62,7 +121,7 @@ TEST(injectionScheduling, InjectionIsScheduledBeforeWraparound) {
|
||||||
// Injection duration of 20ms
|
// Injection duration of 20ms
|
||||||
MockInjectorModel2 im;
|
MockInjectorModel2 im;
|
||||||
EXPECT_CALL(im, getInjectionDuration(_)).WillOnce(Return(20.0f));
|
EXPECT_CALL(im, getInjectionDuration(_)).WillOnce(Return(20.0f));
|
||||||
engine->module<InjectorModel>().set(&im);
|
engine->module<InjectorModelPrimary>().set(&im);
|
||||||
|
|
||||||
engine->rpmCalculator.oneDegreeUs = 100;
|
engine->rpmCalculator.oneDegreeUs = 100;
|
||||||
|
|
||||||
|
@ -73,9 +132,9 @@ TEST(injectionScheduling, InjectionIsScheduledBeforeWraparound) {
|
||||||
// rising edge 5 degrees from now
|
// rising edge 5 degrees from now
|
||||||
float nt5deg = USF2NT(engine->rpmCalculator.oneDegreeUs * 5);
|
float nt5deg = USF2NT(engine->rpmCalculator.oneDegreeUs * 5);
|
||||||
efitick_t startTime = nowNt + nt5deg;
|
efitick_t startTime = nowNt + nt5deg;
|
||||||
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime, _));
|
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime, Not(Truly(ActionArgumentHasLowBitSet))));
|
||||||
// falling edge 20ms later
|
// falling edge 20ms later
|
||||||
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime + MS2NT(20), _));
|
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime + MS2NT(20), Property(&action_s::getArgument, Eq(&event))));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event scheduled at 715 degrees
|
// Event scheduled at 715 degrees
|
||||||
|
@ -101,7 +160,7 @@ TEST(injectionScheduling, InjectionIsScheduledAfterWraparound) {
|
||||||
// Injection duration of 20ms
|
// Injection duration of 20ms
|
||||||
MockInjectorModel2 im;
|
MockInjectorModel2 im;
|
||||||
EXPECT_CALL(im, getInjectionDuration(_)).WillOnce(Return(20.0f));
|
EXPECT_CALL(im, getInjectionDuration(_)).WillOnce(Return(20.0f));
|
||||||
engine->module<InjectorModel>().set(&im);
|
engine->module<InjectorModelPrimary>().set(&im);
|
||||||
|
|
||||||
engine->rpmCalculator.oneDegreeUs = 100;
|
engine->rpmCalculator.oneDegreeUs = 100;
|
||||||
|
|
||||||
|
@ -112,9 +171,9 @@ TEST(injectionScheduling, InjectionIsScheduledAfterWraparound) {
|
||||||
// rising edge 15 degrees from now
|
// rising edge 15 degrees from now
|
||||||
float nt5deg = USF2NT(engine->rpmCalculator.oneDegreeUs * 15);
|
float nt5deg = USF2NT(engine->rpmCalculator.oneDegreeUs * 15);
|
||||||
efitick_t startTime = nowNt + nt5deg;
|
efitick_t startTime = nowNt + nt5deg;
|
||||||
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime, _));
|
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime, Not(Truly(ActionArgumentHasLowBitSet))));
|
||||||
// falling edge 20ms later
|
// falling edge 20ms later
|
||||||
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime + MS2NT(20), _));
|
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime + MS2NT(20), Property(&action_s::getArgument, Eq(&event))));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event scheduled at 5 degrees
|
// Event scheduled at 5 degrees
|
||||||
|
@ -140,7 +199,7 @@ TEST(injectionScheduling, InjectionNotScheduled) {
|
||||||
|
|
||||||
// Expect no calls to injector model
|
// Expect no calls to injector model
|
||||||
StrictMock<MockInjectorModel2> im;
|
StrictMock<MockInjectorModel2> im;
|
||||||
engine->module<InjectorModel>().set(&im);
|
engine->module<InjectorModelPrimary>().set(&im);
|
||||||
|
|
||||||
engine->rpmCalculator.oneDegreeUs = 100;
|
engine->rpmCalculator.oneDegreeUs = 100;
|
||||||
|
|
||||||
|
|
|
@ -520,7 +520,7 @@ static void setTestBug299(EngineTestHelper *eth) {
|
||||||
|
|
||||||
ASSERT_EQ( 1, engine->fuelComputer.running.intakeTemperatureCoefficient) << "iatC";
|
ASSERT_EQ( 1, engine->fuelComputer.running.intakeTemperatureCoefficient) << "iatC";
|
||||||
ASSERT_EQ( 1, engine->fuelComputer.running.coolantTemperatureCoefficient) << "cltC";
|
ASSERT_EQ( 1, engine->fuelComputer.running.coolantTemperatureCoefficient) << "cltC";
|
||||||
ASSERT_EQ( 0, engine->module<InjectorModel>()->getDeadtime()) << "lag";
|
ASSERT_EQ( 0, engine->module<InjectorModelPrimary>()->getDeadtime()) << "lag";
|
||||||
|
|
||||||
ASSERT_EQ( 3000, round(Sensor::getOrZero(SensorType::Rpm))) << "setTestBug299: RPM";
|
ASSERT_EQ( 3000, round(Sensor::getOrZero(SensorType::Rpm))) << "setTestBug299: RPM";
|
||||||
|
|
||||||
|
@ -560,7 +560,7 @@ void doTestFuelSchedulerBug299smallAndMedium(int startUpDelayMs) {
|
||||||
// Injection duration of 12.5ms
|
// Injection duration of 12.5ms
|
||||||
MockInjectorModel2 im;
|
MockInjectorModel2 im;
|
||||||
EXPECT_CALL(im, getInjectionDuration(_)).WillRepeatedly(Return(12.5f));
|
EXPECT_CALL(im, getInjectionDuration(_)).WillRepeatedly(Return(12.5f));
|
||||||
engine->module<InjectorModel>().set(&im);
|
engine->module<InjectorModelPrimary>().set(&im);
|
||||||
|
|
||||||
assertEqualsM("duty for maf=3", 62.5, getInjectorDutyCycle(round(Sensor::getOrZero(SensorType::Rpm))));
|
assertEqualsM("duty for maf=3", 62.5, getInjectorDutyCycle(round(Sensor::getOrZero(SensorType::Rpm))));
|
||||||
|
|
||||||
|
@ -720,7 +720,7 @@ void doTestFuelSchedulerBug299smallAndMedium(int startUpDelayMs) {
|
||||||
// Injection duration of 17.5ms
|
// Injection duration of 17.5ms
|
||||||
MockInjectorModel2 im2;
|
MockInjectorModel2 im2;
|
||||||
EXPECT_CALL(im2, getInjectionDuration(_)).WillRepeatedly(Return(17.5f));
|
EXPECT_CALL(im2, getInjectionDuration(_)).WillRepeatedly(Return(17.5f));
|
||||||
engine->module<InjectorModel>().set(&im2);
|
engine->module<InjectorModelPrimary>().set(&im2);
|
||||||
|
|
||||||
// duty cycle above 75% is a special use-case because 'special' fuel event overlappes the next normal event in batch mode
|
// duty cycle above 75% is a special use-case because 'special' fuel event overlappes the next normal event in batch mode
|
||||||
assertEqualsM("duty for maf=3", 87.5, getInjectorDutyCycle(round(Sensor::getOrZero(SensorType::Rpm))));
|
assertEqualsM("duty for maf=3", 87.5, getInjectorDutyCycle(round(Sensor::getOrZero(SensorType::Rpm))));
|
||||||
|
@ -912,7 +912,7 @@ TEST(big, testFuelSchedulerBug299smallAndLarge) {
|
||||||
// Injection duration of 17.5ms
|
// Injection duration of 17.5ms
|
||||||
MockInjectorModel2 im;
|
MockInjectorModel2 im;
|
||||||
EXPECT_CALL(im, getInjectionDuration(_)).WillRepeatedly(Return(17.5f));
|
EXPECT_CALL(im, getInjectionDuration(_)).WillRepeatedly(Return(17.5f));
|
||||||
engine->module<InjectorModel>().set(&im);
|
engine->module<InjectorModelPrimary>().set(&im);
|
||||||
|
|
||||||
assertEqualsM("Lduty for maf=3", 87.5, getInjectorDutyCycle(round(Sensor::getOrZero(SensorType::Rpm))));
|
assertEqualsM("Lduty for maf=3", 87.5, getInjectorDutyCycle(round(Sensor::getOrZero(SensorType::Rpm))));
|
||||||
|
|
||||||
|
@ -979,7 +979,7 @@ TEST(big, testFuelSchedulerBug299smallAndLarge) {
|
||||||
engine->engineState.injectionDuration = 2.0f;
|
engine->engineState.injectionDuration = 2.0f;
|
||||||
MockInjectorModel2 im2;
|
MockInjectorModel2 im2;
|
||||||
EXPECT_CALL(im2, getInjectionDuration(_)).WillRepeatedly(Return(2.0f));
|
EXPECT_CALL(im2, getInjectionDuration(_)).WillRepeatedly(Return(2.0f));
|
||||||
engine->module<InjectorModel>().set(&im2);
|
engine->module<InjectorModelPrimary>().set(&im2);
|
||||||
|
|
||||||
ASSERT_EQ( 10, getInjectorDutyCycle(round(Sensor::getOrZero(SensorType::Rpm)))) << "Lduty for maf=3";
|
ASSERT_EQ( 10, getInjectorDutyCycle(round(Sensor::getOrZero(SensorType::Rpm)))) << "Lduty for maf=3";
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue