rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
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 applyShapesConfiguration ()
 
angle_t findNextTriggerToothAngle (int nextToothIndex)
 
void prepareTriggerShape ()
 
bool checkIfTriggerConfigChanged ()
 
bool isTriggerConfigChanged ()
 
bool isTriggerDecoderError ()
 
expected< floatgetCurrentEnginePhase (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
uint16_t hwEventCounters [HW_EVENT_TYPES] = {}
 
uint16_t vvtCamCounter = (uint16_t)0
 
uint8_t alignmentFill_at_10 [2] = {}
 
float mapVvt_MAP_AT_SPECIAL_POINT = (float)0
 
float mapVvt_MAP_AT_DIFF = (float)0
 
int8_t mapVvt_MAP_AT_CYCLE_COUNT = (int8_t)0
 
uint8_t mapVvt_sync_counter = (uint8_t)0
 
uint8_t alignmentFill_at_22 [2] = {}
 
float currentEngineDecodedPhase = (float)0
 
float triggerToothAngleError = (float)0
 
uint8_t triggerIgnoredToothCount = (uint8_t)0
 
uint8_t alignmentFill_at_33 [3] = {}
 
angle_t mapCamPrevToothAngle = (angle_t)0
 
bool isDecodingMapCam: 1 {}
 
bool unusedBit_14_1: 1 {}
 
bool unusedBit_14_2: 1 {}
 
bool unusedBit_14_3: 1 {}
 
bool unusedBit_14_4: 1 {}
 
bool unusedBit_14_5: 1 {}
 
bool unusedBit_14_6: 1 {}
 
bool unusedBit_14_7: 1 {}
 
bool unusedBit_14_8: 1 {}
 
bool unusedBit_14_9: 1 {}
 
bool unusedBit_14_10: 1 {}
 
bool unusedBit_14_11: 1 {}
 
bool unusedBit_14_12: 1 {}
 
bool unusedBit_14_13: 1 {}
 
bool unusedBit_14_14: 1 {}
 
bool unusedBit_14_15: 1 {}
 
bool unusedBit_14_16: 1 {}
 
bool unusedBit_14_17: 1 {}
 
bool unusedBit_14_18: 1 {}
 
bool unusedBit_14_19: 1 {}
 
bool unusedBit_14_20: 1 {}
 
bool unusedBit_14_21: 1 {}
 
bool unusedBit_14_22: 1 {}
 
bool unusedBit_14_23: 1 {}
 
bool unusedBit_14_24: 1 {}
 
bool unusedBit_14_25: 1 {}
 
bool unusedBit_14_26: 1 {}
 
bool unusedBit_14_27: 1 {}
 
bool unusedBit_14_28: 1 {}
 
bool unusedBit_14_29: 1 {}
 
bool unusedBit_14_30: 1 {}
 
bool unusedBit_14_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< floatexpectedNextPhase = 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 :
47 triggerState("TRG")
48{
52}
PrimaryTriggerDecoder triggerState
int vvtEventFallCounter[CAM_INPUTS_COUNT]
angle_t vvtPosition[BANKS_COUNT][CAMS_PER_BANK]
TriggerNoiseFilter noiseFilter
int vvtEventRiseCounter[CAM_INPUTS_COUNT]
uint16_t hwEventCounters[HW_EVENT_TYPES]
void setArrayValues(TValue(&array)[TSize], float value)
Here is the call graph for this function:

Member Function Documentation

◆ applyShapesConfiguration()

void TriggerCentral::applyShapesConfiguration ( )

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 1125 of file trigger_central.cpp.

1125 {
1126 // Re-read config in case it's changed
1128 for (int camIndex = 0;camIndex < CAMS_PER_BANK;camIndex++) {
1129 vvtTriggerConfiguration[camIndex].update();
1130 }
1131
1133
1134 /**
1135 * this is only useful while troubleshooting a new trigger shape in the field
1136 * in very VERY rare circumstances
1137 */
1139 int gapIndex = 0;
1140
1142
1143 // copy however many the user wants
1144 for (; gapIndex < engineConfiguration->gapTrackingLengthOverride; gapIndex++) {
1145 float gapOverrideFrom = engineConfiguration->triggerGapOverrideFrom[gapIndex];
1146 float gapOverrideTo = engineConfiguration->triggerGapOverrideTo[gapIndex];
1147 triggerShape.setTriggerSynchronizationGap3(/*gapIndex*/gapIndex, gapOverrideFrom, gapOverrideTo);
1148 }
1149
1150 // fill the remainder with the default gaps
1151 for (; gapIndex < GAP_TRACKING_LENGTH; gapIndex++) {
1152 triggerShape.synchronizationRatioFrom[gapIndex] = NAN;
1153 triggerShape.synchronizationRatioTo[gapIndex] = NAN;
1154 }
1155 }
1156
1158 int length = triggerShape.getLength();
1159 engineCycleEventCount = length;
1160
1161 efiAssertVoid(ObdCode::CUSTOM_SHAPE_LEN_ZERO, length > 0, "shapeLength=0");
1162
1163 triggerErrorDetection.clear();
1164
1165 /**
1166 * 'initState' instance of TriggerDecoderBase is used only to initialize 'this' TriggerWaveform instance
1167 * #192 BUG real hardware trigger events could be coming even while we are initializing trigger
1168 */
1171 initState);
1172 }
1173
1175 int gapIndex = 0;
1176
1177 TriggerWaveform *shape = &vvtShape[0];
1179
1180 for (; gapIndex < engineConfiguration->gapVvtTrackingLengthOverride; gapIndex++) {
1181 float gapOverrideFrom = engineConfiguration->triggerVVTGapOverrideFrom[gapIndex];
1182 float gapOverrideTo = engineConfiguration->triggerVVTGapOverrideTo[gapIndex];
1183 shape->synchronizationRatioFrom[gapIndex] = gapOverrideFrom;
1184 shape->synchronizationRatioTo[gapIndex] = gapOverrideTo;
1185 }
1186 // fill the remainder with the default gaps
1187 for (; gapIndex < VVT_TRACKING_LENGTH; gapIndex++) {
1188 shape->synchronizationRatioFrom[gapIndex] = NAN;
1189 shape->synchronizationRatioTo[gapIndex] = NAN;
1190 }
1191 }
1192
1193 for (int camIndex = 0; camIndex < CAMS_PER_BANK; camIndex++) {
1194 // todo: should 'vvtWithRealDecoder' be used here?
1195 if (engineConfiguration->vvtMode[camIndex] != VVT_INACTIVE) {
1197 vvtShape[camIndex],
1198 vvtTriggerConfiguration[camIndex],
1199 initState
1200 );
1201 }
1202 }
1203
1204 // This is not the right place for this, but further refactoring has to happen before it can get moved.
1206
1207}
TriggerCentral triggerCentral
Definition engine.h:301
void setNeedsDisambiguation(bool needsDisambiguation)
TriggerWaveform vvtShape[CAMS_PER_BANK]
TriggerWaveform triggerShape
cyclic_buffer< int > triggerErrorDetection
VvtTriggerConfiguration vvtTriggerConfiguration[CAMS_PER_BANK]
PrimaryTriggerConfiguration primaryTriggerConfiguration
uint32_t engineCycleEventCount
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, bool isCrankWheel=true)
size_t getLength() const
float synchronizationRatioTo[GAP_TRACKING_LENGTH]
void setTriggerSynchronizationGap3(int index, float syncRatioFrom, float syncRatioTo)
static Engine *const engine
Definition engine.h:386
static constexpr engine_configuration_s * engineConfiguration
@ 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::updateTriggerConfiguration().

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

