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 int riseOnlyIndex = 0;
126
127 size_t length = shape->getLength();
128
130
131 // this may be <length for some triggers like symmetrical crank Miata NB
132 size_t triggerShapeLength = shape->getSize();
133
134 assertAngleRange(triggerShapeSynchPointIndex, "triggerShapeSynchPointIndex", ObdCode::CUSTOM_TRIGGER_SYNC_ANGLE2);
135 efiAssertVoid(ObdCode::CUSTOM_TRIGGER_CYCLE, getTriggerCentral()->engineCycleEventCount != 0, "zero engineCycleEventCount");
136
137 for (size_t eventIndex = 0; eventIndex < length; eventIndex++) {
138 if (eventIndex == 0) {
139 // explicit check for zero to avoid issues where logical zero is not exactly zero due to float nature
140 eventAngles[0] = 0;
141 // this value would be used in case of front-only
142 eventAngles[1] = 0;
143 } else {
144 // Rotate the trigger around so that the sync point is at position 0
145 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 auto triggerDefinitionIndex = wrappedIndex % triggerShapeLength;
150
151 // Compute the relative angle of this tooth to the sync point's tooth
152 float angle = shape->getAngle(wrappedIndex) - firstAngle;
153
154 efiAssertVoid(ObdCode::CUSTOM_TRIGGER_CYCLE, !std::isnan(angle), "trgSyncNaN");
155 // Wrap the angle back in to [0, 720)
157
158 if (shape->useOnlyRisingEdges) {
159 criticalAssertVoid(triggerDefinitionIndex < triggerShapeLength, "trigger shape fail");
160 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 if (shape->isRiseEvent[triggerDefinitionIndex]) {
164 riseOnlyIndex += 2;
165 eventAngles[riseOnlyIndex] = angle;
166 eventAngles[riseOnlyIndex + 1] = angle;
167 }
168 } else {
169 eventAngles[eventIndex] = angle;
170 }
171 }
172 }
173}
174
178
182
188
189
190bool TriggerDecoderBase::isValidIndex(const TriggerWaveform& triggerShape) const {
191 return currentCycle.current_index < triggerShape.getSize();
192}
193
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
215
216angle_t PrimaryTriggerDecoder::syncEnginePhase(int divider, int remainder, angle_t engineCycle) {
217 efiAssert(ObdCode::OBD_PCM_Processor_Fault, divider > 1, "syncEnginePhase divider", false);
218 efiAssert(ObdCode::OBD_PCM_Processor_Fault, remainder < divider, "syncEnginePhase remainder", false);
219 angle_t totalShift = 0;
220 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 */
227 totalShift += engineCycle / divider;
228 }
229
230 // Allow injection/ignition to happen, we've now fully sync'd the crank based on new cam information
232
233 if (totalShift > 0) {
236 }
237
238 return totalShift;
239}
240
244
246 // On trigger error, we've lost full sync
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 if (engine) {
253 // Instant RPM data is now also probably trash, discard it
256 }
257 #pragma GCC diagnostic pop
258}
259
260void PrimaryTriggerDecoder::onNotEnoughTeeth(int /*actual*/, int /*expected*/) {
261 warning(ObdCode::CUSTOM_PRIMARY_NOT_ENOUGH_TEETH, "primary trigger error: not enough teeth between sync points: expected %d/%d got %d/%d",
262 getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_PRIMARY),
263 getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_SECONDARY),
266}
267
268void PrimaryTriggerDecoder::onTooManyTeeth(int /*actual*/, int /*expected*/) {
269 warning(ObdCode::CUSTOM_PRIMARY_TOO_MANY_TEETH, "primary trigger error: too many teeth between sync points: expected %d/%d got %d/%d",
270 getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_PRIMARY),
271 getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_SECONDARY),
274}
275
277switch(value) {
279 return "SHAFT_PRIMARY_FALLING";
281 return "SHAFT_PRIMARY_RISING";
283 return "SHAFT_SECONDARY_FALLING";
285 return "SHAFT_SECONDARY_RISING";
286 }
287 return NULL;
288}
290switch(value) {
292 return "TriggerValue::FALL";
294 return "TriggerValue::RISE";
295 }
296 return NULL;
297}
298
299void VvtTriggerDecoder::onNotEnoughTeeth(int actual, int expected) {
300 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}
302
303void VvtTriggerDecoder::onTooManyTeeth(int actual, int expected) {
304 warning(ObdCode::CUSTOM_CAM_TOO_MANY_TEETH, "cam %s trigger error: too many teeth between sync points: %d > %d", name, actual, expected);
305}
306
307PUBLIC_API_WEAK bool isTriggerCounterError(int8_t triggerCountersError) {
308 return triggerCountersError != 0;
309}
310
312 // We can check if things are fine by comparing the number of events in a cycle with the expected number of event.
313 int countersError = 0;
314 for (int i = 0;i < PWM_PHASE_MAX_WAVE_PER_PWM;i++) {
315 countersError = currentCycle.eventCount[i] - triggerShape.getExpectedEventCount((TriggerWheel)i);
316 if (countersError != 0) {
317 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 return countersError;
331}
332
334 bool wasSynchronized,
335 const efitick_t nowNt,
336 const TriggerWaveform& triggerShape) {
337 startOfCycleNt = nowNt;
339
340 if (wasSynchronized) {
342 } else {
343 // We have just synchronized, this is the zeroth revolution
345 }
346
347 totalEventCountBase += triggerShape.getSize();
348
349#if EFI_UNIT_TEST
350 if (printTriggerDebug) {
351 printf("onShaftSynchronization index=%d %d\r\n",
354 }
355#endif /* EFI_UNIT_TEST */
356}
357
358static bool shouldConsiderEdge(const TriggerWaveform& triggerShape, TriggerWheel triggerWheel, TriggerValue edge) {
359 if (triggerWheel != TriggerWheel::T_PRIMARY && triggerShape.useOnlyPrimaryForSync) {
360 // Non-primary events ignored
361 return false;
362 }
363
364 switch (triggerShape.syncEdge) {
365 case SyncEdge::Both: return true;
367 case SyncEdge::Rise: return edge == TriggerValue::RISE;
368 case SyncEdge::Fall: return edge == TriggerValue::FALL;
369 }
370
371 // how did we get here?
372 // assert(false)?
373
374 return false;
375}
376
377void TriggerDecoderBase::printGaps(const char * prefix,
378 const TriggerConfiguration& triggerConfiguration,
379 const TriggerWaveform& triggerShape) {
380 for (int i = 0;i<triggerShape.gapTrackingLength;i++) {
381 float ratioFrom = triggerShape.synchronizationRatioFrom[i];
382 if (std::isnan(ratioFrom)) {
383 // we do not track gap at this depth
384 continue;
385 }
386
387 float gap = 1.0 * toothDurations[i] / toothDurations[i + 1];
388 if (std::isnan(gap)) {
389 efiPrintf("%s index=%d NaN gap, you have noise issues?", prefix, i);
390 } else {
391 float ratioTo = triggerShape.synchronizationRatioTo[i];
392
393 bool gapOk = isInRange(ratioFrom, gap, ratioTo);
394
395 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,
399 /* cast is needed to make sure we do not put 64 bit value to stack*/ (int)getTimeNowS(),
401 i,
402 gapOk ? "Y" : "n",
403 gap,
404 ratioFrom,
405 ratioTo,
407 }
408 }
409}
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 */
419expected<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) {
427
428#if EFI_PROD_CODE
430#endif
431
432 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 */
438 if (triggerStateListener) {
439 triggerStateListener->OnTriggerSynchronizationLost();
440 }
441 }
442
443 bool useOnlyRisingEdgeForTrigger = triggerShape.useOnlyRisingEdges;
444
445 efiAssert(ObdCode::CUSTOM_TRIGGER_UNEXPECTED, signal <= SHAFT_SECONDARY_RISING, "unexpected signal", unexpected);
446
447 TriggerWheel triggerWheel = eventIndex[signal];
448 TriggerValue type = eventType[signal];
449
450 // Check that we didn't get the same edge twice in a row - that should be impossible
451 if (!useOnlyRisingEdgeForTrigger && prevSignal == signal) {
453 }
454
455 prevSignal = signal;
456
457 currentCycle.eventCount[(int)triggerWheel]++;
458
459 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 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 toothDurations[0] =
470 currentDurationLong > 10 * NT_PER_SECOND ? 10 * NT_PER_SECOND : currentDurationLong;
471
472 if (!shouldConsiderEdge(triggerShape, triggerWheel, type)) {
473#if EFI_UNIT_TEST
474 if (printTriggerTrace) {
475 printf("%s isLessImportant %s now=%d index=%d\r\n",
476 getTrigger_type_e(triggerConfiguration.TriggerType.type),
477 getTrigger_event_e(signal),
478 (int)nowNt,
480 }
481#endif /* EFI_UNIT_TEST */
482
483 // For less important events we simply increment the index.
484 nextTriggerEvent();
485 } else {
486#if !EFI_PROD_CODE
487 if (printTriggerTrace) {
488 printf("%s event %s %lld\r\n",
489 getTrigger_type_e(triggerConfiguration.TriggerType.type),
490 getTrigger_event_e(signal),
491 nowNt);
492 printf("decodeTriggerEvent ratio %.2f: current=%d previous=%d\r\n", 1.0 * toothDurations[0] / toothDurations[1],
494 }
495#endif
496
497 isFirstEvent = false;
498 bool isSynchronizationPoint;
499 bool wasSynchronized = getShaftSynchronized();
500
501 if (triggerShape.isSynchronizationNeeded) {
503
504 if (wasSynchronized && triggerSyncGapRatio > NOISE_RATIO_THRESHOLD) {
506 }
507
508 isSynchronizationPoint = isSyncPoint(triggerShape, triggerConfiguration.TriggerType.type);
509 if (isSynchronizationPoint) {
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 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 if (printTriggerTrace) {
529 for (int i = 0;i<triggerShape.gapTrackingLength;i++) {
530 float gap = 1.0 * toothDurations[i] / toothDurations[i + 1];
531 printf("%sindex=%d: gap=%.2f expected from %.2f to %.2f error=%s\r\n",
532 triggerConfiguration.PrintPrefix,
533 i,
534 gap,
535 triggerShape.synchronizationRatioFrom[i],
536 triggerShape.synchronizationRatioTo[i],
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 unsigned int endOfCycleIndex = triggerShape.getSize() - (useOnlyRisingEdgeForTrigger ? 2 : 1);
550
551 isSynchronizationPoint = !getShaftSynchronized() || (currentCycle.current_index >= endOfCycleIndex);
552
553#if EFI_UNIT_TEST
554 if (printTriggerTrace) {
555 printf("decodeTriggerEvent sync=%d isSynchronizationPoint=%d index=%d size=%d\r\n",
557 isSynchronizationPoint,
559 triggerShape.getSize());
560 }
561#endif /* EFI_UNIT_TEST */
562 }
563#if EFI_UNIT_TEST
564 if (printTriggerTrace) {
565 printf("decodeTriggerEvent gap %s isSynchronizationPoint=%d index=%d %s\r\n",
566 getTrigger_type_e(triggerConfiguration.TriggerType.type),
567 isSynchronizationPoint, currentCycle.current_index,
568 getTrigger_event_e(signal));
569 }
570#endif /* EFI_UNIT_TEST */
571
572 if (isSynchronizationPoint) {
574 bool isDecodingError = isTriggerCounterError(triggerCountersError);
575
576 if (triggerStateListener) {
577 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 if (wasSynchronized && isDecodingError) {
589
590 // Something wrong, no longer synchronized
592
593 // This is a decoding error
595 printGaps("newerr", triggerConfiguration, triggerShape);
596 } else {
597 // If this was the first sync point OR no decode error, we're synchronized!
599 }
600
601 // this call would update duty cycle values
602 nextTriggerEvent();
603
604 onShaftSynchronization(wasSynchronized, nowNt, triggerShape);
605 } else { /* if (!isSynchronizationPoint) */
606 nextTriggerEvent();
607 }
608
609 for (int i = triggerShape.gapTrackingLength; i > 0; i--) {
610 toothDurations[i] = toothDurations[i - 1];
611 }
612
613 toothed_previous_time = nowNt;
614
615#if EFI_UNIT_TEST
616 if (wasSynchronized) {
617 int uiGapIndex = (currentCycle.current_index) % triggerShape.getLength();
618 gapRatio[uiGapIndex] = triggerSyncGapRatio;
619 }
620#endif // EFI_UNIT_TEST
621 }
622
623 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
631 }
632
634
636
637 return unexpected;
638 }
639
641
642 // Needed for early instant-RPM detection
643 TriggerStateListener * l = triggerStateListener;
644 while (l) {
646 l = l->nextListener();
647 }
648
649 if (getShaftSynchronized()) {
651 } else {
652 return unexpected;
653 }
654}
655
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.
668 auto secondGap = (float)toothDurations[1] / toothDurations[2];
669
670 bool currentGapOk = isInRange(triggerShape.synchronizationRatioFrom[0], (float)triggerSyncGapRatio, triggerShape.synchronizationRatioTo[0]);
671 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 if (!currentGapOk || !secondGapOk) {
675 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 return triggerSyncGapRatio < secondGap;
681 }
682
683 for (int i = 0; i < triggerShape.gapTrackingLength; i++) {
684 auto from = triggerShape.synchronizationRatioFrom[i];
685 auto to = triggerShape.synchronizationRatioTo[i];
686
687 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 bool isGapCondition =
697 (toothDurations[i] > toothDurations[i + 1] * from
698 && toothDurations[i] < toothDurations[i + 1] * to);
699
700 if (!isGapCondition) {
701 return false;
702 }
703 }
704
705 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 */
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 resetState();
723
724 if (shape.shapeDefinitionError) {
725 return 0;
726 }
727
728 expected<uint32_t> syncIndex = TriggerStimulatorHelper::findTriggerSyncPoint(shape,
729 triggerConfiguration,
730 *this);
731 if (!syncIndex) {
732 return EFI_ERROR_CODE;
733 }
734
735 // Assert that we found the sync point on the very first revolution
736 efiAssert(ObdCode::CUSTOM_ERR_ASSERT, getSynchronizationCounter() == 0, "findZero_revCounter", EFI_ERROR_CODE);
737
738#if EFI_UNIT_TEST
739 if (printTriggerDebug) {
740 printf("findTriggerZeroEventIndex: syncIndex located %lu!\r\n", syncIndex.Value);
741 }
742#endif /* EFI_UNIT_TEST */
743
745 syncIndex.Value, *this, shape);
746
747 return syncIndex.Value % shape.getSize();
748}
749
750#endif /* EFI_SHAFT_POSITION_INPUT */
751
const char * getTrigger_type_e(trigger_type_e value)
TriggerCentral triggerCentral
Definition engine.h:318
RpmCalculator rpmCalculator
Definition engine.h:306
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:83
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:590
static EngineAccessor engine
Definition engine.h:413
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, 1585, 1.0, -1.0, -1.0, "")
state("state", SensorCategory.SENSOR_INPUTS, FieldType.INT8, 1871, 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")