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 |