rusEFI
The most advanced open source ECU
Public Member Functions | Data Fields | Private Member Functions | Private Attributes
TriggerCentral Class Referencefinal

#include <trigger_central.h>

Inheritance diagram for TriggerCentral:
Inheritance graph
[legend]
Collaboration diagram for TriggerCentral:
Collaboration graph
[legend]

Public Member Functions

 TriggerCentral ()
 
angle_t syncEnginePhaseAndReport (int divider, int remainder)
 
void handleShaftSignal (trigger_event_e signal, efitick_t timestamp)
 
int getHwEventCounter (int index) const
 
void resetCounters ()
 
void validateCamVvtCounters ()
 
void updateWaveform ()
 
angle_t findNextTriggerToothAngle (int nextToothIndex)
 
void prepareTriggerShape ()
 
bool checkIfTriggerConfigChanged ()
 
bool isTriggerConfigChanged ()
 
bool isTriggerDecoderError ()
 
expected< float > getCurrentEnginePhase (efitick_t nowNt) const
 
float getSecondsSinceTriggerEvent (efitick_t nowNt) const
 
bool engineMovedRecently (efitick_t nowNt) const
 
bool engineMovedRecently () const
 
angle_t getVVTPosition (uint8_t bankIndex, uint8_t camIndex)
 

Data Fields

InstantRpmCalculator instantRpm
 
bool hwTriggerInputEnabled = true
 
cyclic_buffer< int > triggerErrorDetection
 
bool directSelfStimulation = false
 
PrimaryTriggerConfiguration primaryTriggerConfiguration
 
VvtTriggerConfiguration vvtTriggerConfiguration [CAMS_PER_BANK] = {{"VVT1 ", 0}}
 
LocalVersionHolder triggerVersion
 
bool isSpinningJustForWatchdog = false
 
float mapCamPrevCycleValue = 0
 
int prevChangeAtCycle = 0
 
uint32_t engineCycleEventCount = 0
 
bool triggerConfigChangedOnLastConfigurationChange = false
 
TriggerNoiseFilter noiseFilter
 
int vvtEventRiseCounter [CAM_INPUTS_COUNT]
 
int vvtEventFallCounter [CAM_INPUTS_COUNT]
 
angle_t currentVVTEventPosition [BANKS_COUNT][CAMS_PER_BANK]
 
angle_t vvtPosition [BANKS_COUNT][CAMS_PER_BANK]
 
PrimaryTriggerDecoder triggerState
 
TriggerWaveform triggerShape
 
VvtTriggerDecoder vvtState [BANKS_COUNT][CAMS_PER_BANK]
 
TriggerWaveform vvtShape [CAMS_PER_BANK]
 
TriggerFormDetails triggerFormDetails
 
Timer m_lastEventTimer
 
bool isEngineSnifferEnabled = false
 
- Data Fields inherited from trigger_central_s
uint32_t hwEventCounters [HW_EVENT_TYPES]
 
uint32_t vvtCamCounter = (uint32_t)0
 
float mapVvt_MAP_AT_SPECIAL_POINT = (float)0
 
float mapVvt_MAP_AT_DIFF = (float)0
 
uint8_t mapVvt_MAP_AT_CYCLE_COUNT = (uint8_t)0
 
uint8_t mapVvt_map_peak = (uint8_t)0
 
uint8_t alignmentFill_at_38 [2]
 
float currentEngineDecodedPhase = (float)0
 
float triggerToothAngleError = (float)0
 
uint8_t triggerIgnoredToothCount = (uint8_t)0
 
uint8_t alignmentFill_at_49 [3]
 
angle_t mapCamPrevToothAngle = (angle_t)0
 
bool isDecodingMapCam: 1 {}
 
bool unusedBit_13_1: 1 {}
 
bool unusedBit_13_2: 1 {}
 
bool unusedBit_13_3: 1 {}
 
bool unusedBit_13_4: 1 {}
 
bool unusedBit_13_5: 1 {}
 
bool unusedBit_13_6: 1 {}
 
bool unusedBit_13_7: 1 {}
 
bool unusedBit_13_8: 1 {}
 
bool unusedBit_13_9: 1 {}
 
bool unusedBit_13_10: 1 {}
 
bool unusedBit_13_11: 1 {}
 
bool unusedBit_13_12: 1 {}
 
bool unusedBit_13_13: 1 {}
 
bool unusedBit_13_14: 1 {}
 
bool unusedBit_13_15: 1 {}
 
bool unusedBit_13_16: 1 {}
 
bool unusedBit_13_17: 1 {}
 
bool unusedBit_13_18: 1 {}
 
bool unusedBit_13_19: 1 {}
 
bool unusedBit_13_20: 1 {}
 
bool unusedBit_13_21: 1 {}
 
bool unusedBit_13_22: 1 {}
 
bool unusedBit_13_23: 1 {}
 
bool unusedBit_13_24: 1 {}
 
bool unusedBit_13_25: 1 {}
 
bool unusedBit_13_26: 1 {}
 
bool unusedBit_13_27: 1 {}
 
bool unusedBit_13_28: 1 {}
 
