Line | Branch | Decision | Exec | Source |
---|---|---|---|---|
1 | /** | |||
2 | * @file trigger_structure.h | |||
3 | * | |||
4 | * rusEFI defines trigger shape programmatically in C code | |||
5 | * For integration we have exportAllTriggers export | |||
6 | * | |||
7 | * @date Dec 22, 2013 | |||
8 | * @author Andrey Belomutskiy, (c) 2012-2020 | |||
9 | */ | |||
10 | ||||
11 | #pragma once | |||
12 | ||||
13 | #include "state_sequence.h" | |||
14 | ||||
15 | #define FOUR_STROKE_ENGINE_CYCLE 720 | |||
16 | ||||
17 | #define TRIGGER_GAP_DEVIATION 0.25f | |||
18 | #define TRIGGER_GAP_DEVIATION_LOW (1.0f - TRIGGER_GAP_DEVIATION) | |||
19 | #define TRIGGER_GAP_DEVIATION_HIGH (1.0f + TRIGGER_GAP_DEVIATION) | |||
20 | ||||
21 | #if EFI_ENABLE_ASSERTS | |||
22 | #define assertAngleRange(angle, msg, code) if (angle > 10000000 || angle < -10000000) { firmwareError(code, "angle range %s %d", msg, (int)angle);angle = 0;} | |||
23 | #else | |||
24 | #define assertAngleRange(angle, msg, code) {UNUSED(code);} | |||
25 | #endif | |||
26 | ||||
27 | // Shifts angle into the [0..720) range for four stroke and [0..360) for two stroke | |||
28 | // See also wrapVvt | |||
29 | void wrapAngle(angle_t& angle, const char* msg, ObdCode code); | |||
30 | ||||
31 | // proper method avoids un-wrapped state of variables | |||
32 | 31500 | inline angle_t wrapAngleMethod(angle_t param, const char *msg = "", ObdCode code = ObdCode::OBD_PCM_Processor_Fault) { | ||
33 | 31500 | wrapAngle(param, msg, code); | ||
34 | 31500 | return param; | ||
35 | } | |||
36 | ||||
37 | class TriggerDecoderBase; | |||
38 | class TriggerFormDetails; | |||
39 | class TriggerConfiguration; | |||
40 | ||||
41 | #include "sync_edge.h" | |||
42 | ||||
43 | /** | |||
44 | * @brief Trigger shape has all the fields needed to describe and decode trigger signal. | |||
45 | * @see TriggerState for trigger decoder state which works based on this trigger shape model | |||
46 | */ | |||
47 | class TriggerWaveform { | |||
48 | public: | |||
49 | TriggerWaveform(); | |||
50 | void initializeTriggerWaveform(operation_mode_e triggerOperationMode, const trigger_config_s &triggerType, bool isCrankWheel = true); | |||
51 | void setShapeDefinitionError(bool value); | |||
52 | ||||
53 | /** | |||
54 | * Simplest trigger shape does not require any synchronization - for example if there is only | |||
55 | * one primary channel tooth each raising (or falling depending on configuration) front would synchronize | |||
56 | */ | |||
57 | bool isSynchronizationNeeded; | |||
58 | ||||
59 | /** | |||
60 | * trigger meta information: is second wheel mounted on crank shaft ('false') or cam shaft ('true') | |||
61 | */ | |||
62 | bool isSecondWheelCam; | |||
63 | /** | |||
64 | * number of consecutive trigger gaps needed to synchronize | |||
65 | */ | |||
66 | int gapTrackingLength = 1; | |||
67 | /** | |||
68 | * special case for triggers which do not provide exact TDC location | |||
69 | * For example pick-up in distributor with mechanical ignition firing order control. | |||
70 | */ | |||
71 | bool shapeWithoutTdc = false; | |||
72 | /** | |||
73 | * this flag tells us if we should ignore events on second input channel | |||
74 | * that's the way to ignore noise from the disconnected wire | |||
75 | */ | |||
76 | bool needSecondTriggerInput = false; | |||
77 | /** | |||
78 | * true value here means that we do not have a valid trigger configuration | |||
79 | */ | |||
80 | bool shapeDefinitionError = false; | |||
81 | ||||
82 | /** | |||
83 | * this variable is incremented after each trigger shape redefinition | |||
84 | */ | |||
85 | int version = 0; | |||
86 | ||||
87 | /** | |||
88 | * Depending on trigger shape, we use between one and three previous gap ranges to detect synchronization. | |||
89 | * | |||
90 | * Usually second or third gap is not needed, but some crazy triggers like 36-2-2-2 require two consecutive | |||
91 | * gaps ratios to sync | |||
92 | */ | |||
93 | ||||
94 | float synchronizationRatioFrom[GAP_TRACKING_LENGTH]; | |||
95 | float synchronizationRatioTo[GAP_TRACKING_LENGTH]; | |||
96 | ||||
97 | ||||
98 | /** | |||
99 | * used by NoiselessTriggerDecoder (See TriggerCentral::handleShaftSignal()) | |||
100 | */ | |||
101 | int syncRatioAvg; | |||
102 | ||||
103 | ||||
104 | /** | |||
105 | * Trigger indexes within trigger cycle are counted from synchronization point, and all | |||
106 | * engine processes are defined in angles from TDC. | |||
107 | * | |||
108 | * That's the angle distance from trigger event #0 and actual engine TDC | |||
109 | * | |||
110 | * see also globalTriggerAngleOffset | |||
111 | */ | |||
112 | angle_t tdcPosition; | |||
113 | ||||
114 | /** | |||
115 | * In case of a multi-channel trigger, do we want to sync based on primary channel only? | |||
116 | * See also gapBothDirections | |||
117 | */ | |||
118 | bool useOnlyPrimaryForSync; | |||
119 | ||||
120 | // Which edge(s) to consider for finding the sync point: rise, fall, or both | |||
121 | SyncEdge syncEdge; | |||
122 | ||||
123 | // If true, falling edges should be fully ignored on this trigger shape. | |||
124 | bool useOnlyRisingEdges; | |||
125 | ||||
126 | void calculateExpectedEventCounts(); | |||
127 | ||||
128 | size_t getExpectedEventCount(TriggerWheel channelIndex) const; | |||
129 | ||||
130 | /** | |||
131 | * This is used for signal validation | |||
132 | */ | |||
133 | size_t expectedEventCount[PWM_PHASE_MAX_WAVE_PER_PWM]; | |||
134 | ||||
135 | #if EFI_UNIT_TEST | |||
136 | /** | |||
137 | * These signals are used for trigger export only | |||
138 | */ | |||
139 | TriggerWheel triggerSignalIndeces[PWM_PHASE_MAX_COUNT]; | |||
140 | TriggerValue triggerSignalStates[PWM_PHASE_MAX_COUNT]; | |||
141 | // see also 'doesTriggerImplyOperationMode' | |||
142 | // todo: reuse doesTriggerImplyOperationMode instead of separate field only which is only used for metadata anyway? | |||
143 | bool knownOperationMode = true; | |||
144 | #endif | |||
145 | ||||
146 | /** | |||
147 | * wave.phaseCount is total count of shaft events per CAM or CRANK shaft revolution. | |||
148 | * TODO this should be migrated to CRANKshaft revolution, this would go together | |||
149 | * this variable is public for performance reasons (I want to avoid costs of method if it's not inlined) | |||
150 | * but name is supposed to hint at the fact that decoders should not be assigning to it | |||
151 | * Please use "getSize()" function to read this value | |||
152 | */ | |||
153 | MultiChannelStateSequenceWithData<PWM_PHASE_MAX_COUNT> wave; | |||
154 | ||||
155 | bool isRiseEvent[PWM_PHASE_MAX_COUNT]; | |||
156 | ||||
157 | /** | |||
158 | * @param angle (0..1] | |||
159 | */ | |||
160 | void addEvent(angle_t angle, TriggerValue const state, TriggerWheel const channelIndex = TriggerWheel::T_PRIMARY); | |||
161 | /* (0..720] angle range | |||
162 | * Deprecated! many usages should be replaced by addEvent360 | |||
163 | */ | |||
164 | void addEvent720(angle_t angle, TriggerValue const state, TriggerWheel const channelIndex = TriggerWheel::T_PRIMARY); | |||
165 | ||||
166 | /** | |||
167 | * this method helps us use real world 360 degrees shape for FOUR_STROKE_CAM_SENSOR and FOUR_STROKE_CRANK_SENSOR | |||
168 | */ | |||
169 | void addEvent360(angle_t angle, TriggerValue const state, TriggerWheel const channelIndex = TriggerWheel::T_PRIMARY); | |||
170 | ||||
171 | void addToothRiseFall(angle_t angle, angle_t width = 10, TriggerWheel const channelIndex = TriggerWheel::T_PRIMARY); | |||
172 | // fun: yet another inconsistency, right?! | |||
173 | void addToothFallRise(angle_t angle, angle_t width = 10, TriggerWheel const channelIndex = TriggerWheel::T_PRIMARY); | |||
174 | ||||
175 | /** | |||
176 | * This version of the method is best when same wheel could be mounted either on crank or cam | |||
177 | * | |||
178 | * This version of 'addEvent...' family considers the angle duration of operationMode in this trigger | |||
179 | * For example, (0..180] for FOUR_STROKE_SYMMETRICAL_CRANK_SENSOR | |||
180 | * | |||
181 | * TODO: one day kill all usages with FOUR_STROKE_CAM_SENSOR 720 cycle and add runtime prohibition | |||
182 | * TODO: for FOUR_STROKE_CAM_SENSOR addEvent360 is the way to go | |||
183 | * | |||
184 | * @param angle (0..360] or (0..720] depending on configuration | |||
185 | */ | |||
186 | void addEventAngle(angle_t angle, TriggerValue const state, TriggerWheel const channelIndex = TriggerWheel::T_PRIMARY); | |||
187 | ||||
188 | /* (0..720] angle range | |||
189 | * Deprecated? | |||
190 | */ | |||
191 | void addEventClamped(angle_t angle, TriggerValue const state, TriggerWheel const channelIndex, float filterLeft, float filterRight); | |||
192 | operation_mode_e getWheelOperationMode() const; | |||
193 | ||||
194 | void initialize(operation_mode_e operationMode, SyncEdge syncEdge); | |||
195 | void setTriggerSynchronizationGap(float syncRatio); | |||
196 | /** | |||
197 | * note that index is in reverse order comparing with chronological order on the documentation images | |||
198 | * https://github.com/rusefi/rusefi/wiki/All-Supported-Triggers | |||
199 | */ | |||
200 | void setTriggerSynchronizationGap3(int index, float syncRatioFrom, float syncRatioTo); | |||
201 |
1/1✓ Decision 'true' taken 3999 times.
|
3999 | void setTriggerSynchronizationGap4(int index, float syncRatio) { | |
202 | 3999 | setTriggerSynchronizationGap3(index, syncRatio * TRIGGER_GAP_DEVIATION_LOW, syncRatio * TRIGGER_GAP_DEVIATION_HIGH); | ||
203 | 3999 | } | ||
204 | void setTriggerSynchronizationGap2(float syncRatioFrom, float syncRatioTo); | |||
205 | void setSecondTriggerSynchronizationGap(float syncRatio); | |||
206 | void setSecondTriggerSynchronizationGap2(float syncRatioFrom, float syncRatioTo); | |||
207 | void setThirdTriggerSynchronizationGap(float syncRatio); | |||
208 | /** | |||
209 | * this one is per CRANKshaft revolution | |||
210 | */ | |||
211 | size_t getLength() const; | |||
212 | size_t getSize() const; | |||
213 | ||||
214 | int getTriggerWaveformSynchPointIndex() const; | |||
215 | ||||
216 | /** | |||
217 | * This private method should only be used to prepare the array of pre-calculated values | |||
218 | * See eventAngles array | |||
219 | */ | |||
220 | angle_t getAngle(int phaseIndex) const; | |||
221 | ||||
222 | angle_t getCycleDuration() const; | |||
223 | ||||
224 | // Returns true if this trigger alone can fully sync the current engine for sequential mode. | |||
225 | bool needsDisambiguation() const; | |||
226 | ||||
227 | /** | |||
228 | * index of synchronization event within TriggerWaveform | |||
229 | * See findTriggerZeroEventIndex() | |||
230 | */ | |||
231 | int triggerShapeSynchPointIndex; | |||
232 | ||||
233 | void initializeSyncPoint( | |||
234 | TriggerDecoderBase& state, | |||
235 | const TriggerConfiguration& triggerConfiguration | |||
236 | ); | |||
237 | ||||
238 | uint16_t findAngleIndex(TriggerFormDetails *details, angle_t angle) const; | |||
239 | ||||
240 | /** | |||
241 | * These angles are in trigger DESCRIPTION coordinates - i.e. the way you add events while declaring trigger shape | |||
242 | */ | |||
243 | angle_t getSwitchAngle(int index) const; | |||
244 | private: | |||
245 | ||||
246 | /** | |||
247 | * This variable is used to confirm that events are added in the right order. | |||
248 | * todo: this variable is probably not needed, could be reimplemented by accessing by index | |||
249 | */ | |||
250 | angle_t previousAngle; | |||
251 | /** | |||
252 | * this is part of performance optimization | |||
253 | */ | |||
254 | operation_mode_e operationMode; | |||
255 | }; | |||
256 | ||||
257 | /** | |||
258 | * Misc values calculated from TriggerWaveform | |||
259 | */ | |||
260 | class TriggerFormDetails { | |||
261 | public: | |||
262 | void prepareEventAngles(TriggerWaveform *shape); | |||
263 | ||||
264 | /** | |||
265 | * These angles are in event coordinates - with synchronization point located at angle zero. | |||
266 | * These values are pre-calculated for performance reasons. | |||
267 | */ | |||
268 | angle_t eventAngles[2 * PWM_PHASE_MAX_COUNT]; | |||
269 | }; | |||
270 |