| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /** | |||
| 2 | * @file trigger_decoder.h | |||
| 3 | * | |||
| 4 | * @date Dec 24, 2013 | |||
| 5 | * @author Andrey Belomutskiy, (c) 2012-2020 | |||
| 6 | */ | |||
| 7 | ||||
| 8 | #pragma once | |||
| 9 | ||||
| 10 | #include "trigger_structure.h" | |||
| 11 | #include "trigger_state_generated.h" | |||
| 12 | #include "trigger_state_primary_generated.h" | |||
| 13 | #include <rusefi/timer.h> | |||
| 14 | ||||
| 15 | const char *getTrigger_event_e(trigger_event_e value); | |||
| 16 | const char *getTrigger_value_e(TriggerValue value); | |||
| 17 | ||||
| 18 | struct TriggerStateListener { | |||
| 19 | #if EFI_SHAFT_POSITION_INPUT | |||
| 20 | virtual void OnTriggerStateProperState(efitick_t nowNt, size_t triggerStateIndex) = 0; | |||
| 21 | virtual void OnTriggerSynchronization(bool wasSynchronized, bool isDecodingError) = 0; | |||
| 22 | virtual void OnTriggerSynchronizationLost() = 0; | |||
| 23 | // todo: replace this dirty hack with proper collection (linked list?) of listeners | |||
| 24 | virtual TriggerStateListener* nextListener() = 0; | |||
| 25 | #endif // EFI_SHAFT_POSITION_INPUT | |||
| 26 | }; | |||
| 27 | ||||
| 28 | class TriggerConfiguration { | |||
| 29 | public: | |||
| 30 | 2188 | explicit TriggerConfiguration(const char* printPrefix) : PrintPrefix(printPrefix) {} | ||
| 31 | void update(); | |||
| 32 | ||||
| 33 | const char* const PrintPrefix; | |||
| 34 | bool VerboseTriggerSynchDetails; | |||
| 35 | trigger_config_s TriggerType; | |||
| 36 | ||||
| 37 | protected: | |||
| 38 | virtual bool isVerboseTriggerSynchDetails() const = 0; | |||
| 39 | public: | |||
| 40 | virtual trigger_config_s getType() const = 0; | |||
| 41 | }; | |||
| 42 | ||||
| 43 | class PrimaryTriggerConfiguration final : public TriggerConfiguration { | |||
| 44 | public: | |||
| 45 | 678 | PrimaryTriggerConfiguration() : TriggerConfiguration("TRG ") {} | ||
| 46 | ||||
| 47 | protected: | |||
| 48 | bool isVerboseTriggerSynchDetails() const override; | |||
| 49 | trigger_config_s getType() const override; | |||
| 50 | }; | |||
| 51 | ||||
| 52 | class VvtTriggerConfiguration final : public TriggerConfiguration { | |||
| 53 | public: | |||
| 54 | const int index; | |||
| 55 | ||||
| 56 | 1356 | VvtTriggerConfiguration(const char * prefix, const int p_index) : TriggerConfiguration(prefix), index(p_index) { | ||
| 57 | 1356 | } | ||
| 58 | ||||
| 59 | protected: | |||
| 60 | bool isVerboseTriggerSynchDetails() const override; | |||
| 61 | trigger_config_s getType() const override; | |||
| 62 | }; | |||
| 63 | ||||
| 64 | typedef struct { | |||
| 65 | /** | |||
| 66 | * index within trigger revolution, from 0 to trigger event count | |||
| 67 | */ | |||
| 68 | uint32_t current_index; | |||
| 69 | /** | |||
| 70 | * Number of actual events of each channel within current trigger cycle, these | |||
| 71 | * values are used to detect trigger signal errors. | |||
| 72 | * see TriggerWaveform | |||
| 73 | */ | |||
| 74 | size_t eventCount[PWM_PHASE_MAX_WAVE_PER_PWM]; | |||
| 75 | ||||
| 76 | } current_cycle_state_s; | |||
| 77 | ||||
| 78 | struct TriggerDecodeResult { | |||
| 79 | uint32_t CurrentIndex; | |||
| 80 | }; | |||
| 81 | ||||
| 82 | /** | |||
| 83 | * @see TriggerWaveform for trigger wheel shape definition | |||
| 84 | */ | |||
| 85 | class TriggerDecoderBase : public trigger_state_s { | |||
| 86 | public: | |||
| 87 | TriggerDecoderBase(const char* name); | |||
| 88 | ||||
| 89 | void printGaps(const char * prefix, | |||
| 90 | const TriggerConfiguration& triggerConfiguration, | |||
| 91 | const TriggerWaveform& triggerShape); | |||
| 92 | ||||
| 93 | /** | |||
| 94 | * current trigger processing index, between zero and #size | |||
| 95 | */ | |||
| 96 | int getCurrentIndex() const; | |||
| 97 | int getSynchronizationCounter() const; | |||
| 98 | /** | |||
| 99 | * this is important for crank-based virtual trigger and VVT magic | |||
| 100 | */ | |||
| 101 | void incrementShaftSynchronizationCounter(); | |||
| 102 | ||||
| 103 | #if EFI_UNIT_TEST | |||
| 104 | /** | |||
| 105 | * used only for trigger export | |||
| 106 | */ | |||
| 107 | float gapRatio[PWM_PHASE_MAX_COUNT * 6]; | |||
| 108 | #endif // EFI_UNIT_TEST | |||
| 109 | ||||
| 110 | int64_t getTotalEventCounter() const; | |||
| 111 | ||||
| 112 | expected<TriggerDecodeResult> decodeTriggerEvent( | |||
| 113 | const char *msg, | |||
| 114 | const TriggerWaveform& triggerShape, | |||
| 115 | TriggerStateListener* triggerStateListener, | |||
| 116 | const TriggerConfiguration& triggerConfiguration, | |||
| 117 | const trigger_event_e signal, | |||
| 118 | const efitick_t nowNt); | |||
| 119 | ||||
| 120 | void onShaftSynchronization( | |||
| 121 | bool wasSynchronized, | |||
| 122 | const efitick_t nowNt, | |||
| 123 | const TriggerWaveform& triggerShape); | |||
| 124 | ||||
| 125 | bool isValidIndex(const TriggerWaveform& triggerShape) const; | |||
| 126 | ||||
| 127 | /** | |||
| 128 | * TRUE if we know where we are | |||
| 129 | */ | |||
| 130 | bool shaft_is_synchronized = false; | |||
| 131 | efitick_t mostRecentSyncTime; | |||
| 132 | ||||
| 133 | Timer previousEventTimer; | |||
| 134 | ||||
| 135 | /** | |||
| 136 | * current duration at index zero and previous durations are following | |||
| 137 | */ | |||
| 138 | uint32_t toothDurations[GAP_TRACKING_LENGTH + 1]; | |||
| 139 | ||||
| 140 | efitick_t toothed_previous_time; | |||
| 141 | ||||
| 142 | current_cycle_state_s currentCycle; | |||
| 143 | const char* const name; | |||
| 144 | ||||
| 145 | /** | |||
| 146 | * how many times since ECU reboot we had unexpected number of teeth in trigger cycle | |||
| 147 | */ | |||
| 148 | uint32_t totalTriggerErrorCounter; | |||
| 149 | uint32_t orderingErrorCounter; | |||
| 150 | ||||
| 151 | virtual void resetState(); | |||
| 152 | void setShaftSynchronized(bool value); | |||
| 153 | bool getShaftSynchronized() const; | |||
| 154 | ||||
| 155 | /** | |||
| 156 | * this is start of real trigger cycle | |||
| 157 | * for virtual double trigger see timeAtVirtualZeroNt | |||
| 158 | */ | |||
| 159 | efitick_t startOfCycleNt; | |||
| 160 | ||||
| 161 | uint32_t findTriggerZeroEventIndex( | |||
| 162 | TriggerWaveform& shape, | |||
| 163 | const TriggerConfiguration& triggerConfiguration | |||
| 164 | ); | |||
| 165 | ||||
| 166 | 203374 | bool someSortOfTriggerError() const { | ||
| 167 | 203374 | return !m_timeSinceDecodeError.hasElapsedSec(0.3); | ||
| 168 | } | |||
| 169 | ||||
| 170 | protected: | |||
| 171 | // Called when some problem is detected with trigger decoding. | |||
| 172 | // That means either: | |||
| 173 | // - Too many events without a sync point | |||
| 174 | // - Saw a sync point but the wrong number of events in the cycle | |||
| 175 | 46 | virtual void onTriggerError() { } | ||
| 176 | ||||
| 177 | 12 | virtual void onNotEnoughTeeth(int, int) { } | ||
| 178 | ✗ | virtual void onTooManyTeeth(int, int) { } | ||
| 179 | ||||
| 180 | private: | |||
| 181 | void setTriggerErrorState(int errorIncrement = 1); | |||
| 182 | void resetCurrentCycleState(); | |||
| 183 | bool isSyncPoint(const TriggerWaveform& triggerShape, trigger_type_e triggerType) const; | |||
| 184 | ||||
| 185 | int getEventCountersError(const TriggerWaveform& triggerShape) const; | |||
| 186 | ||||
| 187 | trigger_event_e prevSignal; | |||
| 188 | int64_t totalEventCountBase; | |||
| 189 | ||||
| 190 | bool isFirstEvent; | |||
| 191 | ||||
| 192 | Timer m_timeSinceDecodeError; | |||
| 193 | }; | |||
| 194 | ||||
| 195 | /** | |||
| 196 | * the reason for sub-class is simply to save RAM but not having statistics in the trigger initialization instance | |||
| 197 | */ | |||
| 198 | class PrimaryTriggerDecoder : public TriggerDecoderBase, public trigger_state_primary_s { | |||
| 199 | public: | |||
| 200 | PrimaryTriggerDecoder(const char* name); | |||
| 201 | void resetState() override; | |||
| 202 | ||||
| 203 | 1792 | void resetHasFullSync() { | ||
| 204 | // If this trigger doesn't need disambiguation, we already have phase sync | |||
| 205 | 1792 | m_hasSynchronizedPhase = !m_needsDisambiguation; | ||
| 206 | 1792 | } | ||
| 207 | ||||
| 208 | /** | |||
| 209 | * returns zero if we were lucky to have correct engine phase, otherwise angle of engine phase correction which was applied. | |||
| 210 | */ | |||
| 211 | angle_t syncEnginePhase(int divider, int remainder, angle_t engineCycle); | |||
| 212 | ||||
| 213 | // Returns true if syncEnginePhase has been called, | |||
| 214 | // i.e. if we have enough VVT information to have full sync on | |||
| 215 | // an indeterminate crank pattern | |||
| 216 | 131463 | bool hasSynchronizedPhase() const { | ||
| 217 | 131463 | return m_hasSynchronizedPhase; | ||
| 218 | } | |||
| 219 | ||||
| 220 | 911 | void setNeedsDisambiguation(bool needsDisambiguation) { | ||
| 221 | 911 | m_needsDisambiguation = needsDisambiguation; | ||
| 222 | ||||
| 223 | 911 | resetHasFullSync(); | ||
| 224 | 911 | } | ||
| 225 | ||||
| 226 | void onTriggerError() override; | |||
| 227 | ||||
| 228 | void onNotEnoughTeeth(int actual, int expected) override; | |||
| 229 | void onTooManyTeeth(int actual, int expected) override; | |||
| 230 | ||||
| 231 | private: | |||
| 232 | ||||
| 233 | bool m_needsDisambiguation = false; | |||
| 234 | }; | |||
| 235 | ||||
| 236 | class VvtTriggerDecoder : public TriggerDecoderBase { | |||
| 237 | public: | |||
| 238 | 2712 | VvtTriggerDecoder(const char* p_name) : TriggerDecoderBase(p_name) { } | ||
| 239 | ||||
| 240 | void onNotEnoughTeeth(int actual, int expected) override; | |||
| 241 | void onTooManyTeeth(int actual, int expected) override; | |||
| 242 | }; | |||
| 243 | ||||
| 244 | angle_t getEngineCycle(operation_mode_e operationMode); | |||
| 245 |