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 for (size_t eventIndex = 0; eventIndex < length; eventIndex++) {
136 if (eventIndex == 0) {
137 // explicit check for zero to avoid issues where logical zero is not exactly zero due to float nature
138 eventAngles[0] = 0;
139 // this value would be used in case of front-only
140 eventAngles[1] = 0;
141 } else {
142 // Rotate the trigger around so that the sync point is at position 0
143 auto wrappedIndex = (triggerShapeSynchPointIndex + eventIndex) % length;
144
145 // Compute this tooth's position within the trigger definition
146 // (wrap, as the trigger def may be smaller than total trigger length)
147 auto triggerDefinitionIndex = wrappedIndex % triggerShapeLength;
148
149 // Compute the relative angle of this tooth to the sync point's tooth
150 float angle = shape->getAngle(wrappedIndex) - firstAngle;
151
152 efiAssertVoid(ObdCode::CUSTOM_TRIGGER_CYCLE, !std::isnan(angle), "trgSyncNaN");
153 // Wrap the angle back in to [0, 720)
155
156 if (shape->useOnlyRisingEdges) {
157 criticalAssertVoid(triggerDefinitionIndex < triggerShapeLength, "trigger shape fail");
158 assertIsInBounds(triggerDefinitionIndex, shape->isRiseEvent, "isRise");
159
160 // In case this is a rising event, replace the following fall event with the rising as well
161 if (shape->isRiseEvent[triggerDefinitionIndex]) {
162 eventAngles[eventIndex] = angle;
163 eventAngles[eventIndex + 1] = angle;
164 }
165 } else {
166 eventAngles[eventIndex] = angle;
167 }
168 }
169 }
170}
171
175
179
185
186
187bool TriggerDecoderBase::isValidIndex(const TriggerWaveform& triggerShape) const {
188 return currentCycle.current_index < triggerShape.getSize();
189}
190
193
194#if EFI_UNIT_TEST
195#define PRINT_INC_INDEX if (printTriggerTrace) {\
196 printf("nextTriggerEvent index=%d\r\n", currentCycle.current_index); \
197 }
198#else
199#define PRINT_INC_INDEX {}
200#endif /* EFI_UNIT_TEST */
201
202#define nextTriggerEvent() \
203 { \
204 if (useOnlyRisingEdgeForTrigger) {currentCycle.current_index++;} \
205 currentCycle.current_index++; \
206 PRINT_INC_INDEX; \
207}
208
212
213angle_t PrimaryTriggerDecoder::syncEnginePhase(int divider, int remainder, angle_t engineCycle) {
214 efiAssert(ObdCode::OBD_PCM_Processor_Fault, divider > 1, "syncEnginePhase divider", false);
215 efiAssert(ObdCode::OBD_PCM_Processor_Fault, remainder < divider, "syncEnginePhase remainder", false);
216 angle_t totalShift = 0;
217 while (getSynchronizationCounter() % divider != remainder) {
218 /**
219 * we are here if we've detected the cam sensor within the wrong crank phase
220 * let's increase the trigger event counter, that would adjust the state of
221 * virtual crank-based trigger
222 */
224 totalShift += engineCycle / divider;
225 }
226
227 // Allow injection/ignition to happen, we've now fully sync'd the crank based on new cam information
229
230 if (totalShift > 0) {
233 }
234
235 return totalShift;
236}
237
241
243 // On trigger error, we've lost full sync
245
246 // Ignore the warning that engine is never null - it might be in unit tests
247 #pragma GCC diagnostic push
248 #pragma GCC diagnostic ignored "-Waddress"
249 if (engine) {
250 // Instant RPM data is now also probably trash, discard it
253 }
254 #pragma GCC diagnostic pop
255}
256
257void PrimaryTriggerDecoder::onNotEnoughTeeth(int /*actual*/, int /*expected*/) {
258 warning(ObdCode::CUSTOM_PRIMARY_NOT_ENOUGH_TEETH, "primary trigger error: not enough teeth between sync points: expected %d/%d got %d/%d",
259 getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_PRIMARY),
260 getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_SECONDARY),
263}
264
265void PrimaryTriggerDecoder::onTooManyTeeth(int /*actual*/, int /*expected*/) {
266 warning(ObdCode::CUSTOM_PRIMARY_TOO_MANY_TEETH, "primary trigger error: too many teeth between sync points: expected %d/%d got %d/%d",
267 getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_PRIMARY),
268 getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_SECONDARY),
271}
272
274switch(value) {
276 return "SHAFT_PRIMARY_FALLING";
278 return "SHAFT_PRIMARY_RISING";
280 return "SHAFT_SECONDARY_FALLING";
282 return "SHAFT_SECONDARY_RISING";
283 }
284 return NULL;
285}
287switch(value) {
289 return "TriggerValue::FALL";
291 return "TriggerValue::RISE";
292 }
293 return NULL;
294}
295
296void VvtTriggerDecoder::onNotEnoughTeeth(int actual, int expected) {
297 warning(ObdCode::CUSTOM_CAM_NOT_ENOUGH_TEETH, "cam %s trigger error: not enough teeth between sync points: actual %d expected %d", name, actual, expected);
298}
299
300void VvtTriggerDecoder::onTooManyTeeth(int actual, int expected) {
301 warning(ObdCode::CUSTOM_CAM_TOO_MANY_TEETH, "cam %s trigger error: too many teeth between sync points: %d > %d", name, actual, expected);
302}
303
304PUBLIC_API_WEAK bool isTriggerCounterError(int8_t triggerCountersError) {
305 return triggerCountersError != 0;
306}
307
309 // We can check if things are fine by comparing the number of events in a cycle with the expected number of event.
310 int countersError = 0;
311 for (int i = 0;i < PWM_PHASE_MAX_WAVE_PER_PWM;i++) {
312 countersError = currentCycle.eventCount[i] - triggerShape.getExpectedEventCount((TriggerWheel)i);
313 if (countersError != 0) {
314 break;
315 }
316 }
317
318#if EFI_DETAILED_LOGGING
319 printf("getEventCountersError: isDecodingError=%d\n", (countersError != 0));
320 if (countersError != 0) {
321 for (int i = 0;i < PWM_PHASE_MAX_WAVE_PER_PWM;i++) {
322 printf(" count: cur=%d exp=%d\n", currentCycle.eventCount[i], triggerShape.getExpectedEventCount((TriggerWheel)i));
323 }
324 }
325#endif /* EFI_UNIT_TEST */
326
327 return countersError;
328}
329
331 bool wasSynchronized,
332 const efitick_t nowNt,
333 const TriggerWaveform& triggerShape) {
334 startOfCycleNt = nowNt;
336
337 if (wasSynchronized) {
339 } else {
340 // We have just synchronized, this is the zeroth revolution
342 }
343
344 totalEventCountBase += triggerShape.getSize();
345
346#if EFI_UNIT_TEST
347 if (printTriggerDebug) {
348 printf("onShaftSynchronization index=%d %d\r\n",
351 }
352#endif /* EFI_UNIT_TEST */
353}
354
355static bool shouldConsiderEdge(const TriggerWaveform& triggerShape, TriggerWheel triggerWheel, TriggerValue edge) {
356 if (triggerWheel != TriggerWheel::T_PRIMARY && triggerShape.useOnlyPrimaryForSync) {
357 // Non-primary events ignored
358 return false;
359 }
360
361 switch (triggerShape.syncEdge) {
362 case SyncEdge::Both: return true;
364 case SyncEdge::Rise: return edge == TriggerValue::RISE;
365 case SyncEdge::Fall: return edge == TriggerValue::FALL;
366 }
367
368 // how did we get here?
369 // assert(false)?
370
371 return false;
372}
373
374void TriggerDecoderBase::printGaps(const char * prefix,
375 const TriggerConfiguration& triggerConfiguration,
376 const TriggerWaveform& triggerShape) {
377 for (int i = 0;i<triggerShape.gapTrackingLength;i++) {
378 float ratioFrom = triggerShape.synchronizationRatioFrom[i];
379 if (std::isnan(ratioFrom)) {
380 // we do not track gap at this depth
381 continue;
382 }
383
384 float gap = 1.0 * toothDurations[i] / toothDurations[i + 1];
385 if (std::isnan(gap)) {
386 efiPrintf("%s index=%d NaN gap, you have noise issues?", prefix, i);
387 } else {
388 float ratioTo = triggerShape.synchronizationRatioTo[i];
389
390 bool gapOk = isInRange(ratioFrom, gap, ratioTo);
391
392 efiPrintf("%s %srpm=%d time=%d eventIndex=%lu gapIndex=%d: %s gap=%.3f expected from %.3f to %.3f error=%s",
393 prefix,
394 triggerConfiguration.PrintPrefix,
396 /* cast is needed to make sure we do not put 64 bit value to stack*/ (int)getTimeNowS(),
398 i,
399 gapOk ? "Y" : "n",
400 gap,
401 ratioFrom,
402 ratioTo,
404 }
405 }
406}
407
408/**
409 * @brief Trigger decoding happens here
410 * VR falls are filtered out and some VR noise detection happens prior to invoking this method, for
411 * Hall this method is invoked every time we have a fall or rise on one of the trigger sensors.
412 * This method changes the state of trigger_state_s data structure according to the trigger event
413 * @param signal type of event which just happened
414 * @param nowNt current time
415 */
416expected<TriggerDecodeResult> TriggerDecoderBase::decodeTriggerEvent(
417 const char *msg,
418 const TriggerWaveform& triggerShape,
419 TriggerStateListener* triggerStateListener,
420 const TriggerConfiguration& triggerConfiguration,
421 const trigger_event_e signal,
422 const efitick_t nowNt) {
424
425#if EFI_PROD_CODE
427#endif
428
429 if (previousEventTimer.getElapsedSecondsAndReset(nowNt) > 1) {
430 /**
431 * We are here if there is a time gap between now and previous shaft event - that means the engine is not running.
432 * That means we have lost synchronization since the engine is not running :)
433 */
435 if (triggerStateListener) {
436 triggerStateListener->OnTriggerSynchronizationLost();
437 }
438 }
439
440 bool useOnlyRisingEdgeForTrigger = triggerShape.useOnlyRisingEdges;
441
442 efiAssert(ObdCode::CUSTOM_TRIGGER_UNEXPECTED, signal <= SHAFT_SECONDARY_RISING, "unexpected signal", unexpected);
443
444 TriggerWheel triggerWheel = eventIndex[signal];
445 TriggerValue type = eventType[signal];
446
447 // Check that we didn't get the same edge twice in a row - that should be impossible
448 if (!useOnlyRisingEdgeForTrigger && prevSignal == signal) {
450 }
451
452 prevSignal = signal;
453
454 currentCycle.eventCount[(int)triggerWheel]++;
455
456 if (toothed_previous_time > nowNt) {
457 firmwareError(ObdCode::CUSTOM_OBD_93, "[%s] toothed_previous_time after nowNt prev=%lu now=%lu", msg, (uint32_t)toothed_previous_time, (uint32_t)nowNt);
458 }
459
460 efidur_t currentDurationLong = isFirstEvent ? 0 : (nowNt - toothed_previous_time);
461
462 /**
463 * For performance reasons, we want to work with 32 bit values. If there has been more then
464 * 10 seconds since previous trigger event we do not really care.
465 */
466 toothDurations[0] =
467 currentDurationLong > 10 * NT_PER_SECOND ? 10 * NT_PER_SECOND : currentDurationLong;
468
469 if (!shouldConsiderEdge(triggerShape, triggerWheel, type)) {
470#if EFI_UNIT_TEST
471 if (printTriggerTrace) {
472 printf("%s isLessImportant %s now=%d index=%d\r\n",
473 getTrigger_type_e(triggerConfiguration.TriggerType.type),
474 getTrigger_event_e(signal),
475 (int)nowNt,
477 }
478#endif /* EFI_UNIT_TEST */
479
480 // For less important events we simply increment the index.
481 nextTriggerEvent();
482 } else {
483#if !EFI_PROD_CODE
484 if (printTriggerTrace) {
485 printf("%s event %s %lld\r\n",
486 getTrigger_type_e(triggerConfiguration.TriggerType.type),
487 getTrigger_event_e(signal),
488 nowNt);
489 printf("decodeTriggerEvent ratio %.2f: current=%d previous=%d\r\n", 1.0 * toothDurations[0] / toothDurations[1],
491 }
492#endif
493
494 isFirstEvent = false;
495 bool isSynchronizationPoint;
496 bool wasSynchronized = getShaftSynchronized();
497
498 if (triggerShape.isSynchronizationNeeded) {
500
501 if (wasSynchronized && triggerSyncGapRatio > NOISE_RATIO_THRESHOLD) {
503 }
504
505 isSynchronizationPoint = isSyncPoint(triggerShape, triggerConfiguration.TriggerType.type);
506 if (isSynchronizationPoint) {
508 }
509
510 /**
511 * todo: technically we can afford detailed logging even with 60/2 as long as low RPM
512 * todo: figure out exact threshold as a function of RPM and tooth count?
513 * Open question what is 'triggerShape.getSize()' for 60/2 is it 58 or 58*2 or 58*4?
514 */
515 bool silentTriggerError = triggerShape.getSize() > 40 && engineConfiguration->silentTriggerError;
516
517#if EFI_PROD_CODE || EFI_SIMULATOR
518 bool verbose = getTriggerCentral()->isEngineSnifferEnabled && triggerConfiguration.VerboseTriggerSynchDetails;
519
520 if (verbose || (someSortOfTriggerError() && !silentTriggerError)) {
521 const char * prefix = verbose ? "[vrb]" : "[err]";
522 printGaps(prefix, triggerConfiguration, triggerShape);
523 }
524#else
525 if (printTriggerTrace) {
526 for (int i = 0;i<triggerShape.gapTrackingLength;i++) {
527 float gap = 1.0 * toothDurations[i] / toothDurations[i + 1];
528 printf("%sindex=%d: gap=%.2f expected from %.2f to %.2f error=%s\r\n",
529 triggerConfiguration.PrintPrefix,
530 i,
531 gap,
532 triggerShape.synchronizationRatioFrom[i],
533 triggerShape.synchronizationRatioTo[i],
535 }
536 }
537#endif /* EFI_PROD_CODE */
538 } else {
539 /**
540 * We are here in case of a wheel without synchronization - we just need to count events,
541 * synchronization point simply happens once we have the right number of events
542 *
543 * in case of noise the counter could be above the expected number of events, that's why 'more or equals' and not just 'equals'
544 */
545
546 unsigned int endOfCycleIndex = triggerShape.getSize() - (useOnlyRisingEdgeForTrigger ? 2 : 1);
547
548 isSynchronizationPoint = !getShaftSynchronized() || (currentCycle.current_index >= endOfCycleIndex);
549
550#if EFI_UNIT_TEST
551 if (printTriggerTrace) {
552 printf("decodeTriggerEvent sync=%d isSynchronizationPoint=%d index=%d size=%d\r\n",
554 isSynchronizationPoint,
556 triggerShape.getSize());
557 }
558#endif /* EFI_UNIT_TEST */
559 }
560#if EFI_UNIT_TEST
561 if (printTriggerTrace) {
562 printf("decodeTriggerEvent gap %s isSynchronizationPoint=%d index=%d %s\r\n",
563 getTrigger_type_e(triggerConfiguration.TriggerType.type),
564 isSynchronizationPoint, currentCycle.current_index,
565 getTrigger_event_e(signal));
566 }
567#endif /* EFI_UNIT_TEST */
568
569 if (isSynchronizationPoint) {
571 bool isDecodingError = isTriggerCounterError(triggerCountersError);
572
573 if (triggerStateListener) {
574 triggerStateListener->OnTriggerSynchronization(wasSynchronized, isDecodingError);
575 }
576
577 // If we got a sync point, but the wrong number of events since the last sync point
578 // One of two things has happened:
579 // - We missed a tooth, and this is the real sync point
580 // - Due to some mistake in timing, we found what looks like a sync point but actually isn't
581 // In either case, we should wait for another sync point before doing anything to try and run an engine,
582 // so we clear the synchronized flag.
583 if (wasSynchronized && isDecodingError) {
586
587 // Something wrong, no longer synchronized
589
590 // This is a decoding error
592 printGaps("newerr", triggerConfiguration, triggerShape);
593 } else {
594 // If this was the first sync point OR no decode error, we're synchronized!
596 }
597
598 // this call would update duty cycle values
599 nextTriggerEvent();
600
601 onShaftSynchronization(wasSynchronized, nowNt, triggerShape);
602 } else { /* if (!isSynchronizationPoint) */
603 nextTriggerEvent();
604 }
605
606 for (int i = triggerShape.gapTrackingLength; i > 0; i--) {
607 toothDurations[i] = toothDurations[i - 1];
608 }
609
610 toothed_previous_time = nowNt;
611
612#if EFI_UNIT_TEST
613 if (wasSynchronized) {
614 int uiGapIndex = (currentCycle.current_index) % triggerShape.getLength();
615 gapRatio[uiGapIndex] = triggerSyncGapRatio;
616 }
617#endif // EFI_UNIT_TEST
618 }
619
620 if (getShaftSynchronized() && !isValidIndex(triggerShape)) {
621 // We've had too many events since the last sync point, we should have seen a sync point by now.
622 // This is a trigger error.
623
624 // let's not show a warning if we are just starting to spin
628 }
629
631
633
634 return unexpected;
635 }
636
638
639 // Needed for early instant-RPM detection
640 TriggerStateListener * l = triggerStateListener;
641 while (l) {
643 l = l->nextListener();
644 }
645
646 if (getShaftSynchronized()) {
648 } else {
649 return unexpected;
650 }
651}
652
654 // Miata NB needs a special decoder.
655 // The problem is that the crank wheel only has 4 teeth, also symmetrical, so the pattern
656 // is long-short-long-short for one crank rotation.
657 // A quick acceleration can result in two successive "short gaps", so we see
658 // long-short-short-short-long instead of the correct long-short-long-short-long
659 // This logic expands the lower bound on a "long" tooth, then compares the last
660 // tooth to the current one.
661
662 // Instead of detecting short/long, this logic first checks for "maybe short" and "maybe long",
663 // then simply tests longer vs. shorter instead of absolute value.
665 auto secondGap = (float)toothDurations[1] / toothDurations[2];
666
667 bool currentGapOk = isInRange(triggerShape.synchronizationRatioFrom[0], (float)triggerSyncGapRatio, triggerShape.synchronizationRatioTo[0]);
668 bool secondGapOk = isInRange(triggerShape.synchronizationRatioFrom[1], secondGap, triggerShape.synchronizationRatioTo[1]);
669
670 // One or both teeth was impossible range, this is not the sync point
671 if (!currentGapOk || !secondGapOk) {
672 return false;
673 }
674
675 // If both teeth are in the range of possibility, return whether this gap is
676 // shorter than the last or not. If it is, this is the sync point.
677 return triggerSyncGapRatio < secondGap;
678 }
679
680 for (int i = 0; i < triggerShape.gapTrackingLength; i++) {
681 auto from = triggerShape.synchronizationRatioFrom[i];
682 auto to = triggerShape.synchronizationRatioTo[i];
683
684 if (std::isnan(from)) {
685 // don't check this gap, skip it
686 continue;
687 }
688
689 // This is transformed to avoid a division and use a cheaper multiply instead
690 // toothDurations[i] / toothDurations[i+1] > from
691 // is an equivalent comparison to
692 // toothDurations[i] > toothDurations[i+1] * from
693 bool isGapCondition =
694 (toothDurations[i] > toothDurations[i + 1] * from
695 && toothDurations[i] < toothDurations[i + 1] * to);
696
697 if (!isGapCondition) {
698 return false;
699 }
700 }
701
702 return true;
703}
704
705/**
706 * Trigger shape is defined in a way which is convenient for trigger shape definition
707 * On the other hand, trigger decoder indexing begins from synchronization event.
708 *
709 * This function finds the index of synchronization event within TriggerWaveform
710 */
712 TriggerWaveform& shape,
713 const TriggerConfiguration& triggerConfiguration) {
714#if EFI_PROD_CODE
715 efiAssert(ObdCode::CUSTOM_ERR_ASSERT, hasLotsOfRemainingStack(), "findPos", -1);
716#endif
717
718
719 resetState();
720
721 if (shape.shapeDefinitionError) {
722 return 0;
723 }
724
725 expected<uint32_t> syncIndex = TriggerStimulatorHelper::findTriggerSyncPoint(shape,
726 triggerConfiguration,
727 *this);
728 if (!syncIndex) {
729 return EFI_ERROR_CODE;
730 }
731
732 // Assert that we found the sync point on the very first revolution
733 efiAssert(ObdCode::CUSTOM_ERR_ASSERT, getSynchronizationCounter() == 0, "findZero_revCounter", EFI_ERROR_CODE);
734
735#if EFI_UNIT_TEST
736 if (printTriggerDebug) {
737 printf("findTriggerZeroEventIndex: syncIndex located %lu!\r\n", syncIndex.Value);
738 }
739#endif /* EFI_UNIT_TEST */
740
742 syncIndex.Value, *this, shape);
743
744 return syncIndex.Value % shape.getSize();
745}
746
747#endif /* EFI_SHAFT_POSITION_INPUT */
748
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:571
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
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:593
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
triggerCountersError("triggerCountersError", SensorCategory.SENSOR_INPUTS, FieldType.INT8, 1601, 1.0, -1.0, -1.0, "")
state("state", SensorCategory.SENSOR_INPUTS, FieldType.INT8, 1886, 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]
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")