◆ checkIfTriggerConfigChanged()

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

Definition at line 1212 of file trigger_central.cpp.

1212 {
1213 // we want to make sure that configuration has changed AND that change has changed trigger specifically
1215 triggerConfigChangedOnLastConfigurationChange = false; // whoever has called the method is supposed to react to changes
1216 return result;
1217}
int getGlobalConfigurationVersion(void) const
Definition engine.cpp:297
bool isOld(int globalVersion)
LocalVersionHolder triggerVersion
bool triggerConfigChangedOnLastConfigurationChange
Here is the call graph for this function:

◆ decodeMapCam()

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

Definition at line 660 of file trigger_central.cpp.

660 {
661 isDecodingMapCam = engineConfiguration->vvtMode[0] == VVT_MAP_V_TWIN &&
663 if (isDecodingMapCam) {
664 // 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.
665 auto toothAngle360 = currentPhase;
666 while (toothAngle360 >= 360) {
667 toothAngle360 -= 360;
668 }
669
670 if (mapCamPrevToothAngle < engineConfiguration->mapCamDetectionAnglePosition && toothAngle360 > engineConfiguration->mapCamDetectionAnglePosition) {
671 // we are somewhere close to 'mapCamDetectionAnglePosition'
672
673 // warning: hack hack hack
675
676 // Compute diff against the last time we were here
677 float instantMapDiffBetweenReadoutAngles = map - mapCamPrevCycleValue;
679
680 if (instantMapDiffBetweenReadoutAngles > engineConfiguration->mapSyncThreshold) {
682 int revolutionCounter = getTriggerCentral()->triggerState.getSynchronizationCounter();
683 mapVvt_MAP_AT_CYCLE_COUNT = revolutionCounter - prevChangeAtCycle;
684 prevChangeAtCycle = revolutionCounter;
685
686 hwHandleVvtCamSignal(TriggerValue::RISE, timestamp, /*index*/0);
687 hwHandleVvtCamSignal(TriggerValue::FALL, timestamp, /*index*/0);
688#if EFI_UNIT_TEST
689 // hack? feature? existing unit test relies on VVT phase available right away
690 // but current implementation which is based on periodicFastCallback would only make result available on NEXT tooth
692#endif // EFI_UNIT_TEST
693 }
694
696 mapVvt_MAP_AT_DIFF = instantMapDiffBetweenReadoutAngles;
697 }
698
699 mapCamPrevToothAngle = toothAngle360;
700 }
701}
TunerStudioOutputChannels outputChannels
Definition engine.h:104
void onFastCallback() override
static float getOrZero(SensorType type)
Definition sensor.h:83
int getSynchronizationCounter() const
LimpManager * getLimpManager()
Definition engine.cpp:594
scaled_channel< uint16_t, 30, 1 > instantMAPValue
TriggerCentral * getTriggerCentral()
Definition engine.cpp:588
void hwHandleVvtCamSignal(TriggerValue front, 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 156 of file trigger_central.h.

156 {
158 }
bool engineMovedRecently() const
efitick_t getTimeNowNt()
Definition efitime.cpp:19

Referenced by engineMovedRecently().

Here is the call graph for this function:
Here is the caller 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 // todo: this user-defined property is a quick solution, proper fix https://github.com/rusefi/rusefi/issues/6593 is needed
141 return false;
142 }
143
144 constexpr float oneRevolutionLimitInSeconds = 60.0 / RPM_LOW_THRESHOLD;
145 auto maxAverageToothTime = oneRevolutionLimitInSeconds / triggerShape.getSize();
146
147 // Some triggers may have long gaps (with many teeth), don't count that as stopped!
148 auto maxAllowedGap = maxAverageToothTime * 10;
149
150 // Clamp between 0.1 seconds ("instant" for a human) and worst case of one engine cycle on low tooth count wheel
151 maxAllowedGap = clampF(0.1f, maxAllowedGap, oneRevolutionLimitInSeconds);
152
153 return (getSecondsSinceTriggerEvent(nowNt) < maxAllowedGap) || directSelfStimulation;
154 }
float getSecondsSinceTriggerEvent(efitick_t nowNt) const
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 763 of file trigger_central.cpp.

