rusEFI
The most advanced open source ECU
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"
28 #include "engine_configuration.h"
29 
30 /**
31  * decoder uses TriggerStimulatorHelper in findTriggerZeroEventIndex
32  */
33 #include "trigger_simulator.h"
34 
35 #ifndef NOISE_RATIO_THRESHOLD
36 #define NOISE_RATIO_THRESHOLD 3000
37 #endif
38 
40  : name(p_name)
41 {
43 }
44 
46  return shaft_is_synchronized;
47 }
48 
50  if (value) {
51  if (!shaft_is_synchronized) {
52  // just got synchronized
54  }
55  } else {
56  // sync loss
58  }
59  shaft_is_synchronized = value;
60 }
61 
63  setShaftSynchronized(false);
65 
66  memset(toothDurations, 0, sizeof(toothDurations));
67 
72 
74  startOfCycleNt = 0;
75 
77 
79  isFirstEvent = true;
80 }
81 
82 void TriggerDecoderBase::setTriggerErrorState(int errorIncrement) {
83  m_timeSinceDecodeError.reset();
84  totalTriggerErrorCounter += errorIncrement;
85 }
86 
88  memset(currentCycle.eventCount, 0, sizeof(currentCycle.eventCount));
90 }
91 
92 #if EFI_SHAFT_POSITION_INPUT
93 
95  : TriggerDecoderBase(p_name)
96 {
97 }
98 
99 #if ! EFI_PROD_CODE
100 bool printTriggerDebug = false;
101 bool printTriggerTrace = false;
102 #endif /* ! EFI_PROD_CODE */
103 
105  const TriggerConfiguration& triggerConfiguration) {
106  triggerShapeSynchPointIndex = state.findTriggerZeroEventIndex(*this, triggerConfiguration);
107 }
108 
110  int triggerShapeSynchPointIndex = shape->triggerShapeSynchPointIndex;
111  if (triggerShapeSynchPointIndex == EFI_ERROR_CODE) {
112  return;
113  }
114  angle_t firstAngle = shape->getAngle(triggerShapeSynchPointIndex);
115  assertAngleRange(firstAngle, "firstAngle", ObdCode::CUSTOM_TRIGGER_SYNC_ANGLE);
116 
117  int riseOnlyIndex = 0;
118 
119  size_t length = shape->getLength();
120 
121  memset(eventAngles, 0, sizeof(eventAngles));
122 
123  // this may be <length for some triggers like symmetrical crank Miata NB
124  size_t triggerShapeLength = shape->getSize();
125 
126  assertAngleRange(shape->triggerShapeSynchPointIndex, "triggerShapeSynchPointIndex", ObdCode::CUSTOM_TRIGGER_SYNC_ANGLE2);
127  efiAssertVoid(ObdCode::CUSTOM_TRIGGER_CYCLE, getTriggerCentral()->engineCycleEventCount != 0, "zero engineCycleEventCount");
128 
129  for (size_t eventIndex = 0; eventIndex < length; eventIndex++) {
130  if (eventIndex == 0) {
131  // explicit check for zero to avoid issues where logical zero is not exactly zero due to float nature
132  eventAngles[0] = 0;
133  // this value would be used in case of front-only
134  eventAngles[1] = 0;
135  } else {
136  // Rotate the trigger around so that the sync point is at position 0
137  auto wrappedIndex = (shape->triggerShapeSynchPointIndex + eventIndex) % length;
138 
139  // Compute this tooth's position within the trigger definition
140  // (wrap, as the trigger def may be smaller than total trigger length)
141  auto triggerDefinitionIndex = wrappedIndex % triggerShapeLength;
142 
143  // Compute the relative angle of this tooth to the sync point's tooth
144  float angle = shape->getAngle(wrappedIndex) - firstAngle;
145 
146  efiAssertVoid(ObdCode::CUSTOM_TRIGGER_CYCLE, !cisnan(angle), "trgSyncNaN");
147  // Wrap the angle back in to [0, 720)
149 
150  if (shape->useOnlyRisingEdges) {
151  criticalAssertVoid(triggerDefinitionIndex < triggerShapeLength, "trigger shape fail");
152  assertIsInBounds(triggerDefinitionIndex, shape->isRiseEvent, "isRise");
153 
154  // In case this is a rising event, replace the following fall event with the rising as well
155  if (shape->isRiseEvent[triggerDefinitionIndex]) {
156  riseOnlyIndex += 2;
157  eventAngles[riseOnlyIndex] = angle;
158  eventAngles[riseOnlyIndex + 1] = angle;
159  }
160  } else {
161  eventAngles[eventIndex] = angle;
162  }
163  }
164  }
165 }
166 
169 }
170 
173 }
174 
177 
179 }
180 
181 
182 bool TriggerDecoderBase::isValidIndex(const TriggerWaveform& triggerShape) const {
183  return currentCycle.current_index < triggerShape.getSize();
184 }
185 
188 
189 #if EFI_UNIT_TEST
190 #define PRINT_INC_INDEX if (printTriggerTrace) {\
191  printf("nextTriggerEvent index=%d\r\n", currentCycle.current_index); \
192  }
193 #else
194 #define PRINT_INC_INDEX {}
195 #endif /* EFI_UNIT_TEST */
196 
197 #define nextTriggerEvent() \
198  { \
199  if (useOnlyRisingEdgeForTrigger) {currentCycle.current_index++;} \
200  currentCycle.current_index++; \
201  PRINT_INC_INDEX; \
202 }
203 
206 }
207 
208 angle_t PrimaryTriggerDecoder::syncEnginePhase(int divider, int remainder, angle_t engineCycle) {
209  efiAssert(ObdCode::OBD_PCM_Processor_Fault, divider > 1, "syncEnginePhase divider", false);
210  efiAssert(ObdCode::OBD_PCM_Processor_Fault, remainder < divider, "syncEnginePhase remainder", false);
211  angle_t totalShift = 0;
212  while (getCrankSynchronizationCounter() % divider != remainder) {
213  /**
214  * we are here if we've detected the cam sensor within the wrong crank phase
215  * let's increase the trigger event counter, that would adjust the state of
216  * virtual crank-based trigger
217  */
219  totalShift += engineCycle / divider;
220  }
221 
222  // Allow injection/ignition to happen, we've now fully sync'd the crank based on new cam information
223  m_hasSynchronizedPhase = true;
224 
225  if (totalShift > 0) {
227  }
228 
229  return totalShift;
230 }
231 
234 }
235 
237  // On trigger error, we've lost full sync
239 
240  // Ignore the warning that engine is never null - it might be in unit tests
241  #pragma GCC diagnostic push
242  #pragma GCC diagnostic ignored "-Waddress"
243  if (engine) {
244  // Instant RPM data is now also probably trash, discard it
247  }
248  #pragma GCC diagnostic pop
249 }
250 
251 void PrimaryTriggerDecoder::onNotEnoughTeeth(int /*actual*/, int /*expected*/) {
252  warning(ObdCode::CUSTOM_PRIMARY_NOT_ENOUGH_TEETH, "primary trigger error: not enough teeth between sync points: expected %d/%d got %d/%d",
253  getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_PRIMARY),
254  getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_SECONDARY),
257 }
258 
259 void PrimaryTriggerDecoder::onTooManyTeeth(int /*actual*/, int /*expected*/) {
260  warning(ObdCode::CUSTOM_PRIMARY_TOO_MANY_TEETH, "primary trigger error: too many teeth between sync points: expected %d/%d got %d/%d",
261  getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_PRIMARY),
262  getTriggerCentral()->triggerShape.getExpectedEventCount(TriggerWheel::T_SECONDARY),
265 }
266 
268 switch(value) {
270  return "SHAFT_PRIMARY_FALLING";
272  return "SHAFT_PRIMARY_RISING";
274  return "SHAFT_SECONDARY_FALLING";
276  return "SHAFT_SECONDARY_RISING";
277  }
278  return NULL;
279 }
280 const char *getTrigger_value_e(TriggerValue value){
281 switch(value) {
282 case TriggerValue::FALL:
283  return "TriggerValue::FALL";
284 case TriggerValue::RISE:
285  return "TriggerValue::RISE";
286  }
287  return NULL;
288 }
289 
290 void VvtTriggerDecoder::onNotEnoughTeeth(int actual, int expected) {
291  warning(ObdCode::CUSTOM_CAM_NOT_ENOUGH_TEETH, "cam %s trigger error: not enough teeth between sync points: actual %d expected %d", name, actual, expected);
292 }
293 
294 void VvtTriggerDecoder::onTooManyTeeth(int actual, int expected) {
295  warning(ObdCode::CUSTOM_CAM_TOO_MANY_TEETH, "cam %s trigger error: too many teeth between sync points: %d > %d", name, actual, expected);
296 }
297 
299  // We can check if things are fine by comparing the number of events in a cycle with the expected number of event.
300  bool isDecodingError = false;
301  for (int i = 0;i < PWM_PHASE_MAX_WAVE_PER_PWM;i++) {
302  isDecodingError |= (currentCycle.eventCount[i] != triggerShape.getExpectedEventCount((TriggerWheel)i));
303  }
304 
305 #if EFI_DEFAILED_LOGGING
306  printf("validateEventCounters: isDecodingError=%d\n", isDecodingError);
307  if (isDecodingError) {
308  for (int i = 0;i < PWM_PHASE_MAX_WAVE_PER_PWM;i++) {
309  printf(" count: cur=%d exp=%d\n", currentCycle.eventCount[i], triggerShape.getExpectedEventCount((TriggerWheel)i));
310  }
311  }
312 #endif /* EFI_UNIT_TEST */
313 
314  return isDecodingError;
315 }
316 
318  bool wasSynchronized,
319  const efitick_t nowNt,
320  const TriggerWaveform& triggerShape) {
321  startOfCycleNt = nowNt;
323 
324  if (wasSynchronized) {
326  } else {
327  // We have just synchronized, this is the zeroth revolution
329  }
330 
331  totalEventCountBase += triggerShape.getSize();
332 
333 #if EFI_UNIT_TEST
334  if (printTriggerDebug) {
335  printf("onShaftSynchronization index=%d %d\r\n",
338  }
339 #endif /* EFI_UNIT_TEST */
340 }
341 
342 static bool shouldConsiderEdge(const TriggerWaveform& triggerShape, TriggerWheel triggerWheel, TriggerValue edge) {
343  if (triggerWheel != TriggerWheel::T_PRIMARY && triggerShape.useOnlyPrimaryForSync) {
344  // Non-primary events ignored
345  return false;
346  }
347 
348  switch (triggerShape.syncEdge) {
349  case SyncEdge::Both: return true;
350  case SyncEdge::RiseOnly:
351  case SyncEdge::Rise: return edge == TriggerValue::RISE;
352  case SyncEdge::Fall: return edge == TriggerValue::FALL;
353  }
354 
355  // how did we get here?
356  // assert(false)?
357 
358  return false;
359 }
360 
361 /**
362  * @brief Trigger decoding happens here
363  * VR falls are filtered out and some VR noise detection happens prior to invoking this method, for
364  * Hall this method is invoked every time we have a fall or rise on one of the trigger sensors.
365  * This method changes the state of trigger_state_s data structure according to the trigger event
366  * @param signal type of event which just happened
367  * @param nowNt current time
368  */
369 expected<TriggerDecodeResult> TriggerDecoderBase::decodeTriggerEvent(
370  const char *msg,
371  const TriggerWaveform& triggerShape,
372  TriggerStateListener* triggerStateListener,
373  const TriggerConfiguration& triggerConfiguration,
374  const trigger_event_e signal,
375  const efitick_t nowNt) {
377 
378  if (previousEventTimer.getElapsedSecondsAndReset(nowNt) > 1) {
379  /**
380  * We are here if there is a time gap between now and previous shaft event - that means the engine is not running.
381  * That means we have lost synchronization since the engine is not running :)
382  */
383  setShaftSynchronized(false);
384  if (triggerStateListener) {
385  triggerStateListener->OnTriggerSynchronizationLost();
386  }
387  }
388 
389  bool useOnlyRisingEdgeForTrigger = triggerShape.useOnlyRisingEdges;
390 
391  efiAssert(ObdCode::CUSTOM_TRIGGER_UNEXPECTED, signal <= SHAFT_SECONDARY_RISING, "unexpected signal", unexpected);
392 
393  TriggerWheel triggerWheel = eventIndex[signal];
394  TriggerValue type = eventType[signal];
395 
396  // Check that we didn't get the same edge twice in a row - that should be impossible
397  if (!useOnlyRisingEdgeForTrigger && prevSignal == signal) {
399  }
400 
401  prevSignal = signal;
402 
403  currentCycle.eventCount[(int)triggerWheel]++;
404 
405  if (toothed_previous_time > nowNt) {
406  firmwareError(ObdCode::CUSTOM_OBD_93, "[%s] toothed_previous_time after nowNt prev=%d now=%d", msg, toothed_previous_time, nowNt);
407  }
408 
409  efitick_t currentDurationLong = isFirstEvent ? 0 : nowNt - toothed_previous_time;
410 
411  /**
412  * For performance reasons, we want to work with 32 bit values. If there has been more then
413  * 10 seconds since previous trigger event we do not really care.
414  */
415  toothDurations[0] =
416  currentDurationLong > 10 * NT_PER_SECOND ? 10 * NT_PER_SECOND : currentDurationLong;
417 
418  if (!shouldConsiderEdge(triggerShape, triggerWheel, type)) {
419 #if EFI_UNIT_TEST
420  if (printTriggerTrace) {
421  printf("%s isLessImportant %s now=%d index=%d\r\n",
422  getTrigger_type_e(triggerConfiguration.TriggerType.type),
423  getTrigger_event_e(signal),
424  (int)nowNt,
426  }
427 #endif /* EFI_UNIT_TEST */
428 
429  // For less important events we simply increment the index.
430  nextTriggerEvent();
431  } else {
432 #if !EFI_PROD_CODE
433  if (printTriggerTrace) {
434  printf("%s event %s %lld\r\n",
435  getTrigger_type_e(triggerConfiguration.TriggerType.type),
436  getTrigger_event_e(signal),
437  nowNt);
438  printf("decodeTriggerEvent ratio %.2f: current=%d previous=%d\r\n", 1.0 * toothDurations[0] / toothDurations[1],
440  }
441 #endif
442 
443  isFirstEvent = false;
444  bool isSynchronizationPoint;
445  bool wasSynchronized = getShaftSynchronized();
446 
447  if (triggerShape.isSynchronizationNeeded) {
449 
450  if (wasSynchronized && triggerSyncGapRatio > NOISE_RATIO_THRESHOLD) {
452  }
453 
454  isSynchronizationPoint = isSyncPoint(triggerShape, triggerConfiguration.TriggerType.type);
455  if (isSynchronizationPoint) {
457  }
458 
459  /**
460  * todo: technically we can afford detailed logging even with 60/2 as long as low RPM
461  * todo: figure out exact threshold as a function of RPM and tooth count?
462  * Open question what is 'triggerShape.getSize()' for 60/2 is it 58 or 58*2 or 58*4?
463  */
464  bool silentTriggerError = triggerShape.getSize() > 40 && engineConfiguration->silentTriggerError;
465 
466 #if EFI_PROD_CODE || EFI_SIMULATOR
467  bool verbose = getTriggerCentral()->isEngineSnifferEnabled && triggerConfiguration.VerboseTriggerSynchDetails;
468 
469  if (verbose || (someSortOfTriggerError() && !silentTriggerError)) {
470  const char * prefix = verbose ? "[vrb]" : "[err]";
471 
472  for (int i = 0;i<triggerShape.gapTrackingLength;i++) {
473  float ratioFrom = triggerShape.synchronizationRatioFrom[i];
474  if (cisnan(ratioFrom)) {
475  // we do not track gap at this depth
476  continue;
477  }
478 
479  float gap = 1.0 * toothDurations[i] / toothDurations[i + 1];
480  if (cisnan(gap)) {
481  efiPrintf("%s index=%d NaN gap, you have noise issues?",
482  i,
483  prefix
484  );
485  } else {
486  float ratioTo = triggerShape.synchronizationRatioTo[i];
487 
488  bool gapOk = isInRange(ratioFrom, gap, ratioTo);
489 
490  efiPrintf("%s %srpm=%d time=%d eventIndex=%d gapIndex=%d: %s gap=%.3f expected from %.3f to %.3f error=%s",
491  prefix,
492  triggerConfiguration.PrintPrefix,
494  /* cast is needed to make sure we do not put 64 bit value to stack*/ (int)getTimeNowS(),
496  i,
497  gapOk ? "Y" : "n",
498  gap,
499  ratioFrom,
500  ratioTo,
502  }
503  }
504  }
505 #else
506  if (printTriggerTrace) {
507  for (int i = 0;i<triggerShape.gapTrackingLength;i++) {
508  float gap = 1.0 * toothDurations[i] / toothDurations[i + 1];
509  printf("%sindex=%d: gap=%.2f expected from %.2f to %.2f error=%s\r\n",
510  triggerConfiguration.PrintPrefix,
511  i,
512  gap,
513  triggerShape.synchronizationRatioFrom[i],
514  triggerShape.synchronizationRatioTo[i],
516  }
517  }
518 #endif /* EFI_PROD_CODE */
519  } else {
520  /**
521  * We are here in case of a wheel without synchronization - we just need to count events,
522  * synchronization point simply happens once we have the right number of events
523  *
524  * in case of noise the counter could be above the expected number of events, that's why 'more or equals' and not just 'equals'
525  */
526 
527  unsigned int endOfCycleIndex = triggerShape.getSize() - (useOnlyRisingEdgeForTrigger ? 2 : 1);
528 
529  isSynchronizationPoint = !getShaftSynchronized() || (currentCycle.current_index >= endOfCycleIndex);
530 
531 #if EFI_UNIT_TEST
532  if (printTriggerTrace) {
533  printf("decodeTriggerEvent sync=%d isSynchronizationPoint=%d index=%d size=%d\r\n",
535  isSynchronizationPoint,
537  triggerShape.getSize());
538  }
539 #endif /* EFI_UNIT_TEST */
540  }
541 #if EFI_UNIT_TEST
542  if (printTriggerTrace) {
543  printf("decodeTriggerEvent %s isSynchronizationPoint=%d index=%d %s\r\n",
544  getTrigger_type_e(triggerConfiguration.TriggerType.type),
545  isSynchronizationPoint, currentCycle.current_index,
546  getTrigger_event_e(signal));
547  }
548 #endif /* EFI_UNIT_TEST */
549 
550  if (isSynchronizationPoint) {
551  bool isDecodingError = validateEventCounters(triggerShape);
552 
553  if (triggerStateListener) {
554  triggerStateListener->OnTriggerSynchronization(wasSynchronized, isDecodingError);
555  }
556 
557  // If we got a sync point, but the wrong number of events since the last sync point
558  // One of two things has happened:
559  // - We missed a tooth, and this is the real sync point
560  // - Due to some mistake in timing, we found what looks like a sync point but actually isn't
561  // In either case, we should wait for another sync point before doing anything to try and run an engine,
562  // so we clear the synchronized flag.
563  if (wasSynchronized && isDecodingError) {
566 
567  // Something wrong, no longer synchronized
568  setShaftSynchronized(false);
569 
570  // This is a decoding error
571  onTriggerError();
572  } else {
573  // If this was the first sync point OR no decode error, we're synchronized!
574  setShaftSynchronized(true);
575  }
576 
577  // this call would update duty cycle values
578  nextTriggerEvent();
579 
580  onShaftSynchronization(wasSynchronized, nowNt, triggerShape);
581  } else { /* if (!isSynchronizationPoint) */
582  nextTriggerEvent();
583  }
584 
585  for (int i = triggerShape.gapTrackingLength; i > 0; i--) {
586  toothDurations[i] = toothDurations[i - 1];
587  }
588 
589  toothed_previous_time = nowNt;
590 
591 #if EFI_UNIT_TEST
592  if (wasSynchronized) {
593  int uiGapIndex = (currentCycle.current_index) % triggerShape.getLength();
594  gapRatio[uiGapIndex] = triggerSyncGapRatio;
595  }
596 #endif // EFI_UNIT_TEST
597  }
598 
599  if (getShaftSynchronized() && !isValidIndex(triggerShape)) {
600  // We've had too many events since the last sync point, we should have seen a sync point by now.
601  // This is a trigger error.
602 
603  // let's not show a warning if we are just starting to spin
607  }
608 
609  onTriggerError();
610 
611  setShaftSynchronized(false);
612 
613  return unexpected;
614  }
615 
616  // Needed for early instant-RPM detection
617  if (triggerStateListener) {
618  triggerStateListener->OnTriggerStateProperState(nowNt);
619  }
620 
622 
623  if (getShaftSynchronized()) {
625  } else {
626  return unexpected;
627  }
628 }
629 
631  // Miata NB needs a special decoder.
632  // The problem is that the crank wheel only has 4 teeth, also symmetrical, so the pattern
633  // is long-short-long-short for one crank rotation.
634  // A quick acceleration can result in two successive "short gaps", so we see
635  // long-short-short-short-long instead of the correct long-short-long-short-long
636  // This logic expands the lower bound on a "long" tooth, then compares the last
637  // tooth to the current one.
638 
639  // Instead of detecting short/long, this logic first checks for "maybe short" and "maybe long",
640  // then simply tests longer vs. shorter instead of absolute value.
642  auto secondGap = (float)toothDurations[1] / toothDurations[2];
643 
644  bool currentGapOk = isInRange(triggerShape.synchronizationRatioFrom[0], (float)triggerSyncGapRatio, triggerShape.synchronizationRatioTo[0]);
645  bool secondGapOk = isInRange(triggerShape.synchronizationRatioFrom[1], secondGap, triggerShape.synchronizationRatioTo[1]);
646 
647  // One or both teeth was impossible range, this is not the sync point
648  if (!currentGapOk || !secondGapOk) {
649  return false;
650  }
651 
652  // If both teeth are in the range of possibility, return whether this gap is
653  // shorter than the last or not. If it is, this is the sync point.
654  return triggerSyncGapRatio < secondGap;
655  }
656 
657  for (int i = 0; i < triggerShape.gapTrackingLength; i++) {
658  auto from = triggerShape.synchronizationRatioFrom[i];
659  auto to = triggerShape.synchronizationRatioTo[i];
660 
661  if (cisnan(from)) {
662  // don't check this gap, skip it
663  continue;
664  }
665 
666  // This is transformed to avoid a division and use a cheaper multiply instead
667  // toothDurations[i] / toothDurations[i+1] > from
668  // is an equivalent comparison to
669  // toothDurations[i] > toothDurations[i+1] * from
670  bool isGapCondition =
671  (toothDurations[i] > toothDurations[i + 1] * from
672  && toothDurations[i] < toothDurations[i + 1] * to);
673 
674  if (!isGapCondition) {
675  return false;
676  }
677  }
678 
679  return true;
680 }
681 
682 /**
683  * Trigger shape is defined in a way which is convenient for trigger shape definition
684  * On the other hand, trigger decoder indexing begins from synchronization event.
685  *
686  * This function finds the index of synchronization event within TriggerWaveform
687  */
689  TriggerWaveform& shape,
690  const TriggerConfiguration& triggerConfiguration) {
691 #if EFI_PROD_CODE
692  efiAssert(ObdCode::CUSTOM_ERR_ASSERT, hasLotsOfRemainingStack(), "findPos", -1);
693 #endif
694 
695 
696  resetState();
697 
698  if (shape.shapeDefinitionError) {
699  return 0;
700  }
701 
702  expected<uint32_t> syncIndex = TriggerStimulatorHelper::findTriggerSyncPoint(shape,
703  triggerConfiguration,
704  *this);
705  if (!syncIndex) {
706  return EFI_ERROR_CODE;
707  }
708 
709  // Assert that we found the sync point on the very first revolution
710  efiAssert(ObdCode::CUSTOM_ERR_ASSERT, getCrankSynchronizationCounter() == 0, "findZero_revCounter", EFI_ERROR_CODE);
711 
712 #if EFI_UNIT_TEST
713  if (printTriggerDebug) {
714  printf("findTriggerZeroEventIndex: syncIndex located %d!\r\n", syncIndex);
715  }
716 #endif /* EFI_UNIT_TEST */
717 
718  TriggerStimulatorHelper::assertSyncPosition(triggerConfiguration,
719  syncIndex.Value, *this, shape);
720 
721  return syncIndex.Value % shape.getSize();
722 }
723 
724 #endif /* EFI_SHAFT_POSITION_INPUT */
725 
const char * getTrigger_type_e(trigger_type_e value)
TriggerCentral triggerCentral
Definition: engine.h:275
RpmCalculator rpmCalculator
Definition: engine.h:262
OutputPin debugTriggerSync
Definition: efi_gpio.h:106
void toggle()
Definition: efi_gpio.cpp:549
PrimaryTriggerDecoder(const char *name)
void onTooManyTeeth(int actual, int expected) override
void resetState() 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:92
InstantRpmCalculator instantRpm
const char *const PrintPrefix
trigger_config_s TriggerType
virtual void resetState()
efitick_t mostRecentSyncTime
void incrementShaftSynchronizationCounter()
TriggerDecoderBase(const char *name)
int getCrankSynchronizationCounter() const
virtual void onTriggerError()
float gapRatio[PWM_PHASE_MAX_COUNT *6]
void onShaftSynchronization(bool wasSynchronized, const efitick_t nowNt, const TriggerWaveform &triggerShape)
int getCurrentIndex() const
const char *const name
uint32_t findTriggerZeroEventIndex(TriggerWaveform &shape, const TriggerConfiguration &triggerConfiguration)
virtual void onNotEnoughTeeth(int, int)
virtual void onTooManyTeeth(int, int)
uint32_t orderingErrorCounter
bool validateEventCounters(const TriggerWaveform &triggerShape) const
efitick_t toothed_previous_time
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
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:18
bool isInRange(T min, T val, T max)
Definition: efilib.h:84
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:589
Main engine configuration data structure.
Engine * engine
trigger_type_e
Definition: engine_types.h:286
bool warning(ObdCode code, const char *fmt,...)
void firmwareError(ObdCode code, const char *fmt,...)
static const char * msg
@ 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
engine_configuration_s * engineConfiguration
TriggerWheel
Definition: rusefi_enums.h:47
float angle_t
Definition: rusefi_types.h:61
static ScState state
trigger_event_e
@ SHAFT_SECONDARY_RISING
@ SHAFT_SECONDARY_FALLING
@ SHAFT_PRIMARY_FALLING
@ SHAFT_PRIMARY_RISING
TriggerValue
virtual void OnTriggerStateProperState(efitick_t nowNt)=0
virtual void OnTriggerSynchronization(bool wasSynchronized, bool isDecodingError)=0
virtual void OnTriggerSynchronizationLost()=0
size_t eventCount[PWM_PHASE_MAX_WAVE_PER_PWM]
uint32_t crankSynchronizationCounter
const char * getTrigger_event_e(trigger_event_e value)
static bool shouldConsiderEdge(const TriggerWaveform &triggerShape, TriggerWheel triggerWheel, TriggerValue edge)
static TriggerValue eventType[4]
bool printTriggerTrace
bool printTriggerDebug
const char * getTrigger_value_e(TriggerValue value)
static TriggerWheel eventIndex[4]
triggerType
void wrapAngle(angle_t &angle, const char *msg, ObdCode code)
printf("\n")