bool unusedBit_13_29: 1 {}
 
bool unusedBit_13_30: 1 {}
 
bool unusedBit_13_31: 1 {}
 
uint32_t triggerElapsedUs = (uint32_t)0
 

Private Member Functions

void decodeMapCam (efitick_t nowNt, float currentPhase)
 
bool isToothExpectedNow (efitick_t timestamp)
 

Private Attributes

Timer m_lastToothTimer
 
float m_lastToothPhaseFromSyncPoint
 
expected< float > expectedNextPhase = unexpected
 

Detailed Description

Maybe merge TriggerCentral and TriggerState classes into one class? Probably not: we have an instance of TriggerState which is used for trigger initialization, also composition probably better than inheritance here

Definition at line 49 of file trigger_central.h.

Constructor & Destructor Documentation

◆ TriggerCentral()

TriggerCentral::TriggerCentral ( )

Definition at line 43 of file trigger_central.cpp.

43  :
46  vvtPosition(),
47  triggerState("TRG")
48 {
49  memset(&hwEventCounters, 0, sizeof(hwEventCounters));
52 }
void resetState() override
PrimaryTriggerDecoder triggerState
int vvtEventFallCounter[CAM_INPUTS_COUNT]
angle_t vvtPosition[BANKS_COUNT][CAMS_PER_BANK]
TriggerNoiseFilter noiseFilter
int vvtEventRiseCounter[CAM_INPUTS_COUNT]
uint32_t hwEventCounters[HW_EVENT_TYPES]
Here is the call graph for this function:

Member Function Documentation

◆ checkIfTriggerConfigChanged()

bool TriggerCentral::checkIfTriggerConfigChanged ( )
Returns
true if configuration just changed, and if that change has affected trigger

Definition at line 1200 of file trigger_central.cpp.

1200  {
1201  // we want to make sure that configuration has changed AND that change has changed trigger specifically
1203  triggerConfigChangedOnLastConfigurationChange = false; // whoever has called the method is supposed to react to changes
1204  return result;
1205 }
int getGlobalConfigurationVersion(void) const
Definition: engine.cpp:300
bool isOld(int globalVersion)
LocalVersionHolder triggerVersion
bool triggerConfigChangedOnLastConfigurationChange
Engine * engine
Here is the call graph for this function:

◆ decodeMapCam()

void TriggerCentral::decodeMapCam ( efitick_t  nowNt,
float  currentPhase 
)
private

Definition at line 644 of file trigger_central.cpp.

644  {
645  isDecodingMapCam = engineConfiguration->vvtMode[0] == VVT_MAP_V_TWIN &&
647  if (isDecodingMapCam) {
648  // we are trying to figure out which 360 half of the total 720 degree cycle is which, so we compare those in 360 degree sense.
649  auto toothAngle360 = currentPhase;
650  while (toothAngle360 >= 360) {
651  toothAngle360 -= 360;
652  }
653 
654  if (mapCamPrevToothAngle < engineConfiguration->mapCamDetectionAnglePosition && toothAngle360 > engineConfiguration->mapCamDetectionAnglePosition) {
655  // we are somewhere close to 'mapCamDetectionAnglePosition'
656 
657  // warning: hack hack hack
659 
660  // Compute diff against the last time we were here
661  float diff = map - mapCamPrevCycleValue;
662  mapCamPrevCycleValue = map;
663 
664  if (diff > 0) {
665  mapVvt_map_peak++;
667  mapVvt_MAP_AT_CYCLE_COUNT = revolutionCounter - prevChangeAtCycle;
668  prevChangeAtCycle = revolutionCounter;
669 
670  hwHandleVvtCamSignal(TriggerValue::RISE, timestamp, /*index*/0);
671  hwHandleVvtCamSignal(TriggerValue::FALL, timestamp, /*index*/0);
672 #if EFI_UNIT_TEST
673  // hack? feature? existing unit test relies on VVT phase available right away
674  // but current implementation which is based on periodicFastCallback would only make result available on NEXT tooth
676 #endif // EFI_UNIT_TEST
677  }
678 
680  mapVvt_MAP_AT_DIFF = diff;
681  }
682 
683  mapCamPrevToothAngle = toothAngle360;
684  }
685 }
TunerStudioOutputChannels outputChannels
Definition: engine.h:99
void onFastCallback() override
static float getOrZero(SensorType type)
Definition: sensor.h:92
float mapCamPrevCycleValue
int getCrankSynchronizationCounter() const
LimpManager * getLimpManager()
Definition: engine.cpp:595
TriggerCentral * getTriggerCentral()
Definition: engine.cpp:589
engine_configuration_s * engineConfiguration
scaled_channel< uint16_t, 30, 1 > instantMAPValue
void hwHandleVvtCamSignal(bool isRising, efitick_t timestamp, int index)

Referenced by handleShaftSignal().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ engineMovedRecently() [1/2]

bool TriggerCentral::engineMovedRecently ( ) const
inline

Definition at line 151 of file trigger_central.h.