763 {
764 int currentToothIndex = p_currentToothIndex;
765 // TODO: is this logic to compute next trigger tooth angle correct?
766 angle_t nextToothAngle = 0;
767
768 int loopAllowance = 2 * engineCycleEventCount + 1000;
769 do {
770 // I don't love this.
771 currentToothIndex = (currentToothIndex + 1) % engineCycleEventCount;
772 nextToothAngle = getTriggerCentral()->triggerFormDetails.eventAngles[currentToothIndex] - tdcPosition();
773 wrapAngle(nextToothAngle, "nextEnginePhase", ObdCode::CUSTOM_ERR_6555);
774 } while (nextToothAngle == currentEngineDecodedPhase && --loopAllowance > 0); // '==' for float works here since both values come from 'eventAngles' array
775 if (nextToothAngle != 0 && loopAllowance == 0) {
776 // HW CI fails here, looks like we sometimes change trigger while still handling it?
777 firmwareError(ObdCode::CUSTOM_ERR_TRIGGER_ZERO, "handleShaftSignal unexpected loop end %d %d %f %f", p_currentToothIndex, engineCycleEventCount, nextToothAngle, currentEngineDecodedPhase);
778 }
779 return nextToothAngle;
780}
TriggerFormDetails triggerFormDetails
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
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 74 of file trigger_central.cpp.

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

