23#if EFI_PRINTF_FUEL_DETAILS || FUEL_MATH_EXTREME_LOGGING
30#if SPARK_EXTREME_LOGGING
44#if SPARK_EXTREME_LOGGING
62 switch (ignitionMode) {
65 case IM_WASTED_SPARK: {
72 case IM_INDIVIDUAL_COILS:
75 return cylinderIndex % 2;
95 ignitionMode = IM_INDIVIDUAL_COILS;
103 event->coilIndex = coilIndex;
106 if (finalIgnitionTiming > 360) {
107 finalIgnitionTiming -= 720;
123 - finalIgnitionTiming
129 event->sparkAngle = sparkAngle;
134 event->outputs[0] = output;
141 if (ignitionMode == IM_WASTED_SPARK && isTwoWireWasted) {
147 secondOutput =
nullptr;
152 event->outputs[1] = secondOutput;
155 angle_t dwellStartAngle = sparkAngle - dwellAngleDuration;
160 event->dwellAngle = dwellStartAngle;
162#if FUEL_MATH_EXTREME_LOGGING
164 printf(
"addIgnitionEvent %s angle=%.1f\n", output->
getName(), dwellStartAngle);
171#if SPARK_EXTREME_LOGGING
172 efiPrintf(
"chargeTrailingSpark %s",
pin->getName());
178#if SPARK_EXTREME_LOGGING
179 efiPrintf(
"fireTrailingSpark %s",
pin->getName());
185#if SPARK_EXTREME_LOGGING
186 efiPrintf(
"overFireSparkAndPrepareNextSchedule %s", event->
outputs[0]->
getName());
202 for (
int i = 0; i< MAX_OUTPUTS_FOR_IGNITION;i++) {
216 float actualDwellMs =
event->actualDwellTimer.getElapsedSeconds(nowNt) * 1e3;
220 float ratio = actualDwellMs /
event->sparkDwell;
221 if (ratio < 0.8 || ratio > 1.2) {
229 if (std::isnan(dwellAngleDuration) || std::isnan(
sparkDwell)) {
236 event->sparksRemaining--;
240#if SPARK_EXTREME_LOGGING
241 efiPrintf(
"schedule multispark");
249#if SPARK_EXTREME_LOGGING
250 efiPrintf(
"scheduleByAngle TrailingSparks");
272 const char *outputName = output->
getName();
281#if SPARK_EXTREME_LOGGING
296#if SPARK_EXTREME_LOGGING
297 efiPrintf(
"[%s] bail spark dwell\n", output->
getName());
311 event->actualDwellTimer.reset(nowNt);
313 bool skippedDwellDueToTriggerNoised =
false;
314 for (
int i = 0; i< MAX_OUTPUTS_FOR_IGNITION;i++) {
316 if (output != NULL) {
330 if (!skippedDwellDueToTriggerNoised) {
349 action_s::make<chargeTrailingSpark>( output )
356 float rpm,
float dwellMs,
float dwellAngle,
float sparkAngle, efitick_t edgeTimestamp,
float currentPhase,
float nextPhase) {
359 float angleOffset = dwellAngle - currentPhase;
360 if (angleOffset < 0) {
369 event->wasSparkLimited = limitedSpark;
371 efitick_t chargeTime = 0;
377#if SPARK_EXTREME_LOGGING
378 efiPrintf(
"scheduling sparkUp revolution=%d [%s] %d later id=%d", getRevolutionCounter(), event->
getOutputForLoggins()->
getName(), (
int)angleOffset,
394#if SPARK_EXTREME_LOGGING
395 efiPrintf(
"sparkUp revolution scheduled=%d for %d ticks [%s] %d later id=%d", getRevolutionCounter(),
time2print(chargeTime), event->
getOutputForLoggins()->
getName(), (
int)angleOffset,
403 event->sparksRemaining = 0;
415 &event->
sparkEvent, edgeTimestamp, sparkAngle,
416 action_s::make<fireSparkAndPrepareNextSchedule>( event ),
417 currentPhase, nextPhase);
419 if (isTimeScheduled) {
421#if SPARK_EXTREME_LOGGING
426#if SPARK_EXTREME_LOGGING
432 efitick_t fireTime =
sumTickAndFloat(chargeTime, MSF2NT(1.5f * dwellMs));
434#if SPARK_EXTREME_LOGGING
455 printf(
"spark dwell@ %.1f spark@ %.2f id=%d sparkCounter=%d\r\n", event->
dwellAngle,
486 float maxAllowedDwellAngle;
517 bool limitedSpark = !limitedSparkState.
value;
520 if (std::isnan(dwellMs) || dwellMs <= 0) {
540 bool enableOddCylinderWastedSpark =
550 angle_t sparkAngle =
event->sparkAngle;
551 if (std::isnan(sparkAngle)) {
556 bool isOddCylWastedEvent =
false;
557 if (enableOddCylinderWastedSpark) {
558 auto dwellAngleWastedEvent = dwellAngle + 360;
559 if (dwellAngleWastedEvent > 720) {
560 dwellAngleWastedEvent -= 720;
565 isOddCylWastedEvent =
isPhaseInRange(dwellAngleWastedEvent, currentPhase, nextPhase);
567 if (isOddCylWastedEvent) {
568 dwellAngle = dwellAngleWastedEvent;
571 if (sparkAngle > 720) {
577 if (!isOddCylWastedEvent && !
isPhaseInRange(dwellAngle, currentPhase, nextPhase)) {
587#if EFI_LAUNCH_CONTROL
595#if EFI_ANTILAG_SYSTEM && EFI_LAUNCH_CONTROL
614 scheduleSparkEvent(limitedSpark, event, rpm, dwellMs, dwellAngle, sparkAngle, edgeTimestamp, currentPhase, nextPhase);
629 case IM_INDIVIDUAL_COILS:
631 case IM_WASTED_SPARK:
645 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_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, 944, 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
uint32_t globalSparkCounter
uint8_t sparkOutOfOrderCounter
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
uint8_t sadDwellRatioCounter
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)