151  {
153  }
bool engineMovedRecently() const
efitick_t getTimeNowNt()
Definition: efitime.cpp:19
Here is the call graph for this function:

◆ engineMovedRecently() [2/2]

bool TriggerCentral::engineMovedRecently ( efitick_t  nowNt) const
inline

Definition at line 138 of file trigger_central.h.

138  {
139  constexpr float oneRevolutionLimitInSeconds = 60.0 / RPM_LOW_THRESHOLD;
140  auto maxAverageToothTime = oneRevolutionLimitInSeconds / triggerShape.getSize();
141 
142  // Some triggers may have long gaps (with many teeth), don't count that as stopped!
143  auto maxAllowedGap = maxAverageToothTime * 10;
144 
145  // Clamp between 0.1 seconds ("instant" for a human) and worst case of one engine cycle on low tooth count wheel
146  maxAllowedGap = clampF(0.1f, maxAllowedGap, oneRevolutionLimitInSeconds);
147 
148  return getSecondsSinceTriggerEvent(nowNt) < maxAllowedGap;
149  }
float getSecondsSinceTriggerEvent(efitick_t nowNt) const
TriggerWaveform triggerShape
size_t getSize() const

Referenced by applyIACposition(), RpmCalculator::checkIfSpinning(), EtbController::checkStatus(), Engine::efiWatchdog(), getAcrState(), RpmCalculator::onSlowCallback(), and FuelPumpController::onSlowCallback().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ findNextTriggerToothAngle()

angle_t TriggerCentral::findNextTriggerToothAngle ( int  nextToothIndex)

Definition at line 747 of file trigger_central.cpp.

747  {
748  int currentToothIndex = p_currentToothIndex;
749  // TODO: is this logic to compute next trigger tooth angle correct?
750  angle_t nextToothAngle = 0;
751 
752  int loopAllowance = 2 * engineCycleEventCount + 1000;
753  do {
754  // I don't love this.
755  currentToothIndex = (currentToothIndex + 1) % engineCycleEventCount;
756  nextToothAngle = getTriggerCentral()->triggerFormDetails.eventAngles[currentToothIndex] - tdcPosition();
757  wrapAngle(nextToothAngle, "nextEnginePhase", ObdCode::CUSTOM_ERR_6555);
758  } while (nextToothAngle == currentEngineDecodedPhase && --loopAllowance > 0); // '==' for float works here since both values come from 'eventAngles' array
759  if (nextToothAngle != 0 && loopAllowance == 0) {
760  // HW CI fails here, looks like we sometimes change trigger while still handling it?
761  firmwareError(ObdCode::CUSTOM_ERR_TRIGGER_ZERO, "handleShaftSignal unexpected loop end %d %d %f %f", p_currentToothIndex, engineCycleEventCount, nextToothAngle, currentEngineDecodedPhase);
762  }
763  return nextToothAngle;
764 }
TriggerFormDetails triggerFormDetails
uint32_t engineCycleEventCount
angle_t eventAngles[2 *PWM_PHASE_MAX_COUNT]
void firmwareError(ObdCode code, const char *fmt,...)
@ CUSTOM_ERR_TRIGGER_ZERO
@ CUSTOM_ERR_6555
float angle_t
Definition: rusefi_types.h:58
void wrapAngle(angle_t &angle, const char *msg, ObdCode code)

Referenced by handleShaftSignal().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getCurrentEnginePhase()

expected< float > TriggerCentral::getCurrentEnginePhase ( efitick_t  nowNt) const
Returns
angle since trigger synchronization point, NOT angle since TDC.

Definition at line 75 of file trigger_central.cpp.

75  {
77 
78  if (cisnan(oneDegreeUs)) {
79  return unexpected;
80  }
81 
82  float elapsed;
83  float toothPhase;
84 
85  {
86  // under lock to avoid mismatched tooth phase and time
87  chibios_rt::CriticalSectionLocker csl;
88 
89  elapsed = m_lastToothTimer.getElapsedUs(nowNt);
90  toothPhase = m_lastToothPhaseFromSyncPoint;
91  }
92 
93  return toothPhase + elapsed / oneDegreeUs;
94 }
RpmCalculator rpmCalculator
Definition: engine.h:273
floatus_t oneDegreeUs
float m_lastToothPhaseFromSyncPoint
float floatus_t
Definition: rusefi_types.h:68

Referenced by getAcrState(), handleVvtCamSignal(), isToothExpectedNow(), onFastAdcComplete(), and rpmShaftPositionCallback().

Here is the caller graph for this function:

◆ getHwEventCounter()

int TriggerCentral::getHwEventCounter ( int  index) const

Definition at line 60 of file trigger_central.cpp.

60  {
61  return hwEventCounters[index];
62 }

Referenced by canDashboardHaltech(), sendQcBenchEventCounters(), triggerInfo(), and updateTunerStudioState().

Here is the caller graph for this function:

◆ getSecondsSinceTriggerEvent()

float TriggerCentral::getSecondsSinceTriggerEvent ( efitick_t  nowNt) const
inline

Definition at line 134 of file trigger_central.h.

134  {
135  return m_lastEventTimer.getElapsedSeconds(nowNt);
136  }