Referenced by getAcrState(), handleVvtCamSignal(), isToothExpectedNow(), HpfpController::pinTurnOff(), and HpfpController::pinTurnOn().

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 64 of file trigger_central.cpp.

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

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 785 of file trigger_central.cpp.

785 {
787 // trigger is broken, we cannot do anything here
788 warning(ObdCode::CUSTOM_ERR_UNEXPECTED_SHAFT_EVENT, "Shaft event while trigger is mis-configured");
789 return;
790 }
791
792 // This code gathers some statistics on signals and compares accumulated periods to filter interference
794 if (!noiseFilter.noiseFilter(timestamp, &triggerState, signal)) {
795 return;
796 }
797 if (!isUsefulSignal(signal, triggerShape)) {
798 return;
799 }
800 }
801
802 if (!isToothExpectedNow(timestamp)) {
804 return;
805 }
806
808
809#if EFI_HD_ACR
810 bool firstEventInAWhile = m_lastEventTimer.hasElapsedSec(1);
811 if (firstEventInAWhile) {
812 // let's open that valve on first sign of movement
813 engine->module<HarleyAcr>()->updateAcr();
814 }
815#endif // EFI_HD_ACR
816
818 m_lastEventTimer.reset(timestamp);
819 }
820
821 int eventIndex = (int) signal;
822 efiAssertVoid(ObdCode::CUSTOM_TRIGGER_EVENT_TYPE, eventIndex >= 0 && eventIndex < HW_EVENT_TYPES, "signal type");
824
825 // Decode the trigger!
826 auto decodeResult = triggerState.decodeTriggerEvent(
827 "trigger",
829 engine,
831 signal, timestamp);
832
833 // Don't propagate state if we don't know where we are
834 if (decodeResult) {
836
837 /**
838 * If we only have a crank position sensor with four stroke, here we are extending crank revolutions with a 360 degree
839 * cycle into a four stroke, 720 degrees cycle.
840 */
842 int crankInternalIndex = triggerState.getSynchronizationCounter() % crankDivider;
843 int triggerIndexForListeners = decodeResult.Value.CurrentIndex + (crankInternalIndex * triggerShape.getSize());
844
845 reportEventToWaveChart(signal, triggerIndexForListeners, triggerShape.useOnlyRisingEdges);
846
847 // Look up this tooth's angle from the sync point. If this tooth is the sync point, we'll get 0 here.
848 auto currentPhaseFromSyncPoint = getTriggerCentral()->triggerFormDetails.eventAngles[triggerIndexForListeners];
849
850 // Adjust so currentPhase is in engine-space angle, not trigger-space angle
851 currentEngineDecodedPhase = wrapAngleMethod(currentPhaseFromSyncPoint - tdcPosition(), "currentEnginePhase", ObdCode::CUSTOM_ERR_6555);
852
853 // Record precise time and phase of the engine. This is used for VVT decode, and to check that the
854 // trigger pattern selected matches reality (ie, we check the next tooth is where we think it should be)
855 {
856 // under lock to avoid mismatched tooth phase and time
857 chibios_rt::CriticalSectionLocker csl;
858
859 m_lastToothTimer.reset(timestamp);
860 m_lastToothPhaseFromSyncPoint = currentPhaseFromSyncPoint;
861 }
862
863#if TRIGGER_EXTREME_LOGGING
864 efiPrintf("trigger %d %d %d", triggerIndexForListeners, getRevolutionCounter(), time2print(getTimeNowUs()));
865#endif /* TRIGGER_EXTREME_LOGGING */
866
867 // Update engine RPM
868 rpmShaftPositionCallback(signal, triggerIndexForListeners, timestamp);
869
870 // Schedule the TDC mark
871 tdcMarkCallback(triggerIndexForListeners, timestamp);
872
873
874#if EFI_LOGIC_ANALYZER
875 waTriggerEventListener(signal, triggerIndexForListeners, timestamp);
876#endif
877
878 angle_t nextPhase = findNextTriggerToothAngle(triggerIndexForListeners);
879
880 float expectNextPhase = nextPhase + tdcPosition();
881 wrapAngle(expectNextPhase, "nextEnginePhase", ObdCode::CUSTOM_ERR_6555);
882 expectedNextPhase = expectNextPhase;
883
884#if EFI_CDM_INTEGRATION
885 if (trgEventIndex == 0 && isBrainPinValid(engineConfiguration->cdmInputPin)) {
887 engine->knockLogic(cdmKnockValue);
888 }
889#endif /* EFI_CDM_INTEGRATION */
890
891 if (engine->rpmCalculator.getCachedRpm() > 0 && triggerIndexForListeners == 0) {
892 engine->module<TpsAccelEnrichment>()->onEngineCycleTps();
893 }
894
895 // Handle ignition and injection
896 mainTriggerCallback(triggerIndexForListeners, timestamp, currentEngineDecodedPhase, nextPhase);
897
898 // Decode the MAP based "cam" sensor
900 } else {
901 // We don't have sync, but report to the wave chart anyway as index 0.
903
904 expectedNextPhase = unexpected;
905 }
906}
int getCurrentCdmValue(int currentRevolution)
constexpr auto & module()
Definition engine.h:189
float getCachedRpm() const
angle_t findNextTriggerToothAngle(int nextToothIndex)
expected< float > expectedNextPhase
bool isToothExpectedNow(efitick_t timestamp)
void decodeMapCam(efitick_t nowNt, float currentPhase)
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)
@ 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)
PUBLIC_API_WEAK bool boardAllowTriggerActions()
int getCrankDivider(operation_mode_e operationMode)
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 703 of file trigger_central.cpp.

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

