| 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 | 6944 | static void fireSparkBySettingPinLow(IgnitionEvent *event, IgnitionOutputPin *output) { | ||
| 30 | #if SPARK_EXTREME_LOGGING | |||
| 31 | 6944 | 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 | 6944 | output->signalFallSparkId = event->sparkCounter; | ||
| 44 | ||||
| 45 |
6/6✓ Branch 0 taken 665 times.
✓ Branch 1 taken 6279 times.
✓ Branch 2 taken 480 times.
✓ Branch 3 taken 185 times.
✓ Branch 4 taken 473 times.
✓ Branch 5 taken 7 times.
|
2/2✓ Decision 'true' taken 473 times.
✓ Decision 'false' taken 6471 times.
|
6944 | if (!output->currentLogicValue && !event->wasSparkLimited && !event->wasSparkCanceled) { |
| 46 | #if SPARK_EXTREME_LOGGING | |||
| 47 | 473 | efiPrintf("out-of-order coil off %s", output->getName()); | ||
| 48 | #endif /* SPARK_EXTREME_LOGGING */ | |||
| 49 | 473 | warning(ObdCode::CUSTOM_OUT_OF_ORDER_COIL, "out-of-order coil off %s", output->getName()); | ||
| 50 | } | |||
| 51 | 6944 | output->setLow(); | ||
| 52 | 6944 | } | ||
| 53 | ||||
| 54 | 7421 | static void assertPinAssigned(IgnitionOutputPin* output) { | ||
| 55 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 7421 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 7421 times.
|
7421 | if (!output->isInitialized()) { |
| 56 | ✗ | warning(ObdCode::CUSTOM_OBD_COIL_PIN_NOT_ASSIGNED, "Pin Not Assigned check configuration #%s", output->getName()); \ | ||
| 57 | } | |||
| 58 | 7421 | } | ||
| 59 | ||||
| 60 | /** | |||
| 61 | * @param cylinderIndex from 0 to cylinderCount, not cylinder number | |||
| 62 | */ | |||
| 63 | 7318 | static int getIgnitionPinForIndex(int cylinderIndex, ignition_mode_e ignitionMode) { | ||
| 64 |
4/5✓ Branch 0 taken 6235 times.
✓ Branch 1 taken 617 times.
✓ Branch 2 taken 454 times.
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
|
7318 | switch (ignitionMode) { | |
| 65 |
1/1✓ Decision 'true' taken 6235 times.
|
6235 | case IM_ONE_COIL: | |
| 66 | 6235 | return 0; | ||
| 67 |
1/1✓ Decision 'true' taken 617 times.
|
617 | case IM_WASTED_SPARK: { | |
| 68 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 617 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 617 times.
|
617 | if (engineConfiguration->cylindersCount == 1) { |
| 69 | // we do not want to divide by zero | |||
| 70 | ✗ | return 0; | ||
| 71 | } | |||
| 72 | 617 | return cylinderIndex % (engineConfiguration->cylindersCount / 2); | ||
| 73 | } | |||
| 74 |
1/1✓ Decision 'true' taken 454 times.
|
454 | case IM_INDIVIDUAL_COILS: | |
| 75 | 454 | 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 | 7318 | 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 | 7318 | event->sparkDwell = sparkDwell; | ||
| 90 | ||||
| 91 |
1/1✓ Branch 1 taken 7318 times.
|
7318 | 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 689 times.
✓ Branch 1 taken 6629 times.
✓ Branch 2 taken 72 times.
✓ Branch 3 taken 617 times.
|
2/2✓ Decision 'true' taken 72 times.
✓ Decision 'false' taken 7246 times.
|
7318 | if (ignitionMode == IM_WASTED_SPARK && engine->engineState.useOddFireWastedSpark) { |
| 97 | 72 | ignitionMode = IM_INDIVIDUAL_COILS; | ||
| 98 | } | |||
| 99 | ||||
| 100 |
1/1✓ Branch 1 taken 7318 times.
|
7318 | const int index = getIgnitionPinForIndex(event->cylinderIndex, ignitionMode); | |
| 101 |
1/1✓ Branch 1 taken 7318 times.
|
7318 | const int coilIndex = getCylinderNumberAtIndex(index); | |
| 102 |
1/1✓ Branch 1 taken 7318 times.
|
7318 | angle_t finalIgnitionTiming = getEngineState()->timingAdvance[coilIndex]; | |
| 103 | // Stash which cylinder we're scheduling so that knock sensing knows which | |||
| 104 | // cylinder just fired | |||
| 105 | 7318 | 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 7287 times.
|
2/2✓ Decision 'true' taken 31 times.
✓ Decision 'false' taken 7287 times.
|
7318 | 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 | 7318 | engine->outputChannels.ignitionAdvanceCyl[event->cylinderIndex] = finalIgnitionTiming; | ||
| 122 | ||||
| 123 | 7318 | 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 7318 times.
|
7318 | + getPerCylinderFiringOrderOffset(event->cylinderIndex, coilIndex); | |
| 128 | ||||
| 129 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 7318 times.
✗ Branch 4 not taken.
|
7318 | efiAssertVoid(ObdCode::CUSTOM_SPARK_ANGLE_1, !std::isnan(sparkAngle), "sparkAngle#1"); | |
| 130 |
1/1✓ Branch 1 taken 7318 times.
|
7318 | wrapAngle(sparkAngle, "findAngle#2", ObdCode::CUSTOM_ERR_6550); | |
| 131 | 7318 | event->sparkAngle = sparkAngle; | ||
| 132 | ||||
| 133 | 7318 | engine->outputChannels.currentIgnitionMode = static_cast<uint8_t>(ignitionMode); | ||
| 134 | ||||
| 135 | 7318 | IgnitionOutputPin *output = &enginePins.coils[coilIndex]; | ||
| 136 | 7318 | 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 7312 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 550 times.
✓ Branch 3 taken 6762 times.
|
7318 | bool isTwoWireWasted = engineConfiguration->twoWireBatchIgnition || (engineConfiguration->ignitionMode == IM_INDIVIDUAL_COILS); | |
| 143 |
4/4✓ Branch 0 taken 617 times.
✓ Branch 1 taken 6701 times.
✓ Branch 2 taken 103 times.
✓ Branch 3 taken 514 times.
|
2/2✓ Decision 'true' taken 103 times.
✓ Decision 'false' taken 7215 times.
|
7318 | if (ignitionMode == IM_WASTED_SPARK && isTwoWireWasted) { |
| 144 | 103 | int secondIndex = index + engineConfiguration->cylindersCount / 2; | ||
| 145 |
1/1✓ Branch 1 taken 103 times.
|
103 | int secondCoilIndex = getCylinderNumberAtIndex(secondIndex); | |
| 146 | 103 | secondOutput = &enginePins.coils[secondCoilIndex]; | ||
| 147 |
1/1✓ Branch 1 taken 103 times.
|
103 | assertPinAssigned(secondOutput); | |
| 148 | 103 | } else { | ||
| 149 | 7215 | secondOutput = nullptr; | ||
| 150 | } | |||
| 151 | ||||
| 152 |
1/1✓ Branch 1 taken 7318 times.
|
7318 | assertPinAssigned(output); | |
| 153 | ||||
| 154 | 7318 | event->outputs[1] = secondOutput; | ||
| 155 | ||||
| 156 | ||||
| 157 | 7318 | angle_t dwellStartAngle = sparkAngle - dwellAngleDuration; | ||
| 158 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 7318 times.
✗ Branch 4 not taken.
|
7318 | efiAssertVoid(ObdCode::CUSTOM_ERR_6590, !std::isnan(dwellStartAngle), "findAngle#5"); | |
| 159 | ||||
| 160 |
2/5✓ Branch 0 taken 7318 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 7318 times.
✗ Branch 5 not taken.
|
7318 | assertAngleRange(dwellStartAngle, "findAngle dwellStartAngle", ObdCode::CUSTOM_ERR_6550); | |
| 161 |
1/1✓ Branch 1 taken 7318 times.
|
7318 | wrapAngle(dwellStartAngle, "findAngle#7", ObdCode::CUSTOM_ERR_6550); | |
| 162 | 7318 | event->dwellAngle = dwellStartAngle; | ||
| 163 | ||||
| 164 | #if FUEL_MATH_EXTREME_LOGGING | |||
| 165 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7318 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 7318 times.
|
7318 | 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 | 315 | static void overFireSparkAndPrepareNextSchedule(IgnitionEvent *event) { | ||
| 187 | #if SPARK_EXTREME_LOGGING | |||
| 188 | 315 | efiPrintf("[%s] %d %s", | ||
| 189 | event->getOutputForLoggins()->getName(), event->sparkCounter, | |||
| 190 | __func__); | |||
| 191 | #endif /* SPARK_EXTREME_LOGGING */ | |||
| 192 | 315 | float actualDwellMs = event->actualDwellTimer.getElapsedSeconds() * 1e3; | ||
| 193 | ||||
| 194 | 630 | warning((ObdCode)((int)ObdCode::CUSTOM_Ignition_Coil_Overcharge_1 + event->cylinderIndex), | ||
| 195 | "cylinder %d %s overcharge %f ms", | |||
| 196 | 315 | event->cylinderIndex + 1, event->outputs[0]->getName(), actualDwellMs); | ||
| 197 | ||||
| 198 | // kill pending fire | |||
| 199 | 315 | engine->module<TriggerScheduler>()->cancel(&event->sparkEvent); | ||
| 200 | ||||
| 201 | 315 | engine->engineState.overDwellCanceledCounter++; | ||
| 202 | 315 | event->wasSparkCanceled = true; | ||
| 203 | 315 | fireSparkAndPrepareNextSchedule(event); | ||
| 204 | 315 | } | ||
| 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 | 6867 | void fireSparkAndPrepareNextSchedule(IgnitionEvent *event) { | ||
| 210 | #if EFI_UNIT_TEST | |||
| 211 |
2/2✓ Branch 1 taken 268 times.
✓ Branch 2 taken 6599 times.
|
2/2✓ Decision 'true' taken 268 times.
✓ Decision 'false' taken 6599 times.
|
6867 | if (engine->onIgnitionEvent) { |
| 212 | 268 | engine->onIgnitionEvent(event, false); | ||
| 213 | } | |||
| 214 | #endif | |||
| 215 | ||||
| 216 |
2/2✓ Branch 0 taken 13734 times.
✓ Branch 1 taken 6867 times.
|
2/2✓ Decision 'true' taken 13734 times.
✓ Decision 'false' taken 6867 times.
|
20601 | for (int i = 0; i< MAX_OUTPUTS_FOR_IGNITION;i++) { |
| 217 | 13734 | IgnitionOutputPin *output = event->outputs[i]; | ||
| 218 | ||||
| 219 |
2/2✓ Branch 0 taken 6944 times.
✓ Branch 1 taken 6790 times.
|
2/2✓ Decision 'true' taken 6944 times.
✓ Decision 'false' taken 6790 times.
|
13734 | if (output) { |
| 220 | 6944 | fireSparkBySettingPinLow(event, output); | ||
| 221 | } | |||
| 222 | } | |||
| 223 | ||||
| 224 | 6867 | efitick_t nowNt = getTimeNowNt(); | ||
| 225 | ||||
| 226 | #if EFI_TOOTH_LOGGER | |||
| 227 | 6867 | LogTriggerCoilState(nowNt, false, event->coilIndex); | ||
| 228 | #endif // EFI_TOOTH_LOGGER | |||
| 229 |
2/2✓ Branch 0 taken 6683 times.
✓ Branch 1 taken 184 times.
|
2/2✓ Decision 'true' taken 6683 times.
✓ Decision 'false' taken 184 times.
|
6867 | 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 | 6683 | float actualDwellMs = event->actualDwellTimer.getElapsedSeconds(nowNt) * 1e3; | ||
| 234 | 6683 | float ratio = actualDwellMs / event->sparkDwell; | ||
| 235 | ||||
| 236 |
2/2✓ Branch 0 taken 607 times.
✓ Branch 1 taken 6076 times.
|
2/2✓ Decision 'true' taken 607 times.
✓ Decision 'false' taken 6076 times.
|
6683 | if (ratio > 1.2) { |
| 237 | 607 | engine->engineState.dwellOverChargeCounter++; | ||
| 238 |
2/2✓ Branch 0 taken 2217 times.
✓ Branch 1 taken 3859 times.
|
2/2✓ Decision 'true' taken 2217 times.
✓ Decision 'false' taken 3859 times.
|
6076 | } else if (ratio < 0.8) { |
| 239 | 2217 | engine->engineState.dwellUnderChargeCounter++; | ||
| 240 | } | |||
| 241 | 6683 | 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 | 6867 | angle_t dwellAngleDuration = engine->ignitionState.dwellDurationAngle; | ||
| 247 | 6867 | floatms_t sparkDwell = engine->ignitionState.getDwell(); | ||
| 248 |
3/6✓ Branch 1 taken 6867 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6867 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 6867 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 6867 times.
|
6867 | 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 6867 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 6867 times.
|
6867 | 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 6866 times.
|
2/2✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 6866 times.
|
6867 | 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 | 6867 | prepareCylinderIgnitionSchedule(dwellAngleDuration, sparkDwell, event); | ||
| 281 | } | |||
| 282 | ||||
| 283 | 6867 | engine->onSparkFireKnockSense(event->cylinderIndex, nowNt); | ||
| 284 | } | |||
| 285 | ||||
| 286 | 6759 | 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 | 6759 | 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 | 6759 | event->wasSparkCanceled = false; | ||
| 309 | ||||
| 310 |
2/2✓ Branch 0 taken 477 times.
✓ Branch 1 taken 6282 times.
|
2/2✓ Decision 'true' taken 477 times.
✓ Decision 'false' taken 6282 times.
|
6759 | 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 477 times.
|
477 | 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 | 477 | engine->engineState.sparkOutOfOrderCounter++; | ||
| 325 | 477 | return true; | ||
| 326 | } | |||
| 327 | ||||
| 328 | 6282 | output->setHigh(); | ||
| 329 | 6282 | return false; | ||
| 330 | } | |||
| 331 | ||||
| 332 | 6683 | void turnSparkPinHighStartCharging(IgnitionEvent *event) { | ||
| 333 | 6683 | efitick_t nowNt = getTimeNowNt(); | ||
| 334 | ||||
| 335 | 6683 | event->actualDwellTimer.reset(nowNt); | ||
| 336 | ||||
| 337 | 6683 | bool skippedDwellDueToTriggerNoised = false; | ||
| 338 |
2/2✓ Branch 0 taken 13366 times.
✓ Branch 1 taken 6683 times.
|
2/2✓ Decision 'true' taken 13366 times.
✓ Decision 'false' taken 6683 times.
|
20049 | for (int i = 0; i< MAX_OUTPUTS_FOR_IGNITION;i++) { |
| 339 | 13366 | IgnitionOutputPin *output = event->outputs[i]; | ||
| 340 |
2/2✓ Branch 0 taken 6759 times.
✓ Branch 1 taken 6607 times.
|
2/2✓ Decision 'true' taken 6759 times.
✓ Decision 'false' taken 6607 times.
|
13366 | 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 | 6759 | skippedDwellDueToTriggerNoised |= startDwellByTurningSparkPinHigh(event, output); | ||
| 346 | } | |||
| 347 | } | |||
| 348 | ||||
| 349 | #if EFI_UNIT_TEST | |||
| 350 | 6683 | engine->incrementBailedOnDwellCount(); | ||
| 351 | #endif | |||
| 352 | ||||
| 353 | ||||
| 354 |
2/2✓ Branch 0 taken 6211 times.
✓ Branch 1 taken 472 times.
|
2/2✓ Decision 'true' taken 6211 times.
✓ Decision 'false' taken 472 times.
|
6683 | if (!skippedDwellDueToTriggerNoised) { |
| 355 | ||||
| 356 | #if EFI_UNIT_TEST | |||
| 357 |
2/2✓ Branch 1 taken 256 times.
✓ Branch 2 taken 5955 times.
|
2/2✓ Decision 'true' taken 256 times.
✓ Decision 'false' taken 5955 times.
|
6211 | if (engine->onIgnitionEvent) { |
| 358 | 256 | engine->onIgnitionEvent(event, true); | ||
| 359 | } | |||
| 360 | #endif | |||
| 361 | ||||
| 362 | #if EFI_TOOTH_LOGGER | |||
| 363 | 6211 | LogTriggerCoilState(nowNt, true, event->coilIndex); | ||
| 364 | #endif // EFI_TOOTH_LOGGER | |||
| 365 | } | |||
| 366 | ||||
| 367 | ||||
| 368 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 6682 times.
|
2/2✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 6682 times.
|
6683 | 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 | 6683 | } | ||
| 377 | ||||
| 378 | ||||
| 379 | 9650 | 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 | 9650 | float angleOffset = dwellAngle - currentPhase; | ||
| 384 |
2/2✓ Branch 0 taken 148 times.
✓ Branch 1 taken 9502 times.
|
2/2✓ Decision 'true' taken 148 times.
✓ Decision 'false' taken 9502 times.
|
9650 | 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 | 9650 | event->sparkCounter = engine->engineState.globalSparkCounter++; | ||
| 393 | 9650 | event->wasSparkLimited = limitedSpark; | ||
| 394 | ||||
| 395 | 9650 | 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 9458 times.
✓ Branch 1 taken 192 times.
|
2/2✓ Decision 'true' taken 9458 times.
✓ Decision 'false' taken 192 times.
|
9650 | if (!limitedSpark) { |
| 401 | #if SPARK_EXTREME_LOGGING | |||
| 402 | 9458 | 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 9458 times.
|
9458 | chargeTime = scheduleByAngle(&event->dwellStartTimer, edgeTimestamp, angleOffset, action_s::make<turnSparkPinHighStartCharging>( event )); | |
| 413 | ||||
| 414 | #if EFI_UNIT_TEST | |||
| 415 | 9458 | engine->onScheduleTurnSparkPinHighStartCharging(*event, edgeTimestamp, angleOffset, chargeTime); | ||
| 416 | #endif | |||
| 417 | ||||
| 418 | #if SPARK_EXTREME_LOGGING | |||
| 419 | 9458 | efitimeus_t chargeTimeUs = NT2US(chargeTime); | ||
| 420 | 9458 | 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 | 9458 | event->sparksRemaining = engine->engineState.multispark.count; | ||
| 426 | } else { | |||
| 427 | // don't fire multispark if spark is cut completely! | |||
| 428 | 192 | event->sparksRemaining = 0; | ||
| 429 | ||||
| 430 | #if SPARK_EXTREME_LOGGING | |||
| 431 | 192 | 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 9650 times.
|
9650 | efiAssertVoid(ObdCode::CUSTOM_ERR_6591, !std::isnan(sparkAngle), "findAngle#4"); | |
| 441 |
2/4✓ Branch 0 taken 9650 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9650 times.
|
9650 | assertAngleRange(sparkAngle, "findAngle#a5", ObdCode::CUSTOM_ERR_6549); | |
| 442 | ||||
| 443 | #if SPARK_EXTREME_LOGGING | |||
| 444 | 9650 | 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 9650 times.
|
19300 | bool isTimeScheduled = engine->module<TriggerScheduler>()->scheduleOrQueue( | |
| 451 | "spark", | |||
| 452 | &event->sparkEvent, edgeTimestamp, sparkAngle, | |||
| 453 | 19300 | action_s::make<fireSparkAndPrepareNextSchedule>( event ), | ||
| 454 | 9650 | currentPhase, nextPhase); | ||
| 455 | ||||
| 456 | #if SPARK_EXTREME_LOGGING | |||
| 457 |
2/2✓ Branch 0 taken 7613 times.
✓ Branch 1 taken 2037 times.
|
9650 | 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 2037 times.
✓ Branch 1 taken 7613 times.
|
2/2✓ Decision 'true' taken 2037 times.
✓ Decision 'false' taken 7613 times.
|
9650 | 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 2030 times.
✓ Branch 1 taken 7 times.
|
2/2✓ Decision 'true' taken 2030 times.
✓ Decision 'false' taken 7 times.
|
2037 | if (!limitedSpark) { |
| 467 | // auto fire spark at 1.5x nominal dwell | |||
| 468 | 2030 | efitick_t fireTime = sumTickAndFloat(chargeTime, MSF2NT(1.5f * dwellMs)); | ||
| 469 | ||||
| 470 | #if SPARK_EXTREME_LOGGING | |||
| 471 | 2030 | efitimeus_t fireTimeUs = NT2US(fireTime); | ||
| 472 | 2030 | 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 2030 times.
|
2030 | engine->scheduler.schedule("overdwell", &event->sparkEvent.eventScheduling, fireTime, action_s::make<overFireSparkAndPrepareNextSchedule>( event )); | |
| 483 | ||||
| 484 | #if EFI_UNIT_TEST | |||
| 485 | 2030 | engine->onScheduleOverFireSparkAndPrepareNextSchedule(*event, fireTime); | ||
| 486 | #endif | |||
| 487 | } else { | |||
| 488 | 7 | engine->engineState.overDwellNotScheduledCounter++; | ||
| 489 | } | |||
| 490 | } | |||
| 491 | ||||
| 492 | #if EFI_UNIT_TEST | |||
| 493 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9650 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 9650 times.
|
9650 | 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 | 110 | void initializeIgnitionActions() { | ||
| 503 | 110 | IgnitionEventList *list = &engine->ignitionEvents; | ||
| 504 | 110 | angle_t dwellAngle = engine->ignitionState.dwellDurationAngle; | ||
| 505 | 110 | floatms_t sparkDwell = engine->ignitionState.getDwell(); | ||
| 506 |
3/6✓ Branch 1 taken 110 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 110 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 110 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 110 times.
|
110 | 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 110 times.
|
110 | efiAssertVoid(ObdCode::CUSTOM_ERR_6592, engineConfiguration->cylindersCount > 0, "cylindersCount"); | |
| 513 | ||||
| 514 |
2/2✓ Branch 0 taken 451 times.
✓ Branch 1 taken 110 times.
|
2/2✓ Decision 'true' taken 451 times.
✓ Decision 'false' taken 110 times.
|
561 | for (size_t cylinderIndex = 0; cylinderIndex < engineConfiguration->cylindersCount; cylinderIndex++) { |
| 515 | 451 | list->elements[cylinderIndex].cylinderIndex = cylinderIndex; | ||
| 516 | 451 | prepareCylinderIgnitionSchedule(dwellAngle, sparkDwell, &list->elements[cylinderIndex]); | ||
| 517 | } | |||
| 518 | 110 | list->isReady = true; | ||
| 519 | } | |||
| 520 | ||||
| 521 | 109 | static void prepareIgnitionSchedule() { | ||
| 522 | 109 | ScopePerf perf(PE::PrepareIgnitionSchedule); | ||
| 523 | ||||
| 524 |
2/2✓ Branch 1 taken 109 times.
✓ Branch 4 taken 109 times.
|
109 | operation_mode_e operationMode = getEngineRotationState()->getOperationMode(); | |
| 525 | float maxAllowedDwellAngle; | |||
| 526 | ||||
| 527 |
3/3✓ Branch 1 taken 109 times.
✓ Branch 3 taken 73 times.
✓ Branch 4 taken 36 times.
|
2/2✓ Decision 'true' taken 73 times.
✓ Decision 'false' taken 36 times.
|
109 | if (getCurrentIgnitionMode() == IM_ONE_COIL) { |
| 528 |
1/1✓ Branch 1 taken 73 times.
|
73 | maxAllowedDwellAngle = getEngineCycle(operationMode) / engineConfiguration->cylindersCount / 1.1; | |
| 529 | } else { | |||
| 530 |
1/1✓ Branch 1 taken 36 times.
|
36 | maxAllowedDwellAngle = (int) (getEngineCycle(operationMode) / 2); // the cast is about making Coverity happy | |
| 531 | } | |||
| 532 | ||||
| 533 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 109 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 109 times.
|
109 | 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 107 times.
|
2/2✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 107 times.
|
109 | 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 109 times.
|
109 | initializeIgnitionActions(); | |
| 543 | 109 | } | ||
| 544 | ||||
| 545 | 33524 | void onTriggerEventSparkLogic(float rpm, efitick_t edgeTimestamp, float currentPhase, float nextPhase) { | ||
| 546 | 33524 | ScopePerf perf(PE::OnTriggerEventSparkLogic); | ||
| 547 | ||||
| 548 |
2/2✓ Branch 0 taken 673 times.
✓ Branch 1 taken 32851 times.
|
2/2✓ Decision 'true' taken 673 times.
✓ Decision 'false' taken 32851 times.
|
33524 | if (!engineConfiguration->isIgnitionEnabled) { |
| 549 | 673 | return; | ||
| 550 | } | |||
| 551 | ||||
| 552 |
2/2✓ Branch 1 taken 32851 times.
✓ Branch 4 taken 32851 times.
|
32851 | LimpState limitedSparkState = getLimpManager()->allowIgnition(); | |
| 553 | ||||
| 554 | // todo: eliminate state copy logic by giving limpManager it's owm limp_manager.txt and leveraging LiveData | |||
| 555 | 32851 | engine->outputChannels.sparkCutReason = (int8_t)limitedSparkState.reason; | ||
| 556 | 32851 | bool limitedSpark = !limitedSparkState.value; | ||
| 557 | ||||
| 558 |
1/1✓ Branch 1 taken 32851 times.
|
32851 | const floatms_t dwellMs = engine->ignitionState.getDwell(); | |
| 559 |
3/6✓ Branch 1 taken 32851 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 32851 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 32851 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 32851 times.
|
32851 | 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 109 times.
✓ Branch 1 taken 32742 times.
|
2/2✓ Decision 'true' taken 109 times.
✓ Decision 'false' taken 32742 times.
|
32851 | if (!engine->ignitionEvents.isReady) { |
| 565 |
1/1✓ Branch 1 taken 109 times.
|
109 | 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 | 32851 | engine->engineState.useOddFireWastedSpark | ||
| 581 |
5/5✓ Branch 0 taken 2467 times.
✓ Branch 1 taken 30384 times.
✓ Branch 3 taken 2467 times.
✓ Branch 5 taken 680 times.
✓ Branch 6 taken 1787 times.
|
32851 | && getCurrentIgnitionMode() == IM_WASTED_SPARK; | |
| 582 | ||||
| 583 |
1/2✓ Branch 0 taken 32851 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 32851 times.
✗ Decision 'false' not taken.
|
32851 | if (engine->ignitionEvents.isReady) { |
| 584 |
2/2✓ Branch 0 taken 130951 times.
✓ Branch 1 taken 32851 times.
|
2/2✓ Decision 'true' taken 130951 times.
✓ Decision 'false' taken 32851 times.
|
163802 | for (size_t i = 0; i < engineConfiguration->cylindersCount; i++) { |
| 585 | 130951 | IgnitionEvent *event = &engine->ignitionEvents.elements[i]; | ||
| 586 | ||||
| 587 | 130951 | angle_t dwellAngle = event->dwellAngle; | ||
| 588 | ||||
| 589 | 130951 | angle_t sparkAngle = event->sparkAngle; | ||
| 590 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 130951 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 130951 times.
|
130951 | if (std::isnan(sparkAngle)) { |
| 591 | ✗ | warning(ObdCode::CUSTOM_ADVANCE_SPARK, "NaN advance"); | ||
| 592 | ✗ | continue; | ||
| 593 | } | |||
| 594 | ||||
| 595 | 130951 | bool isOddCylWastedEvent = false; | ||
| 596 |
2/2✓ Branch 0 taken 1358 times.
✓ Branch 1 taken 129593 times.
|
2/2✓ Decision 'true' taken 1358 times.
✓ Decision 'false' taken 129593 times.
|
130951 | if (enableOddCylinderWastedSpark) { |
| 597 | 1358 | auto dwellAngleWastedEvent = dwellAngle + 360; | ||
| 598 |
2/2✓ Branch 0 taken 1349 times.
✓ Branch 1 taken 9 times.
|
2/2✓ Decision 'true' taken 1349 times.
✓ Decision 'false' taken 9 times.
|
1358 | if (dwellAngleWastedEvent > 720) { |
| 599 | 1349 | 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 1358 times.
|
1358 | isOddCylWastedEvent = isPhaseInRange(dwellAngleWastedEvent, currentPhase, nextPhase); | |
| 605 | ||||
| 606 |
2/2✓ Branch 0 taken 35 times.
✓ Branch 1 taken 1323 times.
|
2/2✓ Decision 'true' taken 35 times.
✓ Decision 'false' taken 1323 times.
|
1358 | if (isOddCylWastedEvent) { |
| 607 | 35 | dwellAngle = dwellAngleWastedEvent; | ||
| 608 | ||||
| 609 | 35 | sparkAngle += 360; | ||
| 610 |
2/2✓ Branch 0 taken 34 times.
✓ Branch 1 taken 1 time.
|
2/2✓ Decision 'true' taken 34 times.
✓ Decision 'false' taken 1 time.
|
35 | if (sparkAngle > 720) { |
| 611 | 34 | sparkAngle -= 720; | ||
| 612 | } | |||
| 613 | } | |||
| 614 | } | |||
| 615 | ||||
| 616 |
7/7✓ Branch 0 taken 130916 times.
✓ Branch 1 taken 35 times.
✓ Branch 3 taken 130916 times.
✓ Branch 5 taken 121301 times.
✓ Branch 6 taken 9615 times.
✓ Branch 7 taken 121301 times.
✓ Branch 8 taken 9650 times.
|
2/2✓ Decision 'true' taken 121301 times.
✓ Decision 'false' taken 9650 times.
|
130951 | if (!isOddCylWastedEvent && !isPhaseInRange(dwellAngle, currentPhase, nextPhase)) { |
| 617 | 121301 | continue; | ||
| 618 | } | |||
| 619 | ||||
| 620 |
4/9✓ Branch 0 taken 2391 times.
✓ Branch 1 taken 7259 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2391 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 10 taken 9650 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 9650 times.
|
9650 | 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 9650 times.
✓ Branch 3 taken 9650 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 9650 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 9650 times.
|
9650 | bool sparkLimited = engine->softSparkLimiter.shouldSkip() || engine->hardSparkLimiter.shouldSkip(); | |
| 628 | 9650 | engine->ignitionState.luaIgnitionSkip = sparkLimited; | ||
| 629 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9650 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 9650 times.
|
9650 | 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 9650 times.
|
9650 | 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 31818 times.
✓ Branch 3 taken 101525 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 31818 times.
|
31818 | case IM_INDIVIDUAL_COILS: | |
| 669 | 31818 | return 1; | ||
| 670 |
1/1✓ Decision 'true' taken 101525 times.
|
101525 | case IM_WASTED_SPARK: | |
| 671 | 101525 | 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 |