Referenced by engineMovedRecently().

Here is the caller graph for this function:

◆ getVVTPosition()

angle_t TriggerCentral::getVVTPosition ( uint8_t  bankIndex,
uint8_t  camIndex 
)

Definition at line 65 of file trigger_central.cpp.

65  {
66  if (bankIndex >= BANKS_COUNT || camIndex >= CAMS_PER_BANK) {
67  return NAN;
68  }
69  return vvtPosition[bankIndex][camIndex];
70 }

Referenced by HpfpLobe::findNextLobe(), VvtController::observePlant(), populateFrame(), readGppwmChannel(), and updateVvtSensors().

Here is the caller graph for this function:

◆ handleShaftSignal()

void TriggerCentral::handleShaftSignal ( trigger_event_e  signal,
efitick_t  timestamp 
)

This method is NOT invoked for VR falls.

If we only have a crank position sensor with four stroke, here we are extending crank revolutions with a 360 degree cycle into a four stroke, 720 degrees cycle.

Definition at line 769 of file trigger_central.cpp.

769  {
771  // trigger is broken, we cannot do anything here
772  warning(ObdCode::CUSTOM_ERR_UNEXPECTED_SHAFT_EVENT, "Shaft event while trigger is mis-configured");
773  // magic value to indicate a problem
774  hwEventCounters[0] = 155;
775  return;
776  }
777 
778  // This code gathers some statistics on signals and compares accumulated periods to filter interference
780  if (!noiseFilter.noiseFilter(timestamp, &triggerState, signal)) {
781  return;
782  }
783  if (!isUsefulSignal(signal, triggerShape)) {
784  return;
785  }
786  }
787 
788  if (!isToothExpectedNow(timestamp)) {
790  return;
791  }
792 
794 
795 #if EFI_HD_ACR
796  bool firstEventInAWhile = m_lastEventTimer.hasElapsedSec(1);
797  if (firstEventInAWhile) {
798  // let's open that valve on first sign of movement
799  engine->module<HarleyAcr>()->updateAcr();
800  }
801 #endif // EFI_HD_ACR
802 
803  if (boardAllowTriggerActions()) {
804  m_lastEventTimer.reset(timestamp);
805  }
806 
807  int eventIndex = (int) signal;
808  efiAssertVoid(ObdCode::CUSTOM_TRIGGER_EVENT_TYPE, eventIndex >= 0 && eventIndex < HW_EVENT_TYPES, "signal type");
810 
811  // Decode the trigger!
812  auto decodeResult = triggerState.decodeTriggerEvent(
813  "trigger",
814  triggerShape,
815  engine,
817  signal, timestamp);
818 
819  // Don't propagate state if we don't know where we are
820  if (decodeResult) {
822 
823  /**
824  * If we only have a crank position sensor with four stroke, here we are extending crank revolutions with a 360 degree
825  * cycle into a four stroke, 720 degrees cycle.
826  */
827  int crankDivider = getCrankDivider(triggerShape.getWheelOperationMode());
828  int crankInternalIndex = triggerState.getCrankSynchronizationCounter() % crankDivider;
829  int triggerIndexForListeners = decodeResult.Value.CurrentIndex + (crankInternalIndex * triggerShape.getSize());
830 
831  reportEventToWaveChart(signal, triggerIndexForListeners, triggerShape.useOnlyRisingEdges);
832 
833  // Look up this tooth's angle from the sync point. If this tooth is the sync point, we'll get 0 here.
834  auto currentPhaseFromSyncPoint = getTriggerCentral()->triggerFormDetails.eventAngles[triggerIndexForListeners];
835 
836  // Adjust so currentPhase is in engine-space angle, not trigger-space angle
837  currentEngineDecodedPhase = wrapAngleMethod(currentPhaseFromSyncPoint - tdcPosition(), "currentEnginePhase", ObdCode::CUSTOM_ERR_6555);
838 
839  // Record precise time and phase of the engine. This is used for VVT decode, and to check that the
840  // trigger pattern selected matches reality (ie, we check the next tooth is where we think it should be)
841  {
842  // under lock to avoid mismatched tooth phase and time
843  chibios_rt::CriticalSectionLocker csl;
844 
845  m_lastToothTimer.reset(timestamp);
846  m_lastToothPhaseFromSyncPoint = currentPhaseFromSyncPoint;
847  }
848 
849 #if TRIGGER_EXTREME_LOGGING
850  efiPrintf("trigger %d %d %d", triggerIndexForListeners, getRevolutionCounter(), time2print(getTimeNowUs()));
851 #endif /* TRIGGER_EXTREME_LOGGING */
852 
853  // Update engine RPM
854  rpmShaftPositionCallback(signal, triggerIndexForListeners, timestamp);
855 
856  // Schedule the TDC mark
857  tdcMarkCallback(triggerIndexForListeners, timestamp);
858 
859 #if !EFI_UNIT_TEST
860 #if EFI_MAP_AVERAGING
861  mapAveragingTriggerCallback(triggerIndexForListeners, timestamp);
862 #endif /* EFI_MAP_AVERAGING */
863 #endif /* EFI_UNIT_TEST */
864 
865 #if EFI_LOGIC_ANALYZER
866  waTriggerEventListener(signal, triggerIndexForListeners, timestamp);
867 #endif
868 
869  angle_t nextPhase = findNextTriggerToothAngle(triggerIndexForListeners);
870 
871  float expectNextPhase = nextPhase + tdcPosition();
872  wrapAngle(expectNextPhase, "nextEnginePhase", ObdCode::CUSTOM_ERR_6555);
873  expectedNextPhase = expectNextPhase;
874 
875 #if EFI_CDM_INTEGRATION
876  if (trgEventIndex == 0 && isBrainPinValid(engineConfiguration->cdmInputPin)) {
878  engine->knockLogic(cdmKnockValue);
879  }
880 #endif /* EFI_CDM_INTEGRATION */
881 
882  if (engine->rpmCalculator.getCachedRpm() > 0 && triggerIndexForListeners == 0) {
884  }
885 
886  // Handle ignition and injection
887  mainTriggerCallback(triggerIndexForListeners, timestamp, currentEngineDecodedPhase, nextPhase);
888 
889  // Decode the MAP based "cam" sensor
891  } else {
892  // We don't have sync, but report to the wave chart anyway as index 0.
894 
895  expectedNextPhase = unexpected;
896  }
897 }
int getCurrentCdmValue(int currentRevolution)
constexpr auto & module()
Definition: engine.h:177
TpsAccelEnrichment tpsAccelEnrichment
Definition: engine.h:283
float getCachedRpm() const
angle_t findNextTriggerToothAngle(int nextToothIndex)
bool isSpinningJustForWatchdog
expected< float > expectedNextPhase
bool isToothExpectedNow(efitick_t timestamp)
void decodeMapCam(efitick_t nowNt, float currentPhase)
PrimaryTriggerConfiguration primaryTriggerConfiguration
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 noiseFilter(efitick_t nowNt, TriggerDecoderBase *triggerState, trigger_event_e signal)
operation_mode_e getWheelOperationMode() const
efitimeus_t getTimeNowUs()
Definition: efitime.cpp:26
int time2print(int64_t time)
Definition: efitime.h:25
bool warning(ObdCode code, const char *fmt,...)
void waTriggerEventListener(trigger_event_e ckpSignalType, uint32_t index, efitick_t edgeTimestamp)
void mainTriggerCallback(uint32_t trgEventIndex, efitick_t edgeTimestamp, angle_t currentPhase, angle_t nextPhase)
void mapAveragingTriggerCallback(uint32_t index, efitick_t edgeTimestamp)
@ CUSTOM_TRIGGER_EVENT_TYPE
@ CUSTOM_ERR_UNEXPECTED_SHAFT_EVENT
@ ShaftPositionListeners
bool isBrainPinValid(brain_pin_e brainPin)
void tdcMarkCallback(uint32_t trgEventIndex, efitick_t nowNt)
void rpmShaftPositionCallback(trigger_event_e ckpSignalType, uint32_t trgEventIndex, efitick_t nowNt)
Shaft position callback used by RPM calculation logic.
static void reportEventToWaveChart(trigger_event_e ckpSignalType, int triggerEventIndex, bool addOppositeEvent)
int getCrankDivider(operation_mode_e operationMode)
BOARD_WEAK bool boardAllowTriggerActions()
static TriggerWheel eventIndex[4]
bool isUsefulSignal(trigger_event_e signal, const TriggerWaveform &shape)
angle_t wrapAngleMethod(angle_t param, const char *msg="", ObdCode code=ObdCode::OBD_PCM_Processor_Fault)

