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<>
|
||||
const injector_model_s* getLiveData(size_t) {
|
||||
return &engine->module<InjectorModel>().unmock();
|
||||
return &engine->module<InjectorModelPrimary>().unmock();
|
||||
}
|
||||
|
||||
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 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
|
||||
|
|
|
@ -150,6 +150,7 @@ static void printEngineSnifferPinMappings() {
|
|||
printOutPin(enginePins.coils[i].getShortName(), engineConfiguration->ignitionPins[i]);
|
||||
printOutPin(enginePins.trailingCoils[i].getShortName(), engineConfiguration->trailingCoilPins[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++) {
|
||||
printOutPin(enginePins.auxValve[i].getShortName(), engineConfiguration->auxValves[i]);
|
||||
|
@ -609,6 +610,7 @@ void updateTunerStudioState() {
|
|||
// 140
|
||||
#if EFI_ENGINE_CONTROL
|
||||
tsOutputChannels->injectorDutyCycle = getInjectorDutyCycle(rpm);
|
||||
tsOutputChannels->injectorDutyCycleStage2 = getInjectorDutyCycleStage2(rpm);
|
||||
#endif
|
||||
|
||||
// 224
|
||||
|
|
|
@ -127,7 +127,8 @@ public:
|
|||
FuelComputer fuelComputer;
|
||||
|
||||
type_list<
|
||||
Mockable<InjectorModel>,
|
||||
Mockable<InjectorModelPrimary>,
|
||||
Mockable<InjectorModelSecondary>,
|
||||
#if EFI_IDLE_CONTROL
|
||||
Mockable<IdleController>,
|
||||
#endif // EFI_IDLE_CONTROL
|
||||
|
|
|
@ -130,10 +130,19 @@ void EngineState::periodicFastCallback() {
|
|||
float untrimmedInjectionMass = getInjectionMass(rpm) * engine->engineState.lua.fuelMult + engine->engineState.lua.fuelAdd;
|
||||
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();
|
||||
|
||||
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);
|
||||
engine->lambdaMonitor.update(rpm, fuelLoad);
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ public:
|
|||
// Per-injection fuel mass, including TPS accel enrich
|
||||
float injectionMass[MAX_CYLINDER_COUNT] = {0};
|
||||
|
||||
float injectionStage2Fraction = 0;
|
||||
|
||||
Timer crankingTimer;
|
||||
|
||||
WarningCodeState warnings;
|
||||
|
@ -76,6 +78,7 @@ public:
|
|||
* @see getInjectionDuration()
|
||||
*/
|
||||
floatms_t injectionDuration = 0;
|
||||
floatms_t injectionDurationStage2 = 0;
|
||||
|
||||
angle_t injectionOffset = 0;
|
||||
|
||||
|
|
|
@ -62,3 +62,7 @@ public:
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (isCranking) {
|
||||
return getCrankingFuel(baseFuelMass);
|
||||
|
@ -312,7 +318,11 @@ float getInjectionMass(int rpm) {
|
|||
float injectionFuelMass = cycleFuelMass * durationMultiplier;
|
||||
|
||||
// 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();
|
||||
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
|
||||
float tpsAccelPerInjection = durationMultiplier * tpsAccelEnrich;
|
||||
|
||||
float tpsFuelMass = engine->module<InjectorModel>()->getFuelMassForDuration(tpsAccelPerInjection);
|
||||
float tpsFuelMass = engine->module<InjectorModelPrimary>()->getFuelMassForDuration(tpsAccelPerInjection);
|
||||
|
||||
return injectionFuelMass + tpsFuelMass;
|
||||
#else
|
||||
|
@ -441,5 +451,31 @@ float getCylinderFuelTrim(size_t cylinderNumber, int rpm, float fuelLoad) {
|
|||
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
|
||||
|
|
|
@ -28,6 +28,8 @@ float getCrankingFuel(float baseFuel);
|
|||
float getCrankingFuel3(float baseFuel, uint32_t revolutionCounterSinceStart);
|
||||
float getInjectionMass(int rpm);
|
||||
percent_t getInjectorDutyCycle(int rpm);
|
||||
percent_t getInjectorDutyCycleStage2(int rpm);
|
||||
float getStage2InjectionFraction(int rpm, float fuelLoad);
|
||||
|
||||
float getStandardAirCharge();
|
||||
float getCylinderFuelTrim(size_t cylinderNumber, int rpm, float fuelLoad);
|
||||
|
|
|
@ -8,8 +8,15 @@
|
|||
|
||||
#if EFI_ENGINE_CONTROL
|
||||
|
||||
void turnInjectionPinHigh(InjectionEvent *event) {
|
||||
void turnInjectionPinHigh(uintptr_t arg) {
|
||||
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++) {
|
||||
InjectorOutputPin *output = event->outputs[i];
|
||||
|
||||
|
@ -17,6 +24,16 @@ void turnInjectionPinHigh(InjectionEvent *event) {
|
|||
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() {
|
||||
|
@ -140,7 +157,8 @@ bool InjectionEvent::update() {
|
|||
// Single point only uses injector 1 (index 0)
|
||||
int injectorIndex = mode == IM_SINGLE_POINT ? 0 : ID2INDEX(getCylinderId(ownIndex));
|
||||
|
||||
InjectorOutputPin *secondOutput = nullptr;
|
||||
InjectorOutputPin* secondOutput = nullptr;
|
||||
InjectorOutputPin* secondOutputStage2 = nullptr;
|
||||
|
||||
if (mode == IM_BATCH) {
|
||||
/**
|
||||
|
@ -152,6 +170,7 @@ bool InjectionEvent::update() {
|
|||
int secondOrder = (ownIndex + (engineConfiguration->cylindersCount / 2)) % engineConfiguration->cylindersCount;
|
||||
int secondIndex = ID2INDEX(getCylinderId(secondOrder));
|
||||
secondOutput = &enginePins.injectors[secondIndex];
|
||||
secondOutputStage2 = &enginePins.injectorsStage2[secondIndex];
|
||||
}
|
||||
|
||||
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
|
||||
cylinderNumber = injectorIndex;
|
||||
|
||||
outputsStage2[0] = &enginePins.injectorsStage2[injectorIndex];
|
||||
outputsStage2[1] = secondOutputStage2;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -48,10 +48,11 @@ private:
|
|||
public:
|
||||
// TODO: this should be private
|
||||
InjectorOutputPin *outputs[MAX_WIRES_COUNT];
|
||||
InjectorOutputPin *outputsStage2[MAX_WIRES_COUNT];
|
||||
float injectionStartAngle = 0;
|
||||
};
|
||||
|
||||
void turnInjectionPinHigh(InjectionEvent *event);
|
||||
void turnInjectionPinHigh(uintptr_t arg);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -63,6 +63,17 @@ void turnInjectionPinLow(InjectionEvent *event) {
|
|||
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) {
|
||||
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
|
||||
// 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);
|
||||
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 (printFuelDebug) {
|
||||
printf("fuel injectionDuration=%.2fms adjusted=%.2fms\n",
|
||||
getEngineState()->injectionDuration,
|
||||
injectionDuration);
|
||||
injectionDurationStage1);
|
||||
}
|
||||
#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) {
|
||||
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");
|
||||
return;
|
||||
}
|
||||
if (injectionDuration < 0) {
|
||||
warning(ObdCode::CUSTOM_OBD_NEG_INJECTION, "Negative injection pulse %.2f", injectionDuration);
|
||||
if (injectionDurationStage1 < 0) {
|
||||
warning(ObdCode::CUSTOM_OBD_NEG_INJECTION, "Negative injection pulse %.2f", injectionDurationStage1);
|
||||
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
|
||||
// as their order may be swapped, resulting in a stuck open injector
|
||||
// see https://github.com/rusefi/rusefi/pull/596 for more details
|
||||
if (injectionDuration < 0.050f)
|
||||
if (injectionDurationStage1 < 0.050f)
|
||||
{
|
||||
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 (printFuelDebug) {
|
||||
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);
|
||||
}
|
||||
#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
|
||||
if (isSimultaneous) {
|
||||
startAction = startSimultaneousInjection;
|
||||
endAction = { &endSimultaneousInjection, this };
|
||||
endActionStage1 = { &endSimultaneousInjection, this };
|
||||
} else {
|
||||
// sequential or batch
|
||||
startAction = { &turnInjectionPinHigh, this };
|
||||
endAction = { &turnInjectionPinLow, this };
|
||||
uintptr_t startActionPtr = reinterpret_cast<uintptr_t>(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;
|
||||
if (angleFromNow < 0) {
|
||||
angleFromNow += getEngineState()->engineCycle;
|
||||
}
|
||||
|
||||
// Schedule opening (stage 1 + stage 2 open together)
|
||||
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
|
||||
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
|
||||
#if EFI_DEFAILED_LOGGING
|
||||
efiPrintf("handleFuel pin=%s eventIndex %d duration=%.2fms %d", outputs[0]->name,
|
||||
injEventIndex,
|
||||
injectionDuration,
|
||||
injectionDurationStage1,
|
||||
getRevolutionCounter());
|
||||
efiPrintf("handleFuel pin=%s delay=%.2f %d", outputs[0]->name, NT2US(startTime - nowNt),
|
||||
getRevolutionCounter());
|
||||
|
|
|
@ -15,3 +15,4 @@ void mainTriggerCallback(uint32_t trgEventIndex, efitick_t edgeTimestamp, angle_
|
|||
|
||||
void endSimultaneousInjection(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
|
||||
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
|
||||
|
|
|
@ -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",
|
||||
"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 RegisteredOutputPin * registeredOutputHead = nullptr;
|
||||
|
@ -167,6 +173,10 @@ EnginePins::EnginePins() :
|
|||
enginePins.injectors[i].injectorIndex = i;
|
||||
enginePins.injectors[i].setName(injectorNames[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");
|
||||
|
@ -196,6 +206,7 @@ bool EnginePins::stopPins() {
|
|||
for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
|
||||
result |= coils[i].stop();
|
||||
result |= injectors[i].stop();
|
||||
result |= injectorsStage2[i].stop();
|
||||
result |= trailingCoils[i].stop();
|
||||
}
|
||||
for (int i = 0; i < AUX_DIGITAL_VALVE_COUNT; i++) {
|
||||
|
@ -264,6 +275,7 @@ void EnginePins::stopIgnitionPins() {
|
|||
void EnginePins::stopInjectionPins() {
|
||||
for (int i = 0; i < MAX_CYLINDER_COUNT; i++) {
|
||||
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],
|
||||
engineConfiguration->injectionPinMode);
|
||||
}
|
||||
|
||||
output = &enginePins.injectorsStage2[i];
|
||||
if (isPinOrModeChanged(injectionPinsStage2[i], injectionPinMode)) {
|
||||
output->initPin(output->getName(), engineConfiguration->injectionPinsStage2[i],
|
||||
engineConfiguration->injectionPinMode);
|
||||
}
|
||||
}
|
||||
#endif /* EFI_PROD_CODE */
|
||||
}
|
||||
|
|
|
@ -118,6 +118,7 @@ public:
|
|||
OutputPin accelerometerCs;
|
||||
|
||||
InjectorOutputPin injectors[MAX_CYLINDER_COUNT];
|
||||
InjectorOutputPin injectorsStage2[MAX_CYLINDER_COUNT];
|
||||
IgnitionOutputPin coils[MAX_CYLINDER_COUNT];
|
||||
IgnitionOutputPin trailingCoils[MAX_CYLINDER_COUNT];
|
||||
NamedOutputPin auxValve[AUX_DIGITAL_VALVE_COUNT];
|
||||
|
|
|
@ -8,6 +8,24 @@
|
|||
|
||||
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 {
|
||||
public:
|
||||
// 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
|
||||
action_s(schfunc_t callback) : action_s(callback, nullptr) { }
|
||||
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.
|
||||
// This constructor means you shouldn't ever have to cast to schfunc_t on your own.
|
||||
template <typename TArg>
|
||||
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();
|
||||
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 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 unused88b11
|
||||
bit enableStagedInjection
|
||||
bit useTLE8888_cranking_hack;
|
||||
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).
|
||||
|
@ -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[MAX_CYLINDER_COUNT iterate] injectionPins;
|
||||
output_pin_e[MAX_CYLINDER_COUNT iterate] injectionPinsStage2;
|
||||
output_pin_e[MAX_CYLINDER_COUNT iterate] ignitionPins;
|
||||
|
||||
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] 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
|
||||
|
||||
! Pedal Position Sensor
|
||||
|
@ -1911,6 +1916,7 @@ end_struct
|
|||
|
||||
#define PROTOCOL_COIL1_SHORT_NAME "c1"
|
||||
#define PROTOCOL_INJ1_SHORT_NAME "i1"
|
||||
#define PROTOCOL_INJ1_STAGE2_SHORT_NAME "j1"
|
||||
|
||||
! some board files override this value using prepend file
|
||||
#define ts_show_vr_threshold_all true
|
||||
|
|
|
@ -139,6 +139,7 @@
|
|||
#define GAUGE_NAME_FUEL_CRANKING "Fuel: cranking"
|
||||
#define GAUGE_NAME_FUEL_RUNNING "Fuel: running"
|
||||
#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_TRIM "Fuel: fuel trim"
|
||||
#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_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_TARGET_AFR "Fuel: target AFR"
|
||||
#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
|
||||
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
|
||||
xBins = solenoidCountArray, 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
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
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 = cylinderBankSelect, "Cylinder bank selection", 0, {isInjectionEnabled == 1}
|
||||
subMenu = injectorNonlinear, "Injector small-pulse correction", 0, {isInjectionEnabled == 1}
|
||||
subMenu = stagedInjection, "Staged injection", 0, {isInjectionEnabled}
|
||||
|
||||
groupMenu = "Cylinder fuel trims"
|
||||
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 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"
|
||||
field = "Spark duration", multisparkSparkDuration, {multisparkEnable}
|
||||
field = "Subsequent spark dwell", multisparkDwell, {multisparkEnable}
|
||||
|
|
|
@ -259,7 +259,7 @@ public class EngineSnifferPanel {
|
|||
signalBody = Color.darkGray;
|
||||
} else if (name.startsWith("HIP")) {
|
||||
signalBody = Color.white;
|
||||
} else if (name.startsWith("i")) {
|
||||
} else if (name.startsWith("i") || name.startsWith("j")) {
|
||||
// injection
|
||||
signalBody = Color.green;
|
||||
} else if (name.startsWith("map")) {
|
||||
|
|
|
@ -20,6 +20,8 @@ public class NameUtil {
|
|||
return "Coil #" + name.substring(1);
|
||||
if (name.charAt(0) == Fields.PROTOCOL_INJ1_SHORT_NAME.charAt(0))
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -48,7 +48,7 @@ TEST(priming, duration) {
|
|||
EngineTestHelper eth(engine_type_e::TEST_ENGINE);
|
||||
|
||||
MockInjectorModel2 injectorModel;
|
||||
engine->module<InjectorModel>().set(&injectorModel);
|
||||
engine->module<InjectorModelPrimary>().set(&injectorModel);
|
||||
|
||||
for (size_t i = 0; i < efi::size(engineConfiguration->primeBins); i++) {
|
||||
engineConfiguration->primeBins[i] = i * 10;
|
||||
|
|
|
@ -6,6 +6,15 @@ using ::testing::_;
|
|||
using ::testing::StrictMock;
|
||||
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) {
|
||||
StrictMock<MockExecutor> mockExec;
|
||||
|
||||
|
@ -22,7 +31,7 @@ TEST(injectionScheduling, InjectionIsScheduled) {
|
|||
// Injection duration of 20ms
|
||||
MockInjectorModel2 im;
|
||||
EXPECT_CALL(im, getInjectionDuration(_)).WillOnce(Return(20.0f));
|
||||
engine->module<InjectorModel>().set(&im);
|
||||
engine->module<InjectorModelPrimary>().set(&im);
|
||||
|
||||
engine->rpmCalculator.oneDegreeUs = 100;
|
||||
|
||||
|
@ -33,11 +42,61 @@ TEST(injectionScheduling, InjectionIsScheduled) {
|
|||
// 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, _));
|
||||
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime, Not(Truly(ActionArgumentHasLowBitSet))));
|
||||
// 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.injectionStartAngle = 125;
|
||||
|
@ -62,7 +121,7 @@ TEST(injectionScheduling, InjectionIsScheduledBeforeWraparound) {
|
|||
// Injection duration of 20ms
|
||||
MockInjectorModel2 im;
|
||||
EXPECT_CALL(im, getInjectionDuration(_)).WillOnce(Return(20.0f));
|
||||
engine->module<InjectorModel>().set(&im);
|
||||
engine->module<InjectorModelPrimary>().set(&im);
|
||||
|
||||
engine->rpmCalculator.oneDegreeUs = 100;
|
||||
|
||||
|
@ -73,9 +132,9 @@ TEST(injectionScheduling, InjectionIsScheduledBeforeWraparound) {
|
|||
// 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, _));
|
||||
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime, Not(Truly(ActionArgumentHasLowBitSet))));
|
||||
// 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
|
||||
|
@ -101,7 +160,7 @@ TEST(injectionScheduling, InjectionIsScheduledAfterWraparound) {
|
|||
// Injection duration of 20ms
|
||||
MockInjectorModel2 im;
|
||||
EXPECT_CALL(im, getInjectionDuration(_)).WillOnce(Return(20.0f));
|
||||
engine->module<InjectorModel>().set(&im);
|
||||
engine->module<InjectorModelPrimary>().set(&im);
|
||||
|
||||
engine->rpmCalculator.oneDegreeUs = 100;
|
||||
|
||||
|
@ -112,9 +171,9 @@ TEST(injectionScheduling, InjectionIsScheduledAfterWraparound) {
|
|||
// rising edge 15 degrees from now
|
||||
float nt5deg = USF2NT(engine->rpmCalculator.oneDegreeUs * 15);
|
||||
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
|
||||
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
|
||||
|
@ -140,7 +199,7 @@ TEST(injectionScheduling, InjectionNotScheduled) {
|
|||
|
||||
// Expect no calls to injector model
|
||||
StrictMock<MockInjectorModel2> im;
|
||||
engine->module<InjectorModel>().set(&im);
|
||||
engine->module<InjectorModelPrimary>().set(&im);
|
||||
|
||||
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.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";
|
||||
|
||||
|
@ -560,7 +560,7 @@ void doTestFuelSchedulerBug299smallAndMedium(int startUpDelayMs) {
|
|||
// Injection duration of 12.5ms
|
||||
MockInjectorModel2 im;
|
||||
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))));
|
||||
|
||||
|
@ -720,7 +720,7 @@ void doTestFuelSchedulerBug299smallAndMedium(int startUpDelayMs) {
|
|||
// Injection duration of 17.5ms
|
||||
MockInjectorModel2 im2;
|
||||
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
|
||||
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
|
||||
MockInjectorModel2 im;
|
||||
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))));
|
||||
|
||||
|
@ -979,7 +979,7 @@ TEST(big, testFuelSchedulerBug299smallAndLarge) {
|
|||
engine->engineState.injectionDuration = 2.0f;
|
||||
MockInjectorModel2 im2;
|
||||
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";
|
||||
|
||||
|
|
Loading…
Reference in New Issue