Trigger System Redesign
Posted: Sat Oct 27, 2018 11:54 pm
There have been a few discussions lately about how Rusefi's trigger system runs on a significant amount of magic. This is an attempt to reduce the amount of magic.
Difficulties with the existing trigger system:
Difficulties with the existing trigger system:
- Cam vs. crank decoding: All trigger types use angles in the range [0, 720), even if they're only decoding the crankshaft. Via some magic these are effectively compressed down to [0, 360), then repeated. If you're on a 2 stroke it's compressed but not repeated, and if it's a true cam sensor system none of this happens and the true values are passed.
- The trigger system relies on determining the index of each tooth, then planning out future events (fuel, ign, etc) based on index. These events are natively angles (not trigger teeth!), so a conversion from angle -> tooth -> angle is happening. Listeners to the trigger system don't care what kind of tooth we just saw, or what index it is in the sequence, but only where we are in the engine's cycle, and how long we have until we should expect to see another tooth.
- The 'zero point' of a trigger is not well defined. It is determined by the trigger system at startup, and as a result requires running the wheel synchronization algorithm to determine what the #1 TDC offset should be. This means that the angles provided during trigger setup effectively mean nothing, and are merely relative offsets, not absolute as is implied.
- It's not clear if more complicated sync patterns are currently possible. The GM 24x pattern has no "special" tooth for synchronization. There's one point in the pattern where remembering the length of the past 4 teeth will resolve position uniquely, or remembering the past 5 teeth can resolve any position uniquely. Adding support for this case to the current trigger system would require either a) a completely different trigger system that gets selected based on whether you're using a gm24x, or b) hack logic to sync more complex trigger shapes that has to run when any trigger shape is decoded, which may have significant performance cost. All triggers currently pay the performance cost of the most complicated trigger.
- Instead of passing edge type and index to ShaftPositionListeners, pass:
- The current sync state (synced, not synced)
- The current engine phase as of the event we just decoded. (angle_t)
- The expected arrival of the next trigger edge. (angle_t)
- The current engine speed, RPM.
- Decouple cam and crank logic. Four stroke engines require a cam decoder (I know what your question is, hang on a sec), and two stroke require a crank decoder. By 'cam decoder' I don't mean something that actually requires reading a camshaft, but instead something that resolves angles to the range [0, 720).
The simplest cam decoder could just consume a crank decoder, then every other revolution of the crankshaft optionally add 360 degrees. This lets a crankshaft decoder have the sole job of decoding a [0, 360) range. For native cam-only setups (for example Nissan/DSM CAS sensors), a cam-only decoder could provide native [0, 720) angles without the use of a crank decoder. - Allow a cam sensor to consume an existing crank sensor. On the physical engine, the cam sensor and crank sensor are physically separate (if you have both). One can be replaced without the other. As such, the implementation of the trigger system should reflect this: If I add a cam sensor to my engine, I should be able to use the same crank decoder (and settings), but add the capability decode the cam as well.
I implemented this model using inheritance and templates. A base class defines the interface for feeding edges to a trigger, and receiving results back (or ignored edges). Two classes inherit the base: two bases for cam and crank decoders. Classes for trigger types that inherit TriggerDecoder only have to implement two methods: One to determine if a particular edge should be decoded, and one to decode an edge, and return the current position of the engine.
If defining a new cam sensor that is an add-on (ie, requires a crank sensor), inherit from CamshaftWheelAdder<TCrank>. All the class inheriting from CamshaftWheelAdder has to do is a) interpret cam signals b) use those to determine engine phase ("A" side or "B" side of a four-stroke) then c) figure out whether or not to add 360 to the result of the crank decoder's output.
What does this actually look like? Here's the inheritance diagram generated by doxygen:
If you wanted to run my Volvo, I have a 60-2 missing tooth wheel, and no cam sensor. So the decoder would be initialized like this:This could probably also be implememented without templates, which may improve runtime configurability, but that loses the complier optimization opportunities of templates.Code: Select all
CamlessDecoder<MissingToothTriggerDecoder> decoder(60, 2);