Referenced by handleShaftSignal().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ isToothExpectedNow()

bool TriggerCentral::isToothExpectedNow ( efitick_t  timestamp)
private

Definition at line 687 of file trigger_central.cpp.

687  {
688  // Check that the expected next phase (from the last tooth) is close to the actual current phase:
689  // basically, check that the tooth width is correct
690  auto estimatedCurrentPhase = getCurrentEnginePhase(timestamp);
691  auto lastToothPhase = m_lastToothPhaseFromSyncPoint;
692 
693  if (expectedNextPhase && estimatedCurrentPhase) {
694  float angleError = expectedNextPhase.Value - estimatedCurrentPhase.Value;
695 
696  // Wrap around correctly at the end of the cycle
697  float cycle = getEngineState()->engineCycle;
698  if (angleError < -cycle / 2) {
699  angleError += cycle;
700  }
701 
702  triggerToothAngleError = angleError;
703 
704  // Only perform checks if engine is spinning quickly
705  // All kinds of garbage happens while cranking
706  if (Sensor::getOrZero(SensorType::Rpm) > 1000) {
707  // Now compute how close we are to the last tooth decoded
708  float angleSinceLastTooth = estimatedCurrentPhase.Value - lastToothPhase;
709  if (angleSinceLastTooth < 0.5f) {
710  // This tooth came impossibly early, ignore it
711  // This rejects things like doubled edges, for example:
712  // |-| |----------------
713  // | | |
714  // ____________| |_|
715  // 1 2
716  // #1 will be decoded
717  // #2 will be ignored
718  // We're not sure which edge was the "real" one, but they were close enough
719  // together that it doesn't really matter.
720  warning(ObdCode::CUSTOM_PRIMARY_DOUBLED_EDGE, "doubled trigger edge after %.2f deg at #%d", angleSinceLastTooth, triggerState.currentCycle.current_index);
721 
722  return false;
723  }
724 
725  // Absolute error from last tooth
726  float absError = absF(angleError);
727  float isRpmEnough = Sensor::getOrZero(SensorType::Rpm) > 1000;
728  // TODO: configurable threshold
729  if (isRpmEnough && absError > 10 && absError < 180) {
730  // This tooth came at a very unexpected time, ignore it
732 
733  // TODO: this causes issues with some real engine logs, should it?
734  // return false;
735  }
736  }
737  } else {
739  }
740 
741  // We aren't ready to reject unexpected teeth, so accept this tooth
742  return true;
743 }
angle_t engineCycle
Definition: engine_state.h:27
expected< float > getCurrentEnginePhase(efitick_t nowNt) const
current_cycle_state_s currentCycle
EngineState * getEngineState()
Definition: engine.cpp:576
@ CUSTOM_PRIMARY_BAD_TOOTH_TIMING
@ CUSTOM_PRIMARY_DOUBLED_EDGE

