| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /* | |||
| 2 | * @file spark_logic.cpp | |||
| 3 | * | |||
| 4 | * @date Sep 15, 2016 | |||
| 5 | * @author Andrey Belomutskiy, (c) 2012-2020 | |||
| 6 | */ | |||
| 7 | ||||
| 8 | #include "pch.h" | |||
| 9 | ||||
| 10 | #include "spark_logic.h" | |||
| 11 | ||||
| 12 | #include "utlist.h" | |||
| 13 | #include "event_queue.h" | |||
| 14 | ||||
| 15 | #include "knock_logic.h" | |||
| 16 | ||||
| 17 | #if EFI_ENGINE_CONTROL | |||
| 18 | ||||
| 19 | #if EFI_UNIT_TEST | |||
| 20 | extern bool verboseMode; | |||
| 21 | #endif /* EFI_UNIT_TEST */ | |||
| 22 | ||||
| 23 | #if EFI_PRINTF_FUEL_DETAILS || FUEL_MATH_EXTREME_LOGGING | |||
| 24 | extern bool printFuelDebug; | |||
| 25 | #endif // EFI_PRINTF_FUEL_DETAILS | |||
| 26 | ||||
| 27 | static const char *prevSparkName = nullptr; | |||
| 28 | ||||
| 29 | 6909 | static void fireSparkBySettingPinLow(IgnitionEvent *event, IgnitionOutputPin *output) { | ||
| 30 | #if SPARK_EXTREME_LOGGING | |||
| 31 | 6909 | efiPrintf("[%s] %d spark goes low revolution %d tick %d current value %d", | ||
| 32 | event->getOutputForLoggins()->getName(), event->sparkCounter, | |||
| 33 | getRevolutionCounter(), time2print(getTimeNowUs()), | |||
| 34 | output->currentLogicValue); | |||
| 35 | #endif /* SPARK_EXTREME_LOGGING */ | |||
| 36 | ||||
| 37 | /** | |||
| 38 | * there are two kinds of 'out-of-order' | |||
| 39 | * 1) low goes before high, everything is fine afterwards | |||
| 40 | * | |||
| 41 | * 2) we have an un-matched low followed by legit pairs | |||
| 42 | */ | |||
| 43 | 6909 | output->signalFallSparkId = event->sparkCounter; | ||
| 44 | ||||
| 45 |
6/6✓ Branch 0 taken 591 times.
✓ Branch 1 taken 6318 times.
✓ Branch 2 taken 477 times.
✓ Branch 3 taken 114 times.
✓ Branch 4 taken 470 times.
✓ Branch 5 taken 7 times.
|
2/2✓ Decision 'true' taken 470 times.
✓ Decision 'false' taken 6439 times.
|
6909 | if (!output->currentLogicValue && !event->wasSparkLimited && !event->wasSparkCanceled) { |
| 46 | #if SPARK_EXTREME_LOGGING | |||
| 47 | 470 | efiPrintf("out-of-order coil off %s", output->getName()); | ||
| 48 | #endif /* SPARK_EXTREME_LOGGING */ | |||
| 49 | 470 | warning(ObdCode::CUSTOM_OUT_OF_ORDER_COIL, "out-of-order coil off %s", output->getName()); | ||
| 50 | } | |||
| 51 | 6909 | output->setLow(); | ||
| 52 | 6909 | } | ||
| 53 | ||||
| 54 | 7380 | static void assertPinAssigned(IgnitionOutputPin* output) { | ||
| 55 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 7380 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 7380 times.
|
7380 | if (!output->isInitialized()) { |
| 56 | ✗ | warning(ObdCode::CUSTOM_OBD_COIL_PIN_NOT_ASSIGNED, "Pin Not Assigned check configuration #%s", output->getName()); \ | ||
| 57 | } | |||
| 58 | 7380 | } | ||
| 59 | ||||
| 60 | /** | |||
| 61 | * @param cylinderIndex from 0 to cylinderCount, not cylinder number | |||
| 62 | */ | |||
| 63 | 7276 | static int getIgnitionPinForIndex(int cylinderIndex, ignition_mode_e ignitionMode) { | ||
| 64 |
4/5✓ Branch 0 taken 6212 times.
✓ Branch 1 taken 618 times.
✓ Branch 2 taken 434 times.
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
|
7276 | switch (ignitionMode) { | |
| 65 |
1/1✓ Decision 'true' taken 6212 times.
|
6212 | case IM_ONE_COIL: | |
| 66 | 6212 | return 0; | ||
| 67 |
1/1✓ Decision 'true' taken 618 times.
|
618 | case IM_WASTED_SPARK: { | |
| 68 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 618 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 618 times.
|
618 | if (engineConfiguration->cylindersCount == 1) { |
| 69 | // we do not want to divide by zero | |||
| 70 | ✗ | return 0; | ||
| 71 | } | |||
| 72 | 618 | return cylinderIndex % (engineConfiguration->cylindersCount / 2); | ||
| 73 | } | |||
| 74 |
1/1✓ Decision 'true' taken 434 times.
|
434 | case IM_INDIVIDUAL_COILS: | |
| 75 | 434 | return cylinderIndex; | ||
| 76 |
1/1✓ Decision 'true' taken 12 times.
|
12 | case IM_TWO_COILS: | |
| 77 | 12 | return cylinderIndex % 2; | ||
| 78 | ||||
| 79 | ✗ | default: | ||
| 80 | ✗ | firmwareError(ObdCode::CUSTOM_OBD_IGNITION_MODE, "Invalid ignition mode getIgnitionPinForIndex(): %d", engineConfiguration->ignitionMode); | ||
| 81 | ✗ | return 0; | ||
| 82 | } | |||
| 83 | } | |||
| 84 | ||||
| 85 | 7276 | static void prepareCylinderIgnitionSchedule(angle_t dwellAngleDuration, floatms_t sparkDwell, IgnitionEvent *event) { | ||
| 86 | // todo: clean up this implementation? does not look too nice as is. | |||
| 87 | ||||
| 88 | // let's save planned duration so that we can later compare it with reality | |||
| 89 | 7276 | event->sparkDwell = sparkDwell; | ||
| 90 | ||||
| 91 |
1/1✓ Branch 1 taken 7276 times.
|
7276 | auto ignitionMode = getCurrentIgnitionMode(); | |
| 92 | ||||
| 93 | // On an odd cylinder (or odd fire) wasted spark engine, map outputs as if in sequential. | |||
| 94 | // During actual scheduling, the events just get scheduled every 360 deg instead | |||
| 95 | // of every 720 deg. | |||
| 96 |
4/4✓ Branch 0 taken 679 times.
✓ Branch 1 taken 6597 times.
✓ Branch 2 taken 61 times.
✓ Branch 3 taken 618 times.
|
2/2✓ Decision 'true' taken 61 times.
✓ Decision 'false' taken 7215 times.
|
7276 | if (ignitionMode == IM_WASTED_SPARK && engine->engineState.useOddFireWastedSpark) { |
| 97 | 61 | ignitionMode = IM_INDIVIDUAL_COILS; | ||
| 98 | } | |||
| 99 | ||||
| 100 |
1/1✓ Branch 1 taken 7276 times.
|
7276 | const int index = getIgnitionPinForIndex(event->cylinderIndex, ignitionMode); | |
| 101 |
1/1✓ Branch 1 taken 7276 times.
|
7276 | const int coilIndex = getCylinderNumberAtIndex(index); | |
| 102 |
1/1✓ Branch 1 taken 7276 times.
|
7276 | angle_t finalIgnitionTiming = getEngineState()->timingAdvance[coilIndex]; | |
| 103 | // Stash which cylinder we're scheduling so that knock sensing knows which | |||
| 104 | // cylinder just fired | |||
| 105 | 7276 | event->coilIndex = coilIndex; | ||
| 106 | ||||
| 107 | // 10 ATDC ends up as 710, convert it to -10 so we can log and clamp correctly | |||
| 108 |
2/2✓ Branch 0 taken 31 times.
✓ Branch 1 taken 7245 times.
|
2/2✓ Decision 'true' taken 31 times.
✓ Decision 'false' taken 7245 times.
|
7276 | if (finalIgnitionTiming > 360) { |
| 109 | 31 | finalIgnitionTiming -= 720; | ||
| 110 | } | |||
| 111 | ||||
| 112 | // Clamp the final ignition timing to the configured limits | |||
| 113 | // finalIgnitionTiming is deg BTDC | |||
| 114 | // minimumIgnitionTiming limits maximum retard | |||
| 115 | // maximumIgnitionTiming limits maximum advance | |||
| 116 | /* | |||
| 117 | https://github.com/rusefi/rusefi/issues/5894 disabling feature for now | |||
| 118 | finalIgnitionTiming = clampF(engineConfiguration->minimumIgnitionTiming, finalIgnitionTiming, engineConfiguration->maximumIgnitionTiming); | |||
| 119 | */ | |||
| 120 | ||||
| 121 | 7276 | engine->outputChannels.ignitionAdvanceCyl[event->cylinderIndex] = finalIgnitionTiming; | ||
| 122 | ||||
| 123 | 7276 | angle_t sparkAngle = | ||
| 124 | // Negate because timing *before* TDC, and we schedule *after* TDC | |||
| 125 | - finalIgnitionTiming | |||
| 126 | // Offset by this cylinder's position in the cycle | |||
| 127 |
1/1✓ Branch 1 taken 7276 times.
|
7276 | + getPerCylinderFiringOrderOffset(event->cylinderIndex, coilIndex); | |
| 128 | ||||
| 129 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 7276 times.
✗ Branch 4 not taken.
|
7276 | efiAssertVoid(ObdCode::CUSTOM_SPARK_ANGLE_1, !std::isnan(sparkAngle), "sparkAngle#1"); | |
| 130 |
1/1✓ Branch 1 taken 7276 times.
|
7276 | wrapAngle(sparkAngle, "findAngle#2", ObdCode::CUSTOM_ERR_6550); | |
| 131 | 7276 | event->sparkAngle = sparkAngle; | ||
| 132 | ||||
| 133 | 7276 | engine->outputChannels.currentIgnitionMode = static_cast<uint8_t>(ignitionMode); | ||
| 134 | ||||
| 135 | 7276 | IgnitionOutputPin *output = &enginePins.coils[coilIndex]; | ||
| 136 | 7276 | event->outputs[0] = output; | ||
| 137 | IgnitionOutputPin *secondOutput; | |||
| 138 | ||||
| 139 | // We need two outputs if: | |||
| 140 | // - we are running wasted spark, and have "two wire" mode enabled | |||
| 141 | // - We are running sequential mode, but we're cranking, so we should run in two wire wasted mode (not one wire wasted) | |||
| 142 |
4/4✓ Branch 0 taken 7270 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 531 times.
✓ Branch 3 taken 6739 times.
|
7276 | bool isTwoWireWasted = engineConfiguration->twoWireBatchIgnition || (engineConfiguration->ignitionMode == IM_INDIVIDUAL_COILS); | |
| 143 |
4/4✓ Branch 0 taken 618 times.
✓ Branch 1 taken 6658 times.
✓ Branch 2 taken 104 times.
✓ Branch 3 taken 514 times.
|
2/2✓ Decision 'true' taken 104 times.
✓ Decision 'false' taken 7172 times.
|
7276 | if (ignitionMode == IM_WASTED_SPARK && isTwoWireWasted) { |
| 144 | 104 | int secondIndex = index + engineConfiguration->cylindersCount / 2; | ||
| 145 |
1/1✓ Branch 1 taken 104 times.
|
104 | int secondCoilIndex = getCylinderNumberAtIndex(secondIndex); | |
| 146 | 104 | secondOutput = &enginePins.coils[secondCoilIndex]; | ||
| 147 |
1/1✓ Branch 1 taken 104 times.
|
104 | assertPinAssigned(secondOutput); | |
| 148 | 104 | } else { | ||
| 149 | 7172 | secondOutput = nullptr; | ||
| 150 | } | |||
| 151 | ||||
| 152 |
1/1✓ Branch 1 taken 7276 times.
|
7276 | assertPinAssigned(output); | |
| 153 | ||||
| 154 | 7276 | event->outputs[1] = secondOutput; | ||
| 155 | ||||
| 156 | ||||
| 157 | 7276 | angle_t dwellStartAngle = sparkAngle - dwellAngleDuration; | ||
| 158 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 7276 times.
✗ Branch 4 not taken.
|
7276 | efiAssertVoid(ObdCode::CUSTOM_ERR_6590, !std::isnan(dwellStartAngle), "findAngle#5"); | |
| 159 | ||||
| 160 |
2/5✓ Branch 0 taken 7276 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7276 times.
✗ Branch 5 not taken.
|
7276 | assertAngleRange(dwellStartAngle, "findAngle dwellStartAngle", ObdCode::CUSTOM_ERR_6550); | |
| 161 |
1/1✓ Branch 1 taken 7276 times.
|
7276 | wrapAngle(dwellStartAngle, "findAngle#7", ObdCode::CUSTOM_ERR_6550); | |
| 162 | 7276 | event->dwellAngle = dwellStartAngle; | ||
| 163 | ||||
| 164 | #if FUEL_MATH_EXTREME_LOGGING | |||
| 165 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7276 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 7276 times.
|
7276 | if (printFuelDebug) { |
| 166 | ✗ | efiPrintf("addIgnitionEvent %s angle=%.1f", output->getName(), dwellStartAngle); | ||
| 167 | } | |||
| 168 | // efiPrintf("addIgnitionEvent %s ind=%d", output->name, event->dwellPosition->eventIndex); | |||
| 169 | #endif /* FUEL_MATH_EXTREME_LOGGING */ | |||
| 170 | } | |||
| 171 | ||||
| 172 | 1 | static void chargeTrailingSpark(IgnitionOutputPin* pin) { | ||
| 173 | #if SPARK_EXTREME_LOGGING | |||
| 174 | 1 | efiPrintf("chargeTrailingSpark %s", pin->getName()); | ||
| 175 | #endif /* SPARK_EXTREME_LOGGING */ | |||
| 176 | 1 | pin->setHigh(); | ||
| 177 | 1 | } | ||
| 178 | ||||
| 179 | 1 | static void fireTrailingSpark(IgnitionOutputPin* pin) { | ||
| 180 | #if SPARK_EXTREME_LOGGING | |||
| 181 | 1 | efiPrintf("fireTrailingSpark %s", pin->getName()); | ||
| 182 | #endif /* SPARK_EXTREME_LOGGING */ | |||
| 183 | 1 | pin->setLow(); | ||
| 184 | 1 | } | ||
| 185 | ||||
| 186 | 335 | static void overFireSparkAndPrepareNextSchedule(IgnitionEvent *event) { | ||
| 187 | #if SPARK_EXTREME_LOGGING | |||
| 188 | 335 | efiPrintf("[%s] %d %s", | ||
| 189 | event->getOutputForLoggins()->getName(), event->sparkCounter, | |||
| 190 | __func__); | |||
| 191 | #endif /* SPARK_EXTREME_LOGGING */ | |||
| 192 | 335 | float actualDwellMs = event->actualDwellTimer.getElapsedSeconds() * 1e3; | ||
| 193 | ||||
| 194 | 670 | warning((ObdCode)((int)ObdCode::CUSTOM_Ignition_Coil_Overcharge_1 + event->cylinderIndex), | ||
| 195 | "cylinder %d %s overcharge %f ms", | |||
| 196 | 335 | event->cylinderIndex + 1, event->outputs[0]->getName(), actualDwellMs); | ||
| 197 | ||||
| 198 | // kill pending fire | |||
| 199 | 335 | engine->module<TriggerScheduler>()->cancel(&event->sparkEvent); | ||
| 200 | ||||
| 201 | 335 | engine->engineState.overDwellCanceledCounter++; | ||
| 202 | 335 | event->wasSparkCanceled = true; | ||
| 203 | 335 | fireSparkAndPrepareNextSchedule(event); | ||
| 204 | 335 | } | ||
| 205 | ||||
| 206 | /** | |||
| 207 | * TL,DR: each IgnitionEvent is in charge of it's own scheduling forever, we plant next event while finishing handling of the current one | |||
| 208 | */ | |||
| 209 | 6831 | void fireSparkAndPrepareNextSchedule(IgnitionEvent *event) { | ||
| 210 | #if EFI_UNIT_TEST | |||
| 211 |
2/2✓ Branch 1 taken 268 times.
✓ Branch 2 taken 6563 times.
|
2/2✓ Decision 'true' taken 268 times.
✓ Decision 'false' taken 6563 times.
|
6831 | if (engine->onIgnitionEvent) { |
| 212 | 268 | engine->onIgnitionEvent(event, false); | ||
| 213 | } | |||
| 214 | #endif | |||
| 215 | ||||
| 216 |
2/2✓ Branch 0 taken 13662 times.
✓ Branch 1 taken 6831 times.
|
2/2✓ Decision 'true' taken 13662 times.
✓ Decision 'false' taken 6831 times.
|
20493 | for (int i = 0; i< MAX_OUTPUTS_FOR_IGNITION;i++) { |
| 217 | 13662 | IgnitionOutputPin *output = event->outputs[i]; | ||
| 218 | ||||
| 219 |
2/2✓ Branch 0 taken 6909 times.
✓ Branch 1 taken 6753 times.
|
2/2✓ Decision 'true' taken 6909 times.
✓ Decision 'false' taken 6753 times.
|
13662 | if (output) { |
| 220 | 6909 | fireSparkBySettingPinLow(event, output); | ||
| 221 | } | |||
| 222 | } | |||
| 223 | ||||
| 224 | 6831 | efitick_t nowNt = getTimeNowNt(); | ||
| 225 | ||||
| 226 | #if EFI_TOOTH_LOGGER | |||
| 227 | 6831 | LogTriggerCoilState(nowNt, false, event->coilIndex); | ||
| 228 | #endif // EFI_TOOTH_LOGGER | |||
| 229 |
2/2✓ Branch 0 taken 6717 times.
✓ Branch 1 taken 114 times.
|
2/2✓ Decision 'true' taken 6717 times.
✓ Decision 'false' taken 114 times.
|
6831 | if (!event->wasSparkLimited) { |
| 230 | /** | |||
| 231 | * ratio of desired dwell duration to actual dwell duration gives us some idea of how good is input trigger jitter | |||
| 232 | */ | |||
| 233 | 6717 | float actualDwellMs = event->actualDwellTimer.getElapsedSeconds(nowNt) * 1e3; | ||
| 234 | 6717 | float ratio = actualDwellMs / event->sparkDwell; | ||
| 235 | ||||
| 236 |
2/2✓ Branch 0 taken 693 times.
✓ Branch 1 taken 6024 times.
|
2/2✓ Decision 'true' taken 693 times.
✓ Decision 'false' taken 6024 times.
|
6717 | if (ratio > 1.2) { |
| 237 | 693 | engine->engineState.dwellOverChargeCounter++; | ||
| 238 |
2/2✓ Branch 0 taken 1751 times.
✓ Branch 1 taken 4273 times.
|
2/2✓ Decision 'true' taken 1751 times.
✓ Decision 'false' taken 4273 times.
|
6024 | } else if (ratio < 0.8) { |
| 239 | 1751 | engine->engineState.dwellUnderChargeCounter++; | ||
| 240 | } | |||
| 241 | 6717 | engine->engineState.dwellActualRatio = ratio; | ||
| 242 | } | |||
| 243 | ||||
| 244 | // now that we've just fired a coil let's prepare the new schedule for the next engine revolution | |||
| 245 | ||||
| 246 | 6831 | angle_t dwellAngleDuration = engine->ignitionState.dwellDurationAngle; | ||
| 247 | 6831 | floatms_t sparkDwell = engine->ignitionState.getDwell(); | ||
| 248 |
3/6✓ Branch 1 taken 6831 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6831 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 6831 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 6831 times.
|
6831 | if (std::isnan(dwellAngleDuration) || std::isnan(sparkDwell)) { |
| 249 | // we are here if engine has just stopped | |||
| 250 | ✗ | return; | ||
| 251 | } | |||
| 252 | ||||
| 253 | // If there are more sparks to fire, schedule them | |||
| 254 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6831 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 6831 times.
|
6831 | if (event->sparksRemaining > 0) { |
| 255 | ✗ | event->sparksRemaining--; | ||
| 256 | ||||
| 257 | ✗ | efitick_t nextDwellStart = nowNt + engine->engineState.multispark.delay; | ||
| 258 | ✗ | efitick_t nextFiring = nextDwellStart + engine->engineState.multispark.dwell; | ||
| 259 | #if SPARK_EXTREME_LOGGING | |||
| 260 | ✗ | efiPrintf("schedule multispark"); | ||
| 261 | #endif /* SPARK_EXTREME_LOGGING */ | |||
| 262 | ||||
| 263 | // We can schedule both of these right away, since we're going for "asap" not "particular angle" | |||
| 264 | ✗ | engine->scheduler.schedule("dwell", &event->dwellStartTimer, nextDwellStart, action_s::make<turnSparkPinHighStartCharging>( event )); | ||
| 265 | ✗ | engine->scheduler.schedule("firing", &event->sparkEvent.eventScheduling, nextFiring, action_s::make<fireSparkAndPrepareNextSchedule>( event )); | ||
| 266 | } else { | |||
| 267 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 6830 times.
|
2/2✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 6830 times.
|
6831 | if (engineConfiguration->enableTrailingSparks) { |
| 268 | #if SPARK_EXTREME_LOGGING | |||
| 269 | 1 | efiPrintf("scheduleByAngle TrailingSparks"); | ||
| 270 | #endif /* SPARK_EXTREME_LOGGING */ | |||
| 271 | ||||
| 272 | // Trailing sparks are enabled - schedule an event for the corresponding trailing coil | |||
| 273 |
1/1✓ Branch 2 taken 1 time.
|
2 | scheduleByAngle( | |
| 274 | 1 | &event->trailingSparkFire, nowNt, engine->ignitionState.trailingSparkAngle, | ||
| 275 | 2 | action_s::make<fireTrailingSpark>( &enginePins.trailingCoils[event->coilIndex] ) | ||
| 276 | ); | |||
| 277 | } | |||
| 278 | ||||
| 279 | // If all events have been scheduled, prepare for next time. | |||
| 280 | 6831 | prepareCylinderIgnitionSchedule(dwellAngleDuration, sparkDwell, event); | ||
| 281 | } | |||
| 282 | ||||
| 283 | 6831 | engine->onSparkFireKnockSense(event->cylinderIndex, nowNt); | ||
| 284 | } | |||
| 285 | ||||
| 286 | 6796 | static bool startDwellByTurningSparkPinHigh(IgnitionEvent *event, IgnitionOutputPin *output) { | ||
| 287 | // todo: no reason for this to be disabled in unit_test mode?! | |||
| 288 | #if ! EFI_UNIT_TEST | |||
| 289 | ||||
| 290 | if (Sensor::getOrZero(SensorType::Rpm) > 2 * engineConfiguration->cranking.rpm) { | |||
| 291 | const char *outputName = output->getName(); | |||
| 292 | if (prevSparkName == outputName && getCurrentIgnitionMode() != IM_ONE_COIL) { | |||
| 293 | warning(ObdCode::CUSTOM_OBD_SKIPPED_SPARK, "looks like skipped spark event revolution=%d [%s]", getRevolutionCounter(), outputName); | |||
| 294 | } | |||
| 295 | prevSparkName = outputName; | |||
| 296 | } | |||
| 297 | #endif /* EFI_UNIT_TEST */ | |||
| 298 | ||||
| 299 | ||||
| 300 | #if SPARK_EXTREME_LOGGING | |||
| 301 | 6796 | efiPrintf("[%s] %d spark goes high revolution %d tick %d current value %d", | ||
| 302 | event->getOutputForLoggins()->getName(), event->sparkCounter, | |||
| 303 | getRevolutionCounter(), time2print(getTimeNowUs()), | |||
| 304 | output->currentLogicValue, event->sparkCounter); | |||
| 305 | #endif /* SPARK_EXTREME_LOGGING */ | |||
| 306 | ||||
| 307 | // Reset error flag(s) | |||
| 308 | 6796 | event->wasSparkCanceled = false; | ||
| 309 | ||||
| 310 |
2/2✓ Branch 0 taken 474 times.
✓ Branch 1 taken 6322 times.
|
2/2✓ Decision 'true' taken 474 times.
✓ Decision 'false' taken 6322 times.
|
6796 | if (output->signalFallSparkId >= event->sparkCounter) { |
| 311 | /** | |||
| 312 | * fact: we schedule both start of dwell and spark firing using a combination of time and trigger event domain | |||
| 313 | * in case of bad/noisy signal we can get unexpected trigger events and a small time delay for spark firing before | |||
| 314 | * we even start dwell if it scheduled with a longer time-only delay with fewer trigger events | |||
| 315 | * | |||
| 316 | * here we are detecting such out-of-order processing and choose the safer route of not even starting dwell | |||
| 317 | * [tag] #6349 | |||
| 318 | */ | |||
| 319 | ||||
| 320 | #if SPARK_EXTREME_LOGGING | |||
| 321 |
1/1✓ Decision 'true' taken 474 times.
|
474 | efiPrintf("[%s] bail spark dwell\n", output->getName()); | |
| 322 | #endif /* SPARK_EXTREME_LOGGING */ | |||
| 323 | // let's save this coil if things do not look right | |||
| 324 | 474 | engine->engineState.sparkOutOfOrderCounter++; | ||
| 325 | 474 | return true; | ||
| 326 | } | |||
| 327 | ||||
| 328 | 6322 | output->setHigh(); | ||
| 329 | 6322 | return false; | ||
| 330 | } | |||
| 331 | ||||
| 332 | 6718 | void turnSparkPinHighStartCharging(IgnitionEvent *event) { | ||
| 333 | 6718 | efitick_t nowNt = getTimeNowNt(); | ||
| 334 | ||||
| 335 | 6718 | event->actualDwellTimer.reset(nowNt); | ||
| 336 | ||||
| 337 | 6718 | bool skippedDwellDueToTriggerNoised = false; | ||
| 338 |
2/2✓ Branch 0 taken 13436 times.
✓ Branch 1 taken 6718 times.
|
2/2✓ Decision 'true' taken 13436 times.
✓ Decision 'false' taken 6718 times.
|
20154 | for (int i = 0; i< MAX_OUTPUTS_FOR_IGNITION;i++) { |
| 339 | 13436 | IgnitionOutputPin *output = event->outputs[i]; | ||
| 340 |
2/2✓ Branch 0 taken 6796 times.
✓ Branch 1 taken 6640 times.
|
2/2✓ Decision 'true' taken 6796 times.
✓ Decision 'false' taken 6640 times.
|
13436 | if (output != NULL) { |
| 341 | // at the moment we have a funny xor as if outputs could have different destiny. That's probably an over exaggeration, | |||
| 342 | // realistically it should be enough to check the sequencing of only the first output but that would be less elegant | |||
| 343 | // | |||
| 344 | // maybe it would have need nicer if instead of an array of outputs we had a linked list of outputs? but that's just daydreaming. | |||
| 345 | 6796 | skippedDwellDueToTriggerNoised |= startDwellByTurningSparkPinHigh(event, output); | ||
| 346 | } | |||
| 347 | } | |||
| 348 | ||||
| 349 | #if EFI_UNIT_TEST | |||
| 350 | 6718 | engine->incrementBailedOnDwellCount(); | ||
| 351 | #endif | |||
| 352 | ||||
| 353 | ||||
| 354 |
2/2✓ Branch 0 taken 6249 times.
✓ Branch 1 taken 469 times.
|
2/2✓ Decision 'true' taken 6249 times.
✓ Decision 'false' taken 469 times.
|
6718 | if (!skippedDwellDueToTriggerNoised) { |
| 355 | ||||
| 356 | #if EFI_UNIT_TEST | |||
| 357 |
2/2✓ Branch 1 taken 259 times.
✓ Branch 2 taken 5990 times.
|
2/2✓ Decision 'true' taken 259 times.
✓ Decision 'false' taken 5990 times.
|
6249 | if (engine->onIgnitionEvent) { |
| 358 | 259 | engine->onIgnitionEvent(event, true); | ||
| 359 | } | |||
| 360 | #endif | |||
| 361 | ||||
| 362 | #if EFI_TOOTH_LOGGER | |||
| 363 | 6249 | LogTriggerCoilState(nowNt, true, event->coilIndex); | ||
| 364 | #endif // EFI_TOOTH_LOGGER | |||
| 365 | } | |||
| 366 | ||||
| 367 | ||||
| 368 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 6717 times.
|
2/2✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 6717 times.
|
6718 | if (engineConfiguration->enableTrailingSparks) { |
| 369 | 1 | IgnitionOutputPin *output = &enginePins.trailingCoils[event->coilIndex]; | ||
| 370 | // Trailing sparks are enabled - schedule an event for the corresponding trailing coil | |||
| 371 |
1/1✓ Branch 2 taken 1 time.
|
2 | scheduleByAngle( | |
| 372 | 1 | &event->trailingSparkCharge, nowNt, engine->ignitionState.trailingSparkAngle, | ||
| 373 | 2 | action_s::make<chargeTrailingSpark>( output ) | ||
| 374 | ); | |||
| 375 | } | |||
| 376 | 6718 | } | ||
| 377 | ||||
| 378 | ||||
| 379 | 9612 | static void scheduleSparkEvent(bool limitedSpark, IgnitionEvent *event, | ||
| 380 | float rpm, float dwellMs, float dwellAngle, float sparkAngle, efitick_t edgeTimestamp, float currentPhase, float nextPhase) { | |||
| 381 | UNUSED(rpm); | |||
| 382 | ||||
| 383 | 9612 | float angleOffset = dwellAngle - currentPhase; | ||
| 384 |
2/2✓ Branch 0 taken 148 times.
✓ Branch 1 taken 9464 times.
|
2/2✓ Decision 'true' taken 148 times.
✓ Decision 'false' taken 9464 times.
|
9612 | if (angleOffset < 0) { |
| 385 | 148 | angleOffset += engine->engineState.engineCycle; | ||
| 386 | } | |||
| 387 | ||||
| 388 | /** | |||
| 389 | * By the way 32-bit value should hold at least 400 hours of events at 6K RPM x 12 events per revolution | |||
| 390 | * [tag:duration_limit] | |||
| 391 | */ | |||
| 392 | 9612 | event->sparkCounter = engine->engineState.globalSparkCounter++; | ||
| 393 | 9612 | event->wasSparkLimited = limitedSpark; | ||
| 394 | ||||
| 395 | 9612 | efitick_t chargeTime = 0; | ||
| 396 | ||||
| 397 | /** | |||
| 398 | * The start of charge is always within the current trigger event range, so just plain time-based scheduling | |||
| 399 | */ | |||
| 400 |
2/2✓ Branch 0 taken 9492 times.
✓ Branch 1 taken 120 times.
|
2/2✓ Decision 'true' taken 9492 times.
✓ Decision 'false' taken 120 times.
|
9612 | if (!limitedSpark) { |
| 401 | #if SPARK_EXTREME_LOGGING | |||
| 402 | 9492 | efiPrintf("[%s] %d sparkUp scheduling revolution %d angle %.1f (+%.1f) later", | ||
| 403 | event->getOutputForLoggins()->getName(), event->sparkCounter, | |||
| 404 | getRevolutionCounter(), dwellAngle, angleOffset); | |||
| 405 | #endif /* SPARK_EXTREME_LOGGING */ | |||
| 406 | ||||
| 407 | /** | |||
| 408 | * Note how we do not check if spark is limited or not while scheduling 'spark down' | |||
| 409 | * This way we make sure that coil dwell started while spark was enabled would fire and not burn | |||
| 410 | * the coil. | |||
| 411 | */ | |||
| 412 |
1/1✓ Branch 3 taken 9492 times.
|
9492 | chargeTime = scheduleByAngle(&event->dwellStartTimer, edgeTimestamp, angleOffset, action_s::make<turnSparkPinHighStartCharging>( event )); | |
| 413 | ||||
| 414 | #if EFI_UNIT_TEST | |||
| 415 | 9492 | engine->onScheduleTurnSparkPinHighStartCharging(*event, edgeTimestamp, angleOffset, chargeTime); | ||
| 416 | #endif | |||
| 417 | ||||
| 418 | #if SPARK_EXTREME_LOGGING | |||
| 419 | 9492 | efitimeus_t chargeTimeUs = NT2US(chargeTime); | ||
| 420 | 9492 | efiPrintf("[%s] %d sparkUp scheduled at %d ticks (%d.%06d)", | ||
| 421 | event->getOutputForLoggins()->getName(), event->sparkCounter, | |||
| 422 | time2print(chargeTime), time2print(chargeTimeUs / (1000 * 1000)), time2print(chargeTimeUs % (1000 * 1000))); | |||
| 423 | #endif /* SPARK_EXTREME_LOGGING */ | |||
| 424 | ||||
| 425 | 9492 | event->sparksRemaining = engine->engineState.multispark.count; | ||
| 426 | } else { | |||
| 427 | // don't fire multispark if spark is cut completely! | |||
| 428 | 120 | event->sparksRemaining = 0; | ||
| 429 | ||||
| 430 | #if SPARK_EXTREME_LOGGING | |||
| 431 | 120 | efiPrintf("[%s] %d sparkUp NOT scheduled because of limitedSpark", | ||
| 432 | event->getOutputForLoggins()->getName(), event->sparkCounter); | |||
| 433 | #endif /* SPARK_EXTREME_LOGGING */ | |||
| 434 | } | |||
| 435 | ||||
| 436 | /** | |||
| 437 | * Spark event is often happening during a later trigger event timeframe | |||
| 438 | */ | |||
| 439 | ||||
| 440 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9612 times.
|
9612 | efiAssertVoid(ObdCode::CUSTOM_ERR_6591, !std::isnan(sparkAngle), "findAngle#4"); | |
| 441 |
2/4✓ Branch 0 taken 9612 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9612 times.
|
9612 | assertAngleRange(sparkAngle, "findAngle#a5", ObdCode::CUSTOM_ERR_6549); | |
| 442 | ||||
| 443 | #if SPARK_EXTREME_LOGGING | |||
| 444 | 9612 | efiPrintf("[%s] %d sparkDown scheduling revolution %d angle %.1f", | ||
| 445 | event->getOutputForLoggins()->getName(), event->sparkCounter, | |||
| 446 | getRevolutionCounter(), sparkAngle); | |||
| 447 | #endif /* FUEL_MATH_EXTREME_LOGGING */ | |||
| 448 | ||||
| 449 | ||||
| 450 |
1/1✓ Branch 3 taken 9612 times.
|
19224 | bool isTimeScheduled = engine->module<TriggerScheduler>()->scheduleOrQueue( | |
| 451 | "spark", | |||
| 452 | &event->sparkEvent, edgeTimestamp, sparkAngle, | |||
| 453 | 19224 | action_s::make<fireSparkAndPrepareNextSchedule>( event ), | ||
| 454 | 9612 | currentPhase, nextPhase); | ||
| 455 | ||||
| 456 | #if SPARK_EXTREME_LOGGING | |||
| 457 |
2/2✓ Branch 0 taken 7521 times.
✓ Branch 1 taken 2091 times.
|
9612 | efiPrintf("[%s] %d sparkDown scheduled %s", | |
| 458 | event->getOutputForLoggins()->getName(), event->sparkCounter, | |||
| 459 | isTimeScheduled ? "later" : "to queue"); | |||
| 460 | #endif /* FUEL_MATH_EXTREME_LOGGING */ | |||
| 461 | ||||
| 462 |
2/2✓ Branch 0 taken 2091 times.
✓ Branch 1 taken 7521 times.
|
2/2✓ Decision 'true' taken 2091 times.
✓ Decision 'false' taken 7521 times.
|
9612 | if (isTimeScheduled) { |
| 463 | // event was scheduled by time, we expect it to happen reliably | |||
| 464 | } else { | |||
| 465 | // event was queued in relation to some expected tooth event in the future which might just never come so we shall protect from over-dwell | |||
| 466 |
2/2✓ Branch 0 taken 2087 times.
✓ Branch 1 taken 4 times.
|
2/2✓ Decision 'true' taken 2087 times.
✓ Decision 'false' taken 4 times.
|
2091 | if (!limitedSpark) { |
| 467 | // auto fire spark at 1.5x nominal dwell | |||
| 468 | 2087 | efitick_t fireTime = sumTickAndFloat(chargeTime, MSF2NT(1.5f * dwellMs)); | ||
| 469 | ||||
| 470 | #if SPARK_EXTREME_LOGGING | |||
| 471 | 2087 | efitimeus_t fireTimeUs = NT2US(fireTime); | ||
| 472 | 2087 | efiPrintf("[%s] %d overdwell scheduling at %d ticks (%d.%06d)", | ||
| 473 | event->getOutputForLoggins()->getName(), event->sparkCounter, | |||
| 474 | time2print(fireTime), time2print(fireTimeUs / (1000 * 1000)), time2print(fireTimeUs % (1000 * 1000))); | |||
| 475 | #endif /* SPARK_EXTREME_LOGGING */ | |||
| 476 | ||||
| 477 | /** | |||
| 478 | * todo: can we please comprehend/document how this even works? we seem to be reusing 'sparkEvent.scheduling' instance | |||
| 479 | * and it looks like current (smart?) re-queuing is effectively cancelling out the overdwell? is that the way this was intended to work? | |||
| 480 | * [tag:overdwell] | |||
| 481 | */ | |||
| 482 |
1/1✓ Branch 3 taken 2087 times.
|
2087 | engine->scheduler.schedule("overdwell", &event->sparkEvent.eventScheduling, fireTime, action_s::make<overFireSparkAndPrepareNextSchedule>( event )); | |
| 483 | ||||
| 484 | #if EFI_UNIT_TEST | |||
| 485 | 2087 | engine->onScheduleOverFireSparkAndPrepareNextSchedule(*event, fireTime); | ||
| 486 | #endif | |||
| 487 | } else { | |||
| 488 | 4 | engine->engineState.overDwellNotScheduledCounter++; | ||
| 489 | } | |||
| 490 | } | |||
| 491 | ||||
| 492 | #if EFI_UNIT_TEST | |||
| 493 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9612 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 9612 times.
|
9612 | if (verboseMode) { |
| 494 | ✗ | efiPrintf("spark dwell@ %.1f spark@ %.2f id=%d sparkCounter=%d", event->dwellAngle, | ||
| 495 | event->sparkEvent.getAngle(), | |||
| 496 | event->coilIndex, | |||
| 497 | event->sparkCounter); | |||
| 498 | } | |||
| 499 | #endif | |||
| 500 | } | |||
| 501 | ||||
| 502 | 108 | void initializeIgnitionActions() { | ||
| 503 | 108 | IgnitionEventList *list = &engine->ignitionEvents; | ||
| 504 | 108 | angle_t dwellAngle = engine->ignitionState.dwellDurationAngle; | ||
| 505 | 108 | floatms_t sparkDwell = engine->ignitionState.getDwell(); | ||
| 506 |
3/6✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 108 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 108 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 108 times.
|
108 | if (std::isnan(engine->engineState.timingAdvance[0]) || std::isnan(dwellAngle)) { |
| 507 | // error should already be reported | |||
| 508 | // need to invalidate previous ignition schedule | |||
| 509 | ✗ | list->isReady = false; | ||
| 510 | ✗ | return; | ||
| 511 | } | |||
| 512 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 108 times.
|
108 | efiAssertVoid(ObdCode::CUSTOM_ERR_6592, engineConfiguration->cylindersCount > 0, "cylindersCount"); | |
| 513 | ||||
| 514 |
2/2✓ Branch 0 taken 445 times.
✓ Branch 1 taken 108 times.
|
2/2✓ Decision 'true' taken 445 times.
✓ Decision 'false' taken 108 times.
|
553 | for (size_t cylinderIndex = 0; cylinderIndex < engineConfiguration->cylindersCount; cylinderIndex++) { |
| 515 | 445 | list->elements[cylinderIndex].cylinderIndex = cylinderIndex; | ||
| 516 | 445 | prepareCylinderIgnitionSchedule(dwellAngle, sparkDwell, &list->elements[cylinderIndex]); | ||
| 517 | } | |||
| 518 | 108 | list->isReady = true; | ||
| 519 | } | |||
| 520 | ||||
| 521 | 107 | static void prepareIgnitionSchedule() { | ||
| 522 | 107 | ScopePerf perf(PE::PrepareIgnitionSchedule); | ||
| 523 | ||||
| 524 |
2/2✓ Branch 1 taken 107 times.
✓ Branch 4 taken 107 times.
|
107 | operation_mode_e operationMode = getEngineRotationState()->getOperationMode(); | |
| 525 | float maxAllowedDwellAngle; | |||
| 526 | ||||
| 527 |
3/3✓ Branch 1 taken 107 times.
✓ Branch 3 taken 72 times.
✓ Branch 4 taken 35 times.
|
2/2✓ Decision 'true' taken 72 times.
✓ Decision 'false' taken 35 times.
|
107 | if (getCurrentIgnitionMode() == IM_ONE_COIL) { |
| 528 |
1/1✓ Branch 1 taken 72 times.
|
72 | maxAllowedDwellAngle = getEngineCycle(operationMode) / engineConfiguration->cylindersCount / 1.1; | |
| 529 | } else { | |||
| 530 |
1/1✓ Branch 1 taken 35 times.
|
35 | maxAllowedDwellAngle = (int) (getEngineCycle(operationMode) / 2); // the cast is about making Coverity happy | |
| 531 | } | |||
| 532 | ||||
| 533 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 107 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 107 times.
|
107 | if (engine->ignitionState.dwellDurationAngle == 0) { |
| 534 | ✗ | warning(ObdCode::CUSTOM_ZERO_DWELL, "dwell is zero?"); | ||
| 535 | } | |||
| 536 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 105 times.
|
2/2✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 105 times.
|
107 | if (engine->ignitionState.dwellDurationAngle > maxAllowedDwellAngle) { |
| 537 |
1/1✓ Branch 1 taken 2 times.
|
2 | warning(ObdCode::CUSTOM_DWELL_TOO_LONG, "dwell angle too long: %.2f", engine->ignitionState.dwellDurationAngle); | |
| 538 | } | |||
| 539 | ||||
| 540 | // todo: add some check for dwell overflow? like 4 times 6 ms while engine cycle is less then that | |||
| 541 | ||||
| 542 |
1/1✓ Branch 1 taken 107 times.
|
107 | initializeIgnitionActions(); | |
| 543 | 107 | } | ||
| 544 | ||||
| 545 | 33121 | void onTriggerEventSparkLogic(float rpm, efitick_t edgeTimestamp, float currentPhase, float nextPhase) { | ||
| 546 | 33121 | ScopePerf perf(PE::OnTriggerEventSparkLogic); | ||
| 547 | ||||
| 548 |
2/2✓ Branch 0 taken 673 times.
✓ Branch 1 taken 32448 times.
|
2/2✓ Decision 'true' taken 673 times.
✓ Decision 'false' taken 32448 times.
|
33121 | if (!engineConfiguration->isIgnitionEnabled) { |
| 549 | 673 | return; | ||
| 550 | } | |||
| 551 | ||||
| 552 |
2/2✓ Branch 1 taken 32448 times.
✓ Branch 4 taken 32448 times.
|
32448 | LimpState limitedSparkState = getLimpManager()->allowIgnition(); | |
| 553 | ||||
| 554 | // todo: eliminate state copy logic by giving limpManager it's owm limp_manager.txt and leveraging LiveData | |||
| 555 | 32448 | engine->outputChannels.sparkCutReason = (int8_t)limitedSparkState.reason; | ||
| 556 | 32448 | bool limitedSpark = !limitedSparkState.value; | ||
| 557 | ||||
| 558 |
1/1✓ Branch 1 taken 32448 times.
|
32448 | const floatms_t dwellMs = engine->ignitionState.getDwell(); | |
| 559 |
3/6✓ Branch 1 taken 32448 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 32448 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 32448 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 32448 times.
|
32448 | if (std::isnan(dwellMs) || dwellMs <= 0) { |
| 560 | ✗ | warning(ObdCode::CUSTOM_DWELL, "invalid dwell to handle: %.2f", dwellMs); | ||
| 561 | ✗ | return; | ||
| 562 | } | |||
| 563 | ||||
| 564 |
2/2✓ Branch 0 taken 107 times.
✓ Branch 1 taken 32341 times.
|
2/2✓ Decision 'true' taken 107 times.
✓ Decision 'false' taken 32341 times.
|
32448 | if (!engine->ignitionEvents.isReady) { |
| 565 |
1/1✓ Branch 1 taken 107 times.
|
107 | prepareIgnitionSchedule(); | |
| 566 | } | |||
| 567 | ||||
| 568 | ||||
| 569 | /** | |||
| 570 | * Ignition schedule is defined once per revolution | |||
| 571 | * See initializeIgnitionActions() | |||
| 572 | */ | |||
| 573 | ||||
| 574 | ||||
| 575 | // Only apply odd cylinder count wasted logic if: | |||
| 576 | // - odd cyl count | |||
| 577 | // - current mode is wasted spark | |||
| 578 | // - four stroke | |||
| 579 | bool enableOddCylinderWastedSpark = | |||
| 580 | 32448 | engine->engineState.useOddFireWastedSpark | ||
| 581 |
5/5✓ Branch 0 taken 2104 times.
✓ Branch 1 taken 30344 times.
✓ Branch 3 taken 2104 times.
✓ Branch 5 taken 593 times.
✓ Branch 6 taken 1511 times.
|
32448 | && getCurrentIgnitionMode() == IM_WASTED_SPARK; | |
| 582 | ||||
| 583 |
1/2✓ Branch 0 taken 32448 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 32448 times.
✗ Decision 'false' not taken.
|
32448 | if (engine->ignitionEvents.isReady) { |
| 584 |
2/2✓ Branch 0 taken 130065 times.
✓ Branch 1 taken 32448 times.
|
2/2✓ Decision 'true' taken 130065 times.
✓ Decision 'false' taken 32448 times.
|
162513 | for (size_t i = 0; i < engineConfiguration->cylindersCount; i++) { |
| 585 | 130065 | IgnitionEvent *event = &engine->ignitionEvents.elements[i]; | ||
| 586 | ||||
| 587 | 130065 | angle_t dwellAngle = event->dwellAngle; | ||
| 588 | ||||
| 589 | 130065 | angle_t sparkAngle = event->sparkAngle; | ||
| 590 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 130065 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 130065 times.
|
130065 | if (std::isnan(sparkAngle)) { |
| 591 | ✗ | warning(ObdCode::CUSTOM_ADVANCE_SPARK, "NaN advance"); | ||
| 592 | ✗ | continue; | ||
| 593 | } | |||
| 594 | ||||
| 595 | 130065 | bool isOddCylWastedEvent = false; | ||
| 596 |
2/2✓ Branch 0 taken 1184 times.
✓ Branch 1 taken 128881 times.
|
2/2✓ Decision 'true' taken 1184 times.
✓ Decision 'false' taken 128881 times.
|
130065 | if (enableOddCylinderWastedSpark) { |
| 597 | 1184 | auto dwellAngleWastedEvent = dwellAngle + 360; | ||
| 598 |
2/2✓ Branch 0 taken 1151 times.
✓ Branch 1 taken 33 times.
|
2/2✓ Decision 'true' taken 1151 times.
✓ Decision 'false' taken 33 times.
|
1184 | if (dwellAngleWastedEvent > 720) { |
| 599 | 1151 | dwellAngleWastedEvent -= 720; | ||
| 600 | } | |||
| 601 | ||||
| 602 | // Check whether this event hits 360 degrees out from now (ie, wasted spark), | |||
| 603 | // and if so, twiddle the dwell and spark angles so it happens now instead | |||
| 604 |
1/1✓ Branch 1 taken 1184 times.
|
1184 | isOddCylWastedEvent = isPhaseInRange(dwellAngleWastedEvent, currentPhase, nextPhase); | |
| 605 | ||||
| 606 |
2/2✓ Branch 0 taken 28 times.
✓ Branch 1 taken 1156 times.
|
2/2✓ Decision 'true' taken 28 times.
✓ Decision 'false' taken 1156 times.
|
1184 | if (isOddCylWastedEvent) { |
| 607 | 28 | dwellAngle = dwellAngleWastedEvent; | ||
| 608 | ||||
| 609 | 28 | sparkAngle += 360; | ||
| 610 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 1 time.
|
2/2✓ Decision 'true' taken 27 times.
✓ Decision 'false' taken 1 time.
|
28 | if (sparkAngle > 720) { |
| 611 | 27 | sparkAngle -= 720; | ||
| 612 | } | |||
| 613 | } | |||
| 614 | } | |||
| 615 | ||||
| 616 |
7/7✓ Branch 0 taken 130037 times.
✓ Branch 1 taken 28 times.
✓ Branch 3 taken 130037 times.
✓ Branch 5 taken 120453 times.
✓ Branch 6 taken 9584 times.
✓ Branch 7 taken 120453 times.
✓ Branch 8 taken 9612 times.
|
2/2✓ Decision 'true' taken 120453 times.
✓ Decision 'false' taken 9612 times.
|
130065 | if (!isOddCylWastedEvent && !isPhaseInRange(dwellAngle, currentPhase, nextPhase)) { |
| 617 | 120453 | continue; | ||
| 618 | } | |||
| 619 | ||||
| 620 |
4/9✓ Branch 0 taken 2380 times.
✓ Branch 1 taken 7232 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2380 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 9612 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 9612 times.
|
9612 | if (i == 0 && engineConfiguration->artificialTestMisfire && (getRevolutionCounter() % ((int)engineConfiguration->scriptSetting[5]) == 0)) { |
| 621 | // artificial misfire on cylinder #1 for testing purposes | |||
| 622 | // enable artificialMisfire | |||
| 623 | ✗ | warning(ObdCode::CUSTOM_ARTIFICIAL_MISFIRE, "artificial misfire on cylinder #1 for testing purposes %d", engine->engineState.globalSparkCounter); | ||
| 624 | ✗ | continue; | ||
| 625 | } | |||
| 626 | #if EFI_LAUNCH_CONTROL | |||
| 627 |
4/6✓ Branch 1 taken 9612 times.
✓ Branch 3 taken 9612 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 9612 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 9612 times.
|
9612 | bool sparkLimited = engine->softSparkLimiter.shouldSkip() || engine->hardSparkLimiter.shouldSkip(); | |
| 628 | 9612 | engine->ignitionState.luaIgnitionSkip = sparkLimited; | ||
| 629 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9612 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 9612 times.
|
9612 | if (sparkLimited) { |
| 630 | ✗ | continue; | ||
| 631 | } | |||
| 632 | #endif // EFI_LAUNCH_CONTROL | |||
| 633 | ||||
| 634 | #if EFI_ANTILAG_SYSTEM && EFI_LAUNCH_CONTROL | |||
| 635 | /* | |||
| 636 | if (engine->antilagController.isAntilagCondition) { | |||
| 637 | if (engine->ALSsoftSparkLimiter.shouldSkip()) { | |||
| 638 | continue; | |||
| 639 | } | |||
| 640 | } | |||
| 641 | float throttleIntent = Sensor::getOrZero(SensorType::DriverThrottleIntent); | |||
| 642 | engine->antilagController.timingALSSkip = interpolate3d( | |||
| 643 | config->ALSIgnSkipTable, | |||
| 644 | config->alsIgnSkipLoadBins, throttleIntent, | |||
| 645 | config->alsIgnSkiprpmBins, rpm | |||
| 646 | ); | |||
| 647 | ||||
| 648 | auto ALSSkipRatio = engine->antilagController.timingALSSkip; | |||
| 649 | engine->ALSsoftSparkLimiter.setTargetSkipRatio(ALSSkipRatio/100); | |||
| 650 | */ | |||
| 651 | #endif // EFI_ANTILAG_SYSTEM | |||
| 652 | ||||
| 653 |
1/1✓ Branch 1 taken 9612 times.
|
9612 | scheduleSparkEvent(limitedSpark, event, rpm, dwellMs, dwellAngle, sparkAngle, edgeTimestamp, currentPhase, nextPhase); | |
| 654 | } | |||
| 655 | } | |||
| 656 | } | |||
| 657 | ||||
| 658 | /** | |||
| 659 | * Number of sparks per physical coil | |||
| 660 | * @see getNumberOfInjections | |||
| 661 | */ | |||
| 662 | 531079 | int getNumberOfSparks(ignition_mode_e mode) { | ||
| 663 |
3/5✓ Branch 0 taken 397736 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25543 times.
✓ Branch 3 taken 107800 times.
✗ Branch 4 not taken.
|
531079 | switch (mode) { | |
| 664 |
1/1✓ Decision 'true' taken 397736 times.
|
397736 | case IM_ONE_COIL: | |
| 665 | 397736 | return engineConfiguration->cylindersCount; | ||
| 666 | ✗ | case IM_TWO_COILS: | ||
| 667 | ✗ | return engineConfiguration->cylindersCount / 2; | ||
| 668 |
1/1✓ Decision 'true' taken 25543 times.
|
25543 | case IM_INDIVIDUAL_COILS: | |
| 669 | 25543 | return 1; | ||
| 670 |
1/1✓ Decision 'true' taken 107800 times.
|
107800 | case IM_WASTED_SPARK: | |
| 671 | 107800 | return 2; | ||
| 672 | ✗ | default: | ||
| 673 | ✗ | firmwareError(ObdCode::CUSTOM_ERR_IGNITION_MODE, "Unexpected ignition_mode_e %d", mode); | ||
| 674 | ✗ | return 1; | ||
| 675 | } | |||
| 676 | } | |||
| 677 | ||||
| 678 | /** | |||
| 679 | * @see getInjectorDutyCycle | |||
| 680 | */ | |||
| 681 | 531079 | percent_t getCoilDutyCycle(float rpm) { | ||
| 682 | 531079 | floatms_t totalPerCycle = engine->ignitionState.getDwell() * getNumberOfSparks(getCurrentIgnitionMode()); | ||
| 683 |
2/2✓ Branch 3 taken 1382 times.
✓ Branch 4 taken 529697 times.
|
531079 | floatms_t engineCycleDuration = getCrankshaftRevolutionTimeMs(rpm) * (getEngineRotationState()->getOperationMode() == TWO_STROKE ? 1 : 2); | |
| 684 | 531079 | return 100 * totalPerCycle / engineCycleDuration; | ||
| 685 | } | |||
| 686 | ||||
| 687 | #endif // EFI_ENGINE_CONTROL | |||
| 688 |