| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /** | |||
| 2 | * @file trigger_decoder.cpp | |||
| 3 | * | |||
| 4 | * @date Dec 24, 2013 | |||
| 5 | * @author Andrey Belomutskiy, (c) 2012-2020 | |||
| 6 | * | |||
| 7 | * | |||
| 8 | * | |||
| 9 | * enable trigger_details | |||
| 10 | * | |||
| 11 | * This file is part of rusEfi - see http://rusefi.com | |||
| 12 | * | |||
| 13 | * rusEfi is free software; you can redistribute it and/or modify it under the terms of | |||
| 14 | * the GNU General Public License as published by the Free Software Foundation; either | |||
| 15 | * version 3 of the License, or (at your option) any later version. | |||
| 16 | * | |||
| 17 | * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without | |||
| 18 | * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| 19 | * GNU General Public License for more details. | |||
| 20 | * | |||
| 21 | * You should have received a copy of the GNU General Public License along with this program. | |||
| 22 | * If not, see <http://www.gnu.org/licenses/>. | |||
| 23 | */ | |||
| 24 | ||||
| 25 | #include "pch.h" | |||
| 26 | ||||
| 27 | #include "global_shared.h" | |||
| 28 | #include "engine_configuration.h" | |||
| 29 | #include "transition_events.h" | |||
| 30 | ||||
| 31 | /** | |||
| 32 | * decoder uses TriggerStimulatorHelper in findTriggerZeroEventIndex | |||
| 33 | */ | |||
| 34 | #include "trigger_simulator.h" | |||
| 35 | ||||
| 36 | #ifndef NOISE_RATIO_THRESHOLD | |||
| 37 | #define NOISE_RATIO_THRESHOLD 3000 | |||
| 38 | #endif | |||
| 39 | ||||
| 40 | 3546 | TriggerDecoderBase::TriggerDecoderBase(const char* p_name) | ||
| 41 | 3546 | : name(p_name) | ||
| 42 | { | |||
| 43 | 3546 | TriggerDecoderBase::resetState(); | ||
| 44 | 3546 | } | ||
| 45 | ||||
| 46 | 717373 | bool TriggerDecoderBase::getShaftSynchronized() const { | ||
| 47 | 717373 | return shaft_is_synchronized; | ||
| 48 | } | |||
| 49 | ||||
| 50 | 19685 | void TriggerDecoderBase::setShaftSynchronized(bool value) { | ||
| 51 | #if EFI_UNIT_TEST | |||
| 52 |
2/2✓ Branch 0 taken 2432 times.
✓ Branch 1 taken 17253 times.
|
2/2✓ Decision 'true' taken 2432 times.
✓ Decision 'false' taken 17253 times.
|
19685 | if (value != shaft_is_synchronized) { |
| 53 | 2432 | LogTriggerSync(value, getTimeNowNt()); | ||
| 54 | } | |||
| 55 | #endif | |||
| 56 | ||||
| 57 |
2/2✓ Branch 0 taken 13244 times.
✓ Branch 1 taken 6441 times.
|
2/2✓ Decision 'true' taken 13244 times.
✓ Decision 'false' taken 6441 times.
|
19685 | if (value) { |
| 58 |
2/2✓ Branch 0 taken 1321 times.
✓ Branch 1 taken 11923 times.
|
2/2✓ Decision 'true' taken 1321 times.
✓ Decision 'false' taken 11923 times.
|
13244 | if (!shaft_is_synchronized) { |
| 59 | // just got synchronized | |||
| 60 | 1321 | mostRecentSyncTime = getTimeNowNt(); | ||
| 61 | } | |||
| 62 | } else { | |||
| 63 | // sync loss | |||
| 64 | 6441 | mostRecentSyncTime = 0; | ||
| 65 | } | |||
| 66 | 19685 | shaft_is_synchronized = value; | ||
| 67 | 19685 | } | ||
| 68 | ||||
| 69 | 6009 | void TriggerDecoderBase::resetState() { | ||
| 70 | 6009 | setShaftSynchronized(false); | ||
| 71 | 6009 | toothed_previous_time = 0; | ||
| 72 | ||||
| 73 | 6009 | setArrayValues(toothDurations, 0); | ||
| 74 | ||||
| 75 | 6009 | synchronizationCounter = 0; | ||
| 76 | 6009 | totalTriggerErrorCounter = 0; | ||
| 77 | 6009 | orderingErrorCounter = 0; | ||
| 78 | 6009 | m_timeSinceDecodeError.init(); | ||
| 79 | ||||
| 80 | 6009 | prevSignal = SHAFT_PRIMARY_FALLING; | ||
| 81 | 6009 | startOfCycleNt = {}; | ||
| 82 | ||||
| 83 | 6009 | resetCurrentCycleState(); | ||
| 84 | ||||
| 85 | 6009 | totalEventCountBase = 0; | ||
| 86 | 6009 | isFirstEvent = true; | ||
| 87 | 6009 | } | ||
| 88 | ||||
| 89 | 114 | void TriggerDecoderBase::setTriggerErrorState(int errorIncrement) { | ||
| 90 | 114 | m_timeSinceDecodeError.reset(); | ||
| 91 | 114 | totalTriggerErrorCounter += errorIncrement; | ||
| 92 | 114 | onTransitionEvent(TransitionEvent::TriggerError); | ||
| 93 | 114 | } | ||
| 94 | ||||
| 95 | 19279 | void TriggerDecoderBase::resetCurrentCycleState() { | ||
| 96 | 19279 | setArrayValues(currentCycle.eventCount, 0); | ||
| 97 | 19279 | currentCycle.current_index = 0; | ||
| 98 | 19279 | } | ||
| 99 | ||||
| 100 | #if EFI_SHAFT_POSITION_INPUT | |||
| 101 | ||||
| 102 | 680 | PrimaryTriggerDecoder::PrimaryTriggerDecoder(const char* p_name) | ||
| 103 | 680 | : TriggerDecoderBase(p_name) | ||
| 104 | { | |||
| 105 | 680 | } | ||
| 106 | ||||
| 107 | #if ! EFI_PROD_CODE | |||
| 108 | bool printTriggerDebug = false; | |||
| 109 | bool printTriggerTrace = false; | |||
| 110 | #endif /* ! EFI_PROD_CODE */ | |||
| 111 | ||||
| 112 | 990 | void TriggerWaveform::initializeSyncPoint(TriggerDecoderBase& state, | ||
| 113 | const TriggerConfiguration& triggerConfiguration) { | |||
| 114 | 990 | triggerShapeSynchPointIndex = state.findTriggerZeroEventIndex(*this, triggerConfiguration); | ||
| 115 | 990 | } | ||
| 116 | ||||
| 117 | 1494 | void TriggerFormDetails::prepareEventAngles(TriggerWaveform *shape) { | ||
| 118 | 1494 | int triggerShapeSynchPointIndex = shape->triggerShapeSynchPointIndex; | ||
| 119 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1494 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1494 times.
|
1494 | if (triggerShapeSynchPointIndex == EFI_ERROR_CODE) { |
| 120 | ✗ | return; | ||
| 121 | } | |||
| 122 | 1494 | angle_t firstAngle = shape->getAngle(triggerShapeSynchPointIndex); | ||
| 123 |
2/4✓ Branch 0 taken 1494 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1494 times.
|
1494 | assertAngleRange(firstAngle, "firstAngle", ObdCode::CUSTOM_TRIGGER_SYNC_ANGLE); | |
| 124 | ||||
| 125 | 1494 | int riseOnlyIndex = 0; | ||
| 126 | ||||
| 127 | 1494 | size_t length = shape->getLength(); | ||
| 128 | ||||
| 129 | 1494 | setArrayValues(eventAngles, 0); | ||
| 130 | ||||
| 131 | // this may be <length for some triggers like symmetrical crank Miata NB | |||
| 132 | 1494 | size_t triggerShapeLength = shape->getSize(); | ||
| 133 | ||||
| 134 |
2/4✓ Branch 0 taken 1494 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1494 times.
|
1494 | assertAngleRange(triggerShapeSynchPointIndex, "triggerShapeSynchPointIndex", ObdCode::CUSTOM_TRIGGER_SYNC_ANGLE2); | |
| 135 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1494 times.
|
1494 | efiAssertVoid(ObdCode::CUSTOM_TRIGGER_CYCLE, getTriggerCentral()->engineCycleEventCount != 0, "zero engineCycleEventCount"); | |
| 136 | ||||
| 137 |
2/2✓ Branch 0 taken 27206 times.
✓ Branch 1 taken 1494 times.
|
2/2✓ Decision 'true' taken 27206 times.
✓ Decision 'false' taken 1494 times.
|
28700 | for (size_t eventIndex = 0; eventIndex < length; eventIndex++) { |
| 138 |
2/2✓ Branch 0 taken 1494 times.
✓ Branch 1 taken 25712 times.
|
2/2✓ Decision 'true' taken 1494 times.
✓ Decision 'false' taken 25712 times.
|
27206 | if (eventIndex == 0) { |
| 139 | // explicit check for zero to avoid issues where logical zero is not exactly zero due to float nature | |||
| 140 | 1494 | eventAngles[0] = 0; | ||
| 141 | // this value would be used in case of front-only | |||
| 142 | 1494 | eventAngles[1] = 0; | ||
| 143 | } else { | |||
| 144 | // Rotate the trigger around so that the sync point is at position 0 | |||
| 145 | 25712 | auto wrappedIndex = (triggerShapeSynchPointIndex + eventIndex) % length; | ||
| 146 | ||||
| 147 | // Compute this tooth's position within the trigger definition | |||
| 148 | // (wrap, as the trigger def may be smaller than total trigger length) | |||
| 149 | 25712 | auto triggerDefinitionIndex = wrappedIndex % triggerShapeLength; | ||
| 150 | ||||
| 151 | // Compute the relative angle of this tooth to the sync point's tooth | |||
| 152 |
1/1✓ Branch 2 taken 25712 times.
|
25712 | float angle = shape->getAngle(wrappedIndex) - firstAngle; | |
| 153 | ||||
| 154 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 25712 times.
✗ Branch 4 not taken.
|
25712 | efiAssertVoid(ObdCode::CUSTOM_TRIGGER_CYCLE, !std::isnan(angle), "trgSyncNaN"); | |
| 155 | // Wrap the angle back in to [0, 720) | |||
| 156 |
1/1✓ Branch 1 taken 25712 times.
|
25712 | wrapAngle(angle, "trgSync", ObdCode::CUSTOM_TRIGGER_SYNC_ANGLE_RANGE); | |
| 157 | ||||
| 158 |
2/2✓ Branch 0 taken 20794 times.
✓ Branch 1 taken 4918 times.
|
2/2✓ Decision 'true' taken 20794 times.
✓ Decision 'false' taken 4918 times.
|
25712 | if (shape->useOnlyRisingEdges) { |
| 159 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 20794 times.
✗ Branch 3 not taken.
|
20794 | criticalAssertVoid(triggerDefinitionIndex < triggerShapeLength, "trigger shape fail"); | |
| 160 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 20794 times.
✗ Branch 4 not taken.
|
20794 | assertIsInBounds(triggerDefinitionIndex, shape->isRiseEvent, "isRise"); | |
| 161 | ||||
| 162 | // In case this is a rising event, replace the following fall event with the rising as well | |||
| 163 |
2/2✓ Branch 1 taken 10253 times.
✓ Branch 2 taken 10541 times.
|
2/2✓ Decision 'true' taken 10253 times.
✓ Decision 'false' taken 10541 times.
|
20794 | if (shape->isRiseEvent[triggerDefinitionIndex]) { |
| 164 | 10253 | riseOnlyIndex += 2; | ||
| 165 | 10253 | eventAngles[riseOnlyIndex] = angle; | ||
| 166 | 10253 | eventAngles[riseOnlyIndex + 1] = angle; | ||
| 167 | } | |||
| 168 | } else { | |||
| 169 | 4918 | eventAngles[eventIndex] = angle; | ||
| 170 | } | |||
| 171 | } | |||
| 172 | } | |||
| 173 | } | |||
| 174 | ||||
| 175 | ✗ | int64_t TriggerDecoderBase::getTotalEventCounter() const { | ||
| 176 | ✗ | return totalEventCountBase + currentCycle.current_index; | ||
| 177 | } | |||
| 178 | ||||
| 179 | 37536 | int TriggerDecoderBase::getSynchronizationCounter() const { | ||
| 180 | 37536 | return synchronizationCounter; | ||
| 181 | } | |||
| 182 | ||||
| 183 | 809 | void PrimaryTriggerDecoder::resetState() { | ||
| 184 | 809 | TriggerDecoderBase::resetState(); | ||
| 185 | ||||
| 186 | 809 | resetHasFullSync(); | ||
| 187 | 809 | } | ||
| 188 | ||||
| 189 | ||||
| 190 | 103525 | bool TriggerDecoderBase::isValidIndex(const TriggerWaveform& triggerShape) const { | ||
| 191 | 103525 | return currentCycle.current_index < triggerShape.getSize(); | ||
| 192 | } | |||
| 193 | ||||
| 194 | static TriggerWheel eventIndex[4] = { TriggerWheel::T_PRIMARY, TriggerWheel::T_PRIMARY, TriggerWheel::T_SECONDARY, TriggerWheel::T_SECONDARY }; | |||
| 195 | static TriggerValue eventType[4] = { TriggerValue::FALL, TriggerValue::RISE, TriggerValue::FALL, TriggerValue::RISE }; | |||
| 196 | ||||
| 197 | #if EFI_UNIT_TEST | |||
| 198 | #define PRINT_INC_INDEX if (printTriggerTrace) {\ | |||
| 199 | printf("nextTriggerEvent index=%d\r\n", currentCycle.current_index); \ | |||
| 200 | } | |||
| 201 | #else | |||
| 202 | #define PRINT_INC_INDEX {} | |||
| 203 | #endif /* EFI_UNIT_TEST */ | |||
| 204 | ||||
| 205 | #define nextTriggerEvent() \ | |||
| 206 | { \ | |||
| 207 | if (useOnlyRisingEdgeForTrigger) {currentCycle.current_index++;} \ | |||
| 208 | currentCycle.current_index++; \ | |||
| 209 | PRINT_INC_INDEX; \ | |||
| 210 | } | |||
| 211 | ||||
| 212 | 12 | int TriggerDecoderBase::getCurrentIndex() const { | ||
| 213 | 12 | return currentCycle.current_index; | ||
| 214 | } | |||
| 215 | ||||
| 216 | 657 | angle_t PrimaryTriggerDecoder::syncEnginePhase(int divider, int remainder, angle_t engineCycle) { | ||
| 217 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 657 times.
|
657 | efiAssert(ObdCode::OBD_PCM_Processor_Fault, divider > 1, "syncEnginePhase divider", false); | |
| 218 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 657 times.
|
657 | efiAssert(ObdCode::OBD_PCM_Processor_Fault, remainder < divider, "syncEnginePhase remainder", false); | |
| 219 | 657 | angle_t totalShift = 0; | ||
| 220 |
2/2✓ Branch 1 taken 87 times.
✓ Branch 2 taken 657 times.
|
2/2✓ Decision 'true' taken 87 times.
✓ Decision 'false' taken 657 times.
|
744 | while (getSynchronizationCounter() % divider != remainder) { |
| 221 | /** | |||
| 222 | * we are here if we've detected the cam sensor within the wrong crank phase | |||
| 223 | * let's increase the trigger event counter, that would adjust the state of | |||
| 224 | * virtual crank-based trigger | |||
| 225 | */ | |||
| 226 | 87 | incrementShaftSynchronizationCounter(); | ||
| 227 | 87 | totalShift += engineCycle / divider; | ||
| 228 | } | |||
| 229 | ||||
| 230 | // Allow injection/ignition to happen, we've now fully sync'd the crank based on new cam information | |||
| 231 | 657 | m_hasSynchronizedPhase = true; | ||
| 232 | ||||
| 233 |
2/2✓ Branch 0 taken 38 times.
✓ Branch 1 taken 619 times.
|
2/2✓ Decision 'true' taken 38 times.
✓ Decision 'false' taken 619 times.
|
657 | if (totalShift > 0) { |
| 234 | 38 | camResyncCounter++; | ||
| 235 | 38 | onTransitionEvent(TransitionEvent::EngineResync); | ||
| 236 | } | |||
| 237 | ||||
| 238 | 657 | return totalShift; | ||
| 239 | } | |||
| 240 | ||||
| 241 | 12036 | void TriggerDecoderBase::incrementShaftSynchronizationCounter() { | ||
| 242 | 12036 | synchronizationCounter++; | ||
| 243 | 12036 | } | ||
| 244 | ||||
| 245 | 72 | void PrimaryTriggerDecoder::onTriggerError() { | ||
| 246 | // On trigger error, we've lost full sync | |||
| 247 | 72 | resetHasFullSync(); | ||
| 248 | ||||
| 249 | // Ignore the warning that engine is never null - it might be in unit tests | |||
| 250 | #pragma GCC diagnostic push | |||
| 251 | #pragma GCC diagnostic ignored "-Waddress" | |||
| 252 |
2/2✓ Branch 0 taken 71 times.
✓ Branch 1 taken 1 time.
|
2/2✓ Decision 'true' taken 71 times.
✓ Decision 'false' taken 1 time.
|
72 | if (engine) { |
| 253 | // Instant RPM data is now also probably trash, discard it | |||
| 254 | 71 | engine->triggerCentral.instantRpm.resetInstantRpm(); | ||
| 255 | 71 | engine->rpmCalculator.lastTdcTimer.init(); | ||
| 256 | } | |||
| 257 | #pragma GCC diagnostic pop | |||
| 258 | 72 | } | ||
| 259 | ||||
| 260 | 11 | void PrimaryTriggerDecoder::onNotEnoughTeeth(int /*actual*/, int /*expected*/) { | ||
| 261 | 11 | warning(ObdCode::CUSTOM_PRIMARY_NOT_ENOUGH_TEETH, "primary trigger error: not enough teeth between sync points: expected %d/%d got %d/%d", | ||
| 262 | 11 | getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_PRIMARY), | ||
| 263 | 11 | getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_SECONDARY), | ||
| 264 | currentCycle.eventCount[0], | |||
| 265 | currentCycle.eventCount[1]); | |||
| 266 | 11 | } | ||
| 267 | ||||
| 268 | 56 | void PrimaryTriggerDecoder::onTooManyTeeth(int /*actual*/, int /*expected*/) { | ||
| 269 | 56 | warning(ObdCode::CUSTOM_PRIMARY_TOO_MANY_TEETH, "primary trigger error: too many teeth between sync points: expected %d/%d got %d/%d", | ||
| 270 | 56 | getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_PRIMARY), | ||
| 271 | 56 | getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_SECONDARY), | ||
| 272 | currentCycle.eventCount[0], | |||
| 273 | currentCycle.eventCount[1]); | |||
| 274 | 56 | } | ||
| 275 | ||||
| 276 | 149834 | const char *getTrigger_event_e(trigger_event_e value){ | ||
| 277 |
4/5✓ Branch 0 taken 8122 times.
✓ Branch 1 taken 131131 times.
✓ Branch 2 taken 4043 times.
✓ Branch 3 taken 6538 times.
✗ Branch 4 not taken.
|
149834 | switch(value) { | |
| 278 |
1/1✓ Decision 'true' taken 8122 times.
|
8122 | case SHAFT_PRIMARY_FALLING: | |
| 279 | 8122 | return "SHAFT_PRIMARY_FALLING"; | ||
| 280 |
1/1✓ Decision 'true' taken 131131 times.
|
131131 | case SHAFT_PRIMARY_RISING: | |
| 281 | 131131 | return "SHAFT_PRIMARY_RISING"; | ||
| 282 |
1/1✓ Decision 'true' taken 4043 times.
|
4043 | case SHAFT_SECONDARY_FALLING: | |
| 283 | 4043 | return "SHAFT_SECONDARY_FALLING"; | ||
| 284 |
1/1✓ Decision 'true' taken 6538 times.
|
6538 | case SHAFT_SECONDARY_RISING: | |
| 285 | 6538 | return "SHAFT_SECONDARY_RISING"; | ||
| 286 | } | |||
| 287 | ✗ | return NULL; | ||
| 288 | } | |||
| 289 | ✗ | const char *getTrigger_value_e(TriggerValue value){ | ||
| 290 | ✗ | switch(value) { | ||
| 291 | ✗ | case TriggerValue::FALL: | ||
| 292 | ✗ | return "TriggerValue::FALL"; | ||
| 293 | ✗ | case TriggerValue::RISE: | ||
| 294 | ✗ | return "TriggerValue::RISE"; | ||
| 295 | } | |||
| 296 | ✗ | return NULL; | ||
| 297 | } | |||
| 298 | ||||
| 299 | 2 | void VvtTriggerDecoder::onNotEnoughTeeth(int actual, int expected) { | ||
| 300 | 2 | warning(ObdCode::CUSTOM_CAM_NOT_ENOUGH_TEETH, "cam %s trigger error: not enough teeth between sync points: actual %d expected %d", name, actual, expected); | ||
| 301 | 2 | } | ||
| 302 | ||||
| 303 | 28 | void VvtTriggerDecoder::onTooManyTeeth(int actual, int expected) { | ||
| 304 | 28 | warning(ObdCode::CUSTOM_CAM_TOO_MANY_TEETH, "cam %s trigger error: too many teeth between sync points: %d > %d", name, actual, expected); | ||
| 305 | 28 | } | ||
| 306 | ||||
| 307 | 13270 | PUBLIC_API_WEAK bool isTriggerCounterError(int8_t triggerCountersError) { | ||
| 308 | 13270 | return triggerCountersError != 0; | ||
| 309 | } | |||
| 310 | ||||
| 311 | 13270 | int TriggerDecoderBase::getEventCountersError(const TriggerWaveform& triggerShape) const { | ||
| 312 | // We can check if things are fine by comparing the number of events in a cycle with the expected number of event. | |||
| 313 | 13270 | int countersError = 0; | ||
| 314 |
2/2✓ Branch 0 taken 25285 times.
✓ Branch 1 taken 11990 times.
|
2/2✓ Decision 'true' taken 25285 times.
✓ Decision 'false' taken 11990 times.
|
37275 | for (int i = 0;i < PWM_PHASE_MAX_WAVE_PER_PWM;i++) { |
| 315 | 25285 | countersError = currentCycle.eventCount[i] - triggerShape.getExpectedEventCount((TriggerWheel)i); | ||
| 316 |
2/2✓ Branch 0 taken 1280 times.
✓ Branch 1 taken 24005 times.
|
2/2✓ Decision 'true' taken 1280 times.
✓ Decision 'false' taken 24005 times.
|
25285 | if (countersError != 0) { |
| 317 | 1280 | break; | ||
| 318 | } | |||
| 319 | } | |||
| 320 | ||||
| 321 | #if EFI_DETAILED_LOGGING | |||
| 322 | printf("getEventCountersError: isDecodingError=%d\n", (countersError != 0)); | |||
| 323 | if (countersError != 0) { | |||
| 324 | for (int i = 0;i < PWM_PHASE_MAX_WAVE_PER_PWM;i++) { | |||
| 325 | printf(" count: cur=%d exp=%d\n", currentCycle.eventCount[i], triggerShape.getExpectedEventCount((TriggerWheel)i)); | |||
| 326 | } | |||
| 327 | } | |||
| 328 | #endif /* EFI_UNIT_TEST */ | |||
| 329 | ||||
| 330 | 13270 | return countersError; | ||
| 331 | } | |||
| 332 | ||||
| 333 | 13270 | void TriggerDecoderBase::onShaftSynchronization( | ||
| 334 | bool wasSynchronized, | |||
| 335 | const efitick_t nowNt, | |||
| 336 | const TriggerWaveform& triggerShape) { | |||
| 337 | 13270 | startOfCycleNt = nowNt; | ||
| 338 | 13270 | resetCurrentCycleState(); | ||
| 339 | ||||
| 340 |
2/2✓ Branch 0 taken 11949 times.
✓ Branch 1 taken 1321 times.
|
2/2✓ Decision 'true' taken 11949 times.
✓ Decision 'false' taken 1321 times.
|
13270 | if (wasSynchronized) { |
| 341 | 11949 | incrementShaftSynchronizationCounter(); | ||
| 342 | } else { | |||
| 343 | // We have just synchronized, this is the zeroth revolution | |||
| 344 | 1321 | synchronizationCounter = 0; | ||
| 345 | } | |||
| 346 | ||||
| 347 | 13270 | totalEventCountBase += triggerShape.getSize(); | ||
| 348 | ||||
| 349 | #if EFI_UNIT_TEST | |||
| 350 |
2/2✓ Branch 0 taken 9014 times.
✓ Branch 1 taken 4256 times.
|
2/2✓ Decision 'true' taken 9014 times.
✓ Decision 'false' taken 4256 times.
|
13270 | if (printTriggerDebug) { |
| 351 | 9014 | printf("onShaftSynchronization index=%d %d\r\n", | ||
| 352 | currentCycle.current_index, | |||
| 353 | synchronizationCounter); | |||
| 354 | } | |||
| 355 | #endif /* EFI_UNIT_TEST */ | |||
| 356 | 13270 | } | ||
| 357 | ||||
| 358 | 191224 | static bool shouldConsiderEdge(const TriggerWaveform& triggerShape, TriggerWheel triggerWheel, TriggerValue edge) { | ||
| 359 |
4/4✓ Branch 0 taken 11853 times.
✓ Branch 1 taken 179371 times.
✓ Branch 2 taken 11036 times.
✓ Branch 3 taken 817 times.
|
2/2✓ Decision 'true' taken 11036 times.
✓ Decision 'false' taken 180188 times.
|
191224 | if (triggerWheel != TriggerWheel::T_PRIMARY && triggerShape.useOnlyPrimaryForSync) { |
| 360 | // Non-primary events ignored | |||
| 361 | 11036 | return false; | ||
| 362 | } | |||
| 363 | ||||
| 364 |
3/4✓ Branch 0 taken 27785 times.
✓ Branch 1 taken 146791 times.
✓ Branch 2 taken 5612 times.
✗ Branch 3 not taken.
|
180188 | switch (triggerShape.syncEdge) { | |
| 365 |
1/1✓ Decision 'true' taken 27785 times.
|
27785 | case SyncEdge::Both: return true; | |
| 366 |
1/1✓ Decision 'true' taken 146791 times.
|
146791 | case SyncEdge::RiseOnly: | |
| 367 |
1/1✓ Decision 'true' taken 146791 times.
|
146791 | case SyncEdge::Rise: return edge == TriggerValue::RISE; | |
| 368 |
1/1✓ Decision 'true' taken 5612 times.
|
5612 | case SyncEdge::Fall: return edge == TriggerValue::FALL; | |
| 369 | } | |||
| 370 | ||||
| 371 | // how did we get here? | |||
| 372 | // assert(false)? | |||
| 373 | ||||
| 374 | ✗ | return false; | ||
| 375 | } | |||
| 376 | ||||
| 377 | 26 | void TriggerDecoderBase::printGaps(const char * prefix, | ||
| 378 | const TriggerConfiguration& triggerConfiguration, | |||
| 379 | const TriggerWaveform& triggerShape) { | |||
| 380 |
2/2✓ Branch 0 taken 64 times.
✓ Branch 1 taken 26 times.
|
2/2✓ Decision 'true' taken 64 times.
✓ Decision 'false' taken 26 times.
|
90 | for (int i = 0;i<triggerShape.gapTrackingLength;i++) { |
| 381 | 64 | float ratioFrom = triggerShape.synchronizationRatioFrom[i]; | ||
| 382 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 64 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 64 times.
|
64 | if (std::isnan(ratioFrom)) { |
| 383 | // we do not track gap at this depth | |||
| 384 | ✗ | continue; | ||
| 385 | } | |||
| 386 | ||||
| 387 | 64 | float gap = 1.0 * toothDurations[i] / toothDurations[i + 1]; | ||
| 388 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 63 times.
|
2/2✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 63 times.
|
64 | if (std::isnan(gap)) { |
| 389 | 1 | efiPrintf("%s index=%d NaN gap, you have noise issues?", prefix, i); | ||
| 390 | } else { | |||
| 391 | 63 | float ratioTo = triggerShape.synchronizationRatioTo[i]; | ||
| 392 | ||||
| 393 | 63 | bool gapOk = isInRange(ratioFrom, gap, ratioTo); | ||
| 394 | ||||
| 395 |
2/2✓ Branch 2 taken 62 times.
✓ Branch 3 taken 1 time.
|
63 | efiPrintf("%s %srpm=%d time=%d eventIndex=%lu gapIndex=%d: %s gap=%.3f expected from %.3f to %.3f error=%s", | |
| 396 | prefix, | |||
| 397 | triggerConfiguration.PrintPrefix, | |||
| 398 | (int)Sensor::getOrZero(SensorType::Rpm), | |||
| 399 | /* cast is needed to make sure we do not put 64 bit value to stack*/ (int)getTimeNowS(), | |||
| 400 | currentCycle.current_index, | |||
| 401 | i, | |||
| 402 | gapOk ? "Y" : "n", | |||
| 403 | gap, | |||
| 404 | ratioFrom, | |||
| 405 | ratioTo, | |||
| 406 | boolToString(someSortOfTriggerError())); | |||
| 407 | } | |||
| 408 | } | |||
| 409 | 26 | } | ||
| 410 | ||||
| 411 | /** | |||
| 412 | * @brief Trigger decoding happens here | |||
| 413 | * VR falls are filtered out and some VR noise detection happens prior to invoking this method, for | |||
| 414 | * Hall this method is invoked every time we have a fall or rise on one of the trigger sensors. | |||
| 415 | * This method changes the state of trigger_state_s data structure according to the trigger event | |||
| 416 | * @param signal type of event which just happened | |||
| 417 | * @param nowNt current time | |||
| 418 | */ | |||
| 419 | 191224 | expected<TriggerDecodeResult> TriggerDecoderBase::decodeTriggerEvent( | ||
| 420 | const char *msg, | |||
| 421 | const TriggerWaveform& triggerShape, | |||
| 422 | TriggerStateListener* triggerStateListener, | |||
| 423 | const TriggerConfiguration& triggerConfiguration, | |||
| 424 | const trigger_event_e signal, | |||
| 425 | const efitick_t nowNt) { | |||
| 426 | 191224 | ScopePerf perf(PE::DecodeTriggerEvent); | ||
| 427 | ||||
| 428 | #if EFI_PROD_CODE | |||
| 429 | getTriggerCentral()->triggerElapsedUs = previousEventTimer.getElapsedUs(); | |||
| 430 | #endif | |||
| 431 | ||||
| 432 |
3/3✓ Branch 1 taken 191224 times.
✓ Branch 3 taken 313 times.
✓ Branch 4 taken 190911 times.
|
2/2✓ Decision 'true' taken 313 times.
✓ Decision 'false' taken 190911 times.
|
191224 | if (previousEventTimer.getElapsedSecondsAndReset(nowNt) > 1) { |
| 433 | /** | |||
| 434 | * We are here if there is a time gap between now and previous shaft event - that means the engine is not running. | |||
| 435 | * That means we have lost synchronization since the engine is not running :) | |||
| 436 | */ | |||
| 437 |
1/1✓ Branch 1 taken 313 times.
|
313 | setShaftSynchronized(false); | |
| 438 |
2/2✓ Branch 0 taken 129 times.
✓ Branch 1 taken 184 times.
|
2/2✓ Decision 'true' taken 129 times.
✓ Decision 'false' taken 184 times.
|
313 | if (triggerStateListener) { |
| 439 |
1/1✓ Branch 1 taken 129 times.
|
129 | triggerStateListener->OnTriggerSynchronizationLost(); | |
| 440 | } | |||
| 441 | } | |||
| 442 | ||||
| 443 | 191224 | bool useOnlyRisingEdgeForTrigger = triggerShape.useOnlyRisingEdges; | ||
| 444 | ||||
| 445 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 191224 times.
✗ Branch 3 not taken.
|
191224 | efiAssert(ObdCode::CUSTOM_TRIGGER_UNEXPECTED, signal <= SHAFT_SECONDARY_RISING, "unexpected signal", unexpected); | |
| 446 | ||||
| 447 | 191224 | TriggerWheel triggerWheel = eventIndex[signal]; | ||
| 448 | 191224 | TriggerValue type = eventType[signal]; | ||
| 449 | ||||
| 450 | // Check that we didn't get the same edge twice in a row - that should be impossible | |||
| 451 |
4/4✓ Branch 0 taken 116824 times.
✓ Branch 1 taken 74400 times.
✓ Branch 2 taken 95 times.
✓ Branch 3 taken 116729 times.
|
2/2✓ Decision 'true' taken 95 times.
✓ Decision 'false' taken 191129 times.
|
191224 | if (!useOnlyRisingEdgeForTrigger && prevSignal == signal) { |
| 452 | 95 | orderingErrorCounter++; | ||
| 453 | } | |||
| 454 | ||||
| 455 | 191224 | prevSignal = signal; | ||
| 456 | ||||
| 457 | 191224 | currentCycle.eventCount[(int)triggerWheel]++; | ||
| 458 | ||||
| 459 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 191224 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 191224 times.
|
191224 | if (toothed_previous_time > nowNt) { |
| 460 | ✗ | firmwareError(ObdCode::CUSTOM_OBD_93, "[%s] toothed_previous_time after nowNt prev=%lu now=%lu", msg, (uint32_t)toothed_previous_time, (uint32_t)nowNt); | ||
| 461 | } | |||
| 462 | ||||
| 463 |
2/2✓ Branch 0 taken 1527 times.
✓ Branch 1 taken 189697 times.
|
191224 | efidur_t currentDurationLong = isFirstEvent ? 0 : (nowNt - toothed_previous_time); | |
| 464 | ||||
| 465 | /** | |||
| 466 | * For performance reasons, we want to work with 32 bit values. If there has been more then | |||
| 467 | * 10 seconds since previous trigger event we do not really care. | |||
| 468 | */ | |||
| 469 | 191224 | toothDurations[0] = | ||
| 470 | 191224 | currentDurationLong > 10 * NT_PER_SECOND ? 10 * NT_PER_SECOND : currentDurationLong; | ||
| 471 | ||||
| 472 |
2/2✓ Branch 1 taken 50871 times.
✓ Branch 2 taken 140353 times.
|
2/2✓ Decision 'true' taken 50871 times.
✓ Decision 'false' taken 140353 times.
|
191224 | if (!shouldConsiderEdge(triggerShape, triggerWheel, type)) { |
| 473 | #if EFI_UNIT_TEST | |||
| 474 |
2/2✓ Branch 0 taken 14936 times.
✓ Branch 1 taken 35935 times.
|
2/2✓ Decision 'true' taken 14936 times.
✓ Decision 'false' taken 35935 times.
|
50871 | if (printTriggerTrace) { |
| 475 |
2/2✓ Branch 1 taken 14936 times.
✓ Branch 4 taken 14936 times.
|
29872 | printf("%s isLessImportant %s now=%d index=%d\r\n", | |
| 476 |
1/1✓ Branch 1 taken 14936 times.
|
14936 | getTrigger_type_e(triggerConfiguration.TriggerType.type), | |
| 477 | getTrigger_event_e(signal), | |||
| 478 | (int)nowNt, | |||
| 479 | currentCycle.current_index); | |||
| 480 | } | |||
| 481 | #endif /* EFI_UNIT_TEST */ | |||
| 482 | ||||
| 483 | // For less important events we simply increment the index. | |||
| 484 |
5/5✓ Branch 0 taken 2361 times.
✓ Branch 1 taken 48510 times.
✓ Branch 2 taken 14936 times.
✓ Branch 3 taken 35935 times.
✓ Branch 5 taken 14936 times.
|
50871 | nextTriggerEvent(); | |
| 485 | } else { | |||
| 486 | #if !EFI_PROD_CODE | |||
| 487 |
2/2✓ Branch 0 taken 67449 times.
✓ Branch 1 taken 72904 times.
|
2/2✓ Decision 'true' taken 67449 times.
✓ Decision 'false' taken 72904 times.
|
140353 | if (printTriggerTrace) { |
| 488 |
2/2✓ Branch 1 taken 67449 times.
✓ Branch 4 taken 67449 times.
|
134898 | printf("%s event %s %lld\r\n", | |
| 489 |
1/1✓ Branch 1 taken 67449 times.
|
67449 | getTrigger_type_e(triggerConfiguration.TriggerType.type), | |
| 490 | getTrigger_event_e(signal), | |||
| 491 | nowNt); | |||
| 492 |
1/1✓ Branch 1 taken 67449 times.
|
67449 | printf("decodeTriggerEvent ratio %.2f: current=%d previous=%d\r\n", 1.0 * toothDurations[0] / toothDurations[1], | |
| 493 | toothDurations[0], toothDurations[1]); | |||
| 494 | } | |||
| 495 | #endif | |||
| 496 | ||||
| 497 | 140353 | isFirstEvent = false; | ||
| 498 | bool isSynchronizationPoint; | |||
| 499 |
1/1✓ Branch 1 taken 140353 times.
|
140353 | bool wasSynchronized = getShaftSynchronized(); | |
| 500 | ||||
| 501 |
2/2✓ Branch 0 taken 132180 times.
✓ Branch 1 taken 8173 times.
|
2/2✓ Decision 'true' taken 132180 times.
✓ Decision 'false' taken 8173 times.
|
140353 | if (triggerShape.isSynchronizationNeeded) { |
| 502 | 132180 | triggerSyncGapRatio = (float)toothDurations[0] / toothDurations[1]; | ||
| 503 | ||||
| 504 |
4/4✓ Branch 0 taken 71142 times.
✓ Branch 1 taken 61038 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 71139 times.
|
2/2✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 132177 times.
|
132180 | if (wasSynchronized && triggerSyncGapRatio > NOISE_RATIO_THRESHOLD) { |
| 505 |
1/1✓ Branch 1 taken 3 times.
|
3 | setTriggerErrorState(100); | |
| 506 | } | |||
| 507 | ||||
| 508 |
1/1✓ Branch 1 taken 132180 times.
|
132180 | isSynchronizationPoint = isSyncPoint(triggerShape, triggerConfiguration.TriggerType.type); | |
| 509 |
2/2✓ Branch 0 taken 6321 times.
✓ Branch 1 taken 125859 times.
|
2/2✓ Decision 'true' taken 6321 times.
✓ Decision 'false' taken 125859 times.
|
132180 | if (isSynchronizationPoint) { |
| 510 |
1/1✓ Branch 1 taken 6321 times.
|
6321 | enginePins.debugTriggerSync.toggle(); | |
| 511 | } | |||
| 512 | ||||
| 513 | /** | |||
| 514 | * todo: technically we can afford detailed logging even with 60/2 as long as low RPM | |||
| 515 | * todo: figure out exact threshold as a function of RPM and tooth count? | |||
| 516 | * Open question what is 'triggerShape.getSize()' for 60/2 is it 58 or 58*2 or 58*4? | |||
| 517 | */ | |||
| 518 |
5/5✓ Branch 1 taken 132180 times.
✓ Branch 3 taken 84065 times.
✓ Branch 4 taken 48115 times.
✓ Branch 5 taken 79178 times.
✓ Branch 6 taken 4887 times.
|
132180 | bool silentTriggerError = triggerShape.getSize() > 40 && engineConfiguration->silentTriggerError; | |
| 519 | ||||
| 520 | #if EFI_PROD_CODE || EFI_SIMULATOR | |||
| 521 | bool verbose = getTriggerCentral()->isEngineSnifferEnabled && triggerConfiguration.VerboseTriggerSynchDetails; | |||
| 522 | ||||
| 523 | if (verbose || (someSortOfTriggerError() && !silentTriggerError)) { | |||
| 524 | const char * prefix = verbose ? "[vrb]" : "[err]"; | |||
| 525 | printGaps(prefix, triggerConfiguration, triggerShape); | |||
| 526 | } | |||
| 527 | #else | |||
| 528 |
2/2✓ Branch 0 taken 62636 times.
✓ Branch 1 taken 69544 times.
|
2/2✓ Decision 'true' taken 62636 times.
✓ Decision 'false' taken 69544 times.
|
132180 | if (printTriggerTrace) { |
| 529 |
2/2✓ Branch 0 taken 203208 times.
✓ Branch 1 taken 62636 times.
|
2/2✓ Decision 'true' taken 203208 times.
✓ Decision 'false' taken 62636 times.
|
265844 | for (int i = 0;i<triggerShape.gapTrackingLength;i++) { |
| 530 | 203208 | float gap = 1.0 * toothDurations[i] / toothDurations[i + 1]; | ||
| 531 | 812832 | printf("%sindex=%d: gap=%.2f expected from %.2f to %.2f error=%s\r\n", | ||
| 532 | 203208 | triggerConfiguration.PrintPrefix, | ||
| 533 | i, | |||
| 534 | gap, | |||
| 535 |
1/1✓ Branch 1 taken 203208 times.
|
203208 | triggerShape.synchronizationRatioFrom[i], | |
| 536 | 203208 | triggerShape.synchronizationRatioTo[i], | ||
| 537 |
2/2✓ Branch 1 taken 203208 times.
✓ Branch 4 taken 203208 times.
|
203208 | boolToString(someSortOfTriggerError())); | |
| 538 | } | |||
| 539 | } | |||
| 540 | #endif /* EFI_PROD_CODE */ | |||
| 541 | } else { | |||
| 542 | /** | |||
| 543 | * We are here in case of a wheel without synchronization - we just need to count events, | |||
| 544 | * synchronization point simply happens once we have the right number of events | |||
| 545 | * | |||
| 546 | * in case of noise the counter could be above the expected number of events, that's why 'more or equals' and not just 'equals' | |||
| 547 | */ | |||
| 548 | ||||
| 549 |
3/3✓ Branch 1 taken 8173 times.
✓ Branch 3 taken 1260 times.
✓ Branch 4 taken 6913 times.
|
1/1✓ Decision 'true' taken 8173 times.
|
8173 | unsigned int endOfCycleIndex = triggerShape.getSize() - (useOnlyRisingEdgeForTrigger ? 2 : 1); |
| 550 | ||||
| 551 |
5/5✓ Branch 1 taken 8173 times.
✓ Branch 3 taken 7436 times.
✓ Branch 4 taken 737 times.
✓ Branch 5 taken 6212 times.
✓ Branch 6 taken 1224 times.
|
8173 | isSynchronizationPoint = !getShaftSynchronized() || (currentCycle.current_index >= endOfCycleIndex); | |
| 552 | ||||
| 553 | #if EFI_UNIT_TEST | |||
| 554 |
2/2✓ Branch 0 taken 4813 times.
✓ Branch 1 taken 3360 times.
|
2/2✓ Decision 'true' taken 4813 times.
✓ Decision 'false' taken 3360 times.
|
8173 | if (printTriggerTrace) { |
| 555 |
1/1✓ Branch 1 taken 4813 times.
|
4813 | printf("decodeTriggerEvent sync=%d isSynchronizationPoint=%d index=%d size=%d\r\n", | |
| 556 |
2/2✓ Branch 1 taken 4813 times.
✓ Branch 4 taken 4813 times.
|
4813 | getShaftSynchronized(), | |
| 557 | isSynchronizationPoint, | |||
| 558 | currentCycle.current_index, | |||
| 559 | triggerShape.getSize()); | |||
| 560 | } | |||
| 561 | #endif /* EFI_UNIT_TEST */ | |||
| 562 | } | |||
| 563 | #if EFI_UNIT_TEST | |||
| 564 |
2/2✓ Branch 0 taken 67449 times.
✓ Branch 1 taken 72904 times.
|
2/2✓ Decision 'true' taken 67449 times.
✓ Decision 'false' taken 72904 times.
|
140353 | if (printTriggerTrace) { |
| 565 |
2/2✓ Branch 1 taken 67449 times.
✓ Branch 4 taken 67449 times.
|
134898 | printf("decodeTriggerEvent gap %s isSynchronizationPoint=%d index=%d %s\r\n", | |
| 566 |
1/1✓ Branch 1 taken 67449 times.
|
67449 | getTrigger_type_e(triggerConfiguration.TriggerType.type), | |
| 567 | isSynchronizationPoint, currentCycle.current_index, | |||
| 568 | getTrigger_event_e(signal)); | |||
| 569 | } | |||
| 570 | #endif /* EFI_UNIT_TEST */ | |||
| 571 | ||||
| 572 |
2/2✓ Branch 0 taken 13270 times.
✓ Branch 1 taken 127083 times.
|
2/2✓ Decision 'true' taken 13270 times.
✓ Decision 'false' taken 127083 times.
|
140353 | if (isSynchronizationPoint) { |
| 573 |
1/1✓ Branch 1 taken 13270 times.
|
13270 | triggerCountersError = getEventCountersError(triggerShape); | |
| 574 |
1/1✓ Branch 1 taken 13270 times.
|
13270 | bool isDecodingError = isTriggerCounterError(triggerCountersError); | |
| 575 | ||||
| 576 |
2/2✓ Branch 0 taken 5249 times.
✓ Branch 1 taken 8021 times.
|
2/2✓ Decision 'true' taken 5249 times.
✓ Decision 'false' taken 8021 times.
|
13270 | if (triggerStateListener) { |
| 577 |
1/1✓ Branch 1 taken 5249 times.
|
5249 | triggerStateListener->OnTriggerSynchronization(wasSynchronized, isDecodingError); | |
| 578 | } | |||
| 579 | ||||
| 580 | // If we got a sync point, but the wrong number of events since the last sync point | |||
| 581 | // One of two things has happened: | |||
| 582 | // - We missed a tooth, and this is the real sync point | |||
| 583 | // - Due to some mistake in timing, we found what looks like a sync point but actually isn't | |||
| 584 | // In either case, we should wait for another sync point before doing anything to try and run an engine, | |||
| 585 | // so we clear the synchronized flag. | |||
| 586 |
4/4✓ Branch 0 taken 11949 times.
✓ Branch 1 taken 1321 times.
✓ Branch 2 taken 26 times.
✓ Branch 3 taken 11923 times.
|
2/2✓ Decision 'true' taken 26 times.
✓ Decision 'false' taken 13244 times.
|
13270 | if (wasSynchronized && isDecodingError) { |
| 587 |
1/1✓ Branch 1 taken 26 times.
|
26 | setTriggerErrorState(); | |
| 588 |
2/2✓ Branch 1 taken 26 times.
✓ Branch 4 taken 26 times.
|
26 | onNotEnoughTeeth(currentCycle.current_index, triggerShape.getSize()); | |
| 589 | ||||
| 590 | // Something wrong, no longer synchronized | |||
| 591 |
1/1✓ Branch 1 taken 26 times.
|
26 | setShaftSynchronized(false); | |
| 592 | ||||
| 593 | // This is a decoding error | |||
| 594 |
1/1✓ Branch 1 taken 26 times.
|
26 | onTriggerError(); | |
| 595 |
1/1✓ Branch 1 taken 26 times.
|
26 | printGaps("newerr", triggerConfiguration, triggerShape); | |
| 596 | } else { | |||
| 597 | // If this was the first sync point OR no decode error, we're synchronized! | |||
| 598 |
1/1✓ Branch 1 taken 13244 times.
|
13244 | setShaftSynchronized(true); | |
| 599 | } | |||
| 600 | ||||
| 601 | // this call would update duty cycle values | |||
| 602 |
5/5✓ Branch 0 taken 5484 times.
✓ Branch 1 taken 7786 times.
✓ Branch 2 taken 9014 times.
✓ Branch 3 taken 4256 times.
✓ Branch 5 taken 9014 times.
|
13270 | nextTriggerEvent(); | |
| 603 | ||||
| 604 |
1/1✓ Branch 1 taken 13270 times.
|
13270 | onShaftSynchronization(wasSynchronized, nowNt, triggerShape); | |
| 605 | } else { /* if (!isSynchronizationPoint) */ | |||
| 606 |
5/5✓ Branch 0 taken 66555 times.
✓ Branch 1 taken 60528 times.
✓ Branch 2 taken 58435 times.
✓ Branch 3 taken 68648 times.
✓ Branch 5 taken 58435 times.
|
127083 | nextTriggerEvent(); | |
| 607 | } | |||
| 608 | ||||
| 609 |
2/2✓ Branch 0 taken 461523 times.
✓ Branch 1 taken 140353 times.
|
2/2✓ Decision 'true' taken 461523 times.
✓ Decision 'false' taken 140353 times.
|
601876 | for (int i = triggerShape.gapTrackingLength; i > 0; i--) { |
| 610 | 461523 | toothDurations[i] = toothDurations[i - 1]; | ||
| 611 | } | |||
| 612 | ||||
| 613 | 140353 | toothed_previous_time = nowNt; | ||
| 614 | ||||
| 615 | #if EFI_UNIT_TEST | |||
| 616 |
2/2✓ Branch 0 taken 78578 times.
✓ Branch 1 taken 61775 times.
|
2/2✓ Decision 'true' taken 78578 times.
✓ Decision 'false' taken 61775 times.
|
140353 | if (wasSynchronized) { |
| 617 |
1/1✓ Branch 1 taken 78578 times.
|
78578 | int uiGapIndex = (currentCycle.current_index) % triggerShape.getLength(); | |
| 618 | 78578 | gapRatio[uiGapIndex] = triggerSyncGapRatio; | ||
| 619 | } | |||
| 620 | #endif // EFI_UNIT_TEST | |||
| 621 | } | |||
| 622 | ||||
| 623 |
8/8✓ Branch 1 taken 191224 times.
✓ Branch 3 taken 103525 times.
✓ Branch 4 taken 87699 times.
✓ Branch 6 taken 103525 times.
✓ Branch 8 taken 93 times.
✓ Branch 9 taken 103432 times.
✓ Branch 10 taken 93 times.
✓ Branch 11 taken 191131 times.
|
2/2✓ Decision 'true' taken 93 times.
✓ Decision 'false' taken 191131 times.
|
191224 | if (getShaftSynchronized() && !isValidIndex(triggerShape)) { |
| 624 | // We've had too many events since the last sync point, we should have seen a sync point by now. | |||
| 625 | // This is a trigger error. | |||
| 626 | ||||
| 627 | // let's not show a warning if we are just starting to spin | |||
| 628 |
3/3✓ Branch 1 taken 93 times.
✓ Branch 3 taken 85 times.
✓ Branch 4 taken 8 times.
|
2/2✓ Decision 'true' taken 85 times.
✓ Decision 'false' taken 8 times.
|
93 | if (Sensor::getOrZero(SensorType::Rpm) != 0) { |
| 629 |
1/1✓ Branch 1 taken 85 times.
|
85 | setTriggerErrorState(); | |
| 630 |
2/2✓ Branch 1 taken 85 times.
✓ Branch 4 taken 85 times.
|
85 | onTooManyTeeth(currentCycle.current_index, triggerShape.getSize()); | |
| 631 | } | |||
| 632 | ||||
| 633 |
1/1✓ Branch 1 taken 93 times.
|
93 | onTriggerError(); | |
| 634 | ||||
| 635 |
1/1✓ Branch 1 taken 93 times.
|
93 | setShaftSynchronized(false); | |
| 636 | ||||
| 637 | 93 | return unexpected; | ||
| 638 | } | |||
| 639 | ||||
| 640 | 191131 | triggerStateIndex = currentCycle.current_index; | ||
| 641 | ||||
| 642 | // Needed for early instant-RPM detection | |||
| 643 | 191131 | TriggerStateListener * l = triggerStateListener; | ||
| 644 |
2/2✓ Branch 0 taken 38630 times.
✓ Branch 1 taken 191131 times.
|
2/2✓ Decision 'true' taken 38630 times.
✓ Decision 'false' taken 191131 times.
|
229761 | while (l) { |
| 645 |
1/1✓ Branch 1 taken 38630 times.
|
38630 | l->OnTriggerStateProperState(nowNt, triggerStateIndex); | |
| 646 |
1/1✓ Branch 1 taken 38630 times.
|
38630 | l = l->nextListener(); | |
| 647 | } | |||
| 648 | ||||
| 649 |
3/3✓ Branch 1 taken 191131 times.
✓ Branch 3 taken 103432 times.
✓ Branch 4 taken 87699 times.
|
2/2✓ Decision 'true' taken 103432 times.
✓ Decision 'false' taken 87699 times.
|
191131 | if (getShaftSynchronized()) { |
| 650 | 103432 | return TriggerDecodeResult{ currentCycle.current_index }; | ||
| 651 | } else { | |||
| 652 | 87699 | return unexpected; | ||
| 653 | } | |||
| 654 | } | |||
| 655 | ||||
| 656 | 132180 | bool TriggerDecoderBase::isSyncPoint(const TriggerWaveform& triggerShape, trigger_type_e triggerType) const { | ||
| 657 | // Miata NB needs a special decoder. | |||
| 658 | // The problem is that the crank wheel only has 4 teeth, also symmetrical, so the pattern | |||
| 659 | // is long-short-long-short for one crank rotation. | |||
| 660 | // A quick acceleration can result in two successive "short gaps", so we see | |||
| 661 | // long-short-short-short-long instead of the correct long-short-long-short-long | |||
| 662 | // This logic expands the lower bound on a "long" tooth, then compares the last | |||
| 663 | // tooth to the current one. | |||
| 664 | ||||
| 665 | // Instead of detecting short/long, this logic first checks for "maybe short" and "maybe long", | |||
| 666 | // then simply tests longer vs. shorter instead of absolute value. | |||
| 667 |
2/2✓ Branch 0 taken 665 times.
✓ Branch 1 taken 131515 times.
|
2/2✓ Decision 'true' taken 665 times.
✓ Decision 'false' taken 131515 times.
|
132180 | if (triggerType == trigger_type_e::TT_MIATA_VVT) { |
| 668 | 665 | auto secondGap = (float)toothDurations[1] / toothDurations[2]; | ||
| 669 | ||||
| 670 | 665 | bool currentGapOk = isInRange(triggerShape.synchronizationRatioFrom[0], (float)triggerSyncGapRatio, triggerShape.synchronizationRatioTo[0]); | ||
| 671 | 665 | bool secondGapOk = isInRange(triggerShape.synchronizationRatioFrom[1], secondGap, triggerShape.synchronizationRatioTo[1]); | ||
| 672 | ||||
| 673 | // One or both teeth was impossible range, this is not the sync point | |||
| 674 |
4/4✓ Branch 0 taken 333 times.
✓ Branch 1 taken 332 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 326 times.
|
2/2✓ Decision 'true' taken 339 times.
✓ Decision 'false' taken 326 times.
|
665 | if (!currentGapOk || !secondGapOk) { |
| 675 | 339 | return false; | ||
| 676 | } | |||
| 677 | ||||
| 678 | // If both teeth are in the range of possibility, return whether this gap is | |||
| 679 | // shorter than the last or not. If it is, this is the sync point. | |||
| 680 | 326 | return triggerSyncGapRatio < secondGap; | ||
| 681 | } | |||
| 682 | ||||
| 683 |
2/2✓ Branch 0 taken 170523 times.
✓ Branch 1 taken 6004 times.
|
2/2✓ Decision 'true' taken 170523 times.
✓ Decision 'false' taken 6004 times.
|
176527 | for (int i = 0; i < triggerShape.gapTrackingLength; i++) { |
| 684 | 170523 | auto from = triggerShape.synchronizationRatioFrom[i]; | ||
| 685 | 170523 | auto to = triggerShape.synchronizationRatioTo[i]; | ||
| 686 | ||||
| 687 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 170523 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 170523 times.
|
170523 | if (std::isnan(from)) { |
| 688 | // don't check this gap, skip it | |||
| 689 | ✗ | continue; | ||
| 690 | } | |||
| 691 | ||||
| 692 | // This is transformed to avoid a division and use a cheaper multiply instead | |||
| 693 | // toothDurations[i] / toothDurations[i+1] > from | |||
| 694 | // is an equivalent comparison to | |||
| 695 | // toothDurations[i] > toothDurations[i+1] * from | |||
| 696 | 170523 | bool isGapCondition = | ||
| 697 | 170523 | (toothDurations[i] > toothDurations[i + 1] * from | ||
| 698 |
4/4✓ Branch 0 taken 90614 times.
✓ Branch 1 taken 79909 times.
✓ Branch 4 taken 45012 times.
✓ Branch 5 taken 45602 times.
|
170523 | && toothDurations[i] < toothDurations[i + 1] * to); | |
| 699 | ||||
| 700 |
2/2✓ Branch 0 taken 125511 times.
✓ Branch 1 taken 45012 times.
|
2/2✓ Decision 'true' taken 125511 times.
✓ Decision 'false' taken 45012 times.
|
170523 | if (!isGapCondition) { |
| 701 | 125511 | return false; | ||
| 702 | } | |||
| 703 | } | |||
| 704 | ||||
| 705 | 6004 | return true; | ||
| 706 | } | |||
| 707 | ||||
| 708 | /** | |||
| 709 | * Trigger shape is defined in a way which is convenient for trigger shape definition | |||
| 710 | * On the other hand, trigger decoder indexing begins from synchronization event. | |||
| 711 | * | |||
| 712 | * This function finds the index of synchronization event within TriggerWaveform | |||
| 713 | */ | |||
| 714 | 1140 | uint32_t TriggerDecoderBase::findTriggerZeroEventIndex( | ||
| 715 | TriggerWaveform& shape, | |||
| 716 | const TriggerConfiguration& triggerConfiguration) { | |||
| 717 | #if EFI_PROD_CODE | |||
| 718 | efiAssert(ObdCode::CUSTOM_ERR_ASSERT, hasLotsOfRemainingStack(), "findPos", -1); | |||
| 719 | #endif | |||
| 720 | ||||
| 721 | ||||
| 722 |
1/1✓ Branch 1 taken 1140 times.
|
1140 | resetState(); | |
| 723 | ||||
| 724 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1140 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1140 times.
|
1140 | if (shape.shapeDefinitionError) { |
| 725 | ✗ | return 0; | ||
| 726 | } | |||
| 727 | ||||
| 728 |
1/1✓ Branch 2 taken 1074 times.
|
1140 | expected<uint32_t> syncIndex = TriggerStimulatorHelper::findTriggerSyncPoint(shape, | |
| 729 | triggerConfiguration, | |||
| 730 | *this); | |||
| 731 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1074 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1074 times.
|
1074 | if (!syncIndex) { |
| 732 | ✗ | return EFI_ERROR_CODE; | ||
| 733 | } | |||
| 734 | ||||
| 735 | // Assert that we found the sync point on the very first revolution | |||
| 736 |
2/4✓ Branch 1 taken 1074 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1074 times.
✗ Branch 6 not taken.
|
1074 | efiAssert(ObdCode::CUSTOM_ERR_ASSERT, getSynchronizationCounter() == 0, "findZero_revCounter", EFI_ERROR_CODE); | |
| 737 | ||||
| 738 | #if EFI_UNIT_TEST | |||
| 739 |
2/2✓ Branch 0 taken 800 times.
✓ Branch 1 taken 274 times.
|
2/2✓ Decision 'true' taken 800 times.
✓ Decision 'false' taken 274 times.
|
1074 | if (printTriggerDebug) { |
| 740 |
1/1✓ Branch 1 taken 800 times.
|
800 | printf("findTriggerZeroEventIndex: syncIndex located %lu!\r\n", syncIndex.Value); | |
| 741 | } | |||
| 742 | #endif /* EFI_UNIT_TEST */ | |||
| 743 | ||||
| 744 |
1/1✓ Branch 1 taken 1074 times.
|
1074 | TriggerStimulatorHelper::assertSyncPosition(triggerConfiguration, | |
| 745 | syncIndex.Value, *this, shape); | |||
| 746 | ||||
| 747 |
1/1✓ Branch 1 taken 1074 times.
|
1074 | return syncIndex.Value % shape.getSize(); | |
| 748 | } | |||
| 749 | ||||
| 750 | #endif /* EFI_SHAFT_POSITION_INPUT */ | |||
| 751 | ||||
| 752 |