◆ isTriggerDecoderError()

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

Definition at line 1252 of file trigger_central.cpp.

1252 {
1253 return triggerErrorDetection.sum(6) > 4;
1254}

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 575 of file trigger_central.cpp.

575 {
576 memset(hwEventCounters, 0, sizeof(hwEventCounters));
577}

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 138 of file trigger_central.cpp.

138 {
139 angle_t engineCycle = getEngineCycle(getEngineRotationState()->getOperationMode());
140
141 angle_t totalShift = triggerState.syncEnginePhase(divider, remainder, engineCycle);
142 if (totalShift != 0) {
143 // Reset instant RPM, since the engine phase has now changed, invalidating the tooth history buffer
144 // maybe TODO: could/should we rotate the buffer around to re-align it instead? Is that worth it?
146 }
147 return totalShift;
148}
angle_t syncEnginePhase(int divider, int remainder, angle_t engineCycle)
InstantRpmCalculator instantRpm
EngineRotationState * getEngineRotationState()
Definition engine.cpp:571
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:

◆ validateCamVvtCounters()

void TriggerCentral::validateCamVvtCounters ( )

Definition at line 1087 of file trigger_central.cpp.

1087 {
1088 // micro-optimized 'synchronizationCounter % 256'
1089 int camVvtValidationIndex = triggerState.getSynchronizationCounter() & 0xFF;
1090 if (camVvtValidationIndex == 0) {
1091 vvtCamCounter = 0;
1092 } else if (camVvtValidationIndex == 0xFE && vvtCamCounter < 60) {
1093 // magic logic: we expect at least 60 CAM/VVT events for each 256 trigger cycles, otherwise throw a code
1094 warning(ObdCode::OBD_Camshaft_Position_Sensor_Circuit_Range_Performance, "No Camshaft Position Sensor signals");
1095 }
1096}
@ 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 169 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 applyShapesConfiguration(), and findNextTriggerToothAngle().

◆ expectedNextPhase

expected<float> TriggerCentral::expectedNextPhase = unexpected
private

Definition at line 222 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 208 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 218 of file trigger_central.h.

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

◆ m_lastToothTimer

Timer TriggerCentral::m_lastToothTimer
private

Definition at line 216 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 173 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 181 of file trigger_central.h.

181 {
182 {
183 "VVT B1 Int",
184#if CAMS_PER_BANK >= 2
185 "VVT B1 Exh"
186#endif
187 },
188#if BANKS_COUNT >= 2
189 {
190 "VVT B2 Int",
191#if CAMS_PER_BANK >= 2
192 "VVT B1 Exh"
193#endif
194 }
195#endif
196 };

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

◆ vvtTriggerConfiguration

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

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