rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
trigger_decoder.cpp
Go to the documentation of this file.
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"
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
41 : name(p_name)
42{
44}
45
49
51#if EFI_UNIT_TEST
52 if (value != shaft_is_synchronized) {
54 }
55#endif
56
57 if (value) {
59 // just got synchronized
61 }
62 } else {
63 // sync loss
65 }
67}
68
88
94
99
100#if EFI_SHAFT_POSITION_INPUT
101
103 : TriggerDecoderBase(p_name)
104{
105}
106
107#if ! EFI_PROD_CODE
108bool printTriggerDebug = false;
109bool printTriggerTrace = false;
110#endif /* ! EFI_PROD_CODE */
111
113 const TriggerConfiguration& triggerConfiguration) {
114 triggerShapeSynchPointIndex = state.findTriggerZeroEventIndex(*this, triggerConfiguration);
115}
116
118 int triggerShapeSynchPointIndex = shape->triggerShapeSynchPointIndex;
119 if (triggerShapeSynchPointIndex == EFI_ERROR_CODE) {
120 return;
121 }
122 angle_t firstAngle = shape->getAngle(triggerShapeSynchPointIndex);
123 assertAngleRange(firstAngle, "firstAngle", ObdCode::CUSTOM_TRIGGER_SYNC_ANGLE);
124
125 size_t length = shape->getLength();
126
128
129 // this may be <length for some triggers like symmetrical crank Miata NB
130 size_t triggerShapeLength = shape->getSize();
131
132 assertAngleRange(triggerShapeSynchPointIndex, "triggerShapeSynchPointIndex", ObdCode::CUSTOM_TRIGGER_SYNC_ANGLE2);
133 efiAssertVoid(ObdCode::CUSTOM_TRIGGER_CYCLE, getTriggerCentral()->engineCycleEventCount != 0, "zero engineCycleEventCount");
134
135 float lastAnglePrimary = 0.0;
136 float lastAngleSecondary = 0.0;
137
138 for (size_t eventIndex = 0; eventIndex < length; eventIndex++) {
139 if (eventIndex == 0) {
140 // explicit check for zero to avoid issues where logical zero is not exactly zero due to float nature
141 eventAngles[0] = 0;
142 // this value would be used in case of front-only
143 eventAngles[1] = 0;
144 } else {
145 // Rotate the trigger around so that the sync point is at position 0
146 auto wrappedIndex = (triggerShapeSynchPointIndex + eventIndex) % length;
147
148 // Compute this tooth's position within the trigger definition
149 // (wrap, as the trigger def may be smaller than total trigger length)
150 auto triggerDefinitionIndex = wrappedIndex % triggerShapeLength;
151
152 // Compute the relative angle of this tooth to the sync point's tooth
153 float angle = shape->getAngle(wrappedIndex) - firstAngle;
154
155 efiAssertVoid(ObdCode::CUSTOM_TRIGGER_CYCLE, !std::isnan(angle), "trgSyncNaN");
156 // Wrap the angle back in to [0, 720)
158
159 if (shape->useOnlyRisingEdges) {
160 criticalAssertVoid(triggerDefinitionIndex < triggerShapeLength, "trigger shape fail");
161 assertIsInBounds(triggerDefinitionIndex, shape->isRiseEvent, "isRise");
162 bool primary = shape->getWheel(triggerDefinitionIndex) == TriggerWheel::T_PRIMARY ? true : false;
163 // In case this is a rising event, store the angle to be used for the next falling event.
164 if (shape->isRiseEvent[triggerDefinitionIndex]) {
165 eventAngles[eventIndex] = angle;
166 if (primary) {
167 lastAnglePrimary = angle;
168 } else {
169 lastAngleSecondary = angle;
170 }
171 // If this is a falling event, apply the angle of the last rising event on this channel.
172 } else if (primary) {
173 eventAngles[eventIndex] = lastAnglePrimary;
174 } else {
175 eventAngles[eventIndex] = lastAngleSecondary;
176 }
177 } else {
178 eventAngles[eventIndex] = angle;
179 }
180 }
181 }
182}
183
187
191
197
198
199bool TriggerDecoderBase::isValidIndex(const TriggerWaveform& triggerShape) const {
200 return currentCycle.current_index < triggerShape.getSize();
201}
202
205
206#if EFI_UNIT_TEST
207#define PRINT_INC_INDEX if (printTriggerTrace) {\
208 printf("nextTriggerEvent index=%d\r\n", currentCycle.current_index); \
209 }
210#else
211#define PRINT_INC_INDEX {}
212#endif /* EFI_UNIT_TEST */
213
214#define nextTriggerEvent() \
215 { \
216 if (useOnlyRisingEdgeForTrigger) {currentCycle.current_index++;} \
217 currentCycle.current_index++; \
218 PRINT_INC_INDEX; \
219}
220
224
225angle_t PrimaryTriggerDecoder::syncEnginePhase(int divider, int remainder, angle_t engineCycle) {
226 efiAssert(ObdCode::OBD_PCM_Processor_Fault, divider > 1, "syncEnginePhase divider", false);
227 efiAssert(ObdCode::OBD_PCM_Processor_Fault, remainder < divider, "syncEnginePhase remainder", false);
228 angle_t totalShift = 0;
229 while (getSynchronizationCounter() % divider != remainder) {
230 /**
231 * we are here if we've detected the cam sensor within the wrong crank phase
232 * let's increase the trigger event counter, that would adjust the state of
233 * virtual crank-based trigger
234 */
236 totalShift += engineCycle / divider;
237 }
238
239 // Allow injection/ignition to happen, we've now fully sync'd the crank based on new cam information
241
242 if (totalShift > 0) {
245 }
246
247 return totalShift;
248}
249
253
255 // On trigger error, we've lost full sync
257
258 // Ignore the warning that engine is never null - it might be in unit tests
259 #pragma GCC diagnostic push
260 #pragma GCC diagnostic ignored "-Waddress"
261 if (engine) {
262 // Instant RPM data is now also probably trash, discard it
265 }
266 #pragma GCC diagnostic pop
267}
268
269void PrimaryTriggerDecoder::onNotEnoughTeeth(int /*actual*/, int /*expected*/) {
270 warning(ObdCode::CUSTOM_PRIMARY_NOT_ENOUGH_TEETH, "primary trigger error: not enough teeth between sync points: expected %d/%d got %d/%d",
271 getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_PRIMARY),
272 getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_SECONDARY),
275}
276
277#if EFI_UNIT_TEST
278// weird: we count trigger error on next sync, but if we never sync - counter does not increase?!
280#endif // EFI_UNIT_TEST
281
282void PrimaryTriggerDecoder::onTooManyTeeth(int /*actual*/, int /*expected*/) {
283 warning(ObdCode::CUSTOM_PRIMARY_TOO_MANY_TEETH, "primary trigger error: too many teeth between sync points: expected %d/%d got %d/%d",
284 getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_PRIMARY),
285 getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_SECONDARY),
288}
289
291switch(value) {
293 return "SHAFT_PRIMARY_FALLING";
295 return "SHAFT_PRIMARY_RISING";
297 return "SHAFT_SECONDARY_FALLING";
299 return "SHAFT_SECONDARY_RISING";
300 }
301 return NULL;
302}
304switch(value) {
306 return "TriggerValue::FALL";
308 return "TriggerValue::RISE";
309 }
310 return NULL;
311}
312
313void VvtTriggerDecoder::onNotEnoughTeeth(int actual, int expected) {
314 warning(ObdCode::CUSTOM_CAM_NOT_ENOUGH_TEETH, "cam %s trigger error: not enough teeth between sync points: actual %d expected %d", name, actual, expected);
315}
316
317void VvtTriggerDecoder::onTooManyTeeth(int actual, int expected) {
318 warning(ObdCode::CUSTOM_CAM_TOO_MANY_TEETH, "cam %s trigger error: too many teeth between sync points: %d > %d", name, actual, expected);
319}
320
321PUBLIC_API_WEAK bool isTriggerCounterError(int8_t triggerCountersError) {
322 return triggerCountersError != 0;
323}
324
326 // We can check if things are fine by comparing the number of events in a cycle with the expected number of event.
327 int countersError = 0;
328 for (int i = 0;i < PWM_PHASE_MAX_WAVE_PER_PWM;i++) {
329 countersError = currentCycle.eventCount[i] - triggerShape.getExpectedEventCount((TriggerWheel)i);
330 if (countersError != 0) {
331 break;
332 }
333 }
334
335#if EFI_DETAILED_LOGGING
336 printf("getEventCountersError: isDecodingError=%d\n", (countersError != 0));
337 if (countersError != 0) {
338 for (int i = 0;i < PWM_PHASE_MAX_WAVE_PER_PWM;i++) {
339 printf(" count: cur=%d exp=%d\n", currentCycle.eventCount[i], triggerShape.getExpectedEventCount((TriggerWheel)i));
340 }
341 }
342#endif /* EFI_UNIT_TEST */
343
344 return countersError;
345}
346
348 bool wasSynchronized,
349 const efitick_t nowNt,
350 const TriggerWaveform& triggerShape) {
351 startOfCycleNt = nowNt;
353
354 if (wasSynchronized) {
356 } else {
357 // We have just synchronized, this is the zeroth revolution
359 }
360
361 totalEventCountBase += triggerShape.getSize();
362
363#if EFI_UNIT_TEST
364 if (printTriggerDebug) {
365 printf("onShaftSynchronization index=%d %d\r\n",
368 }
369#endif /* EFI_UNIT_TEST */
370}
371
372static bool shouldConsiderEdge(const TriggerWaveform& triggerShape, TriggerWheel triggerWheel, TriggerValue edge) {
373 if (triggerWheel != TriggerWheel::T_PRIMARY && triggerShape.useOnlyPrimaryForSync) {
374 // Non-primary events ignored
375 return false;
376 }
377
378 switch (triggerShape.syncEdge) {
379 case SyncEdge::Both: return true;
381 case SyncEdge::Rise: return edge == TriggerValue::RISE;
382 case SyncEdge::Fall: return edge == TriggerValue::FALL;
383 }
384
385 // how did we get here?
386 // assert(false)?
387
388 return false;
389}
390
391void TriggerDecoderBase::printGaps(const char * prefix,
392 const TriggerConfiguration& triggerConfiguration,
393 const TriggerWaveform& triggerShape) {
394 for (int i = 0;i<triggerShape.gapTrackingLength;i++) {
395 float ratioFrom = triggerShape.synchronizationRatioFrom[i];
396 if (std::isnan(ratioFrom)) {
397 // we do not track gap at this depth
398 continue;
399 }
400
401 float gap = 1.0 * toothDurations[i] / toothDurations[i + 1];
402 if (std::isnan(gap)) {
403 efiPrintf("%s index=%d NaN gap, you have noise issues?", prefix, i);
404 } else {
405 float ratioTo = triggerShape.synchronizationRatioTo[i];
406
407 bool gapOk = isInRange(ratioFrom, gap, ratioTo);
408
409 efiPrintf("%s %srpm=%d time=%d eventIndex=%lu gapIndex=%d: %s gap=%.3f expected from %.3f to %.3f error=%s",
410 prefix,
411 triggerConfiguration.PrintPrefix,
413 /* cast is needed to make sure we do not put 64 bit value to stack*/ (int)getTimeNowS(),
415 i,
416 gapOk ? "Y" : "n",
417 gap,
418 ratioFrom,
419 ratioTo,
421 }
422 }
423}
424
425/**
426 * @brief Trigger decoding happens here
427 * VR falls are filtered out and some VR noise detection happens prior to invoking this method, for
428 * Hall this method is invoked every time we have a fall or rise on one of the trigger sensors.
429 * This method changes the state of trigger_state_s data structure according to the trigger event
430 * @param signal type of event which just happened
431 * @param nowNt current time
432 */
433expected<TriggerDecodeResult> TriggerDecoderBase::decodeTriggerEvent(
434 const char *msg,
435 const TriggerWaveform& triggerShape,
436 TriggerStateListener* triggerStateListener,
437 const TriggerConfiguration& triggerConfiguration,
438 const trigger_event_e signal,
439 const efitick_t nowNt) {
441
442#if EFI_PROD_CODE
444#endif
445
446 if (previousEventTimer.getElapsedSecondsAndReset(nowNt) > 1) {
447 /**
448 * We are here if there is a time gap between now and previous shaft event - that means the engine is not running.
449 * That means we have lost synchronization since the engine is not running :)
450 */
452 if (triggerStateListener) {
453 triggerStateListener->OnTriggerSynchronizationLost();
454 }
455 }
456
457 bool useOnlyRisingEdgeForTrigger = triggerShape.useOnlyRisingEdges;
458
459 efiAssert(ObdCode::CUSTOM_TRIGGER_UNEXPECTED, signal <= SHAFT_SECONDARY_RISING, "unexpected signal", unexpected);
460
461 TriggerWheel triggerWheel = eventIndex[signal];
462 TriggerValue type = eventType[signal];
463
464 // Check that we didn't get the same edge twice in a row - that should be impossible
465 if (!useOnlyRisingEdgeForTrigger && prevSignal == signal) {
467 }
468
469 prevSignal = signal;
470
471 currentCycle.eventCount[(int)triggerWheel]++;
472
473 if (toothed_previous_time > nowNt) {
474 firmwareError(ObdCode::CUSTOM_OBD_93, "[%s] toothed_previous_time after nowNt prev=%lu now=%lu", msg, (uint32_t)toothed_previous_time, (uint32_t)nowNt);
475 }
476
477 efidur_t currentDurationLong = isFirstEvent ? 0 : (nowNt - toothed_previous_time);
478
479 /**
480 * For performance reasons, we want to work with 32 bit values. If there has been more then
481 * 10 seconds since previous trigger event we do not really care.
482 */
483 toothDurations[0] =
484 currentDurationLong > 10 * NT_PER_SECOND ? 10 * NT_PER_SECOND : currentDurationLong;
485
486 if (!shouldConsiderEdge(triggerShape, triggerWheel, type)) {
487#if EFI_UNIT_TEST
488 if (printTriggerTrace) {
489 printf("%s isLessImportant %s now=%d index=%d\r\n",
490 getTrigger_type_e(triggerConfiguration.TriggerType.type),
491 getTrigger_event_e(signal),
492 (int)nowNt,
494 }
495#endif /* EFI_UNIT_TEST */
496
497 // For less important events we simply increment the index.
498 nextTriggerEvent();
499 } else {
500#if !EFI_PROD_CODE
501 if (printTriggerTrace) {
502 printf("%s event %s %lld\r\n",
503 getTrigger_type_e(triggerConfiguration.TriggerType.type),
504 getTrigger_event_e(signal),
505 nowNt);
506 printf("decodeTriggerEvent ratio %.2f: current=%d previous=%d\r\n", 1.0 * toothDurations[0] / toothDurations[1],
508 }
509#endif
510
511 isFirstEvent = false;
512 bool isSynchronizationPoint;
513 bool wasSynchronized = getShaftSynchronized();
514
515 if (triggerShape.isSynchronizationNeeded) {
517
518 if (wasSynchronized && triggerSyncGapRatio > NOISE_RATIO_THRESHOLD) {
520 }
521
522 isSynchronizationPoint = isSyncPoint(triggerShape, triggerConfiguration.TriggerType.type);
523 if (isSynchronizationPoint) {
525 }
526
527 /**
528 * todo: technically we can afford detailed logging even with 60/2 as long as low RPM
529 * todo: figure out exact threshold as a function of RPM and tooth count?
530 * Open question what is 'triggerShape.getSize()' for 60/2 is it 58 or 58*2 or 58*4?
531 */
532 bool silentTriggerError = triggerShape.getSize() > 40 && engineConfiguration->silentTriggerError;
533
534#if EFI_PROD_CODE || EFI_SIMULATOR
535 bool verbose = getTriggerCentral()->isEngineSnifferEnabled && triggerConfiguration.VerboseTriggerSynchDetails;
536
537 if (verbose || (someSortOfTriggerError() && !silentTriggerError)) {
538 const char * prefix = verbose ? "[vrb]" : "[err]";
539 printGaps(prefix, triggerConfiguration, triggerShape);
540 }
541#else
542 if (printTriggerTrace) {
543 for (int i = 0;i<triggerShape.gapTrackingLength;i++) {
544 float gap = 1.0 * toothDurations[i] / toothDurations[i + 1];
545 printf("%sindex=%d: gap=%.2f expected from %.2f to %.2f error=%s\r\n",
546 triggerConfiguration.PrintPrefix,
547 i,
548 gap,
549 triggerShape.synchronizationRatioFrom[i],
550 triggerShape.synchronizationRatioTo[i],
552 }
553 }
554#endif /* EFI_PROD_CODE */
555 } else {
556 /**
557 * We are here in case of a wheel without synchronization - we just need to count events,
558 * synchronization point simply happens once we have the right number of events
559 *
560 * in case of noise the counter could be above the expected number of events, that's why 'more or equals' and not just 'equals'
561 */
562
563 unsigned int endOfCycleIndex = triggerShape.getSize() - (useOnlyRisingEdgeForTrigger ? 2 : 1);
564
565 isSynchronizationPoint = !getShaftSynchronized() || (currentCycle.current_index >= endOfCycleIndex);
566
567#if EFI_UNIT_TEST
568 if (printTriggerTrace) {
569 printf("decodeTriggerEvent sync=%d isSynchronizationPoint=%d index=%d size=%d\r\n",
571 isSynchronizationPoint,
573 triggerShape.getSize());
574 }
575#endif /* EFI_UNIT_TEST */
576 }
577#if EFI_UNIT_TEST
578 if (printTriggerTrace) {
579 printf("decodeTriggerEvent gap %s isSynchronizationPoint=%d index=%d %s\r\n",
580 getTrigger_type_e(triggerConfiguration.TriggerType.type),
581 isSynchronizationPoint, currentCycle.current_index,
582 getTrigger_event_e(signal));
583 }
584#endif /* EFI_UNIT_TEST */
585
586 if (isSynchronizationPoint) {
588 bool isDecodingError = isTriggerCounterError(triggerCountersError);
589
590 if (triggerStateListener) {
591 triggerStateListener->OnTriggerSynchronization(wasSynchronized, isDecodingError);
592 }
593
594 // If we got a sync point, but the wrong number of events since the last sync point
595 // One of two things has happened:
596 // - We missed a tooth, and this is the real sync point
597 // - Due to some mistake in timing, we found what looks like a sync point but actually isn't
598 // In either case, we should wait for another sync point before doing anything to try and run an engine,
599 // so we clear the synchronized flag.
600 if (wasSynchronized && isDecodingError) {
603
604 // Something wrong, no longer synchronized
606
607 // This is a decoding error
609 printGaps("newerr", triggerConfiguration, triggerShape);
610 } else {
611 // If this was the first sync point OR no decode error, we're synchronized!
613 }
614
615 // this call would update duty cycle values
616 nextTriggerEvent();
617
618 onShaftSynchronization(wasSynchronized, nowNt, triggerShape);
619 } else { /* if (!isSynchronizationPoint) */
620 nextTriggerEvent();
621 }
622
623 for (int i = triggerShape.gapTrackingLength; i > 0; i--) {
624 toothDurations[i] = toothDurations[i - 1];
625 }
626
627 toothed_previous_time = nowNt;
628
629#if EFI_UNIT_TEST
630 if (wasSynchronized) {
631 int uiGapIndex = (currentCycle.current_index) % triggerShape.getLength();
632 gapRatio[uiGapIndex] = triggerSyncGapRatio;
633 }
634#endif // EFI_UNIT_TEST
635 }
636
637 if (getShaftSynchronized() && !isValidIndex(triggerShape)) {
638 // We've had too many events since the last sync point, we should have seen a sync point by now.
639 // This is a trigger error.
640
641 // let's not show a warning if we are just starting to spin
645 }
646
648
650
651 return unexpected;
652 } else if (!isValidIndex(triggerShape)) {
653#if EFI_UNIT_TEST
655#endif // EFI_UNIT_TEST
656 }
657
659
660 // Needed for early instant-RPM detection
661 TriggerStateListener * l = triggerStateListener;
662 while (l) {
664 l = l->nextListener();
665 }
666
667 if (getShaftSynchronized()) {
669 } else {
670 return unexpected;
671 }
672}
673
675 // Miata NB needs a special decoder.
676 // The problem is that the crank wheel only has 4 teeth, also symmetrical, so the pattern
677 // is long-short-long-short for one crank rotation.
678 // A quick acceleration can result in two successive "short gaps", so we see
679 // long-short-short-short-long instead of the correct long-short-long-short-long
680 // This logic expands the lower bound on a "long" tooth, then compares the last
681 // tooth to the current one.
682
683 // Instead of detecting short/long, this logic first checks for "maybe short" and "maybe long",
684 // then simply tests longer vs. shorter instead of absolute value.
686 auto secondGap = (float)toothDurations[1] / toothDurations[2];
687
688 bool currentGapOk = isInRange(triggerShape.synchronizationRatioFrom[0], (float)triggerSyncGapRatio, triggerShape.synchronizationRatioTo[0]);
689 bool secondGapOk = isInRange(triggerShape.synchronizationRatioFrom[1], secondGap, triggerShape.synchronizationRatioTo[1]);
690
691 // One or both teeth was impossible range, this is not the sync point
692 if (!currentGapOk || !secondGapOk) {
693 return false;
694 }
695
696 // If both teeth are in the range of possibility, return whether this gap is
697 // shorter than the last or not. If it is, this is the sync point.
698 return triggerSyncGapRatio < secondGap;
699 }
700
701 for (int i = 0; i < triggerShape.gapTrackingLength; i++) {
702 auto from = triggerShape.synchronizationRatioFrom[i];
703 auto to = triggerShape.synchronizationRatioTo[i];
704
705 if (std::isnan(from)) {
706 // don't check this gap, skip it
707 continue;
708 }
709
710 // This is transformed to avoid a division and use a cheaper multiply instead
711 // toothDurations[i] / toothDurations[i+1] > from
712 // is an equivalent comparison to
713 // toothDurations[i] > toothDurations[i+1] * from
714 bool isGapCondition =
715 (toothDurations[i] > toothDurations[i + 1] * from
716 && toothDurations[i] < toothDurations[i + 1] * to);
717
718 if (!isGapCondition) {
719 return false;
720 }
721 }
722
723 return true;
724}
725
726/**
727 * Trigger shape is defined in a way which is convenient for trigger shape definition
728 * On the other hand, trigger decoder indexing begins from synchronization event.
729 *
730 * This function finds the index of synchronization event within TriggerWaveform
731 */
733 TriggerWaveform& shape,
734 const TriggerConfiguration& triggerConfiguration) {
735#if EFI_PROD_CODE
736 efiAssert(ObdCode::CUSTOM_ERR_ASSERT, hasLotsOfRemainingStack(), "findPos", -1);
737#endif
738
739
740 resetState();
741
742 if (shape.shapeDefinitionError) {
743 return 0;
744 }
745
746 expected<uint32_t> syncIndex = TriggerStimulatorHelper::findTriggerSyncPoint(shape,
747 triggerConfiguration,
748 *this);
749 if (!syncIndex) {
750 return EFI_ERROR_CODE;
751 }
752
753 // Assert that we found the sync point on the very first revolution
754 efiAssert(ObdCode::CUSTOM_ERR_ASSERT, getSynchronizationCounter() == 0, "findZero_revCounter", EFI_ERROR_CODE);
755
756#if EFI_UNIT_TEST
757 if (printTriggerDebug) {
758 printf("findTriggerZeroEventIndex: syncIndex located %lu!\r\n", syncIndex.Value);
759 }
760#endif /* EFI_UNIT_TEST */
761
763 syncIndex.Value, *this, shape);
764
765 return syncIndex.Value % shape.getSize();
766}
767
768#endif /* EFI_SHAFT_POSITION_INPUT */
769
const char * getTrigger_type_e(trigger_type_e value)
TriggerCentral triggerCentral
Definition engine.h:326
RpmCalculator rpmCalculator
Definition engine.h:314
OutputPin debugTriggerSync
Definition efi_gpio.h:110
void toggle()
Definition efi_gpio.cpp:572
PrimaryTriggerDecoder(const char *name)
void onTooManyTeeth(int actual, int expected) override
void onTriggerError() override
void onNotEnoughTeeth(int actual, int expected) override
angle_t syncEnginePhase(int divider, int remainder, angle_t engineCycle)
static float getOrZero(SensorType type)
Definition sensor.h:87
InstantRpmCalculator instantRpm
const char *const PrintPrefix
trigger_config_s TriggerType
virtual void resetState()
void printGaps(const char *prefix, const TriggerConfiguration &triggerConfiguration, const TriggerWaveform &triggerShape)
void incrementShaftSynchronizationCounter()
TriggerDecoderBase(const char *name)
virtual void onTriggerError()
float gapRatio[PWM_PHASE_MAX_COUNT *6]
void onShaftSynchronization(bool wasSynchronized, const efitick_t nowNt, const TriggerWaveform &triggerShape)
const char *const name
uint32_t findTriggerZeroEventIndex(TriggerWaveform &shape, const TriggerConfiguration &triggerConfiguration)
virtual void onNotEnoughTeeth(int, int)
virtual void onTooManyTeeth(int, int)
efitick_t toothed_previous_time
int getSynchronizationCounter() const
int getEventCountersError(const TriggerWaveform &triggerShape) const
void setShaftSynchronized(bool value)
int64_t getTotalEventCounter() const
trigger_event_e prevSignal
expected< TriggerDecodeResult > decodeTriggerEvent(const char *msg, const TriggerWaveform &triggerShape, TriggerStateListener *triggerStateListener, const TriggerConfiguration &triggerConfiguration, const trigger_event_e signal, const efitick_t nowNt)
Trigger decoding happens here VR falls are filtered out and some VR noise detection happens prior to ...
bool isValidIndex(const TriggerWaveform &triggerShape) const
current_cycle_state_s currentCycle
bool someSortOfTriggerError() const
void setTriggerErrorState(int errorIncrement=1)
uint32_t toothDurations[GAP_TRACKING_LENGTH+1]
uint32_t totalTriggerErrorCounter
bool isSyncPoint(const TriggerWaveform &triggerShape, trigger_type_e triggerType) const
bool getShaftSynchronized() const
angle_t eventAngles[2 *PWM_PHASE_MAX_COUNT]
void prepareEventAngles(TriggerWaveform *shape)
static expected< uint32_t > findTriggerSyncPoint(TriggerWaveform &shape, const TriggerConfiguration &triggerConfiguration, TriggerDecoderBase &state)
static void assertSyncPosition(const TriggerConfiguration &triggerConfiguration, const uint32_t index, TriggerDecoderBase &state, TriggerWaveform &shape)
Trigger shape has all the fields needed to describe and decode trigger signal.
bool isRiseEvent[PWM_PHASE_MAX_COUNT]
void initializeSyncPoint(TriggerDecoderBase &state, const TriggerConfiguration &triggerConfiguration)
float synchronizationRatioFrom[GAP_TRACKING_LENGTH]
size_t getLength() const
float synchronizationRatioTo[GAP_TRACKING_LENGTH]
angle_t getAngle(int phaseIndex) const
TriggerWheel getWheel(size_t index)
size_t getExpectedEventCount(TriggerWheel channelIndex) const
size_t getSize() const
void onTooManyTeeth(int actual, int expected) override
void onNotEnoughTeeth(int actual, int expected) override
EnginePins enginePins
Definition efi_gpio.cpp:24
const char * boolToString(bool value)
Definition efilib.cpp:19
bool isInRange(T min, T val, T max)
Definition efilib.h:82
efitick_t getTimeNowNt()
Definition efitime.cpp:19
efitimesec_t getTimeNowS()
Current system time in seconds (32 bits)
Definition efitime.cpp:42
TriggerCentral * getTriggerCentral()
Definition engine.cpp:592
static EngineAccessor engine
Definition engine.h:415
Main engine configuration data structure.
static constexpr engine_configuration_s * engineConfiguration
trigger_type_e
bool warning(ObdCode code, const char *fmt,...)
void firmwareError(ObdCode code, const char *fmt,...)
@ CUSTOM_TRIGGER_SYNC_ANGLE_RANGE
@ CUSTOM_TRIGGER_SYNC_ANGLE2
@ CUSTOM_CAM_TOO_MANY_TEETH
@ CUSTOM_TRIGGER_CYCLE
@ OBD_PCM_Processor_Fault
@ CUSTOM_TRIGGER_SYNC_ANGLE
@ CUSTOM_OBD_93
@ CUSTOM_PRIMARY_TOO_MANY_TEETH
@ CUSTOM_CAM_NOT_ENOUGH_TEETH
@ CUSTOM_ERR_ASSERT
@ CUSTOM_PRIMARY_NOT_ENOUGH_TEETH
@ CUSTOM_TRIGGER_UNEXPECTED
@ DecodeTriggerEvent
TriggerWheel
efitick_t efidur_t
state("state", SensorCategory.SENSOR_INPUTS, FieldType.INT8, 1890, 1.0, -1.0, -1.0, "")
triggerCountersError("triggerCountersError", SensorCategory.SENSOR_INPUTS, FieldType.INT8, 1605, 1.0, -1.0, -1.0, "")
trigger_event_e
@ SHAFT_SECONDARY_RISING
@ SHAFT_SECONDARY_FALLING
@ SHAFT_PRIMARY_FALLING
@ SHAFT_PRIMARY_RISING
TriggerValue
virtual void OnTriggerStateProperState(efitick_t nowNt, size_t triggerStateIndex)=0
virtual void OnTriggerSynchronization(bool wasSynchronized, bool isDecodingError)=0
virtual void OnTriggerSynchronizationLost()=0
virtual TriggerStateListener * nextListener()=0
size_t eventCount[PWM_PHASE_MAX_WAVE_PER_PWM]
void setArrayValues(TValue(&array)[TSize], float value)
void LogTriggerSync(bool isSync, efitick_t timestamp)
void onTransitionEvent(TransitionEvent event)
const char * getTrigger_value_e(TriggerValue value)
static bool shouldConsiderEdge(const TriggerWaveform &triggerShape, TriggerWheel triggerWheel, TriggerValue edge)
PUBLIC_API_WEAK bool isTriggerCounterError(int8_t triggerCountersError)
static TriggerValue eventType[4]
int tooManyTeethCounter
bool printTriggerTrace
bool printTriggerDebug
const char * getTrigger_event_e(trigger_event_e value)
static TriggerWheel eventIndex[4]
const char * getTrigger_event_e(trigger_event_e value)
triggerType
bool printTriggerDebug
void wrapAngle(angle_t &angle, const char *msg, ObdCode code)
printf("\n")