Merge pull request #1634 from mck1117/refactor-inj

move injector logic in to its own file
This commit is contained in:
rusefillc 2020-07-24 22:47:39 -04:00 committed by GitHub
commit 34df0862b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 203 additions and 185 deletions

View File

@ -25,8 +25,6 @@
#include "engine_math.h"
InjectionEvent::InjectionEvent() {
isSimultanious = false;
ownIndex = 0;
memset(outputs, 0, sizeof(outputs));
}

View File

@ -22,13 +22,17 @@ class Engine;
class InjectionEvent {
public:
InjectionEvent();
// Call this every decoded trigger tooth. It will schedule any relevant events for this injector.
void onTriggerTooth(size_t toothIndex, int rpm, efitick_t nowNt);
/**
* This is a performance optimization for IM_SIMULTANEOUS fuel strategy.
* It's more efficient to handle all injectors together if that's the case
*/
bool isSimultanious;
bool isSimultanious = false;
InjectorOutputPin *outputs[MAX_WIRES_COUNT];
int ownIndex;
int ownIndex = 0;
DECLARE_ENGINE_PTR;
event_trigger_position_s injectionStart;
@ -47,13 +51,16 @@ public:
WallFuel wallFuel;
};
/**
* This class knows about when to inject fuel
*/
class FuelSchedule {
public:
FuelSchedule();
// Call this every trigger tooth. It will schedule all required injector events.
void onTriggerTooth(size_t toothIndex, int rpm, efitick_t nowNt DECLARE_ENGINE_PARAMETER_SUFFIX);
/**
* this method schedules all fuel events for an engine cycle
*/

View File

@ -34,6 +34,7 @@ CONTROLLERS_SRC_CPP = \
$(CONTROLLERS_DIR)/engine_cycle/spark_logic.cpp \
$(CONTROLLERS_DIR)/engine_cycle/main_trigger_callback.cpp \
$(CONTROLLERS_DIR)/engine_cycle/aux_valves.cpp \
$(CONTROLLERS_DIR)/engine_cycle/fuel_schedule.cpp \
$(CONTROLLERS_DIR)/flash_main.cpp \
$(CONTROLLERS_DIR)/bench_test.cpp \
$(CONTROLLERS_DIR)/can/obd2.cpp \

View File

@ -0,0 +1,163 @@
/**
* @file fuel_schedule.cpp
*
* Handles injection scheduling
*/
#include "global.h"
#include "engine.h"
#include "engine_math.h"
#include "event_registry.h"
EXTERN_ENGINE;
#if EFI_ENGINE_CONTROL
FuelSchedule::FuelSchedule() {
clear();
for (int cylinderIndex = 0; cylinderIndex < MAX_INJECTION_OUTPUT_COUNT; cylinderIndex++) {
InjectionEvent *ev = &elements[cylinderIndex];
ev->ownIndex = cylinderIndex;
}
}
void FuelSchedule::clear() {
isReady = false;
}
void FuelSchedule::resetOverlapping() {
for (size_t i = 0; i < efi::size(enginePins.injectors); i++) {
enginePins.injectors[i].reset();
}
}
/**
* @returns false in case of error, true if success
*/
bool FuelSchedule::addFuelEventsForCylinder(int i DECLARE_ENGINE_PARAMETER_SUFFIX) {
efiAssert(CUSTOM_ERR_ASSERT, engine!=NULL, "engine is NULL", false);
floatus_t oneDegreeUs = ENGINE(rpmCalculator.oneDegreeUs); // local copy
if (cisnan(oneDegreeUs)) {
// in order to have fuel schedule we need to have current RPM
// wonder if this line slows engine startup?
return false;
}
/**
* injection phase is scheduled by injection end, so we need to step the angle back
* for the duration of the injection
*
* todo: since this method is not invoked within trigger event handler and
* engineState.injectionOffset is calculated from the same utility timer should we more that logic here?
*/
floatms_t fuelMs = ENGINE(injectionDuration);
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(fuelMs), "NaN fuelMs", false);
angle_t injectionDuration = MS2US(fuelMs) / oneDegreeUs;
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(injectionDuration), "NaN injectionDuration", false);
assertAngleRange(injectionDuration, "injectionDuration_r", CUSTOM_INJ_DURATION);
floatus_t injectionOffset = ENGINE(engineState.injectionOffset);
if (cisnan(injectionOffset)) {
// injection offset map not ready - we are not ready to schedule fuel events
return false;
}
angle_t baseAngle = injectionOffset - injectionDuration;
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(baseAngle), "NaN baseAngle", false);
assertAngleRange(baseAngle, "baseAngle_r", CUSTOM_ERR_6554);
injection_mode_e mode = engine->getCurrentInjectionMode(PASS_ENGINE_PARAMETER_SIGNATURE);
int injectorIndex;
if (mode == IM_SIMULTANEOUS || mode == IM_SINGLE_POINT) {
// These modes only have one injector
injectorIndex = 0;
} else if (mode == IM_SEQUENTIAL || (mode == IM_BATCH && CONFIG(twoWireBatchInjection))) {
// Map order index -> cylinder index (firing order)
injectorIndex = getCylinderId(i PASS_ENGINE_PARAMETER_SUFFIX) - 1;
} else if (mode == IM_BATCH) {
// Loop over the first half of the firing order twice
injectorIndex = i % (engineConfiguration->specs.cylindersCount / 2);
} else {
firmwareError(CUSTOM_OBD_UNEXPECTED_INJECTION_MODE, "Unexpected injection mode %d", mode);
injectorIndex = 0;
}
assertAngleRange(baseAngle, "addFbaseAngle", CUSTOM_ADD_BASE);
int cylindersCount = CONFIG(specs.cylindersCount);
if (cylindersCount < 1) {
// May 2020 this somehow still happens with functional tests, maybe race condition?
warning(CUSTOM_OBD_ZERO_CYLINDER_COUNT, "Invalid cylinder count: %d", cylindersCount);
return false;
}
float angle = baseAngle
+ i * ENGINE(engineCycle) / cylindersCount;
InjectorOutputPin *secondOutput;
if (mode == IM_BATCH && CONFIG(twoWireBatchInjection)) {
/**
* also fire the 2nd half of the injectors so that we can implement a batch mode on individual wires
*/
// Compute the position of this cylinder's twin in the firing order
// Each injector gets fired as a primary (the same as sequential), but also
// fires the injector 360 degrees later in the firing order.
int secondOrder = (i + (CONFIG(specs.cylindersCount) / 2)) % CONFIG(specs.cylindersCount);
int secondIndex = getCylinderId(secondOrder PASS_ENGINE_PARAMETER_SUFFIX) - 1;
secondOutput = &enginePins.injectors[secondIndex];
} else {
secondOutput = nullptr;
}
InjectorOutputPin *output = &enginePins.injectors[injectorIndex];
bool isSimultanious = mode == IM_SIMULTANEOUS;
if (!isSimultanious && !output->isInitialized()) {
// todo: extract method for this index math
warning(CUSTOM_OBD_INJECTION_NO_PIN_ASSIGNED, "no_pin_inj #%s", output->name);
}
InjectionEvent *ev = &elements[i];
ev->ownIndex = i;
INJECT_ENGINE_REFERENCE(ev);
fixAngle(angle, "addFuel#1", CUSTOM_ERR_6554);
ev->outputs[0] = output;
ev->outputs[1] = secondOutput;
ev->isSimultanious = isSimultanious;
if (TRIGGER_WAVEFORM(getSize()) < 1) {
warning(CUSTOM_ERR_NOT_INITIALIZED_TRIGGER, "uninitialized TriggerWaveform");
return false;
}
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(angle), "findAngle#3", false);
assertAngleRange(angle, "findAngle#a33", CUSTOM_ERR_6544);
ev->injectionStart.setAngle(angle PASS_ENGINE_PARAMETER_SUFFIX);
#if EFI_UNIT_TEST
printf("registerInjectionEvent angle=%.2f trgIndex=%d inj %d\r\n", angle, ev->injectionStart.triggerEventIndex, injectorIndex);
#endif
return true;
}
void FuelSchedule::addFuelEvents(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
clear();
for (int cylinderIndex = 0; cylinderIndex < CONFIG(specs.cylindersCount); cylinderIndex++) {
InjectionEvent *ev = &elements[cylinderIndex];
ev->ownIndex = cylinderIndex; // todo: is this assignment needed here? we now initialize in constructor
bool result = addFuelEventsForCylinder(cylinderIndex PASS_ENGINE_PARAMETER_SUFFIX);
if (!result)
return;
}
isReady = true;
}
void FuelSchedule::onTriggerTooth(size_t toothIndex, int rpm, efitick_t nowNt DECLARE_ENGINE_PARAMETER_SUFFIX) {
for (int i = 0; i < CONFIG(specs.cylindersCount); i++) {
elements[i].onTriggerTooth(toothIndex, rpm, nowNt);
}
}
#endif

