17 #if EFI_ENGINE_CONTROL
23 #if EFI_PRINTF_FUEL_DETAILS || FUEL_MATH_EXTREME_LOGGING
30 #if SPARK_EXTREME_LOGGING
31 efiPrintf(
"spark goes low revolution=%d [%s] %d current=%d cnt=%d id=%d", getRevolutionCounter(), output->
getName(), (
int)
getTimeNowUs(),
45 #if SPARK_EXTREME_LOGGING
65 switch (ignitionMode) {
68 case IM_WASTED_SPARK: {
75 case IM_INDIVIDUAL_COILS:
78 return cylinderIndex % 2;
98 event->coilIndex = coilIndex;
101 if (finalIgnitionTiming > 360) {
102 finalIgnitionTiming -= 720;
118 - finalIgnitionTiming
124 event->sparkAngle = sparkAngle;
129 event->outputs[0] = output;
136 if (ignitionMode == IM_WASTED_SPARK && isTwoWireWasted) {
142 secondOutput =
nullptr;
147 event->outputs[1] = secondOutput;
150 angle_t dwellStartAngle = sparkAngle - dwellAngleDuration;
155 event->dwellAngle = dwellStartAngle;
157 #if FUEL_MATH_EXTREME_LOGGING
159 printf(
"addIgnitionEvent %s angle=%.1f\n", output->
getName(), dwellStartAngle);
166 #if SPARK_EXTREME_LOGGING
167 efiPrintf(
"chargeTrailingSpark %s", pin->
getName());
173 #if SPARK_EXTREME_LOGGING
174 efiPrintf(
"fireTrailingSpark %s", pin->
getName());
180 #if SPARK_EXTREME_LOGGING
181 efiPrintf(
"overFireSparkAndPrepareNextSchedule %s", event->
outputs[0]->
getName());
194 for (
int i = 0; i< MAX_OUTPUTS_FOR_IGNITION;i++) {
211 uint32_t actualDwellDurationNt =
getTimeNowLowerNt() -
event->actualStartOfDwellNt;
215 float ratio = NT2US(actualDwellDurationNt) / 1000.0 /
event->sparkDwell;
240 if (cisnan(dwellAngleDuration) || cisnan(
sparkDwell)) {
247 event->sparksRemaining--;
251 #if SPARK_EXTREME_LOGGING
252 efiPrintf(
"schedule multispark");
260 #if SPARK_EXTREME_LOGGING
261 efiPrintf(
"scheduleByAngle TrailingSparks");
267 { &fireTrailingSpark, &enginePins.trailingCoils[event->coilIndex] }
283 const char *outputName = output->
getName();
292 #if SPARK_EXTREME_LOGGING
293 efiPrintf(
"spark goes high revolution=%d [%s] %d current=%d cnt=%d id=%d", getRevolutionCounter(), output->
getName(), (
int)
getTimeNowUs(),
307 #if SPARK_EXTREME_LOGGING
308 efiPrintf(
"[%s] bail spark dwell\n", output->
getName());
333 bool skippedDwellDueToTriggerNoised =
false;
334 for (
int i = 0; i< MAX_OUTPUTS_FOR_IGNITION;i++) {
336 if (output != NULL) {
342 event->bailedOnDwell = skippedDwellDueToTriggerNoised;
346 if (!skippedDwellDueToTriggerNoised) {
365 { &chargeTrailingSpark, output }
371 #define ENABLE_OVERDWELL_PROTECTION (true)
373 #define ENABLE_OVERDWELL_PROTECTION (engine->enableOverdwellProtection)
377 int rpm, efitick_t edgeTimestamp,
float currentPhase,
float nextPhase) {
379 angle_t sparkAngle =
event->sparkAngle;
381 if (cisnan(dwellMs) || dwellMs <= 0) {
385 if (cisnan(sparkAngle)) {
390 float angleOffset =
event->dwellAngle - currentPhase;
391 if (angleOffset < 0) {
399 event->wasSparkLimited = limitedSpark;
401 efitick_t chargeTime = 0;
407 #if SPARK_EXTREME_LOGGING
423 event->sparksRemaining = 0;
435 &event->
sparkEvent, edgeTimestamp, sparkAngle,
437 currentPhase, nextPhase);
439 if (isTimeScheduled) {
441 #if SPARK_EXTREME_LOGGING
442 efiPrintf(
"scheduling sparkDown revolution=%d [%s] now=%d later id=%d", getRevolutionCounter(), event->getOutputForLoggins()->getName(), (
int)
getTimeNowUs(), event->sparkCounter);
446 #if SPARK_EXTREME_LOGGING
447 efiPrintf(
"to queue sparkDown revolution=%d [%s] now=%d for id=%d angle=%.1f", getRevolutionCounter(), event->getOutputForLoggins()->getName(), (
int)
getTimeNowUs(), event->sparkCounter, sparkAngle);
450 if (!limitedSpark && ENABLE_OVERDWELL_PROTECTION) {
452 efitick_t fireTime = chargeTime + MSF2NT(1.5f * dwellMs);
454 #if SPARK_EXTREME_LOGGING
455 efiPrintf(
"scheduling overdwell sparkDown revolution=%d [%s] for %d", getRevolutionCounter(), event->getOutputForLoggins()->getName(), fireTime);
471 printf(
"spark dwell@ %.1f spark@ %.2f id=%d sparkCounter=%d\r\n", event->dwellAngle,
472 event->sparkEvent.getAngle(),
474 event->sparkCounter);
509 float maxAllowedDwellAngle = (int) (
getEngineCycle(operationMode) / 2);
539 bool limitedSpark = !limitedSparkState.
value;
557 if (!
isPhaseInRange(event->dwellAngle, currentPhase, nextPhase)) {
568 #if EFI_LAUNCH_CONTROL
576 #if EFI_ANTILAG_SYSTEM && EFI_LAUNCH_CONTROL
608 case IM_INDIVIDUAL_COILS:
610 case IM_WASTED_SPARK:
624 return 100 * totalPerCycle / engineCycleDuration;
SoftSparkLimiter softSparkLimiter
SoftSparkLimiter ALSsoftSparkLimiter
IgnitionEventList ignitionEvents
IgnitionState ignitionState
constexpr auto & module()
SingleTimerExecutor executor
AntilagSystemBase antilagController
std::function< void(IgnitionEvent *, bool)> onIgnitionEvent
void onSparkFireKnockSense(uint8_t cylinderIndex, efitick_t nowNt)
TunerStudioOutputChannels outputChannels
SoftSparkLimiter hardSparkLimiter
IgnitionOutputPin trailingCoils[MAX_CYLINDER_COUNT]
IgnitionOutputPin coils[MAX_CYLINDER_COUNT]
virtual operation_mode_e getOperationMode() const =0
angle_t trailingSparkAngle
multispark_state multispark
angle_t timingAdvance[MAX_CYLINDER_COUNT]
scheduling_s trailingSparkFire
scheduling_s dwellStartTimer
IgnitionOutputPin * getOutputForLoggins()
AngleBasedEvent sparkEvent
IgnitionOutputPin * outputs[MAX_OUTPUTS_FOR_IGNITION]
scheduling_s trailingSparkCharge
IgnitionEvent elements[MAX_CYLINDER_COUNT]
LimpState allowIgnition() const
const char * getName() const
bool isInitialized() const
static float getOrZero(SensorType type)
void scheduleByTimestampNt(const char *msg, scheduling_s *scheduling, efitick_t timeNt, action_s action) override
void setTargetSkipRatio(float targetSkipRatio)
bool isPhaseInRange(float test, float current, float next)
efitimeus_t getTimeNowUs()
EngineRotationState * getEngineRotationState()
LimpManager * getLimpManager()
EngineState * getEngineState()
angle_t getPerCylinderFiringOrderOffset(uint8_t cylinderIndex, uint8_t cylinderNumber)
size_t getFiringOrderCylinderId(size_t index)
ignition_mode_e getCurrentIgnitionMode()
floatms_t getCrankshaftRevolutionTimeMs(int rpm)
bool warning(ObdCode code, const char *fmt,...)
void firmwareError(ObdCode code, const char *fmt,...)
uint32_t getTimeNowLowerNt()
@ CUSTOM_ERR_IGNITION_MODE
@ CUSTOM_OBD_COIL_PIN_NOT_ASSIGNED
@ CUSTOM_OBD_IGNITION_MODE
@ CUSTOM_ARTIFICIAL_MISFIRE
@ CUSTOM_OBD_SKIPPED_SPARK
@ CUSTOM_OUT_OF_ORDER_COIL
@ OnTriggerEventSparkLogic
@ PrepareIgnitionSchedule
persistent_config_s * config
engine_configuration_s * engineConfiguration
efitick_t scheduleByAngle(scheduling_s *timer, efitick_t nowNt, angle_t angle, action_s action)
sparkDwell("Ignition: coil charge time", SensorCategory.SENSOR_INPUTS, FieldType.INT, 880, 1.0, 0.0, 30.0, "ms")
void onTriggerEventSparkLogic(int rpm, efitick_t edgeTimestamp, float currentPhase, float nextPhase)
static void prepareIgnitionSchedule()
percent_t getCoilDutyCycle(int rpm)
void initializeIgnitionActions()
void turnSparkPinHighStartCharging(IgnitionEvent *event)
static void fireTrailingSpark(IgnitionOutputPin *pin)
static bool startDwellByTurningSparkPinHigh(IgnitionEvent *event, IgnitionOutputPin *output)
static void assertPinAssigned(IgnitionOutputPin *output)
void fireSparkAndPrepareNextSchedule(IgnitionEvent *event)
static void prepareCylinderIgnitionSchedule(angle_t dwellAngleDuration, floatms_t sparkDwell, IgnitionEvent *event)
static int getIgnitionPinForIndex(int cylinderIndex, ignition_mode_e ignitionMode)
static void scheduleSparkEvent(bool limitedSpark, IgnitionEvent *event, int rpm, efitick_t edgeTimestamp, float currentPhase, float nextPhase)
static const char * prevSparkName
static void overFireSparkAndPrepareNextSchedule(IgnitionEvent *event)
int getNumberOfSparks(ignition_mode_e mode)
static void chargeTrailingSpark(IgnitionOutputPin *pin)
static void fireSparkBySettingPinLow(IgnitionEvent *event, IgnitionOutputPin *output)
scaled_channel< int16_t, 100, 1 > timingALSSkip
bool enableTrailingSparks
bool twoWireBatchIgnition
ignition_mode_e ignitionMode
cranking_parameters_s cranking
script_setting_t scriptSetting[SCRIPT_SETTING_COUNT]
bool artificialTestMisfire
uint32_t globalSparkCounter
uint8_t sparkOutOfOrderCounter
uint8_t overDwellNotScheduledCounter
angle_t dwellDurationAngle
scaled_channel< int16_t, 50, 1 > ignitionAdvanceCyl[MAX_CYLINDER_COUNT]
uint8_t currentIgnitionMode
scaled_channel< int16_t, 1, 10 > ALSIgnSkipTable[4][4]
uint16_t alsIgnSkipLoadBins[4]
uint16_t alsIgnSkiprpmBins[4]
void LogTriggerCoilState(efitick_t timestamp, bool state)
void wrapAngle(angle_t &angle, const char *msg, ObdCode code)
angle_t getEngineCycle(operation_mode_e operationMode)