| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /* | |||
| 2 | * @file trigger_central.cpp | |||
| 3 | * Here we have a bunch of higher-level methods which are not directly related to actual signal decoding | |||
| 4 | * | |||
| 5 | * @date Feb 23, 2014 | |||
| 6 | * @author Andrey Belomutskiy, (c) 2012-2020 | |||
| 7 | */ | |||
| 8 | ||||
| 9 | #include "pch.h" | |||
| 10 | ||||
| 11 | #include "trigger_central.h" | |||
| 12 | #include "trigger_decoder.h" | |||
| 13 | #include "main_trigger_callback.h" | |||
| 14 | #include "listener_array.h" | |||
| 15 | #include "logic_analyzer.h" | |||
| 16 | ||||
| 17 | #include "local_version_holder.h" | |||
| 18 | #include "trigger_simulator.h" | |||
| 19 | #include "trigger_emulator_algo.h" | |||
| 20 | ||||
| 21 | #include "map_averaging.h" | |||
| 22 | #include "main_trigger_callback.h" | |||
| 23 | #include "status_loop.h" | |||
| 24 | #include "engine_sniffer.h" | |||
| 25 | #include "auto_generated_sync_edge.h" | |||
| 26 | ||||
| 27 | #if EFI_TUNER_STUDIO | |||
| 28 | #include "tunerstudio.h" | |||
| 29 | #endif /* EFI_TUNER_STUDIO */ | |||
| 30 | ||||
| 31 | #if EFI_ENGINE_SNIFFER | |||
| 32 | WaveChart waveChart; | |||
| 33 | #endif /* EFI_ENGINE_SNIFFER */ | |||
| 34 | ||||
| 35 | #define TRIGGER_WAVEFORM(x) getTriggerCentral()->triggerShape.x | |||
| 36 | ||||
| 37 | #if EFI_SHAFT_POSITION_INPUT | |||
| 38 | ||||
| 39 | 678 | TriggerCentral::TriggerCentral() : | ||
| 40 |
4/4✓ Branch 0 taken 2712 times.
✓ Branch 1 taken 1356 times.
✓ Branch 2 taken 1356 times.
✓ Branch 3 taken 678 times.
|
4746 | vvtPosition(), | |
| 41 |
2/2✓ Branch 14 taken 1356 times.
✓ Branch 15 taken 678 times.
|
2712 | triggerState("TRG") | |
| 42 | { | |||
| 43 | 678 | setArrayValues(hwEventCounters, 0); | ||
| 44 | 678 | triggerState.resetState(); | ||
| 45 | 678 | noiseFilter.resetAccumSignalData(); | ||
| 46 | 678 | } | ||
| 47 | ||||
| 48 | 797 | void TriggerNoiseFilter::resetAccumSignalData() { | ||
| 49 | 797 | memset(lastSignalTimes, 0xff, sizeof(lastSignalTimes)); // = -1 | ||
| 50 | 797 | memset(accumSignalPeriods, 0, sizeof(accumSignalPeriods)); | ||
| 51 | 797 | memset(accumSignalPrevPeriods, 0, sizeof(accumSignalPrevPeriods)); | ||
| 52 | 797 | } | ||
| 53 | ||||
| 54 | 2124312 | int TriggerCentral::getHwEventCounter(int index) const { | ||
| 55 | 2124312 | return hwEventCounters[index]; | ||
| 56 | } | |||
| 57 | ||||
| 58 | 2132286 | angle_t TriggerCentral::getVVTPosition(uint8_t bankIndex, uint8_t camIndex) { | ||
| 59 |
2/4✓ Branch 0 taken 2132286 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2132286 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 2132286 times.
|
2132286 | if (bankIndex >= BANKS_COUNT || camIndex >= CAMS_PER_BANK) { |
| 60 | ✗ | return NAN; | ||
| 61 | } | |||
| 62 | 2132286 | return vvtPosition[bankIndex][camIndex]; | ||
| 63 | } | |||
| 64 | ||||
| 65 | /** | |||
| 66 | * @return angle since trigger synchronization point, NOT angle since TDC. | |||
| 67 | */ | |||
| 68 | 42724 | expected<float> TriggerCentral::getCurrentEnginePhase(efitick_t nowNt) const { | ||
| 69 | 42724 | floatus_t oneDegreeUs = engine->rpmCalculator.oneDegreeUs; | ||
| 70 | ||||
| 71 |
2/2✓ Branch 1 taken 2696 times.
✓ Branch 2 taken 40028 times.
|
2/2✓ Decision 'true' taken 2696 times.
✓ Decision 'false' taken 40028 times.
|
42724 | if (std::isnan(oneDegreeUs)) { |
| 72 | 2696 | return unexpected; | ||
| 73 | } | |||
| 74 | ||||
| 75 | float elapsed; | |||
| 76 | float toothPhase; | |||
| 77 | ||||
| 78 | { | |||
| 79 | // under lock to avoid mismatched tooth phase and time | |||
| 80 | chibios_rt::CriticalSectionLocker csl; | |||
| 81 | ||||
| 82 | 40028 | elapsed = m_lastToothTimer.getElapsedUs(nowNt); | ||
| 83 | 40028 | toothPhase = m_lastToothPhaseFromSyncPoint; | ||
| 84 | } | |||
| 85 | ||||
| 86 | 40028 | return toothPhase + elapsed / oneDegreeUs; | ||
| 87 | } | |||
| 88 | ||||
| 89 | /** | |||
| 90 | * todo: why is this method NOT reciprocal to getRpmMultiplier?! | |||
| 91 | */ | |||
| 92 | 34492 | int getCrankDivider(operation_mode_e operationMode) { | ||
| 93 |
5/7✓ Branch 0 taken 19919 times.
✓ Branch 1 taken 3214 times.
✓ Branch 2 taken 1974 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 213 times.
✓ Branch 5 taken 9172 times.
✗ Branch 6 not taken.
|
34492 | switch (operationMode) { | |
| 94 |
1/1✓ Decision 'true' taken 19919 times.
|
19919 | case FOUR_STROKE_CRANK_SENSOR: | |
| 95 | 19919 | return 2; | ||
| 96 |
1/1✓ Decision 'true' taken 3214 times.
|
3214 | case FOUR_STROKE_SYMMETRICAL_CRANK_SENSOR: | |
| 97 | 3214 | return SYMMETRICAL_CRANK_SENSOR_DIVIDER; | ||
| 98 |
1/1✓ Decision 'true' taken 1974 times.
|
1974 | case FOUR_STROKE_THREE_TIMES_CRANK_SENSOR: | |
| 99 | 1974 | return SYMMETRICAL_THREE_TIMES_CRANK_SENSOR_DIVIDER; | ||
| 100 | ✗ | case FOUR_STROKE_SIX_TIMES_CRANK_SENSOR: | ||
| 101 | ✗ | return SYMMETRICAL_SIX_TIMES_CRANK_SENSOR_DIVIDER; | ||
| 102 |
1/1✓ Decision 'true' taken 213 times.
|
213 | case FOUR_STROKE_TWELVE_TIMES_CRANK_SENSOR: | |
| 103 | 213 | return SYMMETRICAL_TWELVE_TIMES_CRANK_SENSOR_DIVIDER; | ||
| 104 |
1/1✓ Decision 'true' taken 9172 times.
|
9172 | case OM_NONE: | |
| 105 | case FOUR_STROKE_CAM_SENSOR: | |||
| 106 | case TWO_STROKE: | |||
| 107 | // That's easy - trigger cycle matches engine cycle | |||
| 108 |
1/1✓ Decision 'true' taken 9172 times.
|
9172 | return 1; | |
| 109 | /* let's NOT handle default in order to benefit from -Werror=switch */ | |||
| 110 | } | |||
| 111 | /** | |||
| 112 | wow even while we explicitly handle all enumerations in the switch above we still need a return statement due to | |||
| 113 | https://stackoverflow.com/questions/34112483/gcc-how-best-to-handle-warning-about-unreachable-end-of-function-after-switch | |||
| 114 | */ | |||
| 115 | ✗ | criticalError("unreachable getCrankDivider"); | ||
| 116 | ✗ | return 1; | ||
| 117 | } | |||
| 118 | ||||
| 119 | 6125 | PUBLIC_API_WEAK bool boardIsSpecialVvtDecoder(vvt_mode_e vvtMode) { | ||
| 120 | UNUSED(vvtMode); | |||
| 121 | ||||
| 122 | 6125 | return false; | ||
| 123 | } | |||
| 124 | ||||
| 125 | 33836 | PUBLIC_API_WEAK void boardTriggerCallback(efitick_t timestamp, float currentPhase) { UNUSED(timestamp); UNUSED(currentPhase); } | ||
| 126 | ||||
| 127 | 6393 | static bool vvtWithRealDecoder(vvt_mode_e vvtMode) { | ||
| 128 | return vvtMode != VVT_INACTIVE | |||
| 129 |
2/2✓ Branch 0 taken 6320 times.
✓ Branch 1 taken 73 times.
|
6393 | && vvtMode != VVT_TOYOTA_3_TOOTH /* VVT_2JZ is an unusual 3/0 missed tooth symmetrical wheel */ | |
| 130 |
2/2✓ Branch 0 taken 6129 times.
✓ Branch 1 taken 191 times.
|
6320 | && vvtMode != VVT_HONDA_K_INTAKE | |
| 131 |
2/2✓ Branch 0 taken 6125 times.
✓ Branch 1 taken 4 times.
|
6129 | && vvtMode != VVT_MAP_V_TWIN | |
| 132 |
1/2✓ Branch 1 taken 6125 times.
✗ Branch 2 not taken.
|
6125 | && !boardIsSpecialVvtDecoder(vvtMode) | |
| 133 |
3/4✓ Branch 0 taken 6393 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5801 times.
✓ Branch 3 taken 324 times.
|
12786 | && vvtMode != VVT_SINGLE_TOOTH; | |
| 134 | } | |||
| 135 | ||||
| 136 | 656 | angle_t TriggerCentral::syncEnginePhaseAndReport(int divider, int remainder) { | ||
| 137 | 656 | angle_t engineCycle = getEngineCycle(getEngineRotationState()->getOperationMode()); | ||
| 138 | ||||
| 139 | 656 | angle_t totalShift = triggerState.syncEnginePhase(divider, remainder, engineCycle); | ||
| 140 |
2/2✓ Branch 0 taken 38 times.
✓ Branch 1 taken 618 times.
|
2/2✓ Decision 'true' taken 38 times.
✓ Decision 'false' taken 618 times.
|
656 | if (totalShift != 0) { |
| 141 | // Reset instant RPM, since the engine phase has now changed, invalidating the tooth history buffer | |||
| 142 | // maybe TODO: could/should we rotate the buffer around to re-align it instead? Is that worth it? | |||
| 143 | 38 | instantRpm.resetInstantRpm(); | ||
| 144 | } | |||
| 145 | 656 | return totalShift; | ||
| 146 | } | |||
| 147 | ||||
| 148 | ✗ | PUBLIC_API_WEAK angle_t customAdjustCustom(TriggerCentral *tc, vvt_mode_e vvtMode) { | ||
| 149 | UNUSED(tc); | |||
| 150 | UNUSED(vvtMode); | |||
| 151 | ||||
| 152 | ✗ | return 0; | ||
| 153 | } | |||
| 154 | ||||
| 155 | 653 | static angle_t adjustCrankPhase(int camIndex) { | ||
| 156 | 653 | float maxSyncThreshold = engineConfiguration->maxCamPhaseResolveRpm; | ||
| 157 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 653 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 653 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 653 times.
|
653 | if (maxSyncThreshold != 0 && Sensor::getOrZero(SensorType::Rpm) > maxSyncThreshold) { |
| 158 | // The user has elected to stop trying to resolve crank phase after some RPM. | |||
| 159 | // Maybe their cam sensor only works at low RPM or something. | |||
| 160 | // Anyway, don't try to change crank phase at all, and return that we made no change. | |||
| 161 | ✗ | return 0; | ||
| 162 | } | |||
| 163 | ||||
| 164 | 653 | operation_mode_e operationMode = getEngineRotationState()->getOperationMode(); | ||
| 165 | ||||
| 166 | 653 | auto crankDivider = getCrankDivider(operationMode); | ||
| 167 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 653 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 653 times.
|
653 | if (crankDivider == 1) { |
| 168 | // Crank divider of 1 means there's no ambiguity, so don't try to resolve it | |||
| 169 | ✗ | return 0; | ||
| 170 | } | |||
| 171 | ||||
| 172 | 653 | TriggerCentral *tc = getTriggerCentral(); | ||
| 173 | ||||
| 174 | 653 | vvt_mode_e vvtMode = engineConfiguration->vvtMode[camIndex]; | ||
| 175 |
2/6✓ Branch 0 taken 16 times.
✓ Branch 1 taken 637 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
653 | switch (vvtMode) { | |
| 176 |
1/1✓ Decision 'true' taken 16 times.
|
16 | case VVT_MAP_V_TWIN: | |
| 177 | case VVT_MITSUBISHI_4G63: | |||
| 178 | case VVT_UNUSED_17: | |||
| 179 |
1/1✓ Decision 'true' taken 16 times.
|
16 | return tc->syncEnginePhaseAndReport(crankDivider, 1); | |
| 180 |
1/1✓ Decision 'true' taken 637 times.
|
637 | case VVT_SINGLE_TOOTH: | |
| 181 | case VVT_NISSAN_VQ: | |||
| 182 | case VVT_BOSCH_QUICK_START: | |||
| 183 | case VVT_MIATA_NB: | |||
| 184 | case VVT_TOYOTA_3TOOTH_UZ: | |||
| 185 | case VVT_TOYOTA_3_TOOTH: | |||
| 186 | case VVT_TOYOTA_4_1: | |||
| 187 | case VVT_FORD_COYOTE: | |||
| 188 | case VVT_DEV: | |||
| 189 | case VVT_FORD_ST170: | |||
| 190 | case VVT_BARRA_3_PLUS_1: | |||
| 191 | case VVT_NISSAN_MR: | |||
| 192 | case VVT_HR12DDR_IN: | |||
| 193 | case VVT_MAZDA_SKYACTIV: | |||
| 194 | case VVT_MAZDA_L: | |||
| 195 | case VVT_MITSUBISHI_4G69: | |||
| 196 | case VVT_MITSUBISHI_3A92: | |||
| 197 | case VVT_MITSUBISHI_6G72: | |||
| 198 | case VVT_CHRYSLER_PHASER: | |||
| 199 | case VVT_HONDA_K_EXHAUST: | |||
| 200 | case VVT_HONDA_CBR_600: | |||
| 201 | case VVT_SUBARU_7TOOTH: | |||
| 202 |
1/1✓ Decision 'true' taken 637 times.
|
637 | return tc->syncEnginePhaseAndReport(crankDivider, 0); | |
| 203 | ✗ | case VVT_CUSTOM_25: | ||
| 204 | case VVT_CUSTOM_26: | |||
| 205 | ✗ | return customAdjustCustom(tc, vvtMode); | ||
| 206 | ||||
| 207 | ✗ | case VVT_HONDA_K_INTAKE: | ||
| 208 | // with 4 evenly spaced tooth we cannot use this wheel for engine sync | |||
| 209 | ✗ | criticalError("Honda K Intake is not suitable for engine sync"); | ||
| 210 | [[fallthrough]]; | |||
| 211 | ✗ | case VVT_CUSTOM_1: | ||
| 212 | case VVT_CUSTOM_2: | |||
| 213 | case VVT_INACTIVE: | |||
| 214 | // do nothing | |||
| 215 | ✗ | return 0; | ||
| 216 | } | |||
| 217 | ✗ | return 0; | ||
| 218 | } | |||
| 219 | ||||
| 220 | /** | |||
| 221 | * See also wrapAngle | |||
| 222 | */ | |||
| 223 | 827 | static angle_t wrapVvt(angle_t vvtPosition, int period) { | ||
| 224 | // Wrap VVT position in to the range [-360, 360) | |||
| 225 |
2/2✓ Branch 0 taken 3641 times.
✓ Branch 1 taken 827 times.
|
2/2✓ Decision 'true' taken 3641 times.
✓ Decision 'false' taken 827 times.
|
4468 | while (vvtPosition < -period / 2) { |
| 226 | 3641 | vvtPosition += period; | ||
| 227 | } | |||
| 228 |
2/2✓ Branch 0 taken 198 times.
✓ Branch 1 taken 827 times.
|
2/2✓ Decision 'true' taken 198 times.
✓ Decision 'false' taken 827 times.
|
1025 | while (vvtPosition >= period / 2) { |
| 229 | 198 | vvtPosition -= period; | ||
| 230 | } | |||
| 231 | 827 | return vvtPosition; | ||
| 232 | } | |||
| 233 | ||||
| 234 | 6393 | static void logVvtFront(bool useOnlyRise, bool isImportantFront, TriggerValue front, efitick_t nowNt, int index) { | ||
| 235 |
4/4✓ Branch 0 taken 4738 times.
✓ Branch 1 taken 1655 times.
✓ Branch 2 taken 416 times.
✓ Branch 3 taken 4322 times.
|
2/2✓ Decision 'true' taken 2071 times.
✓ Decision 'false' taken 4322 times.
|
6393 | if (!useOnlyRise || engineConfiguration->displayLogicLevelsInEngineSniffer) { |
| 236 | // If we care about both edges OR displayLogicLevel is set, log every front exactly as it is | |||
| 237 | 2071 | addEngineSnifferVvtEvent(index, front == TriggerValue::RISE ? FrontDirection::UP : FrontDirection::DOWN); | ||
| 238 | ||||
| 239 | #if EFI_TOOTH_LOGGER | |||
| 240 | 2071 | LogTriggerCamTooth(front == TriggerValue::RISE, nowNt, index); | ||
| 241 | #endif /* EFI_TOOTH_LOGGER */ | |||
| 242 | } else { | |||
| 243 |
2/2✓ Branch 0 taken 2317 times.
✓ Branch 1 taken 2005 times.
|
2/2✓ Decision 'true' taken 2317 times.
✓ Decision 'false' taken 2005 times.
|
4322 | if (isImportantFront) { |
| 244 | // On the important edge, log a rise+fall pair, and nothing on the real falling edge | |||
| 245 | 2317 | addEngineSnifferVvtEvent(index, FrontDirection::UP); | ||
| 246 | 2317 | addEngineSnifferVvtEvent(index, FrontDirection::DOWN); | ||
| 247 | ||||
| 248 | #if EFI_TOOTH_LOGGER | |||
| 249 | 2317 | LogTriggerCamTooth(true, nowNt, index); | ||
| 250 | 2317 | LogTriggerCamTooth(false, nowNt, index); | ||
| 251 | #endif /* EFI_TOOTH_LOGGER */ | |||
| 252 | } | |||
| 253 | } | |||
| 254 | 6393 | } | ||
| 255 | ||||
| 256 | 69508 | static bool tooSoonToHandleSignal() { | ||
| 257 | #if EFI_PROD_CODE | |||
| 258 | extern bool main_loop_started; | |||
| 259 | if (!main_loop_started) { | |||
| 260 | warning(ObdCode::CUSTOM_ERR_INPUT_DURING_INITIALISATION, "event too early"); | |||
| 261 | return true; | |||
| 262 | } | |||
| 263 | #endif //EFI_PROD_CODE | |||
| 264 | 69508 | return false; | ||
| 265 | } | |||
| 266 | ||||
| 267 | /** | |||
| 268 | * This function is called by all "hardware" trigger inputs: | |||
| 269 | * - Hardware triggers | |||
| 270 | * - Trigger replay from CSV (unit tests) | |||
| 271 | */ | |||
| 272 | 5887 | void hwHandleVvtCamSignal(bool isRising, efitick_t nowNt, int index) { | ||
| 273 | 5887 | int camIndex = CAM_BY_INDEX(index); | ||
| 274 |
2/2✓ Branch 0 taken 5662 times.
✓ Branch 1 taken 225 times.
|
5887 | bool invertSetting = camIndex == 0 ? engineConfiguration->invertCamVVTSignal : engineConfiguration->invertExhaustCamVVTSignal; | |
| 275 | ||||
| 276 |
2/2✓ Branch 0 taken 2950 times.
✓ Branch 1 taken 2937 times.
|
2/2✓ Decision 'true' taken 2950 times.
✓ Decision 'false' taken 2937 times.
|
5887 | if (isRising ^ invertSetting) { |
| 277 | 2950 | hwHandleVvtCamSignal(TriggerValue::RISE, nowNt, index); | ||
| 278 | } else { | |||
| 279 | 2937 | hwHandleVvtCamSignal(TriggerValue::FALL, nowNt, index); | ||
| 280 | } | |||
| 281 | 5887 | } | ||
| 282 | ||||
| 283 | // 'invertCamVVTSignal' is already accounted by the time this method is invoked | |||
| 284 | 6393 | void hwHandleVvtCamSignal(TriggerValue front, efitick_t nowNt, int index) { | ||
| 285 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6393 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 6393 times.
|
6393 | if (tooSoonToHandleSignal()) { |
| 286 | ✗ | return; | ||
| 287 | } | |||
| 288 | 6393 | TriggerCentral *tc = getTriggerCentral(); | ||
| 289 |
2/4✓ Branch 0 taken 6393 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6393 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 6393 times.
|
6393 | if (tc->directSelfStimulation || !tc->hwTriggerInputEnabled) { |
| 290 | // sensor noise + self-stim = loss of trigger sync | |||
| 291 | ✗ | return; | ||
| 292 | } | |||
| 293 | 6393 | handleVvtCamSignal(front, nowNt, index); | ||
| 294 | } | |||
| 295 | ||||
| 296 | /** | |||
| 297 | * @returns true if tooth should be ignored | |||
| 298 | */ | |||
| 299 | 784 | PUBLIC_API_WEAK bool skipToothSpecialShape(size_t index, vvt_mode_e vvtMode, angle_t currentPosition) { | ||
| 300 | UNUSED(index); | |||
| 301 | ||||
| 302 |
2/2✓ Branch 0 taken 33 times.
✓ Branch 1 taken 751 times.
|
784 | switch(vvtMode) { | |
| 303 |
1/1✓ Decision 'true' taken 33 times.
|
33 | case VVT_TOYOTA_3_TOOTH: | |
| 304 | { | |||
| 305 | 33 | int from = engineConfiguration->camDecoder2jzPosition - engineConfiguration->camDecoder2jzPrecision; | ||
| 306 | 33 | int to = engineConfiguration->camDecoder2jzPosition + engineConfiguration->camDecoder2jzPrecision; | ||
| 307 | // we do not know if we are in sync or out of sync, so we have to be looking for both possibilities | |||
| 308 |
3/4✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 32 times.
✓ Branch 3 taken 1 time.
|
2/2✓ Decision 'true' taken 32 times.
✓ Decision 'false' taken 1 time.
|
33 | if ((currentPosition < from || currentPosition > to) && |
| 309 |
3/4✓ Branch 0 taken 11 times.
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
|
32 | (currentPosition < from + 360 || currentPosition > to + 360)) { | |
| 310 | // outside of the expected range | |||
| 311 | 32 | return true; | ||
| 312 | } | |||
| 313 | } | |||
| 314 | 1 | break; | ||
| 315 |
1/1✓ Decision 'true' taken 751 times.
|
751 | default: | |
| 316 | ||||
| 317 | // else, do nothing | |||
| 318 | 751 | break; | ||
| 319 | } | |||
| 320 | 752 | return false; | ||
| 321 | } | |||
| 322 | ||||
| 323 | 6393 | void handleVvtCamSignal(TriggerValue front, efitick_t nowNt, int index) { | ||
| 324 |
1/1✓ Branch 1 taken 6393 times.
|
6393 | TriggerCentral *tc = getTriggerCentral(); | |
| 325 |
2/2✓ Branch 0 taken 5932 times.
✓ Branch 1 taken 461 times.
|
2/2✓ Decision 'true' taken 5932 times.
✓ Decision 'false' taken 461 times.
|
6393 | if (index == 0) { |
| 326 | 5932 | engine->outputChannels.vvtChannel1 = front == TriggerValue::RISE; | ||
| 327 |
2/2✓ Branch 0 taken 227 times.
✓ Branch 1 taken 234 times.
|
2/2✓ Decision 'true' taken 227 times.
✓ Decision 'false' taken 234 times.
|
461 | } else if (index == 1) { |
| 328 | 227 | engine->outputChannels.vvtChannel2 = front == TriggerValue::RISE; | ||
| 329 |
2/2✓ Branch 0 taken 232 times.
✓ Branch 1 taken 2 times.
|
2/2✓ Decision 'true' taken 232 times.
✓ Decision 'false' taken 2 times.
|
234 | } else if (index == 2) { |
| 330 | 232 | engine->outputChannels.vvtChannel3 = front == TriggerValue::RISE; | ||
| 331 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 2 times.
✗ Decision 'false' not taken.
|
2 | } else if (index == 3) { |
| 332 | 2 | engine->outputChannels.vvtChannel4 = front == TriggerValue::RISE; | ||
| 333 | } | |||
| 334 | ||||
| 335 | 6393 | int bankIndex = BANK_BY_INDEX(index); | ||
| 336 | 6393 | int camIndex = CAM_BY_INDEX(index); | ||
| 337 |
2/2✓ Branch 0 taken 3357 times.
✓ Branch 1 taken 3036 times.
|
2/2✓ Decision 'true' taken 3357 times.
✓ Decision 'false' taken 3036 times.
|
6393 | if (front == TriggerValue::RISE) { |
| 338 | 3357 | tc->vvtEventRiseCounter[index]++; | ||
| 339 | } else { | |||
| 340 | 3036 | tc->vvtEventFallCounter[index]++; | ||
| 341 | } | |||
| 342 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6393 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 6393 times.
|
6393 | if (engineConfiguration->vvtMode[camIndex] == VVT_INACTIVE) { |
| 343 | ✗ | warning(ObdCode::CUSTOM_VVT_MODE_NOT_SELECTED, "VVT: event on %d but no mode", camIndex); | ||
| 344 | } | |||
| 345 | ||||
| 346 | 6393 | const auto& vvtShape = tc->vvtShape[camIndex]; | ||
| 347 | ||||
| 348 |
1/1✓ Branch 2 taken 6393 times.
|
6393 | bool isVvtWithRealDecoder = vvtWithRealDecoder(engineConfiguration->vvtMode[camIndex]); | |
| 349 | ||||
| 350 | // Non real decoders only use the rising edge | |||
| 351 |
4/4✓ Branch 0 taken 5801 times.
✓ Branch 1 taken 592 times.
✓ Branch 2 taken 4146 times.
✓ Branch 3 taken 1655 times.
|
6393 | bool vvtUseOnlyRise = !isVvtWithRealDecoder || vvtShape.useOnlyRisingEdges; | |
| 352 |
4/4✓ Branch 0 taken 4738 times.
✓ Branch 1 taken 1655 times.
✓ Branch 2 taken 2526 times.
✓ Branch 3 taken 2212 times.
|
6393 | bool isImportantFront = !vvtUseOnlyRise || (front == TriggerValue::RISE); | |
| 353 | ||||
| 354 |
1/1✓ Branch 1 taken 6393 times.
|
6393 | logVvtFront(vvtUseOnlyRise, isImportantFront, front, nowNt, index); | |
| 355 | ||||
| 356 |
2/2✓ Branch 0 taken 2212 times.
✓ Branch 1 taken 4181 times.
|
2/2✓ Decision 'true' taken 2212 times.
✓ Decision 'false' taken 4181 times.
|
6393 | if (!isImportantFront) { |
| 357 | // This edge is unimportant, ignore it. | |||
| 358 | 2212 | return; | ||
| 359 | } | |||
| 360 | ||||
| 361 | // If the main trigger is not synchronized, don't decode VVT yet | |||
| 362 |
3/3✓ Branch 1 taken 4181 times.
✓ Branch 3 taken 185 times.
✓ Branch 4 taken 3996 times.
|
2/2✓ Decision 'true' taken 185 times.
✓ Decision 'false' taken 3996 times.
|
4181 | if (!tc->triggerState.getShaftSynchronized()) { |
| 363 | 185 | return; | ||
| 364 | } | |||
| 365 | ||||
| 366 | 3996 | TriggerDecoderBase& vvtDecoder = tc->vvtState[bankIndex][camIndex]; | ||
| 367 | ||||
| 368 |
2/2✓ Branch 0 taken 3567 times.
✓ Branch 1 taken 429 times.
|
2/2✓ Decision 'true' taken 3567 times.
✓ Decision 'false' taken 429 times.
|
3996 | if (isVvtWithRealDecoder) { |
| 369 |
3/3✓ Branch 0 taken 2804 times.
✓ Branch 1 taken 763 times.
✓ Branch 3 taken 3567 times.
|
7134 | vvtDecoder.decodeTriggerEvent( | |
| 370 | "vvt", | |||
| 371 | vvtShape, | |||
| 372 | nullptr, | |||
| 373 | 3567 | tc->vvtTriggerConfiguration[camIndex], | ||
| 374 | front == TriggerValue::RISE ? SHAFT_PRIMARY_RISING : SHAFT_PRIMARY_FALLING, nowNt); | |||
| 375 | 3567 | vvtDecoder.vvtToothDurations0 = (uint32_t)NT2US(vvtDecoder.toothDurations[0]); | ||
| 376 | } | |||
| 377 | ||||
| 378 | // here we count all cams together | |||
| 379 | 3996 | tc->vvtCamCounter++; | ||
| 380 | ||||
| 381 |
1/1✓ Branch 2 taken 3996 times.
|
3996 | auto currentPhase = tc->getCurrentEnginePhase(nowNt); | |
| 382 |
2/2✓ Branch 1 taken 66 times.
✓ Branch 2 taken 3930 times.
|
2/2✓ Decision 'true' taken 66 times.
✓ Decision 'false' taken 3930 times.
|
3996 | if (!currentPhase) { |
| 383 | // If we couldn't resolve engine speed (yet primary trigger is sync'd), this | |||
| 384 | // probably means that we have partial crank sync, but not RPM information yet | |||
| 385 | 66 | return; | ||
| 386 | } | |||
| 387 | ||||
| 388 | 3930 | angle_t angleFromPrimarySyncPoint = currentPhase.Value; | ||
| 389 | // convert trigger cycle angle into engine cycle angle | |||
| 390 |
3/3✓ Branch 1 taken 3930 times.
✓ Branch 3 taken 166 times.
✓ Branch 4 taken 3764 times.
|
3930 | angle_t currentPosition = angleFromPrimarySyncPoint - tdcPosition(); | |
| 391 | // https://github.com/rusefi/rusefi/issues/1713 currentPosition could be negative that's expected | |||
| 392 | ||||
| 393 | #if EFI_UNIT_TEST | |||
| 394 | 3930 | tc->currentVVTEventPosition[bankIndex][camIndex] = currentPosition; | ||
| 395 | #endif // EFI_UNIT_TEST | |||
| 396 | ||||
| 397 | 3930 | tc->triggerState.vvtCurrentPosition = currentPosition; | ||
| 398 | ||||
| 399 |
4/4✓ Branch 0 taken 3505 times.
✓ Branch 1 taken 425 times.
✓ Branch 2 taken 3146 times.
✓ Branch 3 taken 359 times.
|
2/2✓ Decision 'true' taken 3146 times.
✓ Decision 'false' taken 784 times.
|
3930 | if (isVvtWithRealDecoder && vvtDecoder.currentCycle.current_index != 0) { |
| 400 | // this is not sync tooth - exiting | |||
| 401 | 3146 | return; | ||
| 402 | } | |||
| 403 | ||||
| 404 | 784 | auto vvtPosition = engineConfiguration->vvtOffsets[bankIndex * CAMS_PER_BANK + camIndex] - currentPosition; | ||
| 405 | 784 | tc->triggerState.vvtToothPosition[index] = vvtPosition; | ||
| 406 | ||||
| 407 |
1/1✓ Branch 2 taken 784 times.
|
784 | bool skipTooth = skipToothSpecialShape(index, engineConfiguration->vvtMode[camIndex], currentPosition); | |
| 408 |
2/2✓ Branch 0 taken 32 times.
✓ Branch 1 taken 752 times.
|
2/2✓ Decision 'true' taken 32 times.
✓ Decision 'false' taken 752 times.
|
784 | if (skipTooth) { |
| 409 | 32 | return; | ||
| 410 | } | |||
| 411 | ||||
| 412 | // this could be just an 'if' but let's have it expandable for future use :) | |||
| 413 |
2/2✓ Branch 1 taken 75 times.
✓ Branch 2 taken 677 times.
|
752 | switch(engineConfiguration->vvtMode[camIndex]) { | |
| 414 |
1/1✓ Decision 'true' taken 75 times.
|
75 | case VVT_HONDA_K_INTAKE: | |
| 415 | // honda K has four tooth in VVT intake trigger, so we just wrap each of those to 720 / 4 | |||
| 416 | 75 | vvtPosition = wrapVvt(vvtPosition, 180); | ||
| 417 | 75 | break; | ||
| 418 |
1/1✓ Decision 'true' taken 677 times.
|
677 | default: | |
| 419 | // else, do nothing | |||
| 420 | 677 | break; | ||
| 421 | } | |||
| 422 | ||||
| 423 | #if EFI_PROD_CODE | |||
| 424 | if (!isBrainPinValid(engineConfiguration->camInputs[engineConfiguration->engineSyncCam]) && | |||
| 425 | engineConfiguration->vvtMode[engineConfiguration->engineSyncCam] != VVT_MAP_V_TWIN) { | |||
| 426 | criticalError("Selected engine sync input not configured: %d", engineConfiguration->engineSyncCam); | |||
| 427 | } | |||
| 428 | #endif // EFI_PROD_CODE | |||
| 429 | ||||
| 430 | // Only do engine sync using one cam, other cams just provide VVT position. | |||
| 431 |
2/2✓ Branch 0 taken 653 times.
✓ Branch 1 taken 99 times.
|
2/2✓ Decision 'true' taken 653 times.
✓ Decision 'false' taken 99 times.
|
752 | if (index == engineConfiguration->engineSyncCam) { |
| 432 |
1/1✓ Branch 1 taken 653 times.
|
653 | angle_t crankOffset = adjustCrankPhase(camIndex); | |
| 433 | // vvtPosition was calculated against wrong crank zero position. Now that we have adjusted crank position we | |||
| 434 | // shall adjust vvt position as well | |||
| 435 | 653 | vvtPosition -= crankOffset; | ||
| 436 | 653 | vvtPosition = wrapVvt(vvtPosition, FOUR_STROKE_CYCLE_DURATION); | ||
| 437 | ||||
| 438 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 653 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 653 times.
|
653 | if (absF(angleFromPrimarySyncPoint) < 7) { |
| 439 | /** | |||
| 440 | * we prefer not to have VVT sync right at trigger sync so that we do not have phase detection error if things happen a bit in | |||
| 441 | * wrong order due to belt flex or else | |||
| 442 | * https://github.com/rusefi/rusefi/issues/3269 | |||
| 443 | */ | |||
| 444 | ✗ | warning(ObdCode::CUSTOM_VVT_SYNC_POSITION, "VVT sync position too close to trigger sync"); | ||
| 445 | } | |||
| 446 | } else { | |||
| 447 | // Not using this cam for engine sync, just wrap the value in to the reasonable range | |||
| 448 | 99 | vvtPosition = wrapVvt(vvtPosition, FOUR_STROKE_CYCLE_DURATION); | ||
| 449 | } | |||
| 450 | ||||
| 451 | // Only record VVT position if we have full engine sync - may be bogus before that point | |||
| 452 |
2/2✓ Branch 1 taken 736 times.
✓ Branch 2 taken 16 times.
|
2/2✓ Decision 'true' taken 736 times.
✓ Decision 'false' taken 16 times.
|
752 | if (tc->triggerState.hasSynchronizedPhase()) { |
| 453 | 736 | tc->vvtPosition[bankIndex][camIndex] = vvtPosition; | ||
| 454 | } else { | |||
| 455 | 16 | tc->vvtPosition[bankIndex][camIndex] = 0; | ||
| 456 | } | |||
| 457 | } | |||
| 458 | ||||
| 459 | int triggerReentrant = 0; | |||
| 460 | int maxTriggerReentrant = 0; | |||
| 461 | uint32_t triggerDuration; | |||
| 462 | uint32_t triggerMaxDuration = 0; | |||
| 463 | ||||
| 464 | /** | |||
| 465 | * This function is called by all "hardware" trigger inputs: | |||
| 466 | * - Hardware triggers | |||
| 467 | * - Trigger replay from CSV (unit tests) | |||
| 468 | */ | |||
| 469 | 63115 | void hwHandleShaftSignal(int signalIndex, bool isRising, efitick_t timestamp) { | ||
| 470 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 63115 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 63115 times.
|
63115 | if (tooSoonToHandleSignal()) { |
| 471 | ✗ | return; | ||
| 472 | } | |||
| 473 |
1/1✓ Branch 1 taken 63115 times.
|
63115 | TriggerCentral *tc = getTriggerCentral(); | |
| 474 | 63115 | ScopePerf perf(PE::HandleShaftSignal); | ||
| 475 | ||||
| 476 |
2/4✓ Branch 0 taken 63115 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 63115 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 63115 times.
|
63115 | if (tc->directSelfStimulation || !tc->hwTriggerInputEnabled) { |
| 477 | // sensor noise + self-stim = loss of trigger sync | |||
| 478 | ✗ | return; | ||
| 479 | } | |||
| 480 | ||||
| 481 |
1/1✓ Branch 1 taken 63115 times.
|
63115 | handleShaftSignal(signalIndex, isRising, timestamp); | |
| 482 | } | |||
| 483 | ||||
| 484 | // Handle all shaft signals - hardware or emulated both | |||
| 485 | 71912 | void handleShaftSignal(int signalIndex, bool isRising, efitick_t timestamp) { | ||
| 486 | 71912 | bool isPrimary = signalIndex == 0; | ||
| 487 |
4/6✓ Branch 0 taken 726 times.
✓ Branch 1 taken 71186 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 726 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 71912 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 71912 times.
|
71912 | if (!isPrimary && !TRIGGER_WAVEFORM(needSecondTriggerInput)) { |
| 488 | ✗ | return; | ||
| 489 | } | |||
| 490 | ||||
| 491 | trigger_event_e signal; | |||
| 492 | // todo: add support for 3rd channel | |||
| 493 |
2/2✓ Branch 0 taken 35976 times.
✓ Branch 1 taken 35936 times.
|
2/2✓ Decision 'true' taken 35976 times.
✓ Decision 'false' taken 35936 times.
|
71912 | if (isRising) { |
| 494 |
2/2✓ Branch 0 taken 35612 times.
✓ Branch 1 taken 364 times.
|
71952 | signal = isPrimary ? | |
| 495 |
2/2✓ Branch 0 taken 1207 times.
✓ Branch 1 taken 34405 times.
|
35612 | (engineConfiguration->invertPrimaryTriggerSignal ? SHAFT_PRIMARY_FALLING : SHAFT_PRIMARY_RISING) : | |
| 496 |
2/2✓ Branch 0 taken 154 times.
✓ Branch 1 taken 210 times.
|
364 | (engineConfiguration->invertSecondaryTriggerSignal ? SHAFT_SECONDARY_FALLING : SHAFT_SECONDARY_RISING); | |
| 497 | } else { | |||
| 498 |
2/2✓ Branch 0 taken 35574 times.
✓ Branch 1 taken 362 times.
|
71872 | signal = isPrimary ? | |
| 499 |
2/2✓ Branch 0 taken 1205 times.
✓ Branch 1 taken 34369 times.
|
35574 | (engineConfiguration->invertPrimaryTriggerSignal ? SHAFT_PRIMARY_RISING : SHAFT_PRIMARY_FALLING) : | |
| 500 |
2/2✓ Branch 0 taken 153 times.
✓ Branch 1 taken 209 times.
|
362 | (engineConfiguration->invertSecondaryTriggerSignal ? SHAFT_SECONDARY_RISING : SHAFT_SECONDARY_FALLING); | |
| 501 | } | |||
| 502 |
2/2✓ Branch 0 taken 71186 times.
✓ Branch 1 taken 726 times.
|
2/2✓ Decision 'true' taken 71186 times.
✓ Decision 'false' taken 726 times.
|
71912 | if (isPrimary) { |
| 503 | 71186 | engine->outputChannels.triggerChannel1 = signal == SHAFT_PRIMARY_RISING; | ||
| 504 | } else { | |||
| 505 | 726 | engine->outputChannels.triggerChannel2 = signal == SHAFT_SECONDARY_RISING; | ||
| 506 | } | |||
| 507 | ||||
| 508 | // Don't accept trigger input in case of some problems | |||
| 509 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 71912 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 71912 times.
|
71912 | if (!getLimpManager()->allowTriggerInput()) { |
| 510 | ✗ | return; | ||
| 511 | } | |||
| 512 | ||||
| 513 | #if EFI_TOOTH_LOGGER | |||
| 514 | // Log to the Tunerstudio tooth logger | |||
| 515 | // We want to do this before anything else as we | |||
| 516 | // actually want to capture any noise/jitter that may be occurring | |||
| 517 | ||||
| 518 |
3/4✓ Branch 0 taken 966 times.
✓ Branch 1 taken 70946 times.
✓ Branch 3 taken 966 times.
✗ Branch 4 not taken.
|
71912 | bool logLogicState = engineConfiguration->displayLogicLevelsInEngineSniffer && getTriggerCentral()->triggerShape.useOnlyRisingEdges; | |
| 519 | ||||
| 520 |
2/2✓ Branch 0 taken 70946 times.
✓ Branch 1 taken 966 times.
|
2/2✓ Decision 'true' taken 70946 times.
✓ Decision 'false' taken 966 times.
|
71912 | if (!logLogicState) { |
| 521 | // we log physical state even if displayLogicLevelsInEngineSniffer if both fronts are used by decoder | |||
| 522 | 70946 | LogTriggerTooth(signal, timestamp); | ||
| 523 | } | |||
| 524 | ||||
| 525 | #endif /* EFI_TOOTH_LOGGER */ | |||
| 526 | ||||
| 527 | // for effective noise filtering, we need both signal edges, | |||
| 528 | // so we pass them to handleShaftSignal() and defer this test | |||
| 529 |
2/2✓ Branch 0 taken 70004 times.
✓ Branch 1 taken 1908 times.
|
2/2✓ Decision 'true' taken 70004 times.
✓ Decision 'false' taken 1908 times.
|
71912 | if (!engineConfiguration->useNoiselessTriggerDecoder) { |
| 530 |
2/2✓ Branch 2 taken 32254 times.
✓ Branch 3 taken 37750 times.
|
2/2✓ Decision 'true' taken 32254 times.
✓ Decision 'false' taken 37750 times.
|
70004 | if (!isUsefulSignal(signal, getTriggerCentral()->triggerShape)) { |
| 531 | /** | |||
| 532 | * no need to process VR falls further | |||
| 533 | */ | |||
| 534 | 32254 | return; | ||
| 535 | } | |||
| 536 | } | |||
| 537 | ||||
| 538 | #if EFI_TOOTH_LOGGER | |||
| 539 |
2/2✓ Branch 0 taken 483 times.
✓ Branch 1 taken 39175 times.
|
2/2✓ Decision 'true' taken 483 times.
✓ Decision 'false' taken 39175 times.
|
39658 | if (logLogicState) { |
| 540 | // first log rising normally | |||
| 541 | 483 | LogTriggerTooth(signal, timestamp); | ||
| 542 | // in 'logLogicState' mode we log opposite front right after logical rising away | |||
| 543 |
1/2✓ Branch 0 taken 483 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 483 times.
✗ Decision 'false' not taken.
|
483 | if (signal == SHAFT_PRIMARY_RISING) { |
| 544 | 483 | LogTriggerTooth(SHAFT_PRIMARY_FALLING, timestamp); | ||
| 545 | } else { | |||
| 546 | ✗ | LogTriggerTooth(SHAFT_SECONDARY_FALLING, timestamp); | ||
| 547 | } | |||
| 548 | } | |||
| 549 | #endif /* EFI_TOOTH_LOGGER */ | |||
| 550 | ||||
| 551 | 39658 | uint32_t triggerHandlerEntryTime = getTimeNowLowerNt(); | ||
| 552 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39658 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 39658 times.
|
39658 | if (triggerReentrant > maxTriggerReentrant) |
| 553 | ✗ | maxTriggerReentrant = triggerReentrant; | ||
| 554 | 39658 | triggerReentrant++; | ||
| 555 | ||||
| 556 | 39658 | getTriggerCentral()->handleShaftSignal(signal, timestamp); | ||
| 557 | ||||
| 558 | 39658 | triggerReentrant--; | ||
| 559 | 39658 | triggerDuration = getTimeNowLowerNt() - triggerHandlerEntryTime; | ||
| 560 | 39658 | triggerMaxDuration = maxI(triggerMaxDuration, triggerDuration); | ||
| 561 | } | |||
| 562 | ||||
| 563 | ✗ | void TriggerCentral::resetCounters() { | ||
| 564 | ✗ | memset(hwEventCounters, 0, sizeof(hwEventCounters)); | ||
| 565 | ✗ | } | ||
| 566 | ||||
| 567 | static const int wheelIndeces[4] = { 0, 0, 1, 1}; | |||
| 568 | ||||
| 569 | 38690 | static void reportEventToWaveChart(trigger_event_e ckpSignalType, int triggerEventIndex, bool addOppositeEvent) { | ||
| 570 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 38690 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 38690 times.
|
38690 | if (!getTriggerCentral()->isEngineSnifferEnabled) { // this is here just as a shortcut so that we avoid engine sniffer as soon as possible |
| 571 | ✗ | return; // engineSnifferRpmThreshold is accounted for inside getTriggerCentral()->isEngineSnifferEnabled | ||
| 572 | } | |||
| 573 | ||||
| 574 | 38690 | int wheelIndex = wheelIndeces[(int )ckpSignalType]; | ||
| 575 | ||||
| 576 | 38690 | bool isUp = isTriggerUpEvent(ckpSignalType); | ||
| 577 | ||||
| 578 |
2/2✓ Branch 0 taken 35957 times.
✓ Branch 1 taken 2733 times.
|
38690 | addEngineSnifferCrankEvent(wheelIndex, triggerEventIndex, isUp ? FrontDirection::UP : FrontDirection::DOWN); | |
| 579 |
2/2✓ Branch 0 taken 33228 times.
✓ Branch 1 taken 5462 times.
|
2/2✓ Decision 'true' taken 33228 times.
✓ Decision 'false' taken 5462 times.
|
38690 | if (addOppositeEvent) { |
| 580 | // let's add the opposite event right away | |||
| 581 |
1/2✓ Branch 0 taken 33228 times.
✗ Branch 1 not taken.
|
33228 | addEngineSnifferCrankEvent(wheelIndex, triggerEventIndex, isUp ? FrontDirection::DOWN : FrontDirection::UP); | |
| 582 | } | |||
| 583 | } | |||
| 584 | ||||
| 585 | /** | |||
| 586 | * This is used to filter noise spikes (interference) in trigger signal. See | |||
| 587 | * The basic idea is to use not just edges, but the average amount of time the signal stays in '0' or '1'. | |||
| 588 | * So we update 'accumulated periods' to track where the signal is. | |||
| 589 | * And then compare between the current period and previous, with some tolerance (allowing for the wheel speed change). | |||
| 590 | * @return true if the signal is passed through. | |||
| 591 | */ | |||
| 592 | 1908 | bool TriggerNoiseFilter::noiseFilter(efitick_t nowNt, | ||
| 593 | TriggerDecoderBase * triggerState, | |||
| 594 | trigger_event_e signal) { | |||
| 595 | // todo: find a better place for these defs | |||
| 596 | static const trigger_event_e opposite[4] = { SHAFT_PRIMARY_RISING, SHAFT_PRIMARY_FALLING, SHAFT_SECONDARY_RISING, SHAFT_SECONDARY_FALLING }; | |||
| 597 | static const TriggerWheel triggerIdx[4] = { TriggerWheel::T_PRIMARY, TriggerWheel::T_PRIMARY, TriggerWheel::T_SECONDARY, TriggerWheel:: T_SECONDARY }; | |||
| 598 | // we process all trigger channels independently | |||
| 599 | 1908 | TriggerWheel ti = triggerIdx[signal]; | ||
| 600 | // falling is opposite to rising, and vise versa | |||
| 601 | 1908 | trigger_event_e os = opposite[signal]; | ||
| 602 | ||||
| 603 | // todo: currently only primary channel is filtered, because there are some weird trigger types on other channels | |||
| 604 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1908 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1908 times.
|
1908 | if (ti != TriggerWheel::T_PRIMARY) |
| 605 | ✗ | return true; | ||
| 606 | ||||
| 607 | // update period accumulator: for rising signal, we update '0' accumulator, and for falling - '1' | |||
| 608 |
2/2✓ Branch 1 taken 1894 times.
✓ Branch 2 taken 14 times.
|
2/2✓ Decision 'true' taken 1894 times.
✓ Decision 'false' taken 14 times.
|
1908 | if (lastSignalTimes[signal] != -1) |
| 609 | 1894 | accumSignalPeriods[signal] += nowNt - lastSignalTimes[signal]; | ||
| 610 | // save current time for this trigger channel | |||
| 611 | 1908 | lastSignalTimes[signal] = nowNt; | ||
| 612 | ||||
| 613 | // now we want to compare current accumulated period to the stored one | |||
| 614 | 1908 | efitick_t currentPeriod = accumSignalPeriods[signal]; | ||
| 615 | // the trick is to compare between different | |||
| 616 | 1908 | efitick_t allowedPeriod = accumSignalPrevPeriods[os]; | ||
| 617 | ||||
| 618 | // but first check if we're expecting a gap | |||
| 619 |
3/4✓ Branch 1 taken 1908 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1771 times.
✓ Branch 5 taken 137 times.
|
3679 | bool isGapExpected = TRIGGER_WAVEFORM(isSynchronizationNeeded) && triggerState->getShaftSynchronized() && | |
| 620 |
2/2✓ Branch 3 taken 32 times.
✓ Branch 4 taken 1739 times.
|
1771 | (triggerState->currentCycle.eventCount[(int)ti] + 1) == TRIGGER_WAVEFORM(getExpectedEventCount(ti)); | |
| 621 | ||||
| 622 |
2/2✓ Branch 0 taken 32 times.
✓ Branch 1 taken 1876 times.
|
2/2✓ Decision 'true' taken 32 times.
✓ Decision 'false' taken 1876 times.
|
1908 | if (isGapExpected) { |
| 623 | // usually we need to extend the period for gaps, based on the trigger info | |||
| 624 | 32 | allowedPeriod *= TRIGGER_WAVEFORM(syncRatioAvg); | ||
| 625 | } | |||
| 626 | ||||
| 627 | // also we need some margin for rapidly changing trigger-wheel speed, | |||
| 628 | // that's why we expect the period to be no less than 2/3 of the previous period (this is just an empirical 'magic' coef.) | |||
| 629 | 1908 | efitick_t minAllowedPeriod = 2 * allowedPeriod / 3; | ||
| 630 | // but no longer than 5/4 of the previous 'normal' period | |||
| 631 | 1908 | efitick_t maxAllowedPeriod = 5 * allowedPeriod / 4; | ||
| 632 | ||||
| 633 | // above all, check if the signal comes not too early | |||
| 634 |
2/2✓ Branch 0 taken 1864 times.
✓ Branch 1 taken 44 times.
|
2/2✓ Decision 'true' taken 1864 times.
✓ Decision 'false' taken 44 times.
|
1908 | if (currentPeriod >= minAllowedPeriod) { |
| 635 | // now we store this period as a reference for the next time, | |||
| 636 | // BUT we store only 'normal' periods, and ignore too long periods (i.e. gaps) | |||
| 637 |
6/6✓ Branch 0 taken 1847 times.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 1825 times.
✓ Branch 3 taken 22 times.
✓ Branch 4 taken 1580 times.
✓ Branch 5 taken 245 times.
|
2/2✓ Decision 'true' taken 1602 times.
✓ Decision 'false' taken 262 times.
|
1864 | if (!isGapExpected && (maxAllowedPeriod == 0 || currentPeriod <= maxAllowedPeriod)) { |
| 638 | 1602 | accumSignalPrevPeriods[signal] = currentPeriod; | ||
| 639 | } | |||
| 640 | // reset accumulator | |||
| 641 | 1864 | accumSignalPeriods[signal] = 0; | ||
| 642 | 1864 | return true; | ||
| 643 | } | |||
| 644 | // all premature or extra-long events are ignored - treated as interference | |||
| 645 | 44 | return false; | ||
| 646 | } | |||
| 647 | ||||
| 648 | 46 | bool TriggerCentral::isMapCamSync(efitick_t timestamp, float currentPhase) { | ||
| 649 | UNUSED(timestamp); | |||
| 650 | ||||
| 651 | // we are trying to figure out which 360 half of the total 720 degree cycle is which, so we compare those in 360 degree sense. | |||
| 652 | 46 | auto toothAngle360 = currentPhase; | ||
| 653 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 46 times.
|
2/2✓ Decision 'true' taken 20 times.
✓ Decision 'false' taken 46 times.
|
66 | while (toothAngle360 >= 360) { |
| 654 | 20 | toothAngle360 -= 360; | ||
| 655 | } | |||
| 656 | ||||
| 657 | bool result; | |||
| 658 |
4/4✓ Branch 0 taken 25 times.
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 23 times.
✓ Branch 3 taken 2 times.
|
2/2✓ Decision 'true' taken 23 times.
✓ Decision 'false' taken 23 times.
|
46 | if (mapCamPrevToothAngle < engineConfiguration->mapCamDetectionAnglePosition && toothAngle360 > engineConfiguration->mapCamDetectionAnglePosition) { |
| 659 | // we are somewhere close to 'mapCamDetectionAnglePosition' | |||
| 660 | ||||
| 661 | // warning: hack hack hack | |||
| 662 | 23 | float map = engine->outputChannels.instantMAPValue; | ||
| 663 | ||||
| 664 | // Compute diff against the last time we were here | |||
| 665 | 23 | float instantMapDiffBetweenReadoutAngles = map - mapCamPrevCycleValue; | ||
| 666 | 23 | mapCamPrevCycleValue = map; | ||
| 667 | ||||
| 668 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 21 times.
|
2/2✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 21 times.
|
23 | if (instantMapDiffBetweenReadoutAngles > engineConfiguration->mapSyncThreshold) { |
| 669 | 2 | mapVvt_sync_counter++; | ||
| 670 | 2 | int revolutionCounter = getTriggerCentral()->triggerState.getSynchronizationCounter(); | ||
| 671 | 2 | mapVvt_MAP_AT_CYCLE_COUNT = revolutionCounter - prevChangeAtCycle; | ||
| 672 | 2 | prevChangeAtCycle = revolutionCounter; | ||
| 673 | 2 | result = true; | ||
| 674 | } else { | |||
| 675 | 21 | result = false; | ||
| 676 | } | |||
| 677 | ||||
| 678 | 23 | mapVvt_MAP_AT_SPECIAL_POINT = map; | ||
| 679 | 23 | mapVvt_MAP_AT_DIFF = instantMapDiffBetweenReadoutAngles; | ||
| 680 | 23 | } else { | ||
| 681 | 23 | result = false; | ||
| 682 | } | |||
| 683 | ||||
| 684 | 46 | mapCamPrevToothAngle = toothAngle360; | ||
| 685 | 46 | return result; | ||
| 686 | } | |||
| 687 | ||||
| 688 | #ifdef TEMP_V_TWIN | |||
| 689 | ||||
| 690 | float mapAtAngle[200]; | |||
| 691 | ||||
| 692 | #endif | |||
| 693 | ||||
| 694 | 33836 | void TriggerCentral::decodeMapCam(int toothIndexForListeners, efitick_t timestamp, float currentPhase) { | ||
| 695 | UNUSED(toothIndexForListeners); | |||
| 696 | ||||
| 697 |
2/2✓ Branch 0 taken 46 times.
✓ Branch 1 taken 33790 times.
|
33882 | isDecodingMapCam = engineConfiguration->vvtMode[0] == VVT_MAP_V_TWIN && | |
| 698 |
1/2✓ Branch 1 taken 46 times.
✗ Branch 2 not taken.
|
46 | Sensor::getOrZero(SensorType::Rpm) < engineConfiguration->cranking.rpm; | |
| 699 |
2/2✓ Branch 0 taken 46 times.
✓ Branch 1 taken 33790 times.
|
2/2✓ Decision 'true' taken 46 times.
✓ Decision 'false' taken 33790 times.
|
33836 | if (isDecodingMapCam) { |
| 700 | ||||
| 701 | ||||
| 702 | #ifdef TEMP_V_TWIN | |||
| 703 | mapAtAngle[toothIndexForListeners] = engine->outputChannels.instantMAPValue; | |||
| 704 | ||||
| 705 | if (toothIndexForListeners > 2) { | |||
| 706 | if (mapAtAngle[toothIndexForListeners - 2] > mapAtAngle[toothIndexForListeners - 1] && | |||
| 707 | mapAtAngle[toothIndexForListeners - 1] < mapAtAngle[toothIndexForListeners - 0]) { | |||
| 708 | mapVvt_min_point_counter++; | |||
| 709 | } | |||
| 710 | ||||
| 711 | } | |||
| 712 | #endif | |||
| 713 | ||||
| 714 | ||||
| 715 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 44 times.
|
2/2✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 44 times.
|
46 | if (isMapCamSync(timestamp, currentPhase)) { |
| 716 | 2 | hwHandleVvtCamSignal(TriggerValue::RISE, timestamp, /*index*/0); | ||
| 717 | 2 | hwHandleVvtCamSignal(TriggerValue::FALL, timestamp, /*index*/0); | ||
| 718 | #if EFI_UNIT_TEST | |||
| 719 | // hack? feature? existing unit test relies on VVT phase available right away | |||
| 720 | // but current implementation which is based on periodicFastCallback would only make result available on NEXT tooth | |||
| 721 | 2 | getLimpManager()->onFastCallback(); | ||
| 722 | #endif // EFI_UNIT_TEST | |||
| 723 | } | |||
| 724 | } | |||
| 725 | 33836 | } | ||
| 726 | ||||
| 727 | 38691 | bool TriggerCentral::isToothExpectedNow(efitick_t timestamp) { | ||
| 728 | // Check that the expected next phase (from the last tooth) is close to the actual current phase: | |||
| 729 | // basically, check that the tooth width is correct | |||
| 730 |
1/1✓ Branch 2 taken 38691 times.
|
38691 | auto estimatedCurrentPhase = getCurrentEnginePhase(timestamp); | |
| 731 | 38691 | auto lastToothPhase = m_lastToothPhaseFromSyncPoint; | ||
| 732 | ||||
| 733 |
6/6✓ Branch 1 taken 33740 times.
✓ Branch 2 taken 4951 times.
✓ Branch 4 taken 33427 times.
✓ Branch 5 taken 313 times.
✓ Branch 6 taken 33427 times.
✓ Branch 7 taken 5264 times.
|
2/2✓ Decision 'true' taken 33427 times.
✓ Decision 'false' taken 5264 times.
|
38691 | if (expectedNextPhase && estimatedCurrentPhase) { |
| 734 | 33427 | float angleError = expectedNextPhase.Value - estimatedCurrentPhase.Value; | ||
| 735 | ||||
| 736 | // Wrap around correctly at the end of the cycle | |||
| 737 |
1/1✓ Branch 1 taken 33427 times.
|
33427 | float cycle = getEngineState()->engineCycle; | |
| 738 |
2/2✓ Branch 0 taken 2391 times.
✓ Branch 1 taken 31036 times.
|
2/2✓ Decision 'true' taken 2391 times.
✓ Decision 'false' taken 31036 times.
|
33427 | if (angleError < -cycle / 2) { |
| 739 | 2391 | angleError += cycle; | ||
| 740 | } | |||
| 741 | ||||
| 742 | // positive value - tooth received earlier than expected | |||
| 743 | // negative value - tooth received later than expected | |||
| 744 | 33427 | triggerToothAngleError = angleError; | ||
| 745 | ||||
| 746 | // Only perform checks if engine is spinning quickly | |||
| 747 | // All kinds of garbage happens while cranking | |||
| 748 |
3/3✓ Branch 1 taken 33427 times.
✓ Branch 3 taken 16018 times.
✓ Branch 4 taken 17409 times.
|
2/2✓ Decision 'true' taken 16018 times.
✓ Decision 'false' taken 17409 times.
|
33427 | if (Sensor::getOrZero(SensorType::Rpm) > 1000) { |
| 749 | // Now compute how close we are to the last tooth decoded | |||
| 750 | 16018 | float angleSinceLastTooth = estimatedCurrentPhase.Value - lastToothPhase; | ||
| 751 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 16017 times.
|
2/2✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 16017 times.
|
16018 | if (angleSinceLastTooth < 0.5f) { |
| 752 | // This tooth came impossibly early, ignore it | |||
| 753 | // This rejects things like doubled edges, for example: | |||
| 754 | // |-| |---------------- | |||
| 755 | // | | | | |||
| 756 | // ____________| |_| | |||
| 757 | // 1 2 | |||
| 758 | // #1 will be decoded | |||
| 759 | // #2 will be ignored | |||
| 760 | // We're not sure which edge was the "real" one, but they were close enough | |||
| 761 | // together that it doesn't really matter. | |||
| 762 |
1/1✓ Branch 1 taken 1 time.
|
1 | warning(ObdCode::CUSTOM_PRIMARY_DOUBLED_EDGE, "doubled trigger edge after %.2f deg at #%d", angleSinceLastTooth, triggerState.currentCycle.current_index); | |
| 763 | ||||
| 764 | 1 | return false; | ||
| 765 | } | |||
| 766 | ||||
| 767 | // Absolute error from last tooth | |||
| 768 | 16017 | float absError = absF(angleError); | ||
| 769 |
2/3✓ Branch 1 taken 16017 times.
✓ Branch 3 taken 16017 times.
✗ Branch 4 not taken.
|
16017 | float isRpmEnough = Sensor::getOrZero(SensorType::Rpm) > 1000; | |
| 770 | // TODO: configurable threshold | |||
| 771 |
5/6✓ Branch 0 taken 16017 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 176 times.
✓ Branch 3 taken 15841 times.
✓ Branch 4 taken 171 times.
✓ Branch 5 taken 5 times.
|
2/2✓ Decision 'true' taken 171 times.
✓ Decision 'false' taken 15846 times.
|
16017 | if (isRpmEnough && absError > 10 && absError < 180) { |
| 772 | // This tooth came at a very unexpected time, ignore it | |||
| 773 |
3/3✓ Branch 0 taken 92 times.
✓ Branch 1 taken 79 times.
✓ Branch 3 taken 171 times.
|
171 | warning((angleError > 0) ? ObdCode::CUSTOM_PRIMARY_BAD_TOOTH_TIMING_EARLY : ObdCode::CUSTOM_PRIMARY_BAD_TOOTH_TIMING_LATE, | |
| 774 | "tooth #%d error of %.1f", triggerState.currentCycle.current_index, angleError); | |||
| 775 | ||||
| 776 | // TODO: this causes issues with some real engine logs, should it? | |||
| 777 | // return false; | |||
| 778 | } | |||
| 779 | } | |||
| 780 | } else { | |||
| 781 | 5264 | triggerToothAngleError = 0; | ||
| 782 | } | |||
| 783 | ||||
| 784 | // We aren't ready to reject unexpected teeth, so accept this tooth | |||
| 785 | 38690 | return true; | ||
| 786 | } | |||
| 787 | ||||
| 788 | 38690 | PUBLIC_API_WEAK bool boardAllowTriggerActions() { return true; } | ||
| 789 | ||||
| 790 | 33836 | angle_t TriggerCentral::findNextTriggerToothAngle(int p_currentToothIndex) { | ||
| 791 | 33836 | int currentToothIndex = p_currentToothIndex; | ||
| 792 | // TODO: is this logic to compute next trigger tooth angle correct? | |||
| 793 | 33836 | angle_t nextToothAngle = 0; | ||
| 794 | ||||
| 795 | 33836 | int loopAllowance = 2 * engineCycleEventCount + 1000; | ||
| 796 | do { | |||
| 797 | // I don't love this. | |||
| 798 | 82617 | currentToothIndex = (currentToothIndex + 1) % engineCycleEventCount; | ||
| 799 |
4/4✓ Branch 1 taken 82617 times.
✓ Branch 5 taken 82617 times.
✓ Branch 7 taken 910 times.
✓ Branch 8 taken 81707 times.
|
82617 | nextToothAngle = getTriggerCentral()->triggerFormDetails.eventAngles[currentToothIndex] - tdcPosition(); | |
| 800 |
1/1✓ Branch 1 taken 82617 times.
|
82617 | wrapAngle(nextToothAngle, "nextEnginePhase", ObdCode::CUSTOM_ERR_6555); | |
| 801 |
6/6✓ Branch 0 taken 48801 times.
✓ Branch 1 taken 33816 times.
✓ Branch 2 taken 48781 times.
✓ Branch 3 taken 20 times.
✓ Branch 4 taken 48781 times.
✓ Branch 5 taken 33836 times.
|
0/1? Decision couldn't be analyzed.
|
82617 | } while (nextToothAngle == currentEngineDecodedPhase && --loopAllowance > 0); // '==' for float works here since both values come from 'eventAngles' array |
| 802 |
3/4✓ Branch 0 taken 32629 times.
✓ Branch 1 taken 1207 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 32629 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 33836 times.
|
33836 | if (nextToothAngle != 0 && loopAllowance == 0) { |
| 803 | // HW CI fails here, looks like we sometimes change trigger while still handling it? | |||
| 804 | ✗ | firmwareError(ObdCode::CUSTOM_ERR_TRIGGER_ZERO, "handleShaftSignal unexpected loop end %d %d %f %f", p_currentToothIndex, engineCycleEventCount, nextToothAngle, currentEngineDecodedPhase); | ||
| 805 | } | |||
| 806 | 33836 | return nextToothAngle; | ||
| 807 | } | |||
| 808 | ||||
| 809 | /** | |||
| 810 | * This method is NOT invoked for VR falls. | |||
| 811 | */ | |||
| 812 | 39660 | void TriggerCentral::handleShaftSignal(trigger_event_e signal, efitick_t timestamp) { | ||
| 813 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39660 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 39660 times.
|
39660 | if (triggerShape.shapeDefinitionError) { |
| 814 | // trigger is broken, we cannot do anything here | |||
| 815 | ✗ | warning(ObdCode::CUSTOM_ERR_UNEXPECTED_SHAFT_EVENT, "Shaft event while trigger is mis-configured"); | ||
| 816 | ✗ | return; | ||
| 817 | } | |||
| 818 | ||||
| 819 | // This code gathers some statistics on signals and compares accumulated periods to filter interference | |||
| 820 |
2/2✓ Branch 0 taken 1908 times.
✓ Branch 1 taken 37752 times.
|
2/2✓ Decision 'true' taken 1908 times.
✓ Decision 'false' taken 37752 times.
|
39660 | if (engineConfiguration->useNoiselessTriggerDecoder) { |
| 821 |
3/3✓ Branch 1 taken 1908 times.
✓ Branch 3 taken 44 times.
✓ Branch 4 taken 1864 times.
|
2/2✓ Decision 'true' taken 44 times.
✓ Decision 'false' taken 1864 times.
|
1908 | if (!noiseFilter.noiseFilter(timestamp, &triggerState, signal)) { |
| 822 | 44 | return; | ||
| 823 | } | |||
| 824 |
3/3✓ Branch 1 taken 1864 times.
✓ Branch 3 taken 925 times.
✓ Branch 4 taken 939 times.
|
2/2✓ Decision 'true' taken 925 times.
✓ Decision 'false' taken 939 times.
|
1864 | if (!isUsefulSignal(signal, triggerShape)) { |
| 825 | 925 | return; | ||
| 826 | } | |||
| 827 | } | |||
| 828 | ||||
| 829 |
3/3✓ Branch 1 taken 38691 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 38690 times.
|
2/2✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 38690 times.
|
38691 | if (!isToothExpectedNow(timestamp)) { |
| 830 | 1 | triggerIgnoredToothCount++; | ||
| 831 | 1 | return; | ||
| 832 | } | |||
| 833 | ||||
| 834 | 38690 | isSpinningJustForWatchdog = true; | ||
| 835 | ||||
| 836 | #if EFI_HD_ACR | |||
| 837 | bool firstEventInAWhile = m_lastEventTimer.hasElapsedSec(1); | |||
| 838 | if (firstEventInAWhile) { | |||
| 839 | // let's open that valve on first sign of movement | |||
| 840 | engine->module<HarleyAcr>()->updateAcr(); | |||
| 841 | } | |||
| 842 | #endif // EFI_HD_ACR | |||
| 843 | ||||
| 844 |
2/3✓ Branch 1 taken 38690 times.
✓ Branch 3 taken 38690 times.
✗ Branch 4 not taken.
|
1/2✓ Decision 'true' taken 38690 times.
✗ Decision 'false' not taken.
|
38690 | if (boardAllowTriggerActions()) { |
| 845 |
1/1✓ Branch 1 taken 38690 times.
|
38690 | m_lastEventTimer.reset(timestamp); | |
| 846 | } | |||
| 847 | ||||
| 848 | 38690 | int eventIndex = (int) signal; | ||
| 849 |
2/5✓ Branch 0 taken 38690 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 38690 times.
✗ Branch 5 not taken.
|
38690 | efiAssertVoid(ObdCode::CUSTOM_TRIGGER_EVENT_TYPE, eventIndex >= 0 && eventIndex < HW_EVENT_TYPES, "signal type"); | |
| 850 | 38690 | hwEventCounters[eventIndex]++; | ||
| 851 | ||||
| 852 | // Decode the trigger! | |||
| 853 | 38690 | auto decodeResult = triggerState.decodeTriggerEvent( | ||
| 854 | "trigger", | |||
| 855 |
1/1✓ Branch 1 taken 38690 times.
|
38690 | triggerShape, | |
| 856 | engine, | |||
| 857 | primaryTriggerConfiguration, | |||
| 858 | signal, timestamp); | |||
| 859 | ||||
| 860 | // Don't propagate state if we don't know where we are | |||
| 861 |
2/2✓ Branch 1 taken 33836 times.
✓ Branch 2 taken 4854 times.
|
2/2✓ Decision 'true' taken 33836 times.
✓ Decision 'false' taken 4854 times.
|
38690 | if (decodeResult) { |
| 862 | 33836 | ScopePerf perf(PE::ShaftPositionListeners); | ||
| 863 | ||||
| 864 | /** | |||
| 865 | * If we only have a crank position sensor with four stroke, here we are extending crank revolutions with a 360 degree | |||
| 866 | * cycle into a four stroke, 720 degrees cycle. | |||
| 867 | */ | |||
| 868 |
2/2✓ Branch 1 taken 33836 times.
✓ Branch 4 taken 33836 times.
|
33836 | int crankDivider = getCrankDivider(triggerShape.getWheelOperationMode()); | |
| 869 |
1/1✓ Branch 1 taken 33836 times.
|
33836 | int crankInternalIndex = triggerState.getSynchronizationCounter() % crankDivider; | |
| 870 |
1/1✓ Branch 1 taken 33836 times.
|
33836 | int triggerIndexForListeners = decodeResult.Value.CurrentIndex + (crankInternalIndex * triggerShape.getSize()); | |
| 871 | ||||
| 872 |
1/1✓ Branch 1 taken 33836 times.
|
33836 | reportEventToWaveChart(signal, triggerIndexForListeners, triggerShape.useOnlyRisingEdges); | |
| 873 | ||||
| 874 | // Look up this tooth's angle from the sync point. If this tooth is the sync point, we'll get 0 here. | |||
| 875 |
1/1✓ Branch 1 taken 33836 times.
|
33836 | auto currentPhaseFromSyncPoint = getTriggerCentral()->triggerFormDetails.eventAngles[triggerIndexForListeners]; | |
| 876 | ||||
| 877 | // Adjust so currentPhase is in engine-space angle, not trigger-space angle | |||
| 878 |
4/4✓ Branch 1 taken 33836 times.
✓ Branch 3 taken 455 times.
✓ Branch 4 taken 33381 times.
✓ Branch 6 taken 33836 times.
|
33836 | currentEngineDecodedPhase = wrapAngleMethod(currentPhaseFromSyncPoint - tdcPosition(), "currentEnginePhase", ObdCode::CUSTOM_ERR_6555); | |
| 879 | ||||
| 880 | // Record precise time and phase of the engine. This is used for VVT decode, and to check that the | |||
| 881 | // trigger pattern selected matches reality (ie, we check the next tooth is where we think it should be) | |||
| 882 | { | |||
| 883 | // under lock to avoid mismatched tooth phase and time | |||
| 884 | chibios_rt::CriticalSectionLocker csl; | |||
| 885 | ||||
| 886 |
1/1✓ Branch 1 taken 33836 times.
|
33836 | m_lastToothTimer.reset(timestamp); | |
| 887 | 33836 | m_lastToothPhaseFromSyncPoint = currentPhaseFromSyncPoint; | ||
| 888 | } | |||
| 889 | ||||
| 890 | #if TRIGGER_EXTREME_LOGGING | |||
| 891 | efiPrintf("trigger %d %d %d", triggerIndexForListeners, getRevolutionCounter(), time2print(getTimeNowUs())); | |||
| 892 | #endif /* TRIGGER_EXTREME_LOGGING */ | |||
| 893 | ||||
| 894 | // Update engine RPM | |||
| 895 |
1/1✓ Branch 1 taken 33836 times.
|
33836 | rpmShaftPositionCallback(signal, triggerIndexForListeners, timestamp); | |
| 896 | ||||
| 897 | // Schedule the TDC mark | |||
| 898 |
1/1✓ Branch 1 taken 33836 times.
|
33836 | tdcMarkCallback(triggerIndexForListeners, timestamp); | |
| 899 | ||||
| 900 | #if EFI_LOGIC_ANALYZER | |||
| 901 | waTriggerEventListener(signal, triggerIndexForListeners, timestamp); | |||
| 902 | #endif | |||
| 903 | ||||
| 904 |
1/1✓ Branch 1 taken 33836 times.
|
33836 | angle_t nextPhase = findNextTriggerToothAngle(triggerIndexForListeners); | |
| 905 | ||||
| 906 |
3/3✓ Branch 2 taken 33836 times.
✓ Branch 4 taken 455 times.
✓ Branch 5 taken 33381 times.
|
33836 | float expectNextPhase = nextPhase + tdcPosition(); | |
| 907 |
1/1✓ Branch 1 taken 33836 times.
|
33836 | wrapAngle(expectNextPhase, "nextEnginePhase", ObdCode::CUSTOM_ERR_6555); | |
| 908 | 33836 | expectedNextPhase = expectNextPhase; | ||
| 909 | ||||
| 910 | #if EFI_CDM_INTEGRATION | |||
| 911 | if (trgEventIndex == 0 && isBrainPinValid(engineConfiguration->cdmInputPin)) { | |||
| 912 | int cdmKnockValue = getCurrentCdmValue(getTriggerCentral()->triggerState.getSynchronizationCounter()); | |||
| 913 | engine->knockLogic(cdmKnockValue); | |||
| 914 | } | |||
| 915 | #endif /* EFI_CDM_INTEGRATION */ | |||
| 916 | ||||
| 917 |
7/7✓ Branch 1 taken 33836 times.
✓ Branch 3 taken 33522 times.
✓ Branch 4 taken 314 times.
✓ Branch 5 taken 2444 times.
✓ Branch 6 taken 31078 times.
✓ Branch 7 taken 2444 times.
✓ Branch 8 taken 31392 times.
|
2/2✓ Decision 'true' taken 2444 times.
✓ Decision 'false' taken 31392 times.
|
33836 | if (engine->rpmCalculator.getCachedRpm() > 0 && triggerIndexForListeners == 0) { |
| 918 |
2/2✓ Branch 1 taken 2444 times.
✓ Branch 5 taken 2444 times.
|
2444 | engine->module<TpsAccelEnrichment>()->onEngineCycleTps(); | |
| 919 | } | |||
| 920 | ||||
| 921 | // Handle ignition and injection | |||
| 922 |
1/1✓ Branch 1 taken 33836 times.
|
33836 | mainTriggerCallback(triggerIndexForListeners, timestamp, currentEngineDecodedPhase, nextPhase); | |
| 923 | ||||
| 924 | 33836 | temp_mapVvt_index = triggerIndexForListeners / 2; | ||
| 925 | ||||
| 926 | // Decode the MAP based "cam" sensor | |||
| 927 |
1/1✓ Branch 1 taken 33836 times.
|
33836 | decodeMapCam(temp_mapVvt_index, timestamp, currentEngineDecodedPhase); | |
| 928 | ||||
| 929 |
1/1✓ Branch 1 taken 33836 times.
|
33836 | boardTriggerCallback(timestamp, currentEngineDecodedPhase); | |
| 930 | } else { | |||
| 931 | // We don't have sync, but report to the wave chart anyway as index 0. | |||
| 932 |
1/1✓ Branch 1 taken 4854 times.
|
4854 | reportEventToWaveChart(signal, 0, triggerShape.useOnlyRisingEdges); | |
| 933 | ||||
| 934 | 4854 | expectedNextPhase = unexpected; | ||
| 935 | } | |||
| 936 | } | |||
| 937 | ||||
| 938 | ✗ | static void triggerShapeInfo() { | ||
| 939 | #if EFI_PROD_CODE || EFI_SIMULATOR | |||
| 940 | TriggerWaveform *shape = &getTriggerCentral()->triggerShape; | |||
| 941 | TriggerFormDetails *triggerFormDetails = &getTriggerCentral()->triggerFormDetails; | |||
| 942 | efiPrintf("syncEdge=%s", getSyncEdge(TRIGGER_WAVEFORM(syncEdge))); | |||
| 943 | efiPrintf("gap from %.2f to %.2f", TRIGGER_WAVEFORM(synchronizationRatioFrom[0]), TRIGGER_WAVEFORM(synchronizationRatioTo[0])); | |||
| 944 | ||||
| 945 | for (size_t i = 0; i < shape->getSize(); i++) { | |||
| 946 | efiPrintf("event %d %.2f", i, triggerFormDetails->eventAngles[i]); | |||
| 947 | } | |||
| 948 | #endif | |||
| 949 | ✗ | } | ||
| 950 | ||||
| 951 | #if EFI_PROD_CODE | |||
| 952 | extern PwmConfig triggerEmulatorSignals[NUM_EMULATOR_CHANNELS]; | |||
| 953 | #endif /* #if EFI_PROD_CODE */ | |||
| 954 | ||||
| 955 | ✗ | void triggerInfo(void) { | ||
| 956 | #if EFI_PROD_CODE || EFI_SIMULATOR | |||
| 957 | ||||
| 958 | TriggerCentral *tc = getTriggerCentral(); | |||
| 959 | TriggerWaveform *ts = &tc->triggerShape; | |||
| 960 | ||||
| 961 | efiPrintf("Template %s (%d) trigger %s (%d) syncEdge=%s tdcOffset=%.2f", | |||
| 962 | getEngine_type_e(engineConfiguration->engineType), | |||
| 963 | (int)engineConfiguration->engineType, | |||
| 964 | getTrigger_type_e(engineConfiguration->trigger.type), | |||
| 965 | (int)engineConfiguration->trigger.type, | |||
| 966 | getSyncEdge(TRIGGER_WAVEFORM(syncEdge)), TRIGGER_WAVEFORM(tdcPosition)); | |||
| 967 | ||||
| 968 | if (engineConfiguration->trigger.type == trigger_type_e::TT_TOOTHED_WHEEL) { | |||
| 969 | efiPrintf("total %d/skipped %d", engineConfiguration->trigger.customTotalToothCount, | |||
| 970 | engineConfiguration->trigger.customSkippedToothCount); | |||
| 971 | } | |||
| 972 | ||||
| 973 | ||||
| 974 | efiPrintf("trigger#1 event counters up=%d/down=%d", tc->getHwEventCounter(0), | |||
| 975 | tc->getHwEventCounter(1)); | |||
| 976 | ||||
| 977 | if (ts->needSecondTriggerInput) { | |||
| 978 | efiPrintf("trigger#2 event counters up=%d/down=%d", tc->getHwEventCounter(2), | |||
| 979 | tc->getHwEventCounter(3)); | |||
| 980 | } | |||
| 981 | efiPrintf("expected cycle events %d/%d", | |||
| 982 | TRIGGER_WAVEFORM(getExpectedEventCount(TriggerWheel::T_PRIMARY)), | |||
| 983 | TRIGGER_WAVEFORM(getExpectedEventCount(TriggerWheel::T_SECONDARY))); | |||
| 984 | ||||
| 985 | efiPrintf("trigger type=%d/need2ndChannel=%s", (int)engineConfiguration->trigger.type, | |||
| 986 | boolToString(TRIGGER_WAVEFORM(needSecondTriggerInput))); | |||
| 987 | ||||
| 988 | ||||
| 989 | efiPrintf("synchronizationNeeded=%s/isError=%s/total errors=%lu ord_err=%lu/total revolutions=%d/self=%s", | |||
| 990 | boolToString(ts->isSynchronizationNeeded), | |||
| 991 | boolToString(tc->isTriggerDecoderError()), | |||
| 992 | tc->triggerState.totalTriggerErrorCounter, | |||
| 993 | tc->triggerState.orderingErrorCounter, | |||
| 994 | tc->triggerState.getSynchronizationCounter(), | |||
| 995 | boolToString(tc->directSelfStimulation)); | |||
| 996 | ||||
| 997 | if (TRIGGER_WAVEFORM(isSynchronizationNeeded)) { | |||
| 998 | efiPrintf("gap from %.2f to %.2f", TRIGGER_WAVEFORM(synchronizationRatioFrom[0]), TRIGGER_WAVEFORM(synchronizationRatioTo[0])); | |||
| 999 | } | |||
| 1000 | ||||
| 1001 | #endif /* EFI_PROD_CODE || EFI_SIMULATOR */ | |||
| 1002 | ||||
| 1003 | #if EFI_PROD_CODE | |||
| 1004 | ||||
| 1005 | efiPrintf("primary trigger input: %s", hwPortname(engineConfiguration->triggerInputPins[0])); | |||
| 1006 | efiPrintf("primary trigger simulator: %s %s freq=%d", | |||
| 1007 | hwPortname(engineConfiguration->triggerSimulatorPins[0]), | |||
| 1008 | getPin_output_mode_e(engineConfiguration->triggerSimulatorPinModes[0]), | |||
| 1009 | engineConfiguration->triggerSimulatorRpm); | |||
| 1010 | ||||
| 1011 | if (ts->needSecondTriggerInput) { | |||
| 1012 | efiPrintf("secondary trigger input: %s", hwPortname(engineConfiguration->triggerInputPins[1])); | |||
| 1013 | #if EFI_EMULATE_POSITION_SENSORS | |||
| 1014 | efiPrintf("secondary trigger simulator: %s %s phase=%d", | |||
| 1015 | hwPortname(engineConfiguration->triggerSimulatorPins[1]), | |||
| 1016 | getPin_output_mode_e(engineConfiguration->triggerSimulatorPinModes[1]), triggerEmulatorSignals[0].safe.phaseIndex); | |||
| 1017 | #endif /* EFI_EMULATE_POSITION_SENSORS */ | |||
| 1018 | } | |||
| 1019 | ||||
| 1020 | ||||
| 1021 | for (int camInputIndex = 0; camInputIndex<CAM_INPUTS_COUNT;camInputIndex++) { | |||
| 1022 | if (isBrainPinValid(engineConfiguration->camInputs[camInputIndex])) { | |||
| 1023 | int camLogicalIndex = camInputIndex % CAMS_PER_BANK; | |||
| 1024 | efiPrintf("VVT input: %s mode %s", hwPortname(engineConfiguration->camInputs[camInputIndex]), | |||
| 1025 | getVvt_mode_e(engineConfiguration->vvtMode[camLogicalIndex])); | |||
| 1026 | efiPrintf("VVT %d event counters: %d/%d", | |||
| 1027 | camInputIndex, | |||
| 1028 | tc->vvtEventRiseCounter[camInputIndex], tc->vvtEventFallCounter[camInputIndex]); | |||
| 1029 | } | |||
| 1030 | } | |||
| 1031 | ||||
| 1032 | efiPrintf("primary logic input: %s", hwPortname(engineConfiguration->logicAnalyzerPins[0])); | |||
| 1033 | efiPrintf("secondary logic input: %s", hwPortname(engineConfiguration->logicAnalyzerPins[1])); | |||
| 1034 | ||||
| 1035 | ||||
| 1036 | efiPrintf("totalTriggerHandlerMaxTime=%lu", triggerMaxDuration); | |||
| 1037 | ||||
| 1038 | #endif /* EFI_PROD_CODE */ | |||
| 1039 | ||||
| 1040 | #if EFI_ENGINE_SNIFFER | |||
| 1041 | ✗ | efiPrintf("engine sniffer current size=%d", waveChart.getSize()); | ||
| 1042 | #endif /* EFI_ENGINE_SNIFFER */ | |||
| 1043 | ||||
| 1044 | ✗ | } | ||
| 1045 | ||||
| 1046 | ✗ | static void resetRunningTriggerCounters() { | ||
| 1047 | #if !EFI_UNIT_TEST | |||
| 1048 | getTriggerCentral()->resetCounters(); | |||
| 1049 | triggerInfo(); | |||
| 1050 | #endif | |||
| 1051 | ✗ | } | ||
| 1052 | ||||
| 1053 | 221 | void onConfigurationChangeTriggerCallback() { | ||
| 1054 | 221 | bool changed = false; | ||
| 1055 | // todo: how do we static_assert here? | |||
| 1056 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 221 times.
|
221 | criticalAssertVoid(efi::size(engineConfiguration->camInputs) == efi::size(engineConfiguration->vvtOffsets), "sizes"); | |
| 1057 | ||||
| 1058 |
2/2✓ Branch 1 taken 884 times.
✓ Branch 2 taken 221 times.
|
2/2✓ Decision 'true' taken 884 times.
✓ Decision 'false' taken 221 times.
|
1105 | for (size_t camIndex = 0; camIndex < efi::size(engineConfiguration->camInputs); camIndex++) { |
| 1059 | 884 | changed |= isConfigurationChanged(camInputs[camIndex]); | ||
| 1060 | 884 | changed |= isConfigurationChanged(vvtOffsets[camIndex]); | ||
| 1061 | } | |||
| 1062 | ||||
| 1063 |
2/2✓ Branch 1 taken 3978 times.
✓ Branch 2 taken 221 times.
|
2/2✓ Decision 'true' taken 3978 times.
✓ Decision 'false' taken 221 times.
|
4199 | for (size_t i = 0; i < efi::size(engineConfiguration->triggerGapOverrideFrom); i++) { |
| 1064 | 3978 | changed |= isConfigurationChanged(triggerGapOverrideFrom[i]); | ||
| 1065 | 3978 | changed |= isConfigurationChanged(triggerGapOverrideTo[i]); | ||
| 1066 | } | |||
| 1067 | ||||
| 1068 |
2/2✓ Branch 1 taken 442 times.
✓ Branch 2 taken 221 times.
|
2/2✓ Decision 'true' taken 442 times.
✓ Decision 'false' taken 221 times.
|
663 | for (size_t i = 0; i < efi::size(engineConfiguration->triggerInputPins); i++) { |
| 1069 | 442 | changed |= isConfigurationChanged(triggerInputPins[i]); | ||
| 1070 | 442 | Gpio pin = engineConfiguration->camInputs[i]; | ||
| 1071 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 442 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 442 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 442 times.
|
442 | if (engineConfiguration->vvtMode[0] == VVT_MAP_V_TWIN && isBrainPinValid(pin)) { |
| 1072 | ✗ | criticalError("Please no physical sensors in CAM by MAP mode index=%d %s", i, hwPortname(pin)); | ||
| 1073 | } | |||
| 1074 | } | |||
| 1075 | ||||
| 1076 |
2/2✓ Branch 1 taken 442 times.
✓ Branch 2 taken 221 times.
|
2/2✓ Decision 'true' taken 442 times.
✓ Decision 'false' taken 221 times.
|
663 | for (size_t i = 0; i < efi::size(engineConfiguration->vvtMode); i++) { |
| 1077 | 442 | changed |= isConfigurationChanged(vvtMode[i]); | ||
| 1078 | } | |||
| 1079 | ||||
| 1080 | 221 | changed |= isConfigurationChanged(trigger.type); | ||
| 1081 | 221 | changed |= isConfigurationChanged(skippedWheelOnCam); | ||
| 1082 | 221 | changed |= isConfigurationChanged(twoStroke); | ||
| 1083 | 221 | changed |= isConfigurationChanged(globalTriggerAngleOffset); | ||
| 1084 | 221 | changed |= isConfigurationChanged(trigger.customTotalToothCount); | ||
| 1085 | 221 | changed |= isConfigurationChanged(trigger.customSkippedToothCount); | ||
| 1086 | 221 | changed |= isConfigurationChanged(overrideTriggerGaps); | ||
| 1087 | 221 | changed |= isConfigurationChanged(gapTrackingLengthOverride); | ||
| 1088 | 221 | changed |= isConfigurationChanged(overrideVvtTriggerGaps); | ||
| 1089 | 221 | changed |= isConfigurationChanged(gapVvtTrackingLengthOverride); | ||
| 1090 | ||||
| 1091 |
2/2✓ Branch 0 taken 109 times.
✓ Branch 1 taken 112 times.
|
2/2✓ Decision 'true' taken 109 times.
✓ Decision 'false' taken 112 times.
|
221 | if (changed) { |
| 1092 | #if EFI_ENGINE_CONTROL | |||
| 1093 | 109 | engine->updateTriggerConfiguration(); | ||
| 1094 | 109 | getTriggerCentral()->noiseFilter.resetAccumSignalData(); | ||
| 1095 | #endif | |||
| 1096 | } | |||
| 1097 | #if EFI_DETAILED_LOGGING | |||
| 1098 | efiPrintf("isTriggerConfigChanged=%d", triggerConfigChanged); | |||
| 1099 | #endif /* EFI_DETAILED_LOGGING */ | |||
| 1100 | ||||
| 1101 | // we do not want to miss two updates in a row | |||
| 1102 |
4/4✓ Branch 1 taken 115 times.
✓ Branch 2 taken 106 times.
✓ Branch 3 taken 108 times.
✓ Branch 4 taken 7 times.
|
221 | getTriggerCentral()->triggerConfigChangedOnLastConfigurationChange = getTriggerCentral()->triggerConfigChangedOnLastConfigurationChange || changed; | |
| 1103 | } | |||
| 1104 | ||||
| 1105 | 82 | static void initVvtShape(int camIndex, TriggerWaveform& shape, const TriggerConfiguration& p_config, TriggerDecoderBase &initState) { | ||
| 1106 | 82 | shape.initializeTriggerWaveform(FOUR_STROKE_CAM_SENSOR, p_config.TriggerType, /*isCrank*/ false); | ||
| 1107 |
2/2✓ Branch 0 taken 78 times.
✓ Branch 1 taken 4 times.
|
2/2✓ Decision 'true' taken 78 times.
✓ Decision 'false' taken 4 times.
|
82 | if (camIndex == 0) { |
| 1108 | // at the moment we only support override of first cam | |||
| 1109 | // nasty code: this implicitly adjusts 'shape' parameter | |||
| 1110 | 78 | getTriggerCentral()->applyCamGapOverride(); | ||
| 1111 | } | |||
| 1112 | 82 | shape.initializeSyncPoint(initState, p_config); | ||
| 1113 | 82 | } | ||
| 1114 | ||||
| 1115 | 702 | void TriggerCentral::validateCamVvtCounters() { | ||
| 1116 | // micro-optimized 'synchronizationCounter % 256' | |||
| 1117 | 702 | int camVvtValidationIndex = triggerState.getSynchronizationCounter() & 0xFF; | ||
| 1118 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 691 times.
|
2/2✓ Decision 'true' taken 11 times.
✓ Decision 'false' taken 691 times.
|
702 | if (camVvtValidationIndex == 0) { |
| 1119 | 11 | vvtCamCounter = 0; | ||
| 1120 |
4/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 687 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2 times.
|
2/2✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 689 times.
|
691 | } else if (camVvtValidationIndex == 0xFE && vvtCamCounter < 60) { |
| 1121 | // magic logic: we expect at least 60 CAM/VVT events for each 256 trigger cycles, otherwise throw a code | |||
| 1122 | 2 | warning(ObdCode::OBD_Camshaft_Position_Sensor_Circuit_Range_Performance, "No Camshaft Position Sensor signals"); | ||
| 1123 | } | |||
| 1124 | 702 | } | ||
| 1125 | /** | |||
| 1126 | * Calculate 'shape.triggerShapeSynchPointIndex' value using 'TriggerDecoderBase *state' | |||
| 1127 | */ | |||
| 1128 | 908 | static void calculateTriggerSynchPoint( | ||
| 1129 | const PrimaryTriggerConfiguration &primaryTriggerConfiguration, | |||
| 1130 | TriggerWaveform& shape, | |||
| 1131 | TriggerDecoderBase& initState) { | |||
| 1132 | ||||
| 1133 | #if EFI_PROD_CODE | |||
| 1134 | efiAssertVoid(ObdCode::CUSTOM_TRIGGER_STACK, hasLotsOfRemainingStack(), "calc s"); | |||
| 1135 | #endif | |||
| 1136 | ||||
| 1137 | 908 | shape.initializeSyncPoint(initState, primaryTriggerConfiguration); | ||
| 1138 | ||||
| 1139 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 908 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 908 times.
|
908 | if (shape.getSize() >= PWM_PHASE_MAX_COUNT) { |
| 1140 | // todo: by the time we are here we had already modified a lot of RAM out of bounds! | |||
| 1141 | ✗ | firmwareError(ObdCode::CUSTOM_ERR_TRIGGER_WAVEFORM_TOO_LONG, "Trigger length above maximum: %d", shape.getSize()); | ||
| 1142 | ✗ | shape.setShapeDefinitionError(true); | ||
| 1143 | ✗ | return; | ||
| 1144 | } | |||
| 1145 | ||||
| 1146 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 908 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 908 times.
|
908 | if (shape.getSize() == 0) { |
| 1147 | ✗ | firmwareError(ObdCode::CUSTOM_ERR_TRIGGER_ZERO, "triggerShape size is zero"); | ||
| 1148 | } | |||
| 1149 | } | |||
| 1150 | ||||
| 1151 | TriggerDecoderBase initState("init"); | |||
| 1152 | ||||
| 1153 | 908 | void TriggerCentral::applyTriggerGapOverride() { | ||
| 1154 | /** | |||
| 1155 | * this is only useful while troubleshooting a new trigger shape in the field | |||
| 1156 | * in very VERY rare circumstances | |||
| 1157 | */ | |||
| 1158 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 886 times.
|
2/2✓ Decision 'true' taken 22 times.
✓ Decision 'false' taken 886 times.
|
908 | if (engineConfiguration->overrideTriggerGaps) { |
| 1159 | 22 | int gapIndex = 0; | ||
| 1160 | ||||
| 1161 | 22 | triggerShape.gapTrackingLength = engineConfiguration->gapTrackingLengthOverride; | ||
| 1162 | ||||
| 1163 | // copy however many the user wants | |||
| 1164 |
2/2✓ Branch 0 taken 64 times.
✓ Branch 1 taken 22 times.
|
2/2✓ Decision 'true' taken 64 times.
✓ Decision 'false' taken 22 times.
|
86 | for (; gapIndex < engineConfiguration->gapTrackingLengthOverride; gapIndex++) { |
| 1165 | 64 | float gapOverrideFrom = engineConfiguration->triggerGapOverrideFrom[gapIndex]; | ||
| 1166 | 64 | float gapOverrideTo = engineConfiguration->triggerGapOverrideTo[gapIndex]; | ||
| 1167 | 64 | triggerShape.setTriggerSynchronizationGap3(/*gapIndex*/gapIndex, gapOverrideFrom, gapOverrideTo); | ||
| 1168 | } | |||
| 1169 | ||||
| 1170 | // fill the remainder with the default gaps | |||
| 1171 |
2/2✓ Branch 0 taken 332 times.
✓ Branch 1 taken 22 times.
|
2/2✓ Decision 'true' taken 332 times.
✓ Decision 'false' taken 22 times.
|
354 | for (; gapIndex < GAP_TRACKING_LENGTH; gapIndex++) { |
| 1172 | 332 | triggerShape.synchronizationRatioFrom[gapIndex] = NAN; | ||
| 1173 | 332 | triggerShape.synchronizationRatioTo[gapIndex] = NAN; | ||
| 1174 | } | |||
| 1175 | } | |||
| 1176 | 908 | } | ||
| 1177 | ||||
| 1178 | 986 | void TriggerCentral::applyCamGapOverride() { | ||
| 1179 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 986 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 986 times.
|
986 | if (engineConfiguration->overrideVvtTriggerGaps) { |
| 1180 | ✗ | int gapIndex = 0; | ||
| 1181 | ||||
| 1182 | ✗ | TriggerWaveform *shape = &vvtShape[0]; | ||
| 1183 | ✗ | shape->gapTrackingLength = engineConfiguration->gapVvtTrackingLengthOverride; | ||
| 1184 | ||||
| 1185 | ✗ | for (; gapIndex < engineConfiguration->gapVvtTrackingLengthOverride; gapIndex++) { | ||
| 1186 | ✗ | float gapOverrideFrom = engineConfiguration->triggerVVTGapOverrideFrom[gapIndex]; | ||
| 1187 | ✗ | float gapOverrideTo = engineConfiguration->triggerVVTGapOverrideTo[gapIndex]; | ||
| 1188 | ✗ | shape->synchronizationRatioFrom[gapIndex] = gapOverrideFrom; | ||
| 1189 | ✗ | shape->synchronizationRatioTo[gapIndex] = gapOverrideTo; | ||
| 1190 | } | |||
| 1191 | // fill the remainder with the default gaps | |||
| 1192 | ✗ | for (; gapIndex < VVT_TRACKING_LENGTH; gapIndex++) { | ||
| 1193 | ✗ | shape->synchronizationRatioFrom[gapIndex] = NAN; | ||
| 1194 | ✗ | shape->synchronizationRatioTo[gapIndex] = NAN; | ||
| 1195 | } | |||
| 1196 | } | |||
| 1197 | 986 | } | ||
| 1198 | ||||
| 1199 | 908 | void TriggerCentral::applyShapesConfiguration() { | ||
| 1200 | // Re-read config in case it's changed | |||
| 1201 | 908 | primaryTriggerConfiguration.update(); | ||
| 1202 |
2/2✓ Branch 0 taken 1816 times.
✓ Branch 1 taken 908 times.
|
2/2✓ Decision 'true' taken 1816 times.
✓ Decision 'false' taken 908 times.
|
2724 | for (int camIndex = 0;camIndex < CAMS_PER_BANK;camIndex++) { |
| 1203 | 1816 | vvtTriggerConfiguration[camIndex].update(); | ||
| 1204 | } | |||
| 1205 | ||||
| 1206 | 908 | triggerShape.initializeTriggerWaveform(lookupOperationMode(), primaryTriggerConfiguration.TriggerType); | ||
| 1207 | ||||
| 1208 | 908 | applyTriggerGapOverride(); | ||
| 1209 | ||||
| 1210 |
1/2✓ Branch 0 taken 908 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 908 times.
✗ Decision 'false' not taken.
|
908 | if (!triggerShape.shapeDefinitionError) { |
| 1211 | 908 | int length = triggerShape.getLength(); | ||
| 1212 | 908 | engineCycleEventCount = length; | ||
| 1213 | ||||
| 1214 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 908 times.
|
908 | efiAssertVoid(ObdCode::CUSTOM_SHAPE_LEN_ZERO, length > 0, "shapeLength=0"); | |
| 1215 | ||||
| 1216 | 908 | triggerErrorDetection.clear(); | ||
| 1217 | ||||
| 1218 | /** | |||
| 1219 | * 'initState' instance of TriggerDecoderBase is used only to initialize 'this' TriggerWaveform instance | |||
| 1220 | * #192 BUG real hardware trigger events could be coming even while we are initializing trigger | |||
| 1221 | */ | |||
| 1222 | 908 | calculateTriggerSynchPoint(primaryTriggerConfiguration, | ||
| 1223 | 908 | triggerShape, | ||
| 1224 | initState); | |||
| 1225 | } | |||
| 1226 | ||||
| 1227 | 908 | applyCamGapOverride(); | ||
| 1228 | ||||
| 1229 |
2/2✓ Branch 0 taken 1816 times.
✓ Branch 1 taken 908 times.
|
2/2✓ Decision 'true' taken 1816 times.
✓ Decision 'false' taken 908 times.
|
2724 | for (int camIndex = 0; camIndex < CAMS_PER_BANK; camIndex++) { |
| 1230 | // todo: should 'vvtWithRealDecoder' be used here? | |||
| 1231 |
2/2✓ Branch 1 taken 82 times.
✓ Branch 2 taken 1734 times.
|
2/2✓ Decision 'true' taken 82 times.
✓ Decision 'false' taken 1734 times.
|
1816 | if (engineConfiguration->vvtMode[camIndex] != VVT_INACTIVE) { |
| 1232 | 164 | initVvtShape( | ||
| 1233 | camIndex, | |||
| 1234 | 82 | vvtShape[camIndex], | ||
| 1235 | 82 | vvtTriggerConfiguration[camIndex], | ||
| 1236 | initState | |||
| 1237 | ); | |||
| 1238 | } | |||
| 1239 | } | |||
| 1240 | ||||
| 1241 | // This is not the right place for this, but further refactoring has to happen before it can get moved. | |||
| 1242 | 908 | triggerState.setNeedsDisambiguation(engine->triggerCentral.triggerShape.needsDisambiguation()); | ||
| 1243 | ||||
| 1244 | } | |||
| 1245 | ||||
| 1246 | /** | |||
| 1247 | * @returns true if configuration just changed, and if that change has affected trigger | |||
| 1248 | */ | |||
| 1249 | 2444 | bool TriggerCentral::checkIfTriggerConfigChanged() { | ||
| 1250 | // we want to make sure that configuration has changed AND that change has changed trigger specifically | |||
| 1251 |
4/4✓ Branch 2 taken 112 times.
✓ Branch 3 taken 2332 times.
✓ Branch 4 taken 87 times.
✓ Branch 5 taken 25 times.
|
2444 | bool result = triggerVersion.isOld(engine->getGlobalConfigurationVersion()) && triggerConfigChangedOnLastConfigurationChange; | |
| 1252 | 2444 | triggerConfigChangedOnLastConfigurationChange = false; // whoever has called the method is supposed to react to changes | ||
| 1253 | 2444 | return result; | ||
| 1254 | } | |||
| 1255 | ||||
| 1256 | #if EFI_UNIT_TEST | |||
| 1257 | 124 | bool TriggerCentral::isTriggerConfigChanged() { | ||
| 1258 | 124 | return triggerConfigChangedOnLastConfigurationChange; | ||
| 1259 | } | |||
| 1260 | #endif // EFI_UNIT_TEST | |||
| 1261 | ||||
| 1262 | 805 | void validateTriggerInputs() { | ||
| 1263 |
4/6✓ Branch 1 taken 798 times.
✓ Branch 2 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 798 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 805 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 805 times.
|
805 | if (!isBrainPinValid(engineConfiguration->triggerInputPins[0]) && isBrainPinValid(engineConfiguration->triggerInputPins[1])) { |
| 1264 | ✗ | criticalError("First trigger channel not configured while second one is."); | ||
| 1265 | } | |||
| 1266 | ||||
| 1267 |
4/6✓ Branch 1 taken 794 times.
✓ Branch 2 taken 11 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 794 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 805 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 805 times.
|
805 | if (!isBrainPinValid(engineConfiguration->camInputs[0]) && isBrainPinValid(engineConfiguration->camInputs[2])) { |
| 1268 | ✗ | criticalError("First bank cam input is required if second bank specified"); | ||
| 1269 | } | |||
| 1270 | 805 | } | ||
| 1271 | ||||
| 1272 | ✗ | void initTriggerCentral() { | ||
| 1273 | ||||
| 1274 | #if EFI_ENGINE_SNIFFER | |||
| 1275 | ✗ | initWaveChart(&waveChart); | ||
| 1276 | #endif /* EFI_ENGINE_SNIFFER */ | |||
| 1277 | ||||
| 1278 | #if EFI_PROD_CODE || EFI_SIMULATOR | |||
| 1279 | addConsoleAction(CMD_TRIGGERINFO, triggerInfo); | |||
| 1280 | addConsoleAction("trigger_shape_info", triggerShapeInfo); | |||
| 1281 | addConsoleAction("reset_trigger", resetRunningTriggerCounters); | |||
| 1282 | #endif // EFI_PROD_CODE || EFI_SIMULATOR | |||
| 1283 | ||||
| 1284 | ✗ | } | ||
| 1285 | ||||
| 1286 | /** | |||
| 1287 | * @return TRUE is something is wrong with trigger decoding | |||
| 1288 | */ | |||
| 1289 | ✗ | bool TriggerCentral::isTriggerDecoderError() { | ||
| 1290 | ✗ | return triggerErrorDetection.sum(6) > 4; | ||
| 1291 | } | |||
| 1292 | ||||
| 1293 | #endif // EFI_SHAFT_POSITION_INPUT | |||
| 1294 |