Referenced by handleShaftSignal().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ isTriggerConfigChanged()

bool TriggerCentral::isTriggerConfigChanged ( )

Definition at line 1208 of file trigger_central.cpp.

1208  {
1210 }

◆ isTriggerDecoderError()

bool TriggerCentral::isTriggerDecoderError ( )
Returns
TRUE is something is wrong with trigger decoding

Definition at line 1240 of file trigger_central.cpp.

1240  {
1241  return triggerErrorDetection.sum(6) > 4;
1242 }
cyclic_buffer< int > triggerErrorDetection

Referenced by isTriggerErrorNow(), and triggerInfo().

Here is the caller graph for this function:

◆ prepareTriggerShape()

void TriggerCentral::prepareTriggerShape ( )
inline

Definition at line 68 of file trigger_central.h.

68  {
69 #if EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT
71  // Nothing to do here if there's a problem with the trigger shape
72  return;
73  }
74 
76 #endif
77  }
void prepareEventAngles(TriggerWaveform *shape)

Referenced by prepareOutputSignals().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ resetCounters()

void TriggerCentral::resetCounters ( )

Definition at line 558 of file trigger_central.cpp.

558  {
559  memset(hwEventCounters, 0, sizeof(hwEventCounters));
560 }

Referenced by resetRunningTriggerCounters().

Here is the caller graph for this function:

◆ syncEnginePhaseAndReport()

angle_t TriggerCentral::syncEnginePhaseAndReport ( int  divider,
int  remainder 
)

we have two kinds of sync: this method is about detecting of exact engine phase with 720 degree precision usually based on cam wheel decoding not to be confused with a totally different trigger wheel sync which could be either crank wheel sync or cam wheel sync

Definition at line 134 of file trigger_central.cpp.

134  {
135  angle_t engineCycle = getEngineCycle(getEngineRotationState()->getOperationMode());
136 
137  angle_t totalShift = triggerState.syncEnginePhase(divider, remainder, engineCycle);
138  if (totalShift != 0) {
139  // Reset instant RPM, since the engine phase has now changed, invalidating the tooth history buffer
140  // maybe TODO: could/should we rotate the buffer around to re-align it instead? Is that worth it?
142  }
143  return totalShift;
144 }
angle_t syncEnginePhase(int divider, int remainder, angle_t engineCycle)
InstantRpmCalculator instantRpm
EngineRotationState * getEngineRotationState()
Definition: engine.cpp:572
angle_t getEngineCycle(operation_mode_e operationMode)

Referenced by adjustCrankPhase().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ updateWaveform()

void TriggerCentral::updateWaveform ( )

this is only useful while troubleshooting a new trigger shape in the field in very VERY rare circumstances

'initState' instance of TriggerDecoderBase is used only to initialize 'this' TriggerWaveform instance #192 BUG real hardware trigger events could be coming even while we are initializing trigger

Definition at line 1114 of file trigger_central.cpp.

