GCC Code Coverage Report


Directory: ./
File: firmware/controllers/algo/launch_control.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 95.4% 103 0 108
Functions: 100.0% 16 0 16
Branches: 76.9% 60 0 78
Decisions: 86.7% 13 - 15

Line Branch Decision Exec Source
1 /*
2 * @file launch_control.cpp
3 *
4 * @date 10. sep. 2019
5 * Author: Ola Ruud
6 * Rework: Patryk Chmura
7 */
8
9 #include "pch.h"
10
11 #if EFI_LAUNCH_CONTROL
12 #include "boost_control.h"
13 #include "launch_control.h"
14 #include "engine_state.h"
15 #include "tinymt32.h" // TL,DR: basic implementation of 'random'
16 #include "gppwm_channel_reader.h"
17
18 /**
19 * We can have active condition from switch or from clutch.
20 * In case we are dependent on VSS we just return true.
21 */
22
1/1
✓ Decision 'true' taken 127 times.
127 bool LaunchControlBase::isInsideSwitchCondition() {
23 127 isSwitchActivated = engineConfiguration->launchActivationMode == SWITCH_INPUT_LAUNCH;
24 127 isClutchActivated = engineConfiguration->launchActivationMode == CLUTCH_INPUT_LAUNCH;
25 127 isBrakePedalActivated = engineConfiguration->launchActivationMode == STOP_INPUT_LAUNCH;
26
27
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 62 times.
2/2
✓ Decision 'true' taken 65 times.
✓ Decision 'false' taken 62 times.
127 if (isSwitchActivated) {
28 #if !EFI_SIMULATOR
29
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 63 times.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 63 times.
65 if (isBrainPinValid(engineConfiguration->launchActivatePin)) {
30 2 launchActivatePinState = efiReadPin(engineConfiguration->launchActivatePin, engineConfiguration->launchActivatePinMode);
31 }
32 #endif // EFI_PROD_CODE
33 65 return launchActivatePinState;
34
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 58 times.
2/2
✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 58 times.
62 } else if (isClutchActivated) {
35 4 return getClutchDownState();
36
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 58 times.
58 } else if (isBrakePedalActivated) {
37 return getBrakePedalState();
38
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 58 times.
58 } else if (engineConfiguration->launchActivationMode == LUA_LAUNCH) {
39 return luaLaunchState;
40 } else {
41 // ALWAYS_ACTIVE_LAUNCH
42 58 return true;
43 }
44 }
45
46 /**
47 * Returns True when Vehicle speed ALLOWS launch control
48 */
49 125 bool LaunchControlBase::isInsideSpeedCondition() const {
50
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 74 times.
2/2
✓ Decision 'true' taken 51 times.
✓ Decision 'false' taken 74 times.
125 if (engineConfiguration->launchSpeedThreshold == 0) {
51 51 return true; // allow launch, speed does not matter
52 }
53
54 74 int speed = Sensor::getOrZero(SensorType::VehicleSpeed);
55
56 74 return engineConfiguration->launchSpeedThreshold > speed;
57 }
58
59 /**
60 * Returns false if TPS is invalid or TPS > preset threshold
61 */
62 123 bool LaunchControlBase::isInsideTpsCondition() const {
63 123 auto tps = Sensor::get(SensorType::DriverThrottleIntent);
64
65 // Disallow launch without valid TPS
66
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 59 times.
2/2
✓ Decision 'true' taken 64 times.
✓ Decision 'false' taken 59 times.
123 if (!tps.Valid) {
67 64 return false;
68 }
69
70 // todo: should this be 'launchTpsThreshold <= tps.Value' so that nicely calibrated TPS of zero does not prevent launch?
71 59 return engineConfiguration->launchTpsThreshold < tps.Value;
72 }
73
74 126 LaunchCondition LaunchControlBase::calculateRPMLaunchCondition(const float rpm) {
75
2/2
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 57 times.
126 if ((engineConfiguration->launchActivationMode == SWITCH_INPUT_LAUNCH)
76
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 69 times.
69 && (engineConfiguration->torqueReductionActivationMode == LAUNCH_BUTTON)
77 && engineConfiguration->torqueReductionEnabled
78 && (engineConfiguration->torqueReductionArmingRpm <= rpm)
79 ) {
80 // We need perform Shift Torque Reduction stuff (see
81 // https://github.com/rusefi/rusefi/issues/5608#issuecomment-2391500472 and
82 // https://github.com/rusefi/rusefi/issues/5608#issuecomment-2391772899 for details)
83 return LaunchCondition::NotMet;
84 }
85
86 126 const int launchRpm = engineConfiguration->launchRpm;
87 126 const int preLaunchRpm = launchRpm - engineConfiguration->launchRpmWindow;
88
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 96 times.
126 if (rpm < preLaunchRpm) {
89 30 return LaunchCondition::NotMet;
90
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 55 times.
96 } else if (launchRpm <= rpm) {
91 41 return LaunchCondition::Launch;
92 } else {
93 55 return LaunchCondition::PreLaunch;
94 }
95 }
96
97 120 LaunchCondition LaunchControlBase::calculateLaunchCondition(const float rpm) {
98 120 const LaunchCondition currentRpmLaunchCondition = calculateRPMLaunchCondition(rpm);
99 120 activateSwitchCondition = isInsideSwitchCondition();
100 120 rpmLaunchCondition = (currentRpmLaunchCondition == LaunchCondition::Launch);
101 120 rpmPreLaunchCondition = (currentRpmLaunchCondition == LaunchCondition::PreLaunch);
102 120 speedCondition = isInsideSpeedCondition();
103 120 tpsCondition = isInsideTpsCondition();
104
105
5/6
✓ Branch 0 taken 118 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 55 times.
✓ Branch 3 taken 63 times.
✓ Branch 4 taken 55 times.
✗ Branch 5 not taken.
120 if(speedCondition && activateSwitchCondition && tpsCondition) {
106 55 return currentRpmLaunchCondition;
107 } else {
108 65 return LaunchCondition::NotMet;
109 }
110 }
111
112 684 LaunchControlBase::LaunchControlBase() {
113 684 launchActivatePinState = false;
114 684 isPreLaunchCondition = false;
115 684 isLaunchCondition = false;
116 684 }
117
118 986 float LaunchControlBase::getFuelCoefficient() const {
119
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 978 times.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
986 return 1 + (isLaunchCondition && engineConfiguration->launchControlEnabled ? engineConfiguration->launchFuelAdderPercent / 100.0 : 0);
120 }
121
122 1145 void LaunchControlBase::update() {
123
2/2
✓ Branch 0 taken 1028 times.
✓ Branch 1 taken 117 times.
1145 if (!engineConfiguration->launchControlEnabled) {
124 1028 return;
125 }
126
127 117 const float rpm = Sensor::getOrZero(SensorType::Rpm);
128 117 const LaunchCondition launchCondition = calculateLaunchCondition(rpm);
129 117 isLaunchCondition = (launchCondition == LaunchCondition::Launch);
130 117 isPreLaunchCondition = (launchCondition == LaunchCondition::PreLaunch);
131
132 //and still recalculate in case user changed the values
133 117 retardThresholdRpm = engineConfiguration->launchRpm;
134
135 117 sparkSkipRatio = calculateSparkSkipRatio(rpm);
136 }
137
138 2340 bool LaunchControlBase::isLaunchRpmRetardCondition() const {
139
5/6
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 2310 times.
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 20 times.
✓ Branch 6 taken 10 times.
2340 return isLaunchCondition && engineConfiguration->launchControlEnabled && (retardThresholdRpm < Sensor::getOrZero(SensorType::Rpm));
140 }
141
142 1172 bool LaunchControlBase::isLaunchSparkRpmRetardCondition() const {
143
3/4
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 1161 times.
✓ Branch 3 taken 11 times.
✗ Branch 4 not taken.
1172 return isLaunchRpmRetardCondition() && engineConfiguration->launchSparkCutEnable;
144 }
145
146 1168 bool LaunchControlBase::isLaunchFuelRpmRetardCondition() const {
147
3/4
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 1159 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 9 times.
1168 return isLaunchRpmRetardCondition() && engineConfiguration->launchFuelCutEnable;
148 }
149
150 117 float LaunchControlBase::calculateSparkSkipRatio(const float rpm) const {
151 117 float result = 0.0f;
152
3/4
✓ Branch 0 taken 117 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 103 times.
✓ Branch 3 taken 14 times.
117 if (engineConfiguration->launchControlEnabled && engineConfiguration->launchSparkCutEnable) {
153
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 86 times.
103 if (isLaunchCondition) {
154 17 result = 1.0f;
155
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 69 times.
86 } else if (isPreLaunchCondition) {
156 17 const int launchRpm = engineConfiguration->launchRpm;
157 17 const int sparkSkipStartRpm = launchRpm - engineConfiguration->launchRpmWindow;
158
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 if (sparkSkipStartRpm <= rpm) {
159 17 const float initialIgnitionCutRatio = engineConfiguration->initialIgnitionCutPercent / 100.0f;
160 17 const int sparkSkipEndRpm = launchRpm - engineConfiguration->launchCorrectionsEndRpm;
161 17 const float finalIgnitionCutRatio = engineConfiguration->finalIgnitionCutPercentBeforeLaunch / 100.0f;
162 17 result = interpolateClamped(sparkSkipStartRpm, initialIgnitionCutRatio, sparkSkipEndRpm, finalIgnitionCutRatio, rpm);
163 }
164 }
165 }
166 117 return result;
167 }
168
169 1355 SoftSparkLimiter::SoftSparkLimiter(const bool p_allowHardCut)
170 1355 : allowHardCut(p_allowHardCut) {
171 #if EFI_UNIT_TEST
172 1355 initLaunchControl();
173 #endif // EFI_UNIT_TEST
174 1355 }
175
176 2220 void SoftSparkLimiter::updateTargetSkipRatio(
177 const float luaSparkSkip,
178 const float tractionControlSparkSkip,
179 const float launchOrShiftTorqueReductionControllerSparkSkipRatio
180 ) {
181 2220 targetSkipRatio = luaSparkSkip;
182
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2218 times.
2220 if (engineConfiguration->useHardSkipInTraction) {
183
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if (allowHardCut) {
184 1 targetSkipRatio += tractionControlSparkSkip;
185 }
186
2/2
✓ Branch 0 taken 1108 times.
✓ Branch 1 taken 1110 times.
2218 } else if (!allowHardCut) {
187 1108 targetSkipRatio += tractionControlSparkSkip;
188 }
189
190
2/2
✓ Branch 0 taken 1111 times.
✓ Branch 1 taken 1109 times.
2220 if (allowHardCut) {
191 /*
192 * We are applying launch controller spark skip ratio only for hard skip limiter (see
193 * https://github.com/rusefi/rusefi/issues/6566#issuecomment-2153149902).
194 */
195 1111 targetSkipRatio += launchOrShiftTorqueReductionControllerSparkSkipRatio;
196 }
197 2220 }
198
199 static tinymt32_t tinymt;
200
201 18746 bool SoftSparkLimiter::shouldSkip() {
202
3/6
✓ Branch 0 taken 1001 times.
✓ Branch 1 taken 17745 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1001 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
18746 if (targetSkipRatio == 0 || (!allowHardCut && wasJustSkipped)) {
203 17745 wasJustSkipped = false;
204 17745 return false;
205 }
206
207 1001 float random = tinymt32_generate_float(&tinymt);
208
1/2
✓ Branch 0 taken 1001 times.
✗ Branch 1 not taken.
1001 wasJustSkipped = random < (allowHardCut ? 1 : 2) * targetSkipRatio;
209 1001 return wasJustSkipped;
210 }
211
212 1939 void initLaunchControl() {
213 1939 tinymt32_init(&tinymt, 1345135);
214 1939 }
215
216 #endif /* EFI_LAUNCH_CONTROL */
217