GCC Code Coverage Report


Directory: ./
File: firmware/controllers/trigger/instant_rpm_calculator.cpp
Date: 2025-11-16 14:52:24
Coverage Exec Excl Total
Lines: 100.0% 51 0 51
Functions: 100.0% 5 0 5
Branches: 80.0% 24 0 30
Decisions: 100.0% 12 - 12

Line Branch Decision Exec Source
1
2 #include "pch.h"
3 #include "instant_rpm_calculator.h"
4
5 /**
6 * sensorChartMode
7 */
8 #include "engine_state.h"
9
10 #if EFI_UNIT_TEST
11 extern bool printTriggerDebug;
12 #endif
13
14 #if EFI_SHAFT_POSITION_INPUT
15
16 684 InstantRpmCalculator::InstantRpmCalculator() :
17 //https://en.cppreference.com/w/cpp/language/zero_initialization
18
2/2
✓ Branch 0 taken 191520 times.
✓ Branch 1 taken 684 times.
192204 timeOfLastEvent()
19
2/2
✓ Branch 0 taken 191520 times.
✓ Branch 1 taken 684 times.
192204 , instantRpmValue()
20 {
21 684 }
22
23 167 void InstantRpmCalculator::movePreSynchTimestamps() {
24 // here we take timestamps of events which happened prior to synchronization and place them
25 // at appropriate locations
26 167 auto triggerSize = getTriggerCentral()->triggerShape.getLength();
27
28 167 size_t eventsToCopy = minI(spinningEventIndex, triggerSize);
29
30 size_t firstSrc;
31 size_t firstDst;
32
33
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 150 times.
2/2
✓ Decision 'true' taken 17 times.
✓ Decision 'false' taken 150 times.
167 if (eventsToCopy >= triggerSize) {
34 // Only copy one trigger length worth of events, filling the whole buffer
35 17 firstSrc = spinningEventIndex - triggerSize;
36 17 firstDst = 0;
37 } else {
38 // There is less than one full cycle, copy to the end of the buffer
39 150 firstSrc = 0;
40 150 firstDst = triggerSize - spinningEventIndex;
41 }
42
43 167 memcpy(timeOfLastEvent + firstDst, spinningEvents + firstSrc, eventsToCopy * sizeof(timeOfLastEvent[0]));
44 167 }
45
46 33433 float InstantRpmCalculator::calculateInstantRpm(
47 TriggerWaveform const & triggerShape, TriggerFormDetails *triggerFormDetails,
48 uint32_t current_index, efitick_t nowNt) {
49
50 // It's OK to truncate from 64b to 32b, ARM with single precision FPU uses an expensive
51 // software function to convert 64b int -> float, while 32b int -> float is very cheap hardware conversion
52 // The difference is guaranteed to be short (it's 90 degrees of engine rotation!), so it won't overflow.
53 33433 uint32_t nowNt32 = nowNt;
54
55
1/3
✗ Branch 1 not taken.
✓ Branch 2 taken 33433 times.
✗ Branch 4 not taken.
33433 assertIsInBoundsWithResult(current_index, timeOfLastEvent, "calc timeOfLastEvent", 0);
56
57 // Record the time of this event so we can calculate RPM from it later
58 33433 timeOfLastEvent[current_index] = nowNt32;
59
60 // Determine where we currently are in the revolution
61 33433 angle_t currentAngle = triggerFormDetails->eventAngles[current_index];
62
1/3
✗ Branch 1 not taken.
✓ Branch 2 taken 33433 times.
✗ Branch 4 not taken.
33433 efiAssert(ObdCode::OBD_PCM_Processor_Fault, !std::isnan(currentAngle), "eventAngles", 0);
63
64 // Hunt for a tooth ~90 degrees ago to compare to the current time
65 33433 angle_t previousAngle = currentAngle - 90;
66
1/1
✓ Branch 1 taken 33433 times.
33433 wrapAngle(previousAngle, "prevAngle", ObdCode::CUSTOM_ERR_TRIGGER_ANGLE_RANGE);
67
1/1
✓ Branch 1 taken 33433 times.
33433 int prevIndex = triggerShape.findAngleIndex(triggerFormDetails, previousAngle);
68
69 // now let's get precise angle for that event
70 33433 angle_t prevIndexAngle = triggerFormDetails->eventAngles[prevIndex];
71 33433 auto time90ago = timeOfLastEvent[prevIndex];
72
73 // No previous timestamp, instant RPM isn't ready yet
74
2/2
✓ Branch 0 taken 356 times.
✓ Branch 1 taken 33077 times.
2/2
✓ Decision 'true' taken 356 times.
✓ Decision 'false' taken 33077 times.
33433 if (time90ago == 0) {
75 356 return prevInstantRpmValue;
76 }
77
78 33077 uint32_t time = nowNt32 - time90ago;
79 33077 angle_t angleDiff = currentAngle - prevIndexAngle;
80
81 // Wrap the angle in to the correct range (ie, could be -630 when we want +90)
82
1/1
✓ Branch 1 taken 33077 times.
33077 wrapAngle(angleDiff, "angleDiff", ObdCode::CUSTOM_ERR_6561);
83
84 // just for safety, avoid divide-by-0
85
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 33056 times.
2/2
✓ Decision 'true' taken 21 times.
✓ Decision 'false' taken 33056 times.
33077 if (time == 0) {
86 21 return prevInstantRpmValue;
87 }
88
89 33056 float instantRpm = (60000000.0 / 360 * US_TO_NT_MULTIPLIER) * angleDiff / time;
90
1/3
✗ Branch 1 not taken.
✓ Branch 2 taken 33056 times.
✗ Branch 4 not taken.
33056 assertIsInBoundsWithResult(current_index, instantRpmValue, "instantRpmValue", 0);
91 33056 instantRpmValue[current_index] = instantRpm;
92
93 // This fixes early RPM instability based on incomplete data
94
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 33041 times.
2/2
✓ Decision 'true' taken 15 times.
✓ Decision 'false' taken 33041 times.
33056 if (instantRpm < RPM_LOW_THRESHOLD) {
95 15 return prevInstantRpmValue;
96 }
97
98 33041 prevInstantRpmValue = instantRpm;
99
100 33041 m_instantRpmRatio = instantRpm / instantRpmValue[prevIndex];
101
102 33041 return instantRpm;
103 }
104
105 3138 void InstantRpmCalculator::setLastEventTimeForInstantRpm(efitick_t nowNt) {
106 // here we remember tooth timestamps which happen prior to synchronization
107
2/2
✓ Branch 1 taken 1119 times.
✓ Branch 2 taken 2019 times.
2/2
✓ Decision 'true' taken 1119 times.
✓ Decision 'false' taken 2019 times.
3138 if (spinningEventIndex >= efi::size(spinningEvents)) {
108 // too many events while trying to find synchronization point
109 // todo: better implementation would be to shift here or use cyclic buffer so that we keep last
110 // 'PRE_SYNC_EVENTS' events
111 1119 return;
112 }
113
114 2019 uint32_t nowNt32 = nowNt;
115 2019 spinningEvents[spinningEventIndex] = nowNt32;
116
117 // If we are using only rising edges, we never write in to the odd-index slots that
118 // would be used by falling edges
119 // TODO: don't reach across to trigger central to get this info
120
2/2
✓ Branch 1 taken 1685 times.
✓ Branch 2 taken 334 times.
2019 spinningEventIndex += getTriggerCentral()->triggerShape.useOnlyRisingEdges ? 2 : 1;
121 }
122
123 33433 void InstantRpmCalculator::updateInstantRpm(
124 uint32_t current_index,
125 TriggerWaveform const & triggerShape, TriggerFormDetails *triggerFormDetails,
126 uint32_t index, efitick_t nowNt) {
127 UNUSED(current_index);
128
129 33433 m_instantRpm = calculateInstantRpm(triggerShape, triggerFormDetails, index,
130 nowNt);
131 #if EFI_UNIT_TEST
132
2/2
✓ Branch 0 taken 740 times.
✓ Branch 1 taken 32693 times.
2/2
✓ Decision 'true' taken 740 times.
✓ Decision 'false' taken 32693 times.
33433 if (printTriggerDebug) {
133 740 printf("instantRpm = %f\n", m_instantRpm);
134 }
135 #endif
136
137 33433 }
138
139 #endif // EFI_SHAFT_POSITION_INPUT
140