1114  {
1115  // Re-read config in case it's changed
1117  for (int camIndex = 0;camIndex < CAMS_PER_BANK;camIndex++) {
1118  vvtTriggerConfiguration[camIndex].update();
1119  }
1120 
1122 
1123  /**
1124  * this is only useful while troubleshooting a new trigger shape in the field
1125  * in very VERY rare circumstances
1126  */
1128  int gapIndex = 0;
1129 
1131 
1132  // copy however many the user wants
1133  for (; gapIndex < engineConfiguration->gapTrackingLengthOverride; gapIndex++) {
1134  float gapOverrideFrom = engineConfiguration->triggerGapOverrideFrom[gapIndex];
1135  float gapOverrideTo = engineConfiguration->triggerGapOverrideTo[gapIndex];
1136  TRIGGER_WAVEFORM(setTriggerSynchronizationGap3(/*gapIndex*/gapIndex, gapOverrideFrom, gapOverrideTo));
1137  }
1138 
1139  // fill the remainder with the default gaps
1140  for (; gapIndex < GAP_TRACKING_LENGTH; gapIndex++) {
1141  triggerShape.synchronizationRatioFrom[gapIndex] = NAN;
1142  triggerShape.synchronizationRatioTo[gapIndex] = NAN;
1143  }
1144  }
1145 
1147  int length = triggerShape.getLength();
1148  engineCycleEventCount = length;
1149 
1150  efiAssertVoid(ObdCode::CUSTOM_SHAPE_LEN_ZERO, length > 0, "shapeLength=0");
1151 
1152  triggerErrorDetection.clear();
1153 
1154  /**
1155  * 'initState' instance of TriggerDecoderBase is used only to initialize 'this' TriggerWaveform instance
1156  * #192 BUG real hardware trigger events could be coming even while we are initializing trigger
1157  */
1159  triggerShape,
1160  initState);
1161  }
1162 
1164  int gapIndex = 0;
1165 
1166  TriggerWaveform *shape = &vvtShape[0];
1167 
1168  for (; gapIndex < engineConfiguration->gapVvtTrackingLengthOverride; gapIndex++) {
1169  float gapOverrideFrom = engineConfiguration->triggerVVTGapOverrideFrom[gapIndex];
1170  float gapOverrideTo = engineConfiguration->triggerVVTGapOverrideTo[gapIndex];
1171  shape->synchronizationRatioFrom[gapIndex] = gapOverrideFrom;
1172  shape->synchronizationRatioTo[gapIndex] = gapOverrideTo;
1173  }
1174  // fill the remainder with the default gaps
1175  for (; gapIndex < VVT_TRACKING_LENGTH; gapIndex++) {
1176  shape->synchronizationRatioFrom[gapIndex] = NAN;
1177  shape->synchronizationRatioTo[gapIndex] = NAN;
1178  }
1179  }
1180 
1181  for (int camIndex = 0; camIndex < CAMS_PER_BANK; camIndex++) {
1182  // todo: should 'vvtWithRealDecoder' be used here?
1183  if (engineConfiguration->vvtMode[camIndex] != VVT_INACTIVE) {
1184  initVvtShape(
1185  vvtShape[camIndex],
1186  vvtTriggerConfiguration[camIndex],
1187  initState
1188  );
1189  }
1190  }
1191 
1192  // This is not the right place for this, but further refactoring has to happen before it can get moved.
1194 
1195 }
TriggerCentral triggerCentral
Definition: engine.h:286
void setNeedsDisambiguation(bool needsDisambiguation)
TriggerWaveform vvtShape[CAMS_PER_BANK]
VvtTriggerConfiguration vvtTriggerConfiguration[CAMS_PER_BANK]
trigger_config_s TriggerType
Trigger shape has all the fields needed to describe and decode trigger signal.
bool needsDisambiguation() const
float synchronizationRatioFrom[GAP_TRACKING_LENGTH]
void initializeTriggerWaveform(operation_mode_e triggerOperationMode, const trigger_config_s &triggerType)
size_t getLength() const
float synchronizationRatioTo[GAP_TRACKING_LENGTH]
@ CUSTOM_SHAPE_LEN_ZERO
operation_mode_e lookupOperationMode()
TriggerDecoderBase initState("init")
static void initVvtShape(TriggerWaveform &shape, const TriggerConfiguration &p_config, TriggerDecoderBase &initState)
static void calculateTriggerSynchPoint(const PrimaryTriggerConfiguration &primaryTriggerConfiguration, TriggerWaveform &shape, TriggerDecoderBase &initState)

Referenced by Engine::updateTriggerWaveform().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ validateCamVvtCounters()

void TriggerCentral::validateCamVvtCounters ( )

Definition at line 1076 of file trigger_central.cpp.

1076  {
1077  // micro-optimized 'crankSynchronizationCounter % 256'
1078  int camVvtValidationIndex = triggerState.getCrankSynchronizationCounter() & 0xFF;
1079  if (camVvtValidationIndex == 0) {
1080  vvtCamCounter = 0;
1081  } else if (camVvtValidationIndex == 0xFE && vvtCamCounter < 60) {
1082  // magic logic: we expect at least 60 CAM/VVT events for each 256 trigger cycles, otherwise throw a code
1083  warning(ObdCode::OBD_Camshaft_Position_Sensor_Circuit_Range_Performance, "No Camshaft Position Sensor signals");
1084  }
1085 }
@ OBD_Camshaft_Position_Sensor_Circuit_Range_Performance

Referenced by rpmShaftPositionCallback().

Here is the call graph for this function:
Here is the caller graph for this function:

Field Documentation

◆ currentVVTEventPosition

angle_t TriggerCentral::currentVVTEventPosition[BANKS_COUNT][CAMS_PER_BANK]

Definition at line 164 of file trigger_central.h.

