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 | 3541 | TriggerDecoderBase::TriggerDecoderBase(const char* p_name) | ||
41 | 3541 | : name(p_name) | ||
42 | { | |||
43 | 3541 | TriggerDecoderBase::resetState(); | ||
44 | 3541 | } | ||
45 | ||||
46 | 698666 | bool TriggerDecoderBase::getShaftSynchronized() const { | ||
47 | 698666 | return shaft_is_synchronized; | ||
48 | } | |||
49 | ||||
50 | 18756 | void TriggerDecoderBase::setShaftSynchronized(bool value) { | ||
51 | #if EFI_UNIT_TEST | |||
52 |
2/2✓ Branch 0 taken 2402 times.
✓ Branch 1 taken 16354 times.
|
2/2✓ Decision 'true' taken 2402 times.
✓ Decision 'false' taken 16354 times.
|
18756 | if (value != shaft_is_synchronized) { |
53 | 2402 | LogTriggerSync(value, getTimeNowNt()); | ||
54 | } | |||
55 | #endif | |||
56 | ||||
57 |
2/2✓ Branch 0 taken 12342 times.
✓ Branch 1 taken 6414 times.
|
2/2✓ Decision 'true' taken 12342 times.
✓ Decision 'false' taken 6414 times.
|
18756 | if (value) { |
58 |
2/2✓ Branch 0 taken 1305 times.
✓ Branch 1 taken 11037 times.
|
2/2✓ Decision 'true' taken 1305 times.
✓ Decision 'false' taken 11037 times.
|
12342 | if (!shaft_is_synchronized) { |
59 | // just got synchronized | |||
60 | 1305 | mostRecentSyncTime = getTimeNowNt(); | ||
61 | } | |||
62 | } else { | |||
63 | // sync loss | |||
64 | 6414 | mostRecentSyncTime = 0; | ||
65 | } | |||
66 | 18756 | shaft_is_synchronized = value; | ||
67 | 18756 | } | ||
68 | ||||
69 | 5993 | void TriggerDecoderBase::resetState() { | ||
70 | 5993 | setShaftSynchronized(false); | ||
71 | 5993 | toothed_previous_time = 0; | ||
72 | ||||
73 | 5993 | setArrayValues(toothDurations, 0); | ||
74 | ||||
75 | 5993 | synchronizationCounter = 0; | ||
76 | 5993 | totalTriggerErrorCounter = 0; | ||
77 | 5993 | orderingErrorCounter = 0; | ||
78 | 5993 | m_timeSinceDecodeError.init(); | ||
79 | ||||
80 | 5993 | prevSignal = SHAFT_PRIMARY_FALLING; | ||
81 | 5993 | startOfCycleNt = {}; | ||
82 | ||||
83 | 5993 | resetCurrentCycleState(); | ||
84 | ||||
85 | 5993 | totalEventCountBase = 0; | ||
86 | 5993 | isFirstEvent = true; | ||
87 | 5993 | } | ||
88 | ||||
89 | 108 | void TriggerDecoderBase::setTriggerErrorState(int errorIncrement) { | ||
90 | 108 | m_timeSinceDecodeError.reset(); | ||
91 | 108 | totalTriggerErrorCounter += errorIncrement; | ||
92 | 108 | onTransitionEvent(TransitionEvent::TriggerError); | ||
93 | 108 | } | ||
94 | ||||
95 | 18361 | void TriggerDecoderBase::resetCurrentCycleState() { | ||
96 | 18361 | setArrayValues(currentCycle.eventCount, 0); | ||
97 | 18361 | currentCycle.current_index = 0; | ||
98 | 18361 | } | ||
99 | ||||
100 | #if EFI_SHAFT_POSITION_INPUT | |||
101 | ||||
102 | 679 | PrimaryTriggerDecoder::PrimaryTriggerDecoder(const char* p_name) | ||
103 | 679 | : TriggerDecoderBase(p_name) | ||
104 | { | |||
105 | 679 | } | ||
106 | ||||
107 | #if ! EFI_PROD_CODE | |||
108 | bool printTriggerDebug = false; | |||
109 | bool printTriggerTrace = false; | |||
110 | #endif /* ! EFI_PROD_CODE */ | |||
111 | ||||
112 | 985 | void TriggerWaveform::initializeSyncPoint(TriggerDecoderBase& state, | ||
113 | const TriggerConfiguration& triggerConfiguration) { | |||
114 | 985 | triggerShapeSynchPointIndex = state.findTriggerZeroEventIndex(*this, triggerConfiguration); | ||
115 | 985 | } | ||
116 | ||||
117 | 1490 | void TriggerFormDetails::prepareEventAngles(TriggerWaveform *shape) { | ||
118 | 1490 | int triggerShapeSynchPointIndex = shape->triggerShapeSynchPointIndex; | ||
119 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1490 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1490 times.
|
1490 | if (triggerShapeSynchPointIndex == EFI_ERROR_CODE) { |
120 | ✗ | return; | ||
121 | } | |||
122 | 1490 | angle_t firstAngle = shape->getAngle(triggerShapeSynchPointIndex); | ||
123 |
2/4✓ Branch 0 taken 1490 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1490 times.
|
1490 | assertAngleRange(firstAngle, "firstAngle", ObdCode::CUSTOM_TRIGGER_SYNC_ANGLE); | |
124 | ||||
125 | 1490 | int riseOnlyIndex = 0; | ||
126 | ||||
127 | 1490 | size_t length = shape->getLength(); | ||
128 | ||||
129 | 1490 | setArrayValues(eventAngles, 0); | ||
130 | ||||
131 | // this may be <length for some triggers like symmetrical crank Miata NB | |||
132 | 1490 | size_t triggerShapeLength = shape->getSize(); | ||
133 | ||||
134 |
2/4✓ Branch 0 taken 1490 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1490 times.
|
1490 | assertAngleRange(triggerShapeSynchPointIndex, "triggerShapeSynchPointIndex", ObdCode::CUSTOM_TRIGGER_SYNC_ANGLE2); | |
135 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1490 times.
|
1490 | efiAssertVoid(ObdCode::CUSTOM_TRIGGER_CYCLE, getTriggerCentral()->engineCycleEventCount != 0, "zero engineCycleEventCount"); | |
136 | ||||
137 |
2/2✓ Branch 0 taken 27150 times.
✓ Branch 1 taken 1490 times.
|
2/2✓ Decision 'true' taken 27150 times.
✓ Decision 'false' taken 1490 times.
|
28640 | for (size_t eventIndex = 0; eventIndex < length; eventIndex++) { |
138 |
2/2✓ Branch 0 taken 1490 times.
✓ Branch 1 taken 25660 times.
|
2/2✓ Decision 'true' taken 1490 times.
✓ Decision 'false' taken 25660 times.
|
27150 | if (eventIndex == 0) { |
139 | // explicit check for zero to avoid issues where logical zero is not exactly zero due to float nature | |||
140 | 1490 | eventAngles[0] = 0; | ||
141 | // this value would be used in case of front-only | |||
142 | 1490 | eventAngles[1] = 0; | ||
143 | } else { | |||
144 | // Rotate the trigger around so that the sync point is at position 0 | |||
145 | 25660 | 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 | 25660 | 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 25660 times.
|
25660 | float angle = shape->getAngle(wrappedIndex) - firstAngle; | |
153 | ||||
154 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 25660 times.
✗ Branch 4 not taken.
|
25660 | efiAssertVoid(ObdCode::CUSTOM_TRIGGER_CYCLE, !std::isnan(angle), "trgSyncNaN"); | |
155 | // Wrap the angle back in to [0, 720) | |||
156 |
1/1✓ Branch 1 taken 25660 times.
|
25660 | wrapAngle(angle, "trgSync", ObdCode::CUSTOM_TRIGGER_SYNC_ANGLE_RANGE); | |
157 | ||||
158 |
2/2✓ Branch 0 taken 20748 times.
✓ Branch 1 taken 4912 times.
|
2/2✓ Decision 'true' taken 20748 times.
✓ Decision 'false' taken 4912 times.
|
25660 | if (shape->useOnlyRisingEdges) { |
159 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 20748 times.
✗ Branch 3 not taken.
|
20748 | criticalAssertVoid(triggerDefinitionIndex < triggerShapeLength, "trigger shape fail"); | |
160 |
1/3✗ Branch 1 not taken.
✓ Branch 2 taken 20748 times.
✗ Branch 4 not taken.
|
20748 | 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 10231 times.
✓ Branch 2 taken 10517 times.
|
2/2✓ Decision 'true' taken 10231 times.
✓ Decision 'false' taken 10517 times.
|
20748 | if (shape->isRiseEvent[triggerDefinitionIndex]) { |
164 | 10231 | riseOnlyIndex += 2; | ||
165 | 10231 | eventAngles[riseOnlyIndex] = angle; | ||
166 | 10231 | eventAngles[riseOnlyIndex + 1] = angle; | ||
167 | } | |||
168 | } else { | |||
169 | 4912 | eventAngles[eventIndex] = angle; | ||
170 | } | |||
171 | } | |||
172 | } | |||
173 | } | |||
174 | ||||
175 | ✗ | int64_t TriggerDecoderBase::getTotalEventCounter() const { | ||
176 | ✗ | return totalEventCountBase + currentCycle.current_index; | ||
177 | } | |||
178 | ||||
179 | 35095 | int TriggerDecoderBase::getSynchronizationCounter() const { | ||
180 | 35095 | return synchronizationCounter; | ||
181 | } | |||
182 | ||||
183 | 807 | void PrimaryTriggerDecoder::resetState() { | ||
184 | 807 | TriggerDecoderBase::resetState(); | ||
185 | ||||
186 | 807 | resetHasFullSync(); | ||
187 | 807 | } | ||
188 | ||||
189 | ||||
190 | 100432 | bool TriggerDecoderBase::isValidIndex(const TriggerWaveform& triggerShape) const { | ||
191 | 100432 | 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 | 575 | angle_t PrimaryTriggerDecoder::syncEnginePhase(int divider, int remainder, angle_t engineCycle) { | ||
217 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 575 times.
|
575 | efiAssert(ObdCode::OBD_PCM_Processor_Fault, divider > 1, "syncEnginePhase divider", false); | |
218 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 575 times.
|
575 | efiAssert(ObdCode::OBD_PCM_Processor_Fault, remainder < divider, "syncEnginePhase remainder", false); | |
219 | 575 | angle_t totalShift = 0; | ||
220 |
2/2✓ Branch 1 taken 85 times.
✓ Branch 2 taken 575 times.
|
2/2✓ Decision 'true' taken 85 times.
✓ Decision 'false' taken 575 times.
|
660 | 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 | 85 | incrementShaftSynchronizationCounter(); | ||
227 | 85 | 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 | 575 | m_hasSynchronizedPhase = true; | ||
232 | ||||
233 |
2/2✓ Branch 0 taken 37 times.
✓ Branch 1 taken 538 times.
|
2/2✓ Decision 'true' taken 37 times.
✓ Decision 'false' taken 538 times.
|
575 | if (totalShift > 0) { |
234 | 37 | camResyncCounter++; | ||
235 | 37 | onTransitionEvent(TransitionEvent::EngineResync); | ||
236 | } | |||
237 | ||||
238 | 575 | return totalShift; | ||
239 | } | |||
240 | ||||
241 | 11148 | void TriggerDecoderBase::incrementShaftSynchronizationCounter() { | ||
242 | 11148 | synchronizationCounter++; | ||
243 | 11148 | } | ||
244 | ||||
245 | 70 | void PrimaryTriggerDecoder::onTriggerError() { | ||
246 | // On trigger error, we've lost full sync | |||
247 | 70 | 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 69 times.
✓ Branch 1 taken 1 time.
|
2/2✓ Decision 'true' taken 69 times.
✓ Decision 'false' taken 1 time.
|
70 | if (engine) { |
253 | // Instant RPM data is now also probably trash, discard it | |||
254 | 69 | engine->triggerCentral.instantRpm.resetInstantRpm(); | ||
255 | 69 | engine->rpmCalculator.lastTdcTimer.init(); | ||
256 | } | |||
257 | #pragma GCC diagnostic pop | |||
258 | 70 | } | ||
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 | 141808 | const char *getTrigger_event_e(trigger_event_e value){ | ||
277 |
4/5✓ Branch 0 taken 8116 times.
✓ Branch 1 taken 123123 times.
✓ Branch 2 taken 4037 times.
✓ Branch 3 taken 6532 times.
✗ Branch 4 not taken.
|
141808 | switch(value) { | |
278 |
1/1✓ Decision 'true' taken 8116 times.
|
8116 | case SHAFT_PRIMARY_FALLING: | |
279 | 8116 | return "SHAFT_PRIMARY_FALLING"; | ||
280 |
1/1✓ Decision 'true' taken 123123 times.
|
123123 | case SHAFT_PRIMARY_RISING: | |
281 | 123123 | return "SHAFT_PRIMARY_RISING"; | ||
282 |
1/1✓ Decision 'true' taken 4037 times.
|
4037 | case SHAFT_SECONDARY_FALLING: | |
283 | 4037 | return "SHAFT_SECONDARY_FALLING"; | ||
284 |
1/1✓ Decision 'true' taken 6532 times.
|
6532 | case SHAFT_SECONDARY_RISING: | |
285 | 6532 | 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 | 22 | void VvtTriggerDecoder::onTooManyTeeth(int actual, int expected) { | ||
304 | 22 | warning(ObdCode::CUSTOM_CAM_TOO_MANY_TEETH, "cam %s trigger error: too many teeth between sync points: %d > %d", name, actual, expected); | ||
305 | 22 | } | ||
306 | ||||
307 | 12368 | PUBLIC_API_WEAK bool isTriggerCounterError(int8_t triggerCountersError) { | ||
308 | 12368 | return triggerCountersError != 0; | ||
309 | } | |||
310 | ||||
311 | 12368 | 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 | 12368 | int countersError = 0; | ||
314 |
2/2✓ Branch 0 taken 23497 times.
✓ Branch 1 taken 11104 times.
|
2/2✓ Decision 'true' taken 23497 times.
✓ Decision 'false' taken 11104 times.
|
34601 | for (int i = 0;i < PWM_PHASE_MAX_WAVE_PER_PWM;i++) { |
315 | 23497 | countersError = currentCycle.eventCount[i] - triggerShape.getExpectedEventCount((TriggerWheel)i); | ||
316 |
2/2✓ Branch 0 taken 1264 times.
✓ Branch 1 taken 22233 times.
|
2/2✓ Decision 'true' taken 1264 times.
✓ Decision 'false' taken 22233 times.
|
23497 | if (countersError != 0) { |
317 | 1264 | 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 | 12368 | return countersError; | ||
331 | } | |||
332 | ||||
333 | 12368 | void TriggerDecoderBase::onShaftSynchronization( | ||
334 | bool wasSynchronized, | |||
335 | const efitick_t nowNt, | |||
336 | const TriggerWaveform& triggerShape) { | |||
337 | 12368 | startOfCycleNt = nowNt; | ||
338 | 12368 | resetCurrentCycleState(); | ||
339 | ||||
340 |
2/2✓ Branch 0 taken 11063 times.
✓ Branch 1 taken 1305 times.
|
2/2✓ Decision 'true' taken 11063 times.
✓ Decision 'false' taken 1305 times.
|
12368 | if (wasSynchronized) { |
341 | 11063 | incrementShaftSynchronizationCounter(); | ||
342 | } else { | |||
343 | // We have just synchronized, this is the zeroth revolution | |||
344 | 1305 | synchronizationCounter = 0; | ||
345 | } | |||
346 | ||||
347 | 12368 | totalEventCountBase += triggerShape.getSize(); | ||
348 | ||||
349 | #if EFI_UNIT_TEST | |||
350 |
2/2✓ Branch 0 taken 8112 times.
✓ Branch 1 taken 4256 times.
|
2/2✓ Decision 'true' taken 8112 times.
✓ Decision 'false' taken 4256 times.
|
12368 | if (printTriggerDebug) { |
351 | 8112 | printf("onShaftSynchronization index=%d %d\r\n", | ||
352 | currentCycle.current_index, | |||
353 | synchronizationCounter); | |||
354 | } | |||
355 | #endif /* EFI_UNIT_TEST */ | |||
356 | 12368 | } | ||
357 | ||||
358 | 187202 | static bool shouldConsiderEdge(const TriggerWaveform& triggerShape, TriggerWheel triggerWheel, TriggerValue edge) { | ||
359 |
4/4✓ Branch 0 taken 11841 times.
✓ Branch 1 taken 175361 times.
✓ Branch 2 taken 11024 times.
✓ Branch 3 taken 817 times.
|
2/2✓ Decision 'true' taken 11024 times.
✓ Decision 'false' taken 176178 times.
|
187202 | if (triggerWheel != TriggerWheel::T_PRIMARY && triggerShape.useOnlyPrimaryForSync) { |
360 | // Non-primary events ignored | |||
361 | 11024 | return false; | ||
362 | } | |||
363 | ||||
364 |
3/4✓ Branch 0 taken 27785 times.
✓ Branch 1 taken 142781 times.
✓ Branch 2 taken 5612 times.
✗ Branch 3 not taken.
|
176178 | switch (triggerShape.syncEdge) { | |
365 |
1/1✓ Decision 'true' taken 27785 times.
|
27785 | case SyncEdge::Both: return true; | |
366 |
1/1✓ Decision 'true' taken 142781 times.
|
142781 | case SyncEdge::RiseOnly: | |
367 |
1/1✓ Decision 'true' taken 142781 times.
|
142781 | 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 | 187202 | 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 | 187202 | ScopePerf perf(PE::DecodeTriggerEvent); | ||
427 | ||||
428 | #if EFI_PROD_CODE | |||
429 | getTriggerCentral()->triggerElapsedUs = previousEventTimer.getElapsedUs(); | |||
430 | #endif | |||
431 | ||||
432 |
3/3✓ Branch 1 taken 187202 times.
✓ Branch 3 taken 311 times.
✓ Branch 4 taken 186891 times.
|
2/2✓ Decision 'true' taken 311 times.
✓ Decision 'false' taken 186891 times.
|
187202 | 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 311 times.
|
311 | setShaftSynchronized(false); | |
438 |
2/2✓ Branch 0 taken 128 times.
✓ Branch 1 taken 183 times.
|
2/2✓ Decision 'true' taken 128 times.
✓ Decision 'false' taken 183 times.
|
311 | if (triggerStateListener) { |
439 |
1/1✓ Branch 1 taken 128 times.
|
128 | triggerStateListener->OnTriggerSynchronizationLost(); | |
440 | } | |||
441 | } | |||
442 | ||||
443 | 187202 | bool useOnlyRisingEdgeForTrigger = triggerShape.useOnlyRisingEdges; | ||
444 | ||||
445 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 187202 times.
✗ Branch 3 not taken.
|
187202 | efiAssert(ObdCode::CUSTOM_TRIGGER_UNEXPECTED, signal <= SHAFT_SECONDARY_RISING, "unexpected signal", unexpected); | |
446 | ||||
447 | 187202 | TriggerWheel triggerWheel = eventIndex[signal]; | ||
448 | 187202 | 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 116799 times.
✓ Branch 1 taken 70403 times.
✓ Branch 2 taken 95 times.
✓ Branch 3 taken 116704 times.
|
2/2✓ Decision 'true' taken 95 times.
✓ Decision 'false' taken 187107 times.
|
187202 | if (!useOnlyRisingEdgeForTrigger && prevSignal == signal) { |
452 | 95 | orderingErrorCounter++; | ||
453 | } | |||
454 | ||||
455 | 187202 | prevSignal = signal; | ||
456 | ||||
457 | 187202 | currentCycle.eventCount[(int)triggerWheel]++; | ||
458 | ||||
459 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 187202 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 187202 times.
|
187202 | 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 1520 times.
✓ Branch 1 taken 185682 times.
|
187202 | 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 | 187202 | toothDurations[0] = | ||
470 | 187202 | currentDurationLong > 10 * NT_PER_SECOND ? 10 * NT_PER_SECOND : currentDurationLong; | ||
471 | ||||
472 |
2/2✓ Branch 1 taken 50853 times.
✓ Branch 2 taken 136349 times.
|
2/2✓ Decision 'true' taken 50853 times.
✓ Decision 'false' taken 136349 times.
|
187202 | if (!shouldConsiderEdge(triggerShape, triggerWheel, type)) { |
473 | #if EFI_UNIT_TEST | |||
474 |
2/2✓ Branch 0 taken 14918 times.
✓ Branch 1 taken 35935 times.
|
2/2✓ Decision 'true' taken 14918 times.
✓ Decision 'false' taken 35935 times.
|
50853 | if (printTriggerTrace) { |
475 |
2/2✓ Branch 1 taken 14918 times.
✓ Branch 4 taken 14918 times.
|
29836 | printf("%s isLessImportant %s now=%d index=%d\r\n", | |
476 |
1/1✓ Branch 1 taken 14918 times.
|
14918 | 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 48492 times.
✓ Branch 2 taken 14918 times.
✓ Branch 3 taken 35935 times.
✓ Branch 5 taken 14918 times.
|
50853 | nextTriggerEvent(); | |
485 | } else { | |||
486 | #if !EFI_PROD_CODE | |||
487 |
2/2✓ Branch 0 taken 63445 times.
✓ Branch 1 taken 72904 times.
|
2/2✓ Decision 'true' taken 63445 times.
✓ Decision 'false' taken 72904 times.
|
136349 | if (printTriggerTrace) { |
488 |
2/2✓ Branch 1 taken 63445 times.
✓ Branch 4 taken 63445 times.
|
126890 | printf("%s event %s %lld\r\n", | |
489 |
1/1✓ Branch 1 taken 63445 times.
|
63445 | getTrigger_type_e(triggerConfiguration.TriggerType.type), | |
490 | getTrigger_event_e(signal), | |||
491 | nowNt); | |||
492 |
1/1✓ Branch 1 taken 63445 times.
|
63445 | 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 | 136349 | isFirstEvent = false; | ||
498 | bool isSynchronizationPoint; | |||
499 |
1/1✓ Branch 1 taken 136349 times.
|
136349 | bool wasSynchronized = getShaftSynchronized(); | |
500 | ||||
501 |
2/2✓ Branch 0 taken 128183 times.
✓ Branch 1 taken 8166 times.
|
2/2✓ Decision 'true' taken 128183 times.
✓ Decision 'false' taken 8166 times.
|
136349 | if (triggerShape.isSynchronizationNeeded) { |
502 | 128183 | triggerSyncGapRatio = (float)toothDurations[0] / toothDurations[1]; | ||
503 | ||||
504 |
4/4✓ Branch 0 taken 68089 times.
✓ Branch 1 taken 60094 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 68086 times.
|
2/2✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 128180 times.
|
128183 | 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 128183 times.
|
128183 | isSynchronizationPoint = isSyncPoint(triggerShape, triggerConfiguration.TriggerType.type); | |
509 |
2/2✓ Branch 0 taken 5426 times.
✓ Branch 1 taken 122757 times.
|
2/2✓ Decision 'true' taken 5426 times.
✓ Decision 'false' taken 122757 times.
|
128183 | if (isSynchronizationPoint) { |
510 |
1/1✓ Branch 1 taken 5426 times.
|
5426 | 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 128183 times.
✓ Branch 3 taken 84065 times.
✓ Branch 4 taken 44118 times.
✓ Branch 5 taken 79178 times.
✓ Branch 6 taken 4887 times.
|
128183 | 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 58639 times.
✓ Branch 1 taken 69544 times.
|
2/2✓ Decision 'true' taken 58639 times.
✓ Decision 'false' taken 69544 times.
|
128183 | if (printTriggerTrace) { |
529 |
2/2✓ Branch 0 taken 196015 times.
✓ Branch 1 taken 58639 times.
|
2/2✓ Decision 'true' taken 196015 times.
✓ Decision 'false' taken 58639 times.
|
254654 | for (int i = 0;i<triggerShape.gapTrackingLength;i++) { |
530 | 196015 | float gap = 1.0 * toothDurations[i] / toothDurations[i + 1]; | ||
531 | 784060 | printf("%sindex=%d: gap=%.2f expected from %.2f to %.2f error=%s\r\n", | ||
532 | 196015 | triggerConfiguration.PrintPrefix, | ||
533 | i, | |||
534 | gap, | |||
535 |
1/1✓ Branch 1 taken 196015 times.
|
196015 | triggerShape.synchronizationRatioFrom[i], | |
536 | 196015 | triggerShape.synchronizationRatioTo[i], | ||
537 |
2/2✓ Branch 1 taken 196015 times.
✓ Branch 4 taken 196015 times.
|
196015 | 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 8166 times.
✓ Branch 3 taken 1260 times.
✓ Branch 4 taken 6906 times.
|
1/1✓ Decision 'true' taken 8166 times.
|
8166 | unsigned int endOfCycleIndex = triggerShape.getSize() - (useOnlyRisingEdgeForTrigger ? 2 : 1); |
550 | ||||
551 |
5/5✓ Branch 1 taken 8166 times.
✓ Branch 3 taken 7430 times.
✓ Branch 4 taken 736 times.
✓ Branch 5 taken 6206 times.
✓ Branch 6 taken 1224 times.
|
8166 | isSynchronizationPoint = !getShaftSynchronized() || (currentCycle.current_index >= endOfCycleIndex); | |
552 | ||||
553 | #if EFI_UNIT_TEST | |||
554 |
2/2✓ Branch 0 taken 4806 times.
✓ Branch 1 taken 3360 times.
|
2/2✓ Decision 'true' taken 4806 times.
✓ Decision 'false' taken 3360 times.
|
8166 | if (printTriggerTrace) { |
555 |
1/1✓ Branch 1 taken 4806 times.
|
4806 | printf("decodeTriggerEvent sync=%d isSynchronizationPoint=%d index=%d size=%d\r\n", | |
556 |
2/2✓ Branch 1 taken 4806 times.
✓ Branch 4 taken 4806 times.
|
4806 | 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 63445 times.
✓ Branch 1 taken 72904 times.
|
2/2✓ Decision 'true' taken 63445 times.
✓ Decision 'false' taken 72904 times.
|
136349 | if (printTriggerTrace) { |
565 |
2/2✓ Branch 1 taken 63445 times.
✓ Branch 4 taken 63445 times.
|
126890 | printf("decodeTriggerEvent gap %s isSynchronizationPoint=%d index=%d %s\r\n", | |
566 |
1/1✓ Branch 1 taken 63445 times.
|
63445 | 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 12368 times.
✓ Branch 1 taken 123981 times.
|
2/2✓ Decision 'true' taken 12368 times.
✓ Decision 'false' taken 123981 times.
|
136349 | if (isSynchronizationPoint) { |
573 |
1/1✓ Branch 1 taken 12368 times.
|
12368 | triggerCountersError = getEventCountersError(triggerShape); | |
574 |
1/1✓ Branch 1 taken 12368 times.
|
12368 | bool isDecodingError = isTriggerCounterError(triggerCountersError); | |
575 | ||||
576 |
2/2✓ Branch 0 taken 4466 times.
✓ Branch 1 taken 7902 times.
|
2/2✓ Decision 'true' taken 4466 times.
✓ Decision 'false' taken 7902 times.
|
12368 | if (triggerStateListener) { |
577 |
1/1✓ Branch 1 taken 4466 times.
|
4466 | 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 11063 times.
✓ Branch 1 taken 1305 times.
✓ Branch 2 taken 26 times.
✓ Branch 3 taken 11037 times.
|
2/2✓ Decision 'true' taken 26 times.
✓ Decision 'false' taken 12342 times.
|
12368 | 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 12342 times.
|
12342 | setShaftSynchronized(true); | |
599 | } | |||
600 | ||||
601 | // this call would update duty cycle values | |||
602 |
5/5✓ Branch 0 taken 4589 times.
✓ Branch 1 taken 7779 times.
✓ Branch 2 taken 8112 times.
✓ Branch 3 taken 4256 times.
✓ Branch 5 taken 8112 times.
|
12368 | nextTriggerEvent(); | |
603 | ||||
604 |
1/1✓ Branch 1 taken 12368 times.
|
12368 | onShaftSynchronization(wasSynchronized, nowNt, triggerShape); | |
605 | } else { /* if (!isSynchronizationPoint) */ | |||
606 |
5/5✓ Branch 0 taken 63453 times.
✓ Branch 1 taken 60528 times.
✓ Branch 2 taken 55333 times.
✓ Branch 3 taken 68648 times.
✓ Branch 5 taken 55333 times.
|
123981 | nextTriggerEvent(); | |
607 | } | |||
608 | ||||
609 |
2/2✓ Branch 0 taken 454323 times.
✓ Branch 1 taken 136349 times.
|
2/2✓ Decision 'true' taken 454323 times.
✓ Decision 'false' taken 136349 times.
|
590672 | for (int i = triggerShape.gapTrackingLength; i > 0; i--) { |
610 | 454323 | toothDurations[i] = toothDurations[i - 1]; | ||
611 | } | |||
612 | ||||
613 | 136349 | toothed_previous_time = nowNt; | ||
614 | ||||
615 | #if EFI_UNIT_TEST | |||
616 |
2/2✓ Branch 0 taken 75519 times.
✓ Branch 1 taken 60830 times.
|
2/2✓ Decision 'true' taken 75519 times.
✓ Decision 'false' taken 60830 times.
|
136349 | if (wasSynchronized) { |
617 |
1/1✓ Branch 1 taken 75519 times.
|
75519 | int uiGapIndex = (currentCycle.current_index) % triggerShape.getLength(); | |
618 | 75519 | gapRatio[uiGapIndex] = triggerSyncGapRatio; | ||
619 | } | |||
620 | #endif // EFI_UNIT_TEST | |||
621 | } | |||
622 | ||||
623 |
8/8✓ Branch 1 taken 187202 times.
✓ Branch 3 taken 100432 times.
✓ Branch 4 taken 86770 times.
✓ Branch 6 taken 100432 times.
✓ Branch 8 taken 84 times.
✓ Branch 9 taken 100348 times.
✓ Branch 10 taken 84 times.
✓ Branch 11 taken 187118 times.
|
2/2✓ Decision 'true' taken 84 times.
✓ Decision 'false' taken 187118 times.
|
187202 | 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 84 times.
✓ Branch 3 taken 79 times.
✓ Branch 4 taken 5 times.
|
2/2✓ Decision 'true' taken 79 times.
✓ Decision 'false' taken 5 times.
|
84 | if (Sensor::getOrZero(SensorType::Rpm) != 0) { |
629 |
1/1✓ Branch 1 taken 79 times.
|
79 | setTriggerErrorState(); | |
630 |
2/2✓ Branch 1 taken 79 times.
✓ Branch 4 taken 79 times.
|
79 | onTooManyTeeth(currentCycle.current_index, triggerShape.getSize()); | |
631 | } | |||
632 | ||||
633 |
1/1✓ Branch 1 taken 84 times.
|
84 | onTriggerError(); | |
634 | ||||
635 |
1/1✓ Branch 1 taken 84 times.
|
84 | setShaftSynchronized(false); | |
636 | ||||
637 | 84 | return unexpected; | ||
638 | } | |||
639 | ||||
640 | 187118 | triggerStateIndex = currentCycle.current_index; | ||
641 | ||||
642 | // Needed for early instant-RPM detection | |||
643 | 187118 | TriggerStateListener * l = triggerStateListener; | ||
644 |
2/2✓ Branch 0 taken 36279 times.
✓ Branch 1 taken 187118 times.
|
2/2✓ Decision 'true' taken 36279 times.
✓ Decision 'false' taken 187118 times.
|
223397 | while (l) { |
645 |
1/1✓ Branch 1 taken 36279 times.
|
36279 | l->OnTriggerStateProperState(nowNt, triggerStateIndex); | |
646 |
1/1✓ Branch 1 taken 36279 times.
|
36279 | l = l->nextListener(); | |
647 | } | |||
648 | ||||
649 |
3/3✓ Branch 1 taken 187118 times.
✓ Branch 3 taken 100348 times.
✓ Branch 4 taken 86770 times.
|
2/2✓ Decision 'true' taken 100348 times.
✓ Decision 'false' taken 86770 times.
|
187118 | if (getShaftSynchronized()) { |
650 | 100348 | return TriggerDecodeResult{ currentCycle.current_index }; | ||
651 | } else { | |||
652 | 86770 | return unexpected; | ||
653 | } | |||
654 | } | |||
655 | ||||
656 | 128183 | 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 127518 times.
|
2/2✓ Decision 'true' taken 665 times.
✓ Decision 'false' taken 127518 times.
|
128183 | 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 166130 times.
✓ Branch 1 taken 5109 times.
|
2/2✓ Decision 'true' taken 166130 times.
✓ Decision 'false' taken 5109 times.
|
171239 | for (int i = 0; i < triggerShape.gapTrackingLength; i++) { |
684 | 166130 | auto from = triggerShape.synchronizationRatioFrom[i]; | ||
685 | 166130 | auto to = triggerShape.synchronizationRatioTo[i]; | ||
686 | ||||
687 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 166130 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 166130 times.
|
166130 | 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 | 166130 | bool isGapCondition = | ||
697 | 166130 | (toothDurations[i] > toothDurations[i + 1] * from | ||
698 |
4/4✓ Branch 0 taken 87721 times.
✓ Branch 1 taken 78409 times.
✓ Branch 4 taken 43721 times.
✓ Branch 5 taken 44000 times.
|
166130 | && toothDurations[i] < toothDurations[i + 1] * to); | |
699 | ||||
700 |
2/2✓ Branch 0 taken 122409 times.
✓ Branch 1 taken 43721 times.
|
2/2✓ Decision 'true' taken 122409 times.
✓ Decision 'false' taken 43721 times.
|
166130 | if (!isGapCondition) { |
701 | 122409 | return false; | ||
702 | } | |||
703 | } | |||
704 | ||||
705 | 5109 | 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 | 1135 | 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 1135 times.
|
1135 | resetState(); | |
723 | ||||
724 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1135 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1135 times.
|
1135 | if (shape.shapeDefinitionError) { |
725 | ✗ | return 0; | ||
726 | } | |||
727 | ||||
728 |
1/1✓ Branch 2 taken 1069 times.
|
1135 | expected<uint32_t> syncIndex = TriggerStimulatorHelper::findTriggerSyncPoint(shape, | |
729 | triggerConfiguration, | |||
730 | *this); | |||
731 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1069 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1069 times.
|
1069 | 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 1069 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1069 times.
✗ Branch 6 not taken.
|
1069 | efiAssert(ObdCode::CUSTOM_ERR_ASSERT, getSynchronizationCounter() == 0, "findZero_revCounter", EFI_ERROR_CODE); | |
737 | ||||
738 | #if EFI_UNIT_TEST | |||
739 |
2/2✓ Branch 0 taken 795 times.
✓ Branch 1 taken 274 times.
|
2/2✓ Decision 'true' taken 795 times.
✓ Decision 'false' taken 274 times.
|
1069 | if (printTriggerDebug) { |
740 |
1/1✓ Branch 1 taken 795 times.
|
795 | printf("findTriggerZeroEventIndex: syncIndex located %lu!\r\n", syncIndex.Value); | |
741 | } | |||
742 | #endif /* EFI_UNIT_TEST */ | |||
743 | ||||
744 |
1/1✓ Branch 1 taken 1069 times.
|
1069 | TriggerStimulatorHelper::assertSyncPosition(triggerConfiguration, | |
745 | syncIndex.Value, *this, shape); | |||
746 | ||||
747 |
1/1✓ Branch 1 taken 1069 times.
|
1069 | return syncIndex.Value % shape.getSize(); | |
748 | } | |||
749 | ||||
750 | #endif /* EFI_SHAFT_POSITION_INPUT */ | |||
751 | ||||
752 |