View File

@ -187,9 +187,13 @@ void turnInjectionPinLow(InjectionEvent *event) {
ENGINE(injectionEvents.addFuelEventsForCylinder(event->ownIndex PASS_ENGINE_PARAMETER_SUFFIX));
}
// todo: rename to 'scheduleInjectorOpenAndClose'?
void handleFuelInjectionEvent(int injEventIndex, InjectionEvent *event,
int rpm, efitick_t nowNt DECLARE_ENGINE_PARAMETER_SUFFIX) {
void InjectionEvent::onTriggerTooth(size_t trgEventIndex, int rpm, efitick_t nowNt) {
uint32_t eventIndex = injectionStart.triggerEventIndex;
// right after trigger change we are still using old & invalid fuel schedule. good news is we do not change trigger on the fly in real life
// efiAssertVoid(CUSTOM_ERR_ASSERT_VOID, eventIndex < ENGINE(triggerShape.getLength()), "handleFuel/event sch index");
if (eventIndex != trgEventIndex) {
return;
}
/**
* todo: this is a bit tricky with batched injection. is it? Does the same
@ -197,11 +201,11 @@ void handleFuelInjectionEvent(int injEventIndex, InjectionEvent *event,
* x2 or /2?
*/
const floatms_t injectionDuration = event->wallFuel.adjust(ENGINE(injectionDuration) PASS_ENGINE_PARAMETER_SUFFIX);
const floatms_t injectionDuration = wallFuel.adjust(ENGINE(injectionDuration) PASS_ENGINE_PARAMETER_SUFFIX);
#if EFI_PRINTF_FUEL_DETAILS
if (printFuelDebug) {
printf("fuel index=%d injectionDuration=%.2fms adjusted=%.2fms\n",
injEventIndex,
eventIndex,
ENGINE(injectionDuration),
injectionDuration);
}
@ -245,7 +249,7 @@ void handleFuelInjectionEvent(int injEventIndex, InjectionEvent *event,
// we are ignoring low RPM in order not to handle "engine was stopped to engine now running" transition
if (rpm > 2 * engineConfiguration->cranking.rpm) {
const char *outputName = event->outputs[0]->name;
const char *outputName = outputs[0]->name;
if (prevOutputName == outputName
&& engineConfiguration->injectionMode != IM_SIMULTANEOUS
&& engineConfiguration->injectionMode != IM_SINGLE_POINT) {
@ -256,48 +260,48 @@ void handleFuelInjectionEvent(int injEventIndex, InjectionEvent *event,
#if EFI_PRINTF_FUEL_DETAILS
if (printFuelDebug) {
InjectorOutputPin *output = event->outputs[0];
InjectorOutputPin *output = outputs[0];
printf("handleFuelInjectionEvent fuelout %s injection_duration %dus engineCycleDuration=%.1fms\t\n", output->name, (int)durationUs,
(int)MS2US(getCrankshaftRevolutionTimeMs(GET_RPM_VALUE)) / 1000.0);
}
#endif /*EFI_PRINTF_FUEL_DETAILS */
if (event->isScheduled) {
if (isScheduled) {
#if EFI_PRINTF_FUEL_DETAILS
if (printFuelDebug) {
InjectorOutputPin *output = event->outputs[0];
InjectorOutputPin *output = outputs[0];
printf("handleFuelInjectionEvent still used %s now=%.1fms\r\n", output->name, (int)getTimeNowUs() / 1000.0);
}
#endif /*EFI_PRINTF_FUEL_DETAILS */
return; // this InjectionEvent is still needed for an extremely long injection scheduled previously
}
event->isScheduled = true;
isScheduled = true;
action_s startAction, endAction;
// We use different callbacks based on whether we're running sequential mode or not - everything else is the same
if (event->isSimultanious) {
if (isSimultanious) {
startAction = { &startSimultaniousInjection, engine };
endAction = { &endSimultaniousInjection, event };
endAction = { &endSimultaniousInjection, this };
} else {
// sequential or batch
startAction = { &turnInjectionPinHigh, event };
endAction = { &turnInjectionPinLow, event };
startAction = { &turnInjectionPinHigh, this };
endAction = { &turnInjectionPinLow, this };
}
efitick_t startTime = scheduleByAngle(&event->signalTimerUp, nowNt, event->injectionStart.angleOffsetFromTriggerEvent, startAction PASS_ENGINE_PARAMETER_SUFFIX);
efitick_t startTime = scheduleByAngle(&signalTimerUp, nowNt, injectionStart.angleOffsetFromTriggerEvent, startAction PASS_ENGINE_PARAMETER_SUFFIX);
efitick_t turnOffTime = startTime + US2NT((int)durationUs);
engine->executor.scheduleByTimestampNt(&event->endOfInjectionEvent, turnOffTime, endAction);
engine->executor.scheduleByTimestampNt(&endOfInjectionEvent, turnOffTime, endAction);
#if EFI_UNIT_TEST
printf("scheduling injection angle=%.2f/delay=%.2f injectionDuration=%.2f\r\n", event->injectionStart.angleOffsetFromTriggerEvent, NT2US(startTime - nowNt), injectionDuration);
printf("scheduling injection angle=%.2f/delay=%.2f injectionDuration=%.2f\r\n", injectionStart.angleOffsetFromTriggerEvent, NT2US(startTime - nowNt), injectionDuration);
#endif
#if EFI_DEFAILED_LOGGING
scheduleMsg(logger, "handleFuel pin=%s eventIndex %d duration=%.2fms %d", event->outputs[0]->name,
scheduleMsg(logger, "handleFuel pin=%s eventIndex %d duration=%.2fms %d", outputs[0]->name,
injEventIndex,
injectionDuration,
getRevolutionCounter());
scheduleMsg(logger, "handleFuel pin=%s delay=%.2f %d", event->outputs[0]->name, NT2US(startTime - nowNt),
scheduleMsg(logger, "handleFuel pin=%s delay=%.2f %d", outputs[0]->name, NT2US(startTime - nowNt),
getRevolutionCounter());
#endif /* EFI_DEFAILED_LOGGING */
}
@ -322,7 +326,7 @@ static ALWAYS_INLINE void handleFuel(const bool limitedFuel, uint32_t trgEventIn
}
/**
* Ignition events are defined by addFuelEvents() according to selected
* Injection events are defined by addFuelEvents() according to selected
* fueling strategy
*/
FuelSchedule *fs = &ENGINE(injectionEvents);
@ -342,16 +346,7 @@ static ALWAYS_INLINE void handleFuel(const bool limitedFuel, uint32_t trgEventIn
ENGINE(engineLoadAccelEnrichment.onEngineCycle(PASS_ENGINE_PARAMETER_SIGNATURE));
}
for (int injEventIndex = 0; injEventIndex < CONFIG(specs.cylindersCount); injEventIndex++) {
InjectionEvent *event = &fs->elements[injEventIndex];
uint32_t eventIndex = event->injectionStart.triggerEventIndex;
// right after trigger change we are still using old & invalid fuel schedule. good news is we do not change trigger on the fly in real life
// efiAssertVoid(CUSTOM_ERR_ASSERT_VOID, eventIndex < ENGINE(triggerShape.getLength()), "handleFuel/event sch index");
if (eventIndex != trgEventIndex) {
continue;
}
handleFuelInjectionEvent(injEventIndex, event, rpm, nowNt PASS_ENGINE_PARAMETER_SUFFIX);
}
fs->onTriggerTooth(trgEventIndex, rpm, nowNt PASS_ENGINE_PARAMETER_SUFFIX);
}
#if EFI_PROD_CODE
@ -473,6 +468,8 @@ static bool isPrimeInjectionPulseSkipped(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
* See testStartOfCrankingPrimingPulse()
*/
void startPrimeInjectionPulse(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
INJECT_ENGINE_REFERENCE(&primeInjEvent);
// First, we need a protection against 'fake' ignition switch on and off (i.e. no engine started), to avoid repeated prime pulses.
// So we check and update the ignition switch counter in non-volatile backup-RAM
#if EFI_PROD_CODE
@ -490,10 +487,6 @@ void startPrimeInjectionPulse(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
ignSwitchCounter = -1;
// start prime injection if this is a 'fresh start'
if (ignSwitchCounter == 0) {
// fill-in the prime event struct
#if EFI_UNIT_TEST
primeInjEvent.engine = engine;
#endif /* EFI_UNIT_TEST */
primeInjEvent.ownIndex = 0;
primeInjEvent.isSimultanious = true;

View File

@ -103,151 +103,6 @@ void setSingleCoilDwell(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
engineConfiguration->sparkDwellValues[7] = 0;
}
#if EFI_ENGINE_CONTROL
FuelSchedule::FuelSchedule() {
clear();
for (int cylinderIndex = 0; cylinderIndex < MAX_INJECTION_OUTPUT_COUNT; cylinderIndex++) {
InjectionEvent *ev = &elements[cylinderIndex];
ev->ownIndex = cylinderIndex;
}
}
void FuelSchedule::clear() {
isReady = false;
}
void FuelSchedule::resetOverlapping() {
for (size_t i = 0; i < efi::size(enginePins.injectors); i++) {
enginePins.injectors[i].reset();
}
}
/**
* @returns false in case of error, true if success
*/
bool FuelSchedule::addFuelEventsForCylinder(int i DECLARE_ENGINE_PARAMETER_SUFFIX) {
efiAssert(CUSTOM_ERR_ASSERT, engine!=NULL, "engine is NULL", false);
floatus_t oneDegreeUs = ENGINE(rpmCalculator.oneDegreeUs); // local copy
if (cisnan(oneDegreeUs)) {
// in order to have fuel schedule we need to have current RPM
// wonder if this line slows engine startup?
return false;
}
/**
* injection phase is scheduled by injection end, so we need to step the angle back
* for the duration of the injection
*
* todo: since this method is not invoked within trigger event handler and
* engineState.injectionOffset is calculated from the same utility timer should we more that logic here?
*/
floatms_t fuelMs = ENGINE(injectionDuration);
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(fuelMs), "NaN fuelMs", false);
angle_t injectionDuration = MS2US(fuelMs) / oneDegreeUs;
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(injectionDuration), "NaN injectionDuration", false);
assertAngleRange(injectionDuration, "injectionDuration_r", CUSTOM_INJ_DURATION);
floatus_t injectionOffset = ENGINE(engineState.injectionOffset);
if (cisnan(injectionOffset)) {
// injection offset map not ready - we are not ready to schedule fuel events
return false;
}
angle_t baseAngle = injectionOffset - injectionDuration;
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(baseAngle), "NaN baseAngle", false);
assertAngleRange(baseAngle, "baseAngle_r", CUSTOM_ERR_6554);
injection_mode_e mode = engine->getCurrentInjectionMode(PASS_ENGINE_PARAMETER_SIGNATURE);
int injectorIndex;
if (mode == IM_SIMULTANEOUS || mode == IM_SINGLE_POINT) {
// These modes only have one injector
injectorIndex = 0;
} else if (mode == IM_SEQUENTIAL || (mode == IM_BATCH && CONFIG(twoWireBatchInjection))) {
// Map order index -> cylinder index (firing order)
injectorIndex = getCylinderId(i PASS_ENGINE_PARAMETER_SUFFIX) - 1;
} else if (mode == IM_BATCH) {
// Loop over the first half of the firing order twice
injectorIndex = i % (engineConfiguration->specs.cylindersCount / 2);
} else {
firmwareError(CUSTOM_OBD_UNEXPECTED_INJECTION_MODE, "Unexpected injection mode %d", mode);
injectorIndex = 0;
}
assertAngleRange(baseAngle, "addFbaseAngle", CUSTOM_ADD_BASE);
int cylindersCount = CONFIG(specs.cylindersCount);
if (cylindersCount < 1) {
// May 2020 this somehow still happens with functional tests, maybe race condition?
warning(CUSTOM_OBD_ZERO_CYLINDER_COUNT, "Invalid cylinder count: %d", cylindersCount);
return false;
}
float angle = baseAngle
+ i * ENGINE(engineCycle) / cylindersCount;
InjectorOutputPin *secondOutput;
if (mode == IM_BATCH && CONFIG(twoWireBatchInjection)) {
/**
* also fire the 2nd half of the injectors so that we can implement a batch mode on individual wires
*/
// Compute the position of this cylinder's twin in the firing order
// Each injector gets fired as a primary (the same as sequential), but also
// fires the injector 360 degrees later in the firing order.
int secondOrder = (i + (CONFIG(specs.cylindersCount) / 2)) % CONFIG(specs.cylindersCount);
int secondIndex = getCylinderId(secondOrder PASS_ENGINE_PARAMETER_SUFFIX) - 1;
secondOutput = &enginePins.injectors[secondIndex];
} else {
secondOutput = nullptr;
}
InjectorOutputPin *output = &enginePins.injectors[injectorIndex];
bool isSimultanious = mode == IM_SIMULTANEOUS;
if (!isSimultanious && !output->isInitialized()) {
// todo: extract method for this index math
warning(CUSTOM_OBD_INJECTION_NO_PIN_ASSIGNED, "no_pin_inj #%s", output->name);
}
InjectionEvent *ev = &elements[i];
ev->ownIndex = i;
INJECT_ENGINE_REFERENCE(ev);
fixAngle(angle, "addFuel#1", CUSTOM_ERR_6554);
ev->outputs[0] = output;
ev->outputs[1] = secondOutput;
ev->isSimultanious = isSimultanious;
if (TRIGGER_WAVEFORM(getSize()) < 1) {
warning(CUSTOM_ERR_NOT_INITIALIZED_TRIGGER, "uninitialized TriggerWaveform");
return false;
}
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(angle), "findAngle#3", false);
assertAngleRange(angle, "findAngle#a33", CUSTOM_ERR_6544);
ev->injectionStart.setAngle(angle PASS_ENGINE_PARAMETER_SUFFIX);
#if EFI_UNIT_TEST
printf("registerInjectionEvent angle=%.2f trgIndex=%d inj %d\r\n", angle, ev->injectionStart.triggerEventIndex, injectorIndex);
#endif
return true;
}
void FuelSchedule::addFuelEvents(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
clear();
for (int cylinderIndex = 0; cylinderIndex < CONFIG(specs.cylindersCount); cylinderIndex++) {
InjectionEvent *ev = &elements[cylinderIndex];
ev->ownIndex = cylinderIndex; // todo: is this assignment needed here? we now initialize in constructor
bool result = addFuelEventsForCylinder(cylinderIndex PASS_ENGINE_PARAMETER_SUFFIX);
if (!result)
return;
}
isReady = true;
}
#endif
static floatms_t getCrankingSparkDwell(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
if (engineConfiguration->useConstantDwellDuringCranking) {
return engineConfiguration->ignitionDwellForCrankingMs;

View File

@ -17,6 +17,7 @@ TEST(injectionScheduling, NormalDutyCycle) {
efitick_t nowNt = 1000000;
InjectionEvent event;
INJECT_ENGINE_REFERENCE(&event);
InjectorOutputPin pin;
pin.injectorIndex = 0;
event.outputs[0] = &pin;
@ -36,5 +37,5 @@ TEST(injectionScheduling, NormalDutyCycle) {
engine->rpmCalculator.oneDegreeUs = 100;
handleFuelInjectionEvent(0, &event, 1000, nowNt PASS_ENGINE_PARAMETER_SUFFIX);
event.onTriggerTooth(0, 1000, nowNt);
}