Referenced by handleVvtCamSignal().

◆ directSelfStimulation

bool TriggerCentral::directSelfStimulation = false

◆ engineCycleEventCount

uint32_t TriggerCentral::engineCycleEventCount = 0

value of 'triggerShape.getLength()' pre-calculating this value is a performance optimization

Definition at line 118 of file trigger_central.h.

Referenced by findNextTriggerToothAngle(), and updateWaveform().

◆ expectedNextPhase

expected<float> TriggerCentral::expectedNextPhase = unexpected
private

Definition at line 217 of file trigger_central.h.

Referenced by handleShaftSignal(), and isToothExpectedNow().

◆ hwTriggerInputEnabled

bool TriggerCentral::hwTriggerInputEnabled = true

◆ instantRpm

InstantRpmCalculator TriggerCentral::instantRpm

◆ isEngineSnifferEnabled

bool TriggerCentral::isEngineSnifferEnabled = false

this is based on engineSnifferRpmThreshold settings and current RPM

Definition at line 203 of file trigger_central.h.

Referenced by TriggerDecoderBase::decodeTriggerEvent(), and Engine::updateSlowSensors().

◆ isSpinningJustForWatchdog

bool TriggerCentral::isSpinningJustForWatchdog = false

By the way: 'cranking' means engine is not stopped and the rpm are below crankingRpm 'running' means RPM are above crankingRpm 'spinning' means the engine is not stopped this is set to true each time we register a trigger tooth signal

Definition at line 109 of file trigger_central.h.

Referenced by Engine::efiWatchdog(), and handleShaftSignal().

◆ m_lastEventTimer

Timer TriggerCentral::m_lastEventTimer

◆ m_lastToothPhaseFromSyncPoint

float TriggerCentral::m_lastToothPhaseFromSyncPoint
private

Definition at line 213 of file trigger_central.h.

Referenced by getCurrentEnginePhase(), handleShaftSignal(), and isToothExpectedNow().

◆ m_lastToothTimer

Timer TriggerCentral::m_lastToothTimer
private

Definition at line 211 of file trigger_central.h.

Referenced by getCurrentEnginePhase(), and handleShaftSignal().

◆ mapCamPrevCycleValue

float TriggerCentral::mapCamPrevCycleValue = 0

Definition at line 111 of file trigger_central.h.

Referenced by decodeMapCam().

◆ noiseFilter

TriggerNoiseFilter TriggerCentral::noiseFilter

◆ prevChangeAtCycle

int TriggerCentral::prevChangeAtCycle = 0

Definition at line 112 of file trigger_central.h.

Referenced by decodeMapCam().

◆ primaryTriggerConfiguration

PrimaryTriggerConfiguration TriggerCentral::primaryTriggerConfiguration

◆ triggerConfigChangedOnLastConfigurationChange

bool TriggerCentral::triggerConfigChangedOnLastConfigurationChange = false

true if a recent configuration change has changed any of the trigger settings which we have not adjusted for yet

Definition at line 123 of file trigger_central.h.

Referenced by checkIfTriggerConfigChanged(), isTriggerConfigChanged(), and onConfigurationChangeTriggerCallback().

◆ triggerErrorDetection

cyclic_buffer<int> TriggerCentral::triggerErrorDetection

◆ triggerFormDetails

TriggerFormDetails TriggerCentral::triggerFormDetails

◆ triggerShape

TriggerWaveform TriggerCentral::triggerShape

◆ triggerState

PrimaryTriggerDecoder TriggerCentral::triggerState

◆ triggerVersion

LocalVersionHolder TriggerCentral::triggerVersion

Definition at line 97 of file trigger_central.h.

Referenced by checkIfTriggerConfigChanged().

◆ vvtEventFallCounter

int TriggerCentral::vvtEventFallCounter[CAM_INPUTS_COUNT]

◆ vvtEventRiseCounter

int TriggerCentral::vvtEventRiseCounter[CAM_INPUTS_COUNT]

◆ vvtPosition

angle_t TriggerCentral::vvtPosition[BANKS_COUNT][CAMS_PER_BANK]

Definition at line 168 of file trigger_central.h.

Referenced by getVVTPosition(), and handleVvtCamSignal().

◆ vvtShape

TriggerWaveform TriggerCentral::vvtShape[CAMS_PER_BANK]

◆ vvtState

VvtTriggerDecoder TriggerCentral::vvtState[BANKS_COUNT][CAMS_PER_BANK]
Initial value:
= {
{
"VVT B1 Int",
#if CAMS_PER_BANK >= 2
"VVT B1 Exh"
#endif
},
#if BANKS_COUNT >= 2
{
"VVT B2 Int",
#if CAMS_PER_BANK >= 2
"VVT B1 Exh"
#endif
}
#endif
}

Definition at line 176 of file trigger_central.h.

Referenced by handleVvtCamSignal(), and Engine::OnTriggerSynchronizationLost().

◆ vvtTriggerConfiguration

VvtTriggerConfiguration TriggerCentral::vvtTriggerConfiguration = {{"VVT1 ", 0}}

The documentation for this class was generated from the following files: