23#if EFI_PRINTF_FUEL_DETAILS || FUEL_MATH_EXTREME_LOGGING
30#if SPARK_EXTREME_LOGGING
31 efiPrintf(
"[%s] %d spark goes low revolution %d tick %d current value %d",
46#if SPARK_EXTREME_LOGGING
47 efiPrintf(
"out-of-order coil off %s", output->
getName());
64 switch (ignitionMode) {
67 case IM_WASTED_SPARK: {
74 case IM_INDIVIDUAL_COILS:
77 return cylinderIndex % 2;
97 ignitionMode = IM_INDIVIDUAL_COILS;
105 event->coilIndex = coilIndex;
108 if (finalIgnitionTiming > 360) {
109 finalIgnitionTiming -= 720;
125 - finalIgnitionTiming
131 event->sparkAngle = sparkAngle;
136 event->outputs[0] = output;
143 if (ignitionMode == IM_WASTED_SPARK && isTwoWireWasted) {
149 secondOutput =
nullptr;
154 event->outputs[1] = secondOutput;
157 angle_t dwellStartAngle = sparkAngle - dwellAngleDuration;
162 event->dwellAngle = dwellStartAngle;
164#if FUEL_MATH_EXTREME_LOGGING
166 efiPrintf(
"addIgnitionEvent %s angle=%.1f", output->
getName(), dwellStartAngle);
173#if SPARK_EXTREME_LOGGING
174 efiPrintf(
"chargeTrailingSpark %s",
pin->getName());
180#if SPARK_EXTREME_LOGGING
181 efiPrintf(
"fireTrailingSpark %s",
pin->getName());
187#if SPARK_EXTREME_LOGGING
188 efiPrintf(
"[%s] %d %s",
192 float actualDwellMs =
event->actualDwellTimer.getElapsedSeconds() * 1e3;
195 "cylinder %d %s overcharge %f ms",
202 event->wasSparkCanceled =
true;
216 for (
int i = 0; i< MAX_OUTPUTS_FOR_IGNITION;i++) {
233 float actualDwellMs =
event->actualDwellTimer.getElapsedSeconds(nowNt) * 1e3;
234 float ratio = actualDwellMs /
event->sparkDwell;
238 }
else if (ratio < 0.8) {
248 if (std::isnan(dwellAngleDuration) || std::isnan(
sparkDwell)) {
255 event->sparksRemaining--;
259#if SPARK_EXTREME_LOGGING
260 efiPrintf(
"schedule multispark");
268#if SPARK_EXTREME_LOGGING
269 efiPrintf(
"scheduleByAngle TrailingSparks");
291 const char *outputName = output->
getName();
300#if SPARK_EXTREME_LOGGING
301 efiPrintf(
"[%s] %d spark goes high revolution %d tick %d current value %d",
308 event->wasSparkCanceled =
false;
320#if SPARK_EXTREME_LOGGING
321 efiPrintf(
"[%s] bail spark dwell\n", output->
getName());
335 event->actualDwellTimer.reset(nowNt);
337 bool skippedDwellDueToTriggerNoised =
false;
338 for (
int i = 0; i< MAX_OUTPUTS_FOR_IGNITION;i++) {
340 if (output != NULL) {
354 if (!skippedDwellDueToTriggerNoised) {
373 action_s::make<chargeTrailingSpark>( output )
380 float rpm,
float dwellMs,
float dwellAngle,
float sparkAngle, efitick_t edgeTimestamp,
float currentPhase,
float nextPhase) {
383 float angleOffset = dwellAngle - currentPhase;
384 if (angleOffset < 0) {
393 event->wasSparkLimited = limitedSpark;
395 efitick_t chargeTime = 0;
401#if SPARK_EXTREME_LOGGING
402 efiPrintf(
"[%s] %d sparkUp scheduling revolution %d angle %.1f (+%.1f) later",
404 getRevolutionCounter(), dwellAngle, angleOffset);
418#if SPARK_EXTREME_LOGGING
419 efitimeus_t chargeTimeUs = NT2US(chargeTime);
420 efiPrintf(
"[%s] %d sparkUp scheduled at %d ticks (%d.%06d)",
428 event->sparksRemaining = 0;
430#if SPARK_EXTREME_LOGGING
431 efiPrintf(
"[%s] %d sparkUp NOT scheduled because of limitedSpark",
443#if SPARK_EXTREME_LOGGING
444 efiPrintf(
"[%s] %d sparkDown scheduling revolution %d angle %.1f",
446 getRevolutionCounter(), sparkAngle);
452 &event->
sparkEvent, edgeTimestamp, sparkAngle,
453 action_s::make<fireSparkAndPrepareNextSchedule>( event ),
454 currentPhase, nextPhase);
456#if SPARK_EXTREME_LOGGING
457 efiPrintf(
"[%s] %d sparkDown scheduled %s",
459 isTimeScheduled ?
"later" :
"to queue");
462 if (isTimeScheduled) {
468 efitick_t fireTime =
sumTickAndFloat(chargeTime, MSF2NT(1.5f * dwellMs));
470#if SPARK_EXTREME_LOGGING
471 efitimeus_t fireTimeUs = NT2US(fireTime);
472 efiPrintf(
"[%s] %d overdwell scheduling at %d ticks (%d.%06d)",
494 efiPrintf(
"spark dwell@ %.1f spark@ %.2f id=%d sparkCounter=%d", event->
dwellAngle,
525 float maxAllowedDwellAngle;
556 bool limitedSpark = !limitedSparkState.
value;
559 if (std::isnan(dwellMs) || dwellMs <= 0) {
579 bool enableOddCylinderWastedSpark =
589 angle_t sparkAngle =
event->sparkAngle;
590 if (std::isnan(sparkAngle)) {
595 bool isOddCylWastedEvent =
false;
596 if (enableOddCylinderWastedSpark) {
597 auto dwellAngleWastedEvent = dwellAngle + 360;
598 if (dwellAngleWastedEvent > 720) {
599 dwellAngleWastedEvent -= 720;
604 isOddCylWastedEvent =
isPhaseInRange(dwellAngleWastedEvent, currentPhase, nextPhase);
606 if (isOddCylWastedEvent) {
607 dwellAngle = dwellAngleWastedEvent;
610 if (sparkAngle > 720) {
616 if (!isOddCylWastedEvent && !
isPhaseInRange(dwellAngle, currentPhase, nextPhase)) {
626#if EFI_LAUNCH_CONTROL
634#if EFI_ANTILAG_SYSTEM && EFI_LAUNCH_CONTROL
653 scheduleSparkEvent(limitedSpark, event, rpm, dwellMs, dwellAngle, sparkAngle, edgeTimestamp, currentPhase, nextPhase);
668 case IM_INDIVIDUAL_COILS:
670 case IM_WASTED_SPARK:
684 return 100 * totalPerCycle / engineCycleDuration;
SoftSparkLimiter softSparkLimiter
IgnitionEventList ignitionEvents
IgnitionState ignitionState
SingleTimerExecutor scheduler
void incrementBailedOnDwellCount()
std::function< void(IgnitionEvent *, bool)> onIgnitionEvent
void onSparkFireKnockSense(uint8_t cylinderIndex, efitick_t nowNt)
TunerStudioOutputChannels outputChannels
SoftSparkLimiter hardSparkLimiter
constexpr auto & module()
std::function< void(const IgnitionEvent &, efitick_t)> onScheduleOverFireSparkAndPrepareNextSchedule
std::function< void(const IgnitionEvent &, efitick_t, angle_t, efitick_t)> onScheduleTurnSparkPinHighStartCharging
IgnitionOutputPin trailingCoils[MAX_CYLINDER_COUNT]
IgnitionOutputPin coils[MAX_CYLINDER_COUNT]
virtual operation_mode_e getOperationMode() const =0
bool useOddFireWastedSpark
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]
floatms_t getDwell() const
LimpState allowIgnition() const
const char * getName() const
bool isInitialized() const
static float getOrZero(SensorType type)
void schedule(const char *msg, scheduling_s *scheduling, efitick_t timeNt, action_s const &action) override
Schedule an action to be executed in the future.
bool isPhaseInRange(float test, float current, float next)
efitimeus_t getTimeNowUs()
int time2print(int64_t time)
efitick_t sumTickAndFloat(efitick_t ticks, float extra)
LimpManager * getLimpManager()
EngineRotationState * getEngineRotationState()
EngineState * getEngineState()
static EngineAccessor engine
static constexpr engine_configuration_s * engineConfiguration
angle_t getPerCylinderFiringOrderOffset(uint8_t cylinderIndex, uint8_t cylinderNumber)
floatms_t getCrankshaftRevolutionTimeMs(float rpm)
ignition_mode_e getCurrentIgnitionMode()
bool warning(ObdCode code, const char *fmt,...)
void firmwareError(ObdCode code, const char *fmt,...)
size_t getCylinderNumberAtIndex(size_t cylinderIndex)
UNUSED(samplingTimeSeconds)
@ CUSTOM_ERR_IGNITION_MODE
@ CUSTOM_Ignition_Coil_Overcharge_1
@ CUSTOM_OBD_COIL_PIN_NOT_ASSIGNED
@ CUSTOM_OBD_IGNITION_MODE
@ CUSTOM_ARTIFICIAL_MISFIRE
@ CUSTOM_OBD_SKIPPED_SPARK
@ CUSTOM_OUT_OF_ORDER_COIL
@ OnTriggerEventSparkLogic
@ PrepareIgnitionSchedule
efitick_t scheduleByAngle(scheduling_s *timer, efitick_t nowNt, angle_t angle, action_s const &action)
sparkDwell("Ignition: coil charge time", SensorCategory.SENSOR_INPUTS, FieldType.INT, 940, 1.0, 0.0, 30.0, "ms")
static void prepareIgnitionSchedule()
void onTriggerEventSparkLogic(float rpm, efitick_t edgeTimestamp, float currentPhase, float nextPhase)
void initializeIgnitionActions()
percent_t getCoilDutyCycle(float rpm)
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 void scheduleSparkEvent(bool limitedSpark, IgnitionEvent *event, float rpm, float dwellMs, float dwellAngle, float sparkAngle, efitick_t edgeTimestamp, float currentPhase, float nextPhase)
static int getIgnitionPinForIndex(int cylinderIndex, ignition_mode_e ignitionMode)
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)
scheduling_s eventScheduling
bool enableTrailingSparks
bool twoWireBatchIgnition
ignition_mode_e ignitionMode
cranking_parameters_s cranking
script_setting_t scriptSetting[SCRIPT_SETTING_COUNT]
bool artificialTestMisfire
uint8_t overDwellCanceledCounter
uint32_t globalSparkCounter
uint8_t sparkOutOfOrderCounter
uint8_t dwellOverChargeCounter
uint8_t dwellUnderChargeCounter
uint8_t overDwellNotScheduledCounter
scaled_channel< int16_t, 100, 1 > trailingSparkAngle
angle_t dwellDurationAngle
scaled_channel< int16_t, 50, 1 > ignitionAdvanceCyl[MAX_CYLINDER_COUNT]
uint8_t currentIgnitionMode
void LogTriggerCoilState(efitick_t timestamp, size_t index, bool state)
void wrapAngle(angle_t &angle, const char *msg, ObdCode code)
angle_t getEngineCycle(operation_mode_e operationMode)