| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /** | |||
| 2 | * @file electronic_throttle.cpp | |||
| 3 | * @brief Electronic Throttle driver | |||
| 4 | * | |||
| 5 | * @see test test_etb.cpp | |||
| 6 | * | |||
| 7 | * PPS=pedal position sensor=AcceleratorPedal | |||
| 8 | * TPS=throttle position sensor, this one is inside ETB=electronic throttle body | |||
| 9 | * | |||
| 10 | * Limited user documentation at https://github.com/rusefi/rusefi/wiki/HOWTO_electronic_throttle_body | |||
| 11 | * | |||
| 12 | * | |||
| 13 | * ETB is controlled according to pedal position input (pedal position sensor is a potentiometer) | |||
| 14 | * pedal 0% means pedal not pressed / idle | |||
| 15 | * pedal 100% means pedal all the way down | |||
| 16 | * (not TPS - not the one you can calibrate in TunerStudio) | |||
| 17 | * | |||
| 18 | * | |||
| 19 | * See also pid.cpp | |||
| 20 | * | |||
| 21 | * @date Dec 7, 2013 | |||
| 22 | * @author Andrey Belomutskiy, (c) 2012-2020 | |||
| 23 | * | |||
| 24 | * This file is part of rusEfi - see http://rusefi.com | |||
| 25 | * | |||
| 26 | * rusEfi is free software; you can redistribute it and/or modify it under the terms of | |||
| 27 | * the GNU General Public License as published by the Free Software Foundation; either | |||
| 28 | * version 3 of the License, or (at your option) any later version. | |||
| 29 | * | |||
| 30 | * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without | |||
| 31 | * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| 32 | * GNU General Public License for more details. | |||
| 33 | * | |||
| 34 | * You should have received a copy of the GNU General Public License along with this program. | |||
| 35 | * If not, see <http://www.gnu.org/licenses/>. | |||
| 36 | */ | |||
| 37 | ||||
| 38 | #include "pch.h" | |||
| 39 | ||||
| 40 | #include "electronic_throttle_impl.h" | |||
| 41 | ||||
| 42 | #if EFI_ELECTRONIC_THROTTLE_BODY | |||
| 43 | ||||
| 44 | #include "dc_motor.h" | |||
| 45 | #include "dc_motors.h" | |||
| 46 | #include "defaults.h" | |||
| 47 | #include "tunerstudio.h" | |||
| 48 | #include "tunerstudio_calibration_channel.h" | |||
| 49 | #include "transition_events.h" | |||
| 50 | ||||
| 51 | #if defined(HAS_OS_ACCESS) | |||
| 52 | #error "Unexpected OS ACCESS HERE" | |||
| 53 | #endif | |||
| 54 | ||||
| 55 | #if HW_PROTEUS | |||
| 56 | #include "proteus_meta.h" | |||
| 57 | #endif // HW_PROTEUS | |||
| 58 | ||||
| 59 | #ifndef ETB_MAX_COUNT | |||
| 60 | #define ETB_MAX_COUNT 2 | |||
| 61 | #endif /* ETB_MAX_COUNT */ | |||
| 62 | ||||
| 63 | #ifndef ETB_INTERMITTENT_LIMIT | |||
| 64 | #define ETB_INTERMITTENT_LIMIT 50 | |||
| 65 | #endif | |||
| 66 | ||||
| 67 | static pedal2tps_t pedal2tpsMap{"p2t"}; | |||
| 68 | static Map3D<ETB2_TRIM_RPM_SIZE, ETB2_TRIM_SIZE, int8_t, uint8_t, uint8_t> throttle2TrimTable{"t2t"}; | |||
| 69 | static Map3D<TRACTION_CONTROL_ETB_DROP_SLIP_SIZE, TRACTION_CONTROL_ETB_DROP_SPEED_SIZE, int8_t, uint16_t, uint8_t> tcEtbDropTable{"tce"}; | |||
| 70 | ||||
| 71 | constexpr float etbPeriodSeconds = 1.0f / ETB_LOOP_FREQUENCY; | |||
| 72 | ||||
| 73 | //static bool startupPositionError = false; | |||
| 74 | ||||
| 75 | //#define STARTUP_NEUTRAL_POSITION_ERROR_THRESHOLD 5 | |||
| 76 | ||||
| 77 | static const float hardCodedetbHitachiBiasBins[8] = {0.0, 19.0, 21.0, 22.0, 23.0, 25.0, 30.0, 100.0}; | |||
| 78 | ||||
| 79 | static const float hardCodedetbHitachiBiasValues[8] = {-18.0, -17.0, -15.0, 0.0, 16.0, 20.0, 20.0, 20.0}; | |||
| 80 | ||||
| 81 | /* Generated by TS2C on Thu Aug 20 21:10:02 EDT 2020*/ | |||
| 82 | 1 | void setHitachiEtbBiasBins() { | ||
| 83 | 1 | copyArray(config->etbBiasBins, hardCodedetbHitachiBiasBins); | ||
| 84 | 1 | copyArray(config->etbBiasValues, hardCodedetbHitachiBiasValues); | ||
| 85 | 1 | } | ||
| 86 | ||||
| 87 | 2090 | static SensorType functionToPositionSensor(dc_function_e func) { | ||
| 88 |
4/5✓ Branch 0 taken 1053 times.
✓ Branch 1 taken 1030 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
|
2090 | switch(func) { | |
| 89 |
1/1✓ Decision 'true' taken 1053 times.
|
1053 | case DC_Throttle1: return SensorType::Tps1; | |
| 90 |
1/1✓ Decision 'true' taken 1030 times.
|
1030 | case DC_Throttle2: return SensorType::Tps2; | |
| 91 |
1/1✓ Decision 'true' taken 3 times.
|
3 | case DC_IdleValve: return SensorType::IdlePosition; | |
| 92 |
1/1✓ Decision 'true' taken 4 times.
|
4 | case DC_Wastegate: return SensorType::WastegatePosition; | |
| 93 | ✗ | default: return SensorType::Invalid; | ||
| 94 | } | |||
| 95 | } | |||
| 96 | ||||
| 97 | 2083 | static SensorType functionToTpsSensor(dc_function_e func) { | ||
| 98 |
2/5✓ Branch 0 taken 1053 times.
✓ Branch 1 taken 1030 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
2083 | switch(func) { | |
| 99 |
1/1✓ Decision 'true' taken 1053 times.
|
1053 | case DC_Throttle1: return SensorType::Tps1; | |
| 100 |
1/1✓ Decision 'true' taken 1030 times.
|
1030 | case DC_Throttle2: return SensorType::Tps2; | |
| 101 | ✗ | case DC_IdleValve: return SensorType::IdlePosition; | ||
| 102 | ✗ | case DC_Wastegate: return SensorType::WastegatePosition; | ||
| 103 | ✗ | default: return SensorType::Invalid; | ||
| 104 | } | |||
| 105 | } | |||
| 106 | ||||
| 107 | ✗ | static SensorType functionToTpsSensorPrimary(dc_function_e func) { | ||
| 108 | ✗ | switch(func) { | ||
| 109 | ✗ | case DC_Throttle1: return SensorType::Tps1Primary; | ||
| 110 | ✗ | case DC_Throttle2: return SensorType::Tps2Primary; | ||
| 111 | ✗ | case DC_IdleValve: return SensorType::IdlePosition; | ||
| 112 | ✗ | case DC_Wastegate: return SensorType::WastegatePosition; | ||
| 113 | ✗ | default: return SensorType::Invalid; | ||
| 114 | } | |||
| 115 | } | |||
| 116 | ||||
| 117 | ✗ | static SensorType functionToTpsSensorSecondary(dc_function_e func) { | ||
| 118 | ✗ | switch(func) { | ||
| 119 | ✗ | case DC_Throttle1: return SensorType::Tps1Secondary; | ||
| 120 | ✗ | case DC_Throttle2: return SensorType::Tps2Secondary; | ||
| 121 | /* No secondary sensors for Idle and EWG */ | |||
| 122 | ✗ | default: return SensorType::Invalid; | ||
| 123 | } | |||
| 124 | } | |||
| 125 | ||||
| 126 | #if EFI_TUNER_STUDIO | |||
| 127 | ✗ | static TsCalMode functionToCalModePriMin(dc_function_e func) { | ||
| 128 | ✗ | switch (func) { | ||
| 129 | ✗ | case DC_Throttle1: return TsCalMode::Tps1Min; | ||
| 130 | ✗ | case DC_Throttle2: return TsCalMode::Tps2Min; | ||
| 131 | ✗ | case DC_Wastegate: return TsCalMode::EwgPosMin; | ||
| 132 | ✗ | default: return TsCalMode::None; | ||
| 133 | } | |||
| 134 | } | |||
| 135 | ||||
| 136 | ✗ | static TsCalMode functionToCalModePriMax(dc_function_e func) { | ||
| 137 | ✗ | switch (func) { | ||
| 138 | ✗ | case DC_Throttle1: return TsCalMode::Tps1Max; | ||
| 139 | ✗ | case DC_Throttle2: return TsCalMode::Tps2Max; | ||
| 140 | ✗ | case DC_Wastegate: return TsCalMode::EwgPosMax; | ||
| 141 | ✗ | default: return TsCalMode::None; | ||
| 142 | } | |||
| 143 | } | |||
| 144 | ||||
| 145 | ✗ | static TsCalMode functionToCalModeSecMin(dc_function_e func) { | ||
| 146 | ✗ | switch (func) { | ||
| 147 | ✗ | case DC_Throttle1: return TsCalMode::Tps1SecondaryMin; | ||
| 148 | ✗ | case DC_Throttle2: return TsCalMode::Tps2SecondaryMin; | ||
| 149 | ✗ | default: return TsCalMode::None; | ||
| 150 | } | |||
| 151 | } | |||
| 152 | ||||
| 153 | ✗ | static TsCalMode functionToCalModeSecMax(dc_function_e func) { | ||
| 154 | ✗ | switch (func) { | ||
| 155 | ✗ | case DC_Throttle1: return TsCalMode::Tps1SecondaryMax; | ||
| 156 | ✗ | case DC_Throttle2: return TsCalMode::Tps2SecondaryMax; | ||
| 157 | ✗ | default: return TsCalMode::None; | ||
| 158 | } | |||
| 159 | } | |||
| 160 | #endif // EFI_TUNER_STUDIO | |||
| 161 | ||||
| 162 | #define ETB_DUTY_LIMIT 0.9 | |||
| 163 | // this macro clamps both positive and negative percentages from about -100% to 100% | |||
| 164 | #define ETB_PERCENT_TO_DUTY(x) (clampF(-ETB_DUTY_LIMIT, 0.01f * (x), ETB_DUTY_LIMIT)) | |||
| 165 | ||||
| 166 | 236 | PUBLIC_API_WEAK bool isBoardAllowingLackOfPps() { | ||
| 167 | 236 | return false; | ||
| 168 | } | |||
| 169 | ||||
| 170 | 2091 | bool EtbController::init(dc_function_e function, DcMotor *motor, pid_s *pidParameters, const ValueProvider3D* pedalProvider) { | ||
| 171 | 2091 | state = (uint8_t)EtbState::InInit; | ||
| 172 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2090 times.
|
2/2✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 2090 times.
|
2091 | if (function == DC_None) { |
| 173 | // if not configured, don't init. | |||
| 174 | 1 | state = (uint8_t)EtbState::NotEbt; | ||
| 175 | 1 | etbErrorCode = (int8_t)EtbStatus::NotConfigured; | ||
| 176 | 1 | return false; | ||
| 177 | } | |||
| 178 | ||||
| 179 | 2090 | m_function = function; | ||
| 180 | 2090 | m_positionSensor = functionToPositionSensor(function); | ||
| 181 | ||||
| 182 | // If we are a throttle, require redundant TPS sensor | |||
| 183 |
2/2✓ Branch 1 taken 2083 times.
✓ Branch 2 taken 7 times.
|
2/2✓ Decision 'true' taken 2083 times.
✓ Decision 'false' taken 7 times.
|
2090 | if (isEtbMode()) { |
| 184 | // If no sensor is configured for this throttle, skip initialization. | |||
| 185 |
2/2✓ Branch 2 taken 2051 times.
✓ Branch 3 taken 32 times.
|
2/2✓ Decision 'true' taken 2051 times.
✓ Decision 'false' taken 32 times.
|
2083 | if (!Sensor::hasSensor(functionToTpsSensor(function))) { |
| 186 | 2051 | etbErrorCode = (int8_t)EtbStatus::TpsError; | ||
| 187 | 2051 | return false; | ||
| 188 | } | |||
| 189 | ||||
| 190 |
5/6✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 30 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 30 times.
|
2/2✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 30 times.
|
32 | if (!isBoardAllowingLackOfPps() && !Sensor::isRedundant(m_positionSensor)) { |
| 191 | 2 | etbErrorCode = (int8_t)EtbStatus::Redundancy; | ||
| 192 | 2 | return false; | ||
| 193 | } | |||
| 194 | } | |||
| 195 | ||||
| 196 | 37 | m_motor = motor; | ||
| 197 | 37 | m_pedalProvider = pedalProvider; | ||
| 198 | ||||
| 199 | 37 | m_pid.initPidClass(pidParameters); | ||
| 200 | ||||
| 201 | #if !EFI_UNIT_TEST | |||
| 202 | if (isEtbMode()) { | |||
| 203 | m_pid.iTermMin = engineConfiguration->etb_iTermMin; | |||
| 204 | m_pid.iTermMax = engineConfiguration->etb_iTermMax; | |||
| 205 | } else { | |||
| 206 | // Some defaults from setDefaultEtbParameters(), find better values for EWG and Idle or add config options | |||
| 207 | m_pid.iTermMin = -30; | |||
| 208 | m_pid.iTermMax = 30; | |||
| 209 | } | |||
| 210 | #endif | |||
| 211 | ||||
| 212 | // Ignore 3% position error before complaining | |||
| 213 | 37 | m_targetErrorAccumulator.init(3.0f, etbPeriodSeconds); | ||
| 214 | ||||
| 215 | 37 | state = (uint8_t)EtbState::SuccessfulInit; | ||
| 216 | 37 | return true; | ||
| 217 | } | |||
| 218 | ||||
| 219 | #if EFI_UNIT_TEST | |||
| 220 | int ebtResetCounter; | |||
| 221 | #endif // EFI_UNIT_TEST | |||
| 222 | ||||
| 223 | 17 | void EtbController::reset(const char *reason) { | ||
| 224 | 17 | efiPrintf("ETB reset %s", reason); | ||
| 225 | 17 | m_shouldResetPid = true; | ||
| 226 | 17 | etbTpsErrorCounter = 0; | ||
| 227 | 17 | etbPpsErrorCounter = 0; | ||
| 228 | #if EFI_UNIT_TEST | |||
| 229 | 17 | ebtResetCounter++; | ||
| 230 | #endif // EFI_UNIT_TEST | |||
| 231 | ||||
| 232 | 17 | } | ||
| 233 | ||||
| 234 | // todo: document why is EtbController not engine_module? | |||
| 235 | 442 | void EtbController::onConfigurationChange(pid_s* previousConfiguration) { | ||
| 236 |
5/6✓ Branch 0 taken 16 times.
✓ Branch 1 taken 426 times.
✓ Branch 3 taken 16 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 16 times.
✓ Branch 6 taken 426 times.
|
2/2✓ Decision 'true' taken 16 times.
✓ Decision 'false' taken 426 times.
|
442 | if (m_motor && !m_pid.isSame(previousConfiguration)) { |
| 237 | 16 | efiPrintf(" ETB m_shouldResetPid"); | ||
| 238 | 16 | m_shouldResetPid = true; | ||
| 239 | } | |||
| 240 | ||||
| 241 | 442 | doInitElectronicThrottle(/*isStartupInit*/false); | ||
| 242 | 442 | } | ||
| 243 | ||||
| 244 | ✗ | void EtbController::showStatus() { | ||
| 245 | ✗ | m_pid.showPidStatus("ETB"); | ||
| 246 | ✗ | } | ||
| 247 | ||||
| 248 | 208 | expected<percent_t> EtbController::observePlant() { | ||
| 249 |
1/1✓ Branch 2 taken 208 times.
|
208 | expected<percent_t> plant = Sensor::get(m_positionSensor); | |
| 250 | 208 | validPlantPosition = plant.Valid; | ||
| 251 | 208 | return plant; | ||
| 252 | } | |||
| 253 | ||||
| 254 | 2213 | void EtbController::setIdlePosition(percent_t pos) { | ||
| 255 | 2213 | m_idlePosition = pos; | ||
| 256 | 2213 | } | ||
| 257 | ||||
| 258 | 2212 | void EtbController::setWastegatePosition(percent_t pos) { | ||
| 259 | 2212 | m_wastegatePosition = pos; | ||
| 260 | 2212 | } | ||
| 261 | ||||
| 262 | 255 | expected<percent_t> EtbController::getSetpoint() { | ||
| 263 |
4/4✓ Branch 0 taken 244 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 time.
|
255 | switch (m_function) { | |
| 264 |
1/1✓ Decision 'true' taken 244 times.
|
244 | case DC_Throttle1: | |
| 265 | case DC_Throttle2: | |||
| 266 |
1/1✓ Decision 'true' taken 244 times.
|
244 | return getSetpointEtb(); | |
| 267 |
1/1✓ Decision 'true' taken 5 times.
|
5 | case DC_IdleValve: | |
| 268 | 5 | return getSetpointIdleValve(); | ||
| 269 |
1/1✓ Decision 'true' taken 5 times.
|
5 | case DC_Wastegate: | |
| 270 | 5 | return getSetpointWastegate(); | ||
| 271 |
1/1✓ Decision 'true' taken 1 time.
|
1 | default: | |
| 272 | 1 | return unexpected; | ||
| 273 | } | |||
| 274 | } | |||
| 275 | ||||
| 276 | 5 | expected<percent_t> EtbController::getSetpointIdleValve() const { | ||
| 277 | // VW ETB idle mode uses an ETB only for idle (a mini-ETB sets the lower stop, and a normal cable | |||
| 278 | // can pull the throttle up off the stop.), so we directly control the throttle with the idle position. | |||
| 279 | #if EFI_TUNER_STUDIO && (EFI_PROD_CODE || EFI_SIMULATOR) | |||
| 280 | // todo: where do we want to log this? engine->outputChannels.etbTarget = m_idlePosition; | |||
| 281 | #endif // EFI_TUNER_STUDIO | |||
| 282 |
1/1✓ Branch 2 taken 5 times.
|
5 | return clampPercentValue(m_idlePosition); | |
| 283 | } | |||
| 284 | ||||
| 285 | 5 | expected<percent_t> EtbController::getSetpointWastegate() const { | ||
| 286 | 5 | percent_t targetPosition = m_wastegatePosition + getLuaAdjustment(); | ||
| 287 | ||||
| 288 |
1/1✓ Branch 2 taken 5 times.
|
5 | return clampPercentValue(targetPosition); | |
| 289 | } | |||
| 290 | ||||
| 291 | 243 | float getSanitizedPedal() { | ||
| 292 |
1/1✓ Branch 2 taken 243 times.
|
243 | auto pedalPosition = Sensor::get(SensorType::AcceleratorPedal); | |
| 293 | // If the pedal has failed, just use 0 position. | |||
| 294 | // This is safer than disabling throttle control - we can at least push the throttle closed | |||
| 295 | // and let the engine idle. | |||
| 296 |
1/1✓ Branch 2 taken 243 times.
|
486 | return clampPercentValue(pedalPosition.value_or(0)); | |
| 297 | } | |||
| 298 | ||||
| 299 | 243 | PUBLIC_API_WEAK float boardAdjustEtbTarget(float currentEtbTarget) { | ||
| 300 | 243 | return currentEtbTarget; | ||
| 301 | } | |||
| 302 | ||||
| 303 | 244 | expected<percent_t> EtbController::getSetpointEtb() { | ||
| 304 | // Autotune runs with 50% target position | |||
| 305 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 244 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 244 times.
|
244 | if (m_isAutotune) { |
| 306 | ✗ | return 50.0f; | ||
| 307 | } | |||
| 308 | ||||
| 309 | // // A few extra preconditions if throttle control is invalid | |||
| 310 | // if (startupPositionError) { | |||
| 311 | // return unexpected; | |||
| 312 | // } | |||
| 313 | ||||
| 314 | // If the pedal map hasn't been set, we can't provide a setpoint. | |||
| 315 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 243 times.
|
2/2✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 243 times.
|
244 | if (!m_pedalProvider) { |
| 316 | 1 | state = (uint8_t)EtbState::NoPedal; | ||
| 317 | 1 | return unexpected; | ||
| 318 | } | |||
| 319 | ||||
| 320 |
1/1✓ Branch 1 taken 243 times.
|
243 | float sanitizedPedal = getSanitizedPedal(); | |
| 321 | ||||
| 322 |
1/1✓ Branch 1 taken 243 times.
|
243 | float rpm = Sensor::getOrZero(SensorType::Rpm); | |
| 323 |
1/1✓ Branch 1 taken 243 times.
|
243 | percent_t preBoard = m_pedalProvider->getValue(rpm, sanitizedPedal); | |
| 324 |
1/1✓ Branch 1 taken 243 times.
|
243 | etbCurrentTarget = boardAdjustEtbTarget(preBoard); | |
| 325 | 243 | boardEtbAdjustment = etbCurrentTarget - preBoard; | ||
| 326 | ||||
| 327 |
1/1✓ Branch 1 taken 243 times.
|
243 | percent_t etbIdlePosition = clampPercentValue(m_idlePosition); | |
| 328 | 243 | percent_t etbIdleAddition = PERCENT_DIV * engineConfiguration->etbIdleThrottleRange * etbIdlePosition; | ||
| 329 | ||||
| 330 | // Interpolate so that the idle adder just "compresses" the throttle's range upward. | |||
| 331 | // [0, 100] -> [idle, 100] | |||
| 332 | // 0% target from table -> idle position as target | |||
| 333 | // 100% target from table -> 100% target position | |||
| 334 |
1/1✓ Branch 1 taken 243 times.
|
243 | targetWithIdlePosition = interpolateClamped(0, etbIdleAddition, 100, 100, etbCurrentTarget); | |
| 335 | ||||
| 336 |
1/1✓ Branch 2 taken 243 times.
|
243 | percent_t targetPosition = targetWithIdlePosition + getLuaAdjustment(); | |
| 337 | // just an additional logging data point | |||
| 338 | 243 | adjustedEtbTarget = targetPosition; | ||
| 339 | ||||
| 340 | #if EFI_ANTILAG_SYSTEM | |||
| 341 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 243 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 243 times.
|
243 | if (engine->antilagController.isAntilagCondition) { |
| 342 | ✗ | targetPosition += engineConfiguration->ALSEtbAdd; | ||
| 343 | } | |||
| 344 | #endif /* EFI_ANTILAG_SYSTEM */ | |||
| 345 | ||||
| 346 |
1/1✓ Branch 1 taken 243 times.
|
243 | float vehicleSpeed = Sensor::getOrZero(SensorType::VehicleSpeed); | |
| 347 |
1/1✓ Branch 1 taken 243 times.
|
243 | float wheelSlip = Sensor::getOrZero(SensorType::WheelSlipRatio); | |
| 348 |
1/1✓ Branch 1 taken 243 times.
|
243 | tcEtbDrop = tcEtbDropTable.getValue(wheelSlip, vehicleSpeed); | |
| 349 | ||||
| 350 | // Apply any adjustment that this throttle alone needs | |||
| 351 | // Clamped to +-10 to prevent anything too wild | |||
| 352 |
2/2✓ Branch 1 taken 243 times.
✓ Branch 4 taken 243 times.
|
243 | trim = clampF(-10, getThrottleTrim(rpm, targetPosition), 10); | |
| 353 | 243 | targetPosition += trim + tcEtbDrop; | ||
| 354 | ||||
| 355 | // Clamp before rev limiter to avoid ineffective rev limit due to crazy out of range position target | |||
| 356 |
1/1✓ Branch 1 taken 243 times.
|
243 | targetPosition = clampPercentValue(targetPosition); | |
| 357 | ||||
| 358 | // Lastly, apply ETB rev limiter | |||
| 359 | 243 | auto etbRpmLimit = engineConfiguration->etbRevLimitStart; | ||
| 360 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 238 times.
|
2/2✓ Decision 'true' taken 5 times.
✓ Decision 'false' taken 238 times.
|
243 | if (etbRpmLimit != 0) { |
| 361 | 5 | auto fullyLimitedRpm = etbRpmLimit + engineConfiguration->etbRevLimitRange; | ||
| 362 | ||||
| 363 | 5 | float targetPositionBefore = targetPosition; | ||
| 364 | // Linearly taper throttle to closed from the limit across the range | |||
| 365 |
1/1✓ Branch 1 taken 5 times.
|
5 | targetPosition = interpolateClamped(etbRpmLimit, targetPosition, fullyLimitedRpm, 0, rpm); | |
| 366 | ||||
| 367 | // rev limit active if the position was changed by rev limiter | |||
| 368 | 5 | etbRevLimitActive = std::abs(targetPosition - targetPositionBefore) > 0.1f; | ||
| 369 | } | |||
| 370 | ||||
| 371 | 243 | float minPosition = engineConfiguration->etbMinimumPosition; | ||
| 372 | ||||
| 373 | // Keep the throttle just barely off the lower stop, and less than the user-configured maximum | |||
| 374 | 243 | float maxPosition = engineConfiguration->etbMaximumPosition; | ||
| 375 | // Don't allow max position over 100 | |||
| 376 | 243 | maxPosition = std::min(maxPosition, 100.0f); | ||
| 377 | ||||
| 378 |
1/1✓ Branch 1 taken 243 times.
|
243 | targetPosition = clampF(minPosition, targetPosition, maxPosition); | |
| 379 | 243 | m_adjustedTarget = targetPosition; | ||
| 380 | ||||
| 381 | 243 | return targetPosition; | ||
| 382 | } | |||
| 383 | ||||
| 384 | 6 | void EtbController::setLuaAdjustment(float adjustment) { | ||
| 385 | 6 | luaAdjustment = adjustment; | ||
| 386 | 6 | m_luaAdjustmentTimer.reset(); | ||
| 387 | 6 | } | ||
| 388 | ||||
| 389 | /** | |||
| 390 | * positive adjustment opens TPS, negative closes TPS | |||
| 391 | */ | |||
| 392 | 248 | float EtbController::getLuaAdjustment() const { | ||
| 393 | // If the lua position hasn't been set in 0.2 second, don't adjust! | |||
| 394 | // This avoids a stuck throttle due to hung/rogue/etc Lua script | |||
| 395 |
2/2✓ Branch 1 taken 241 times.
✓ Branch 2 taken 7 times.
|
2/2✓ Decision 'true' taken 241 times.
✓ Decision 'false' taken 7 times.
|
248 | if (m_luaAdjustmentTimer.getElapsedSeconds() > 0.2f) { |
| 396 | 241 | return 0; | ||
| 397 | } else { | |||
| 398 | 7 | return luaAdjustment; | ||
| 399 | } | |||
| 400 | } | |||
| 401 | ||||
| 402 | 1 | percent_t EtbController2::getThrottleTrim(float rpm, percent_t targetPosition) const { | ||
| 403 | 1 | return m_throttle2Trim.getValue(rpm, targetPosition); | ||
| 404 | } | |||
| 405 | ||||
| 406 | 169 | expected<percent_t> EtbController::getOpenLoop(percent_t target) { | ||
| 407 | // Don't apply open loop for idle valve, only real ETB or wastegate | |||
| 408 |
3/4✓ Branch 0 taken 159 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
|
169 | switch(m_function){ | |
| 409 |
1/1✓ Decision 'true' taken 159 times.
|
159 | case DC_Throttle1: | |
| 410 | case DC_Throttle2: { | |||
| 411 |
1/1✓ Decision 'true' taken 159 times.
|
159 | etbFeedForward = interpolate2d(target, config->etbBiasBins, config->etbBiasValues); | |
| 412 | 159 | break; | ||
| 413 | } | |||
| 414 |
1/1✓ Decision 'true' taken 5 times.
|
5 | case DC_Wastegate: { | |
| 415 | 5 | etbFeedForward = interpolate2d(target, config->dcWastegateBiasBins, config->dcWastegateBiasValues); | ||
| 416 | 5 | break; | ||
| 417 | } | |||
| 418 |
1/1✓ Decision 'true' taken 5 times.
|
5 | case DC_IdleValve: { | |
| 419 | 5 | etbFeedForward = 0; | ||
| 420 | 5 | break; | ||
| 421 | } | |||
| 422 | ✗ | default: { // or DC_None | ||
| 423 | ✗ | etbFeedForward = 0; | ||
| 424 | } | |||
| 425 | } | |||
| 426 | ||||
| 427 | 169 | return etbFeedForward; | ||
| 428 | } | |||
| 429 | ||||
| 430 | ✗ | expected<percent_t> EtbController::getClosedLoopAutotune(percent_t target, percent_t actualThrottlePosition) { | ||
| 431 | // Estimate gain at current position - this should be well away from the spring and in the linear region | |||
| 432 | // GetSetpoint sets this to 50% | |||
| 433 | ✗ | bool isPositive = actualThrottlePosition > target; | ||
| 434 | ||||
| 435 | ✗ | float autotuneAmplitude = 20; | ||
| 436 | ||||
| 437 | // End of cycle - record & reset | |||
| 438 | ✗ | if (!isPositive && m_lastIsPositive) { | ||
| 439 | // Determine period | |||
| 440 | ✗ | float tu = m_cycleTimer.getElapsedSecondsAndReset(getTimeNowNt()); | ||
| 441 | ||||
| 442 | // Determine amplitude | |||
| 443 | ✗ | float a = m_maxCycleTps - m_minCycleTps; | ||
| 444 | ||||
| 445 | // Filter - it's pretty noisy since the ultimate period is not very many loop periods | |||
| 446 | ✗ | constexpr float alpha = 0.05; | ||
| 447 | ✗ | m_a = alpha * a + (1 - alpha) * m_a; | ||
| 448 | ✗ | m_tu = alpha * tu + (1 - alpha) * m_tu; | ||
| 449 | ||||
| 450 | // Reset bounds | |||
| 451 | ✗ | m_minCycleTps = 100; | ||
| 452 | ✗ | m_maxCycleTps = 0; | ||
| 453 | ||||
| 454 | // Math is for Åström–Hägglund (relay) auto tuning | |||
| 455 | // https://warwick.ac.uk/fac/cross_fac/iatl/reinvention/archive/volume5issue2/hornsey | |||
| 456 | ||||
| 457 | // Amplitude of input (duty cycle %) | |||
| 458 | ✗ | float b = 2 * autotuneAmplitude; | ||
| 459 | ||||
| 460 | // Ultimate gain per A-H relay tuning rule | |||
| 461 | ✗ | float ku = 4 * b / (CONST_PI * m_a); | ||
| 462 | ||||
| 463 | // The multipliers below are somewhere near the "no overshoot" | |||
| 464 | // and "some overshoot" flavors of the Ziegler-Nichols method | |||
| 465 | // Kp | |||
| 466 | ✗ | float kp = 0.35f * ku; | ||
| 467 | ✗ | float ki = 0.25f * ku / m_tu; | ||
| 468 | ✗ | float kd = 0.08f * ku * m_tu; | ||
| 469 | ||||
| 470 | // Publish to TS state | |||
| 471 | #if EFI_TUNER_STUDIO | |||
| 472 | // Every 5 cycles (of the throttle), cycle to the next value | |||
| 473 | ✗ | if (m_autotuneCounter >= 5) { | ||
| 474 | ✗ | m_autotuneCounter = 0; | ||
| 475 | ✗ | m_autotuneCurrentParam = (m_autotuneCurrentParam + 1) % 3; // three ETB calibs: P-I-D | ||
| 476 | } | |||
| 477 | ||||
| 478 | ✗ | m_autotuneCounter++; | ||
| 479 | ||||
| 480 | ✗ | switch (m_autotuneCurrentParam) { | ||
| 481 | ✗ | case 0: | ||
| 482 | ✗ | tsCalibrationSetData(TsCalMode::EtbKp, kp); | ||
| 483 | ✗ | break; | ||
| 484 | ✗ | case 1: | ||
| 485 | ✗ | tsCalibrationSetData(TsCalMode::EtbKi, ki); | ||
| 486 | ✗ | break; | ||
| 487 | ✗ | case 2: | ||
| 488 | ✗ | tsCalibrationSetData(TsCalMode::EtbKd, kd); | ||
| 489 | ✗ | break; | ||
| 490 | } | |||
| 491 | ||||
| 492 | // Also output to debug channels if configured | |||
| 493 | ✗ | if (engineConfiguration->debugMode == DBG_ETB_AUTOTUNE) { | ||
| 494 | // a - amplitude of output (TPS %) | |||
| 495 | ✗ | engine->outputChannels.debugFloatField1 = m_a; | ||
| 496 | // b - amplitude of input (Duty cycle %) | |||
| 497 | ✗ | engine->outputChannels.debugFloatField2 = b; | ||
| 498 | // Tu - oscillation period (seconds) | |||
| 499 | ✗ | engine->outputChannels.debugFloatField3 = m_tu; | ||
| 500 | ||||
| 501 | ✗ | engine->outputChannels.debugFloatField4 = ku; | ||
| 502 | ✗ | engine->outputChannels.debugFloatField5 = kp; | ||
| 503 | ✗ | engine->outputChannels.debugFloatField6 = ki; | ||
| 504 | ✗ | engine->outputChannels.debugFloatField7 = kd; | ||
| 505 | } | |||
| 506 | #endif | |||
| 507 | // TODO: directly update PID settings in engineConfiguration | |||
| 508 | } | |||
| 509 | ||||
| 510 | ✗ | m_lastIsPositive = isPositive; | ||
| 511 | ||||
| 512 | // Find the min/max of each cycle | |||
| 513 | ✗ | if (actualThrottlePosition < m_minCycleTps) { | ||
| 514 | ✗ | m_minCycleTps = actualThrottlePosition; | ||
| 515 | } | |||
| 516 | ||||
| 517 | ✗ | if (actualThrottlePosition > m_maxCycleTps) { | ||
| 518 | ✗ | m_maxCycleTps = actualThrottlePosition; | ||
| 519 | } | |||
| 520 | ||||
| 521 | // Bang-bang control the output to induce oscillation | |||
| 522 | ✗ | return autotuneAmplitude * (isPositive ? -1 : 1); | ||
| 523 | } | |||
| 524 | ||||
| 525 | 158 | expected<percent_t> EtbController::getClosedLoop(percent_t target, percent_t observation) { | ||
| 526 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 155 times.
|
2/2✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 155 times.
|
158 | if (m_shouldResetPid) { |
| 527 | 3 | m_pid.reset(); | ||
| 528 | 3 | onTransitionEvent(TransitionEvent::EtbPidReset); | ||
| 529 | 3 | m_shouldResetPid = false; | ||
| 530 | } | |||
| 531 | ||||
| 532 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 158 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 158 times.
|
158 | if (m_isAutotune) { |
| 533 | ✗ | state = (uint8_t)EtbState::Autotune; | ||
| 534 | ||||
| 535 | ✗ | m_targetErrorAccumulator.reset(); | ||
| 536 | ||||
| 537 | ✗ | return getClosedLoopAutotune(target, observation); | ||
| 538 | } else { | |||
| 539 | 158 | checkJam(target, observation); | ||
| 540 | ||||
| 541 | 158 | integralError = m_targetErrorAccumulator.accumulate(target - observation); | ||
| 542 | ||||
| 543 | 158 | float dt = m_cycleTimer.getElapsedSecondsAndReset(getTimeNowNt()); | ||
| 544 | 158 | m_lastPidDtMs = dt * 1000.0; | ||
| 545 | ||||
| 546 | // Normal case - use PID to compute closed loop part | |||
| 547 |
1/1✓ Branch 2 taken 158 times.
|
158 | return m_pid.getOutput(target, observation, dt); | |
| 548 | } | |||
| 549 | } | |||
| 550 | ||||
| 551 | 212 | void EtbController::setOutput(expected<percent_t> outputValue) { | ||
| 552 | #if EFI_TUNER_STUDIO | |||
| 553 | // Only report first-throttle stats | |||
| 554 |
2/2✓ Branch 0 taken 211 times.
✓ Branch 1 taken 1 time.
|
2/2✓ Decision 'true' taken 211 times.
✓ Decision 'false' taken 1 time.
|
212 | if (m_function == DC_Throttle1) { |
| 555 | 211 | engine->outputChannels.etb1DutyCycle = outputValue.value_or(0); | ||
| 556 | } | |||
| 557 | #endif | |||
| 558 | ||||
| 559 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 211 times.
|
2/2✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 211 times.
|
212 | if (!m_motor) { |
| 560 | 1 | state = (uint8_t)EtbState::NoMotor; | ||
| 561 | 1 | return; | ||
| 562 | } | |||
| 563 | ||||
| 564 | bool isEnabled; | |||
| 565 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 211 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 211 times.
|
211 | if (!isEtbMode()) { |
| 566 | // technical debt: non-ETB usages of DC motor are still mixed into ETB controller? | |||
| 567 | ✗ | state = (uint8_t)EtbState::NotEbt; | ||
| 568 | ✗ | isEnabled = true; | ||
| 569 |
2/2✓ Branch 2 taken 1 time.
✓ Branch 3 taken 210 times.
|
2/2✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 210 times.
|
211 | } else if (!getLimpManager()->allowElectronicThrottle()) { |
| 570 | 1 | state = (uint8_t)EtbState::LimpProhibited; | ||
| 571 | 1 | isEnabled = false; | ||
| 572 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 209 times.
|
2/2✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 209 times.
|
210 | } else if (engineConfiguration->pauseEtbControl) { |
| 573 | 1 | state = (uint8_t)EtbState::Paused; | ||
| 574 | 1 | isEnabled = false; | ||
| 575 |
2/2✓ Branch 1 taken 51 times.
✓ Branch 2 taken 158 times.
|
2/2✓ Decision 'true' taken 51 times.
✓ Decision 'false' taken 158 times.
|
209 | } else if (!outputValue) { |
| 576 | 51 | state = (uint8_t)EtbState::NoOutput; | ||
| 577 | 51 | isEnabled = false; | ||
| 578 | } else { | |||
| 579 | 158 | state = (uint8_t)EtbState::Active; | ||
| 580 | 158 | isEnabled = true; | ||
| 581 | } | |||
| 582 | ||||
| 583 | // If not ETB, or ETB is allowed, output is valid, and we aren't paused, output to motor. | |||
| 584 |
2/2✓ Branch 0 taken 158 times.
✓ Branch 1 taken 53 times.
|
2/2✓ Decision 'true' taken 158 times.
✓ Decision 'false' taken 53 times.
|
211 | if (isEnabled) { |
| 585 | 158 | m_motor->enable(); | ||
| 586 | 158 | m_motor->set(ETB_PERCENT_TO_DUTY(outputValue.Value)); | ||
| 587 | } else { | |||
| 588 | // Otherwise disable the motor. | |||
| 589 | 53 | m_motor->disable("no-ETB"); | ||
| 590 | } | |||
| 591 | } | |||
| 592 | ||||
| 593 | 206 | bool EtbController::checkStatus() { | ||
| 594 | #if EFI_TUNER_STUDIO | |||
| 595 | // Only debug throttle #1 | |||
| 596 |
1/2✓ Branch 0 taken 206 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 206 times.
✗ Decision 'false' not taken.
|
206 | if (m_function == DC_Throttle1) { |
| 597 | 206 | m_pid.postState(engine->outputChannels.etbStatus); | ||
| 598 | ✗ | } else if (m_function == DC_Wastegate) { | ||
| 599 | ✗ | m_pid.postState(engine->outputChannels.wastegateDcStatus); | ||
| 600 | } | |||
| 601 | #endif /* EFI_TUNER_STUDIO */ | |||
| 602 | ||||
| 603 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 206 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 206 times.
|
206 | if (!isEtbMode()) { |
| 604 | // no validation for h-bridge or idle mode | |||
| 605 | ✗ | return true; | ||
| 606 | } | |||
| 607 | // ETB-specific code belo. The whole mix-up between DC and ETB is shameful :( | |||
| 608 | ||||
| 609 | // Only allow autotune with stopped engine, and on the first throttle | |||
| 610 | // Update local state about autotune | |||
| 611 | 206 | m_isAutotune = Sensor::getOrZero(SensorType::Rpm) == 0 | ||
| 612 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 206 times.
|
206 | && engine->etbAutoTune | |
| 613 |
1/4✓ Branch 0 taken 206 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
412 | && m_function == DC_Throttle1; | |
| 614 | ||||
| 615 | 206 | bool shouldCheckSensorFunction = engine->module<SensorChecker>()->analogSensorsShouldWork(); | ||
| 616 | ||||
| 617 |
3/4✓ Branch 0 taken 206 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 204 times.
✓ Branch 3 taken 2 times.
|
2/2✓ Decision 'true' taken 204 times.
✓ Decision 'false' taken 2 times.
|
206 | if (!m_isAutotune && shouldCheckSensorFunction) { |
| 618 | 204 | bool isTpsError = !Sensor::get(m_positionSensor).Valid; | ||
| 619 | ||||
| 620 | // If we have an error that's new, increment the counter | |||
| 621 |
3/4✓ Branch 0 taken 51 times.
✓ Branch 1 taken 153 times.
✓ Branch 2 taken 51 times.
✗ Branch 3 not taken.
|
2/2✓ Decision 'true' taken 51 times.
✓ Decision 'false' taken 153 times.
|
204 | if (isTpsError && !hadTpsError) { |
| 622 | 51 | etbTpsErrorCounter++; | ||
| 623 | } | |||
| 624 | ||||
| 625 | 204 | hadTpsError = isTpsError; | ||
| 626 | ||||
| 627 | 204 | bool isPpsError = !Sensor::get(SensorType::AcceleratorPedal).Valid; | ||
| 628 | ||||
| 629 | // If we have an error that's new, increment the counter | |||
| 630 |
4/4✓ Branch 0 taken 153 times.
✓ Branch 1 taken 51 times.
✓ Branch 2 taken 52 times.
✓ Branch 3 taken 101 times.
|
2/2✓ Decision 'true' taken 52 times.
✓ Decision 'false' taken 152 times.
|
204 | if (isPpsError && !hadPpsError) { |
| 631 | 52 | etbPpsErrorCounter++; | ||
| 632 | } | |||
| 633 | ||||
| 634 | 204 | hadPpsError = isPpsError; | ||
| 635 | 204 | } else { | ||
| 636 | // Either sensors are expected to not work, or autotune is running, so reset the error counter | |||
| 637 | 2 | etbTpsErrorCounter = 0; | ||
| 638 | 2 | etbPpsErrorCounter = 0; | ||
| 639 | } | |||
| 640 | ||||
| 641 | 206 | EtbStatus localReason = EtbStatus::None; | ||
| 642 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 205 times.
|
2/2✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 205 times.
|
206 | if (etbTpsErrorCounter > ETB_INTERMITTENT_LIMIT) { |
| 643 | 1 | localReason = EtbStatus::IntermittentTps; | ||
| 644 | #if EFI_SHAFT_POSITION_INPUT | |||
| 645 | 410 | } else if (engineConfiguration->disableEtbWhenEngineStopped | ||
| 646 | ✗ | && !engine->triggerCentral.engineMovedRecently() | ||
| 647 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 205 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 205 times.
|
205 | && !engine->etbAutoTune) { | |
| 648 | ✗ | localReason = EtbStatus::EngineStopped; | ||
| 649 | #endif // EFI_SHAFT_POSITION_INPUT | |||
| 650 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 204 times.
|
2/2✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 204 times.
|
205 | } else if (etbPpsErrorCounter > ETB_INTERMITTENT_LIMIT) { |
| 651 | 1 | localReason = EtbStatus::IntermittentPps; | ||
| 652 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 204 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 204 times.
|
204 | } else if (engine->engineState.lua.luaDisableEtb) { |
| 653 | ✗ | localReason = EtbStatus::Lua; | ||
| 654 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 204 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 204 times.
|
204 | } else if (!getLimpManager()->allowElectronicThrottle()) { |
| 655 | ✗ | localReason = EtbStatus::JamDetected; | ||
| 656 |
3/6✓ Branch 1 taken 204 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 204 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 204 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 204 times.
|
204 | } else if(!isBoardAllowingLackOfPps() && !Sensor::isRedundant(SensorType::AcceleratorPedal)) { |
| 657 | ✗ | localReason = EtbStatus::Redundancy; | ||
| 658 | } | |||
| 659 | ||||
| 660 | 206 | etbErrorCode = (int8_t)localReason; | ||
| 661 | ||||
| 662 | 206 | return localReason == EtbStatus::None; | ||
| 663 | } | |||
| 664 | ||||
| 665 | 206 | void EtbController::update() { | ||
| 666 | #if !EFI_UNIT_TEST | |||
| 667 | // If we didn't get initialized, fail fast | |||
| 668 | if (!m_motor) { | |||
| 669 | state = (uint8_t)EtbState::FailFast; | |||
| 670 | return; | |||
| 671 | } | |||
| 672 | #endif // EFI_UNIT_TEST | |||
| 673 | ||||
| 674 | 206 | bool isOk = checkStatus(); | ||
| 675 | ||||
| 676 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 204 times.
|
2/2✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 204 times.
|
206 | if (!isOk) { |
| 677 | // If engine is stopped and so configured, skip the ETB update entirely | |||
| 678 | // This is quieter and pulls less power than leaving it on all the time | |||
| 679 | 2 | m_motor->disable("etb status"); | ||
| 680 | 2 | return; | ||
| 681 | } | |||
| 682 | ||||
| 683 | 204 | ClosedLoopController::update(); | ||
| 684 | ||||
| 685 |
5/6✓ Branch 1 taken 204 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 50 times.
✓ Branch 4 taken 154 times.
✓ Branch 5 taken 50 times.
✓ Branch 6 taken 154 times.
|
2/2✓ Decision 'true' taken 50 times.
✓ Decision 'false' taken 154 times.
|
204 | if (isEtbMode() && !validPlantPosition) { |
| 686 | 50 | etbErrorCode = (int8_t)EtbStatus::TpsError; | ||
| 687 | } | |||
| 688 | } | |||
| 689 | ||||
| 690 | 162 | void EtbController::checkJam(percent_t setpoint, percent_t observation) { | ||
| 691 | 162 | float absError = std::abs(setpoint - observation); | ||
| 692 | ||||
| 693 | 162 | auto jamDetectThreshold = engineConfiguration->etbJamDetectThreshold; | ||
| 694 | 162 | auto jamTimeout = engineConfiguration->etbJamTimeout; | ||
| 695 | ||||
| 696 |
5/6✓ Branch 0 taken 162 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 158 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 158 times.
|
2/2✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 158 times.
|
162 | if (jamDetectThreshold != 0 && jamTimeout != 0) { |
| 697 |
1/1✓ Branch 1 taken 4 times.
|
4 | auto nowNt = getTimeNowNt(); | |
| 698 | ||||
| 699 |
7/8✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 time.
✓ Branch 3 taken 3 times.
✓ Branch 7 taken 3 times.
✓ Branch 9 taken 3 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 3 times.
✓ Branch 12 taken 1 time.
|
2/2✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 1 time.
|
4 | if (absError > jamDetectThreshold && engine->module<IgnitionController>()->getIgnState()) { |
| 700 |
3/3✓ Branch 2 taken 3 times.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 2 times.
|
2/2✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 2 times.
|
3 | if (m_jamDetectTimer.hasElapsedSec(jamTimeout)) { |
| 701 |
1/1✓ Branch 1 taken 1 time.
|
1 | efiPrintf(" ************* ETB is jammed! ***************"); | |
| 702 | 1 | jamDetected = true; | ||
| 703 | ||||
| 704 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
|
1 | getLimpManager()->reportEtbProblem(); | |
| 705 | } | |||
| 706 | } else { | |||
| 707 |
1/1✓ Branch 1 taken 1 time.
|
1 | m_jamDetectTimer.reset(nowNt); | |
| 708 | 1 | jamDetected = false; | ||
| 709 | } | |||
| 710 | ||||
| 711 |
1/1✓ Branch 2 taken 4 times.
|
4 | jamTimer = m_jamDetectTimer.getElapsedSeconds(nowNt); | |
| 712 | } | |||
| 713 | 162 | } | ||
| 714 | ||||
| 715 | #if !EFI_UNIT_TEST | |||
| 716 | /** | |||
| 717 | * Things running on a timer (instead of a thread) don't participate it the RTOS's thread priority system, | |||
| 718 | * and operate essentially "first come first serve", which risks starvation. | |||
| 719 | * Since ETB is a safety critical device, we need the hard RTOS guarantee that it will be scheduled over other less important tasks. | |||
| 720 | */ | |||
| 721 | #include "periodic_thread_controller.h" | |||
| 722 | #endif // EFI_UNIT_TEST | |||
| 723 | ||||
| 724 | #include <utility> | |||
| 725 | ||||
| 726 | // real implementation (we mock for some unit tests) | |||
| 727 | EtbImpl<EtbController1> etb1; | |||
| 728 | EtbImpl<EtbController2> etb2(throttle2TrimTable); | |||
| 729 | ||||
| 730 | static EtbController* etbControllers[] = { &etb1, &etb2 }; | |||
| 731 | static_assert(ETB_COUNT == sizeof(etbControllers) / sizeof(EtbController*)); | |||
| 732 | ||||
| 733 | 531078 | void blinkEtbErrorCodes(bool blinkPhase) { | ||
| 734 |
2/2✓ Branch 0 taken 1062156 times.
✓ Branch 1 taken 531078 times.
|
2/2✓ Decision 'true' taken 1062156 times.
✓ Decision 'false' taken 531078 times.
|
1593234 | for (int i = 0;i<ETB_COUNT;i++) { |
| 735 | 1062156 | int8_t etbErrorCode = etbControllers[i]->etbErrorCode; | ||
| 736 |
2/4✓ Branch 0 taken 1062156 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1062156 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1062156 times.
|
1062156 | if (etbErrorCode && engine->etbAutoTune) { |
| 737 | ✗ | etbErrorCode = (int8_t)EtbStatus::AutoTune; | ||
| 738 | } | |||
| 739 |
2/2✓ Branch 0 taken 526668 times.
✓ Branch 1 taken 535488 times.
|
1062156 | etbControllers[i]->etbErrorCodeBlinker = blinkPhase ? 0 : etbErrorCode; | |
| 740 | } | |||
| 741 | 531078 | } | ||
| 742 | ||||
| 743 | #if !EFI_UNIT_TEST | |||
| 744 | ||||
| 745 | struct DcThread final : public PeriodicController<512> { | |||
| 746 | DcThread() : PeriodicController("DC", PRIO_ETB, ETB_LOOP_FREQUENCY) {} | |||
| 747 | ||||
| 748 | void PeriodicTask(efitick_t) override { | |||
| 749 | // Simply update all controllers | |||
| 750 | for (int i = 0 ; i < ETB_COUNT; i++) { | |||
| 751 | auto controller = engine->etbControllers[i]; | |||
| 752 | assertNotNullVoid(controller); | |||
| 753 | etbControllers[i]->update(); | |||
| 754 | } | |||
| 755 | } | |||
| 756 | }; | |||
| 757 | ||||
| 758 | static DcThread dcThread CCM_OPTIONAL; | |||
| 759 | ||||
| 760 | #endif // !EFI_UNIT_TEST | |||
| 761 | ||||
| 762 | #if EFI_UNIT_TEST | |||
| 763 | 4 | void etbPidReset() { | ||
| 764 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 4 times.
|
2/2✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 4 times.
|
12 | for (int i = 0 ; i < ETB_COUNT; i++) { |
| 765 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
1/2✓ Decision 'true' taken 8 times.
✗ Decision 'false' not taken.
|
8 | if (auto controller = engine->etbControllers[i]) { |
| 766 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | assertNotNullVoid(controller); | |
| 767 | 8 | controller->reset("unit_test"); | ||
| 768 | } | |||
| 769 | } | |||
| 770 | 4 | ebtResetCounter = 0; | ||
| 771 | } | |||
| 772 | #endif // EFI_UNIT_TEST | |||
| 773 | ||||
| 774 | ✗ | void etbAutocal(dc_function_e function, bool reportToTs) { | ||
| 775 | ✗ | for (size_t i = 0 ; i < ETB_COUNT; i++) { | ||
| 776 | /* TODO: use from engine, add getFunction() to base class */ | |||
| 777 | //if (auto controller = engine->etbControllers[i]) { | |||
| 778 | ✗ | if (auto controller = etbControllers[i]) { | ||
| 779 | ✗ | assertNotNullVoid(controller); | ||
| 780 | ✗ | if (controller->getFunction() == function) { | ||
| 781 | /* TODO: is it possible that we have several controllers with same function? */ | |||
| 782 | ✗ | controller->autoCalibrateTps(reportToTs); | ||
| 783 | // todo fix root cause! work-around: make sure not to write bad tune since that would brick requestBurn(); | |||
| 784 | } | |||
| 785 | } | |||
| 786 | } | |||
| 787 | } | |||
| 788 | ||||
| 789 | ✗ | EtbStatus etbGetState(size_t throttleIndex) | ||
| 790 | { | |||
| 791 | ✗ | if (throttleIndex >= ETB_COUNT) { | ||
| 792 | ✗ | return EtbStatus::NotConfigured; | ||
| 793 | } | |||
| 794 | ||||
| 795 | ✗ | return (EtbStatus)etbControllers[throttleIndex]->etbErrorCode; | ||
| 796 | } | |||
| 797 | ||||
| 798 | /** | |||
| 799 | * This specific throttle has default position of about 7% open | |||
| 800 | */ | |||
| 801 | static const float boschBiasBins[] = { | |||
| 802 | 0, 1, 5, 7, 14, 65, 66, 100 | |||
| 803 | }; | |||
| 804 | static const float boschBiasValues[] = { | |||
| 805 | -15, -15, -10, 0, 19, 20, 26, 28 | |||
| 806 | }; | |||
| 807 | ||||
| 808 | ✗ | void setBoschVAGETB() { | ||
| 809 | ✗ | engineConfiguration->tpsMin = 890; // convert 12to10 bit (ADC/4) | ||
| 810 | ✗ | engineConfiguration->tpsMax = 70; // convert 12to10 bit (ADC/4) | ||
| 811 | ||||
| 812 | ✗ | engineConfiguration->tps1SecondaryMin = 102; | ||
| 813 | ✗ | engineConfiguration->tps1SecondaryMax = 891; | ||
| 814 | ||||
| 815 | ✗ | engineConfiguration->etb.pFactor = 5.12; | ||
| 816 | ✗ | engineConfiguration->etb.iFactor = 47; | ||
| 817 | ✗ | engineConfiguration->etb.dFactor = 0.088; | ||
| 818 | ✗ | engineConfiguration->etb.offset = 0; | ||
| 819 | ✗ | } | ||
| 820 | ||||
| 821 | ✗ | void setBoschVNH2SP30Curve() { | ||
| 822 | ✗ | copyArray(config->etbBiasBins, boschBiasBins); | ||
| 823 | ✗ | copyArray(config->etbBiasValues, boschBiasValues); | ||
| 824 | ✗ | } | ||
| 825 | ||||
| 826 | 587 | void setDefaultEtbParameters() { | ||
| 827 | 587 | engineConfiguration->etbIdleThrottleRange = 15; | ||
| 828 | ||||
| 829 | 587 | setLinearCurve(config->pedalToTpsPedalBins, /*from*/0, /*to*/100, 1); | ||
| 830 | 587 | setRpmTableBin(config->pedalToTpsRpmBins); | ||
| 831 | ||||
| 832 |
2/2✓ Branch 0 taken 4696 times.
✓ Branch 1 taken 587 times.
|
2/2✓ Decision 'true' taken 4696 times.
✓ Decision 'false' taken 587 times.
|
5283 | for (int pedalIndex = 0;pedalIndex<PEDAL_TO_TPS_SIZE;pedalIndex++) { |
| 833 |
2/2✓ Branch 0 taken 37568 times.
✓ Branch 1 taken 4696 times.
|
2/2✓ Decision 'true' taken 37568 times.
✓ Decision 'false' taken 4696 times.
|
42264 | for (int rpmIndex = 0;rpmIndex<PEDAL_TO_TPS_RPM_SIZE;rpmIndex++) { |
| 834 | 37568 | config->pedalToTpsTable[pedalIndex][rpmIndex] = config->pedalToTpsPedalBins[pedalIndex]; | ||
| 835 | } | |||
| 836 | } | |||
| 837 | ||||
| 838 | // Default is to run each throttle off its respective hbridge | |||
| 839 | 587 | engineConfiguration->etbFunctions[0] = DC_Throttle1; | ||
| 840 | 587 | engineConfiguration->etbFunctions[1] = DC_Throttle2; | ||
| 841 | ||||
| 842 | 587 | engineConfiguration->etbFreq = DEFAULT_ETB_PWM_FREQUENCY; | ||
| 843 | ||||
| 844 | // voltage, not ADC like with TPS | |||
| 845 | 587 | setPPSCalibration(0, 5, 5, 0); | ||
| 846 | ||||
| 847 | 587 | engineConfiguration->etb = { | ||
| 848 | 1, // Kp | |||
| 849 | 10, // Ki | |||
| 850 | 0.05, // Kd | |||
| 851 | 0, // offset | |||
| 852 | 0, // Update rate, unused | |||
| 853 | -100, 100 // min/max | |||
| 854 | }; | |||
| 855 | ||||
| 856 | 587 | engineConfiguration->etb_iTermMin = -30; | ||
| 857 | 587 | engineConfiguration->etb_iTermMax = 30; | ||
| 858 | ||||
| 859 | 587 | engineConfiguration->etbJamDetectThreshold = 10; | ||
| 860 | // engineConfiguration->etbJamTimeout = 1; | |||
| 861 | 587 | } | ||
| 862 | ||||
| 863 | 221 | void onConfigurationChangeElectronicThrottleCallback(engine_configuration_s *previousConfiguration) { | ||
| 864 |
2/2✓ Branch 0 taken 442 times.
✓ Branch 1 taken 221 times.
|
2/2✓ Decision 'true' taken 442 times.
✓ Decision 'false' taken 221 times.
|
663 | for (int i = 0; i < ETB_COUNT; i++) { |
| 865 | 442 | etbControllers[i]->onConfigurationChange(&previousConfiguration->etb); | ||
| 866 | } | |||
| 867 | 221 | } | ||
| 868 | ||||
| 869 | static const float defaultBiasBins[] = { | |||
| 870 | 0, 1, 2, 4, 7, 98, 99, 100 | |||
| 871 | }; | |||
| 872 | static const float defaultBiasValues[] = { | |||
| 873 | -20, -18, -17, 0, 20, 21, 22, 25 | |||
| 874 | }; | |||
| 875 | ||||
| 876 | 587 | void setDefaultEtbBiasCurve() { | ||
| 877 | 587 | copyArray(config->etbBiasBins, defaultBiasBins); | ||
| 878 | 587 | copyArray(config->etbBiasValues, defaultBiasValues); | ||
| 879 | 587 | copyArray(config->dcWastegateBiasBins, defaultBiasBins); | ||
| 880 | 587 | copyArray(config->dcWastegateBiasValues, defaultBiasValues); | ||
| 881 | 587 | } | ||
| 882 | ||||
| 883 | 1389 | void unregisterEtbPins() { | ||
| 884 | // todo: we probably need an implementation here?! | |||
| 885 | 1389 | } | ||
| 886 | ||||
| 887 | 2068 | static pid_s* getPidForDcFunction(dc_function_e function) { | ||
| 888 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2066 times.
|
2068 | switch (function) { | |
| 889 |
1/1✓ Decision 'true' taken 2 times.
|
2 | case DC_Wastegate: return &engineConfiguration->etbWastegatePid; | |
| 890 |
1/1✓ Decision 'true' taken 2066 times.
|
2066 | default: return &engineConfiguration->etb; | |
| 891 | } | |||
| 892 | } | |||
| 893 | ||||
| 894 | 2068 | PUBLIC_API_WEAK ValueProvider3D* pedal2TpsProvider() { | ||
| 895 | 2068 | return &pedal2tpsMap; | ||
| 896 | } | |||
| 897 | ||||
| 898 | 1039 | void doInitElectronicThrottle(bool isStartupInit) { | ||
| 899 | 1039 | bool anyEtbConfigured = false; | ||
| 900 | ||||
| 901 | // todo: technical debt: we still have DC motor code initialization in ETB-specific file while DC motors are used not just as ETB | |||
| 902 | // like DC motor wastegate code flow should probably NOT go through electronic_throttle.cpp right? | |||
| 903 | // todo: rename etbFunctions to something-without-etb for same reason? | |||
| 904 |
2/2✓ Branch 0 taken 2078 times.
✓ Branch 1 taken 1039 times.
|
2/2✓ Decision 'true' taken 2078 times.
✓ Decision 'false' taken 1039 times.
|
3117 | for (int i = 0 ; i < ETB_COUNT; i++) { |
| 905 | 2078 | auto func = engineConfiguration->etbFunctions[i]; | ||
| 906 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2068 times.
|
2/2✓ Decision 'true' taken 10 times.
✓ Decision 'false' taken 2068 times.
|
2078 | if (func == DC_None) { |
| 907 | // do not touch HW pins if function not selected, this way Lua can use DC motor hardware pins directly | |||
| 908 | 10 | continue; | ||
| 909 | } | |||
| 910 | 6204 | auto motor = initDcMotor("ETB disable", | ||
| 911 | 2068 | engineConfiguration->etbIo[i], i, engineConfiguration->etb_use_two_wires); | ||
| 912 | ||||
| 913 | 2068 | auto controller = engine->etbControllers[i]; | ||
| 914 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2068 times.
|
2068 | criticalAssertVoid(controller != nullptr, "null ETB"); | |
| 915 | ||||
| 916 | 2068 | auto pid = getPidForDcFunction(func); | ||
| 917 | ||||
| 918 | 2068 | bool dcConfigured = controller->init(func, motor, pid, pedal2TpsProvider()); | ||
| 919 |
4/4✓ Branch 0 taken 1177 times.
✓ Branch 1 taken 891 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 1168 times.
|
2/2✓ Decision 'true' taken 9 times.
✓ Decision 'false' taken 2059 times.
|
2068 | if (isStartupInit && dcConfigured) { |
| 920 | 9 | controller->reset("init"); | ||
| 921 | } | |||
| 922 |
4/4✓ Branch 0 taken 15 times.
✓ Branch 1 taken 2053 times.
✓ Branch 3 taken 13 times.
✓ Branch 4 taken 2 times.
|
2068 | anyEtbConfigured |= dcConfigured && controller->isEtbMode(); | |
| 923 | } | |||
| 924 | ||||
| 925 | // It's not valid to have a PPS without any ETBs - check that at least one ETB was enabled along with the pedal | |||
| 926 |
6/6✓ Branch 0 taken 1028 times.
✓ Branch 1 taken 11 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 1027 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 1038 times.
|
2/2✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 1038 times.
|
1039 | if (!anyEtbConfigured && Sensor::hasSensor(SensorType::AcceleratorPedalPrimary)) { |
| 927 | 1 | criticalError("A pedal position sensor was configured, but no electronic throttles are configured."); | ||
| 928 | } | |||
| 929 | ||||
| 930 | #if 0 && ! EFI_UNIT_TEST | |||
| 931 | percent_t startupThrottlePosition = getTPS(); | |||
| 932 | if (std::abs(startupThrottlePosition - engineConfiguration->etbNeutralPosition) > STARTUP_NEUTRAL_POSITION_ERROR_THRESHOLD) { | |||
| 933 | /** | |||
| 934 | * Unexpected electronic throttle start-up position is worth a critical error | |||
| 935 | */ | |||
| 936 | firmwareError(ObdCode::OBD_Throttle_Actuator_Control_Range_Performance_Bank_1, "startup ETB position %.2f not %d", | |||
| 937 | startupThrottlePosition, | |||
| 938 | engineConfiguration->etbNeutralPosition); | |||
| 939 | startupPositionError = true; | |||
| 940 | } | |||
| 941 | #endif /* EFI_UNIT_TEST */ | |||
| 942 | ||||
| 943 | #if !EFI_UNIT_TEST | |||
| 944 | static bool started = false; | |||
| 945 | if (started == false) { | |||
| 946 | dcThread.start(); | |||
| 947 | started = true; | |||
| 948 | } | |||
| 949 | #endif | |||
| 950 | } | |||
| 951 | ||||
| 952 | 586 | void initElectronicThrottle() { | ||
| 953 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 586 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 586 times.
|
586 | if (hasFirmwareError()) { |
| 954 | ✗ | return; | ||
| 955 | } | |||
| 956 | ||||
| 957 |
2/2✓ Branch 0 taken 1172 times.
✓ Branch 1 taken 586 times.
|
2/2✓ Decision 'true' taken 1172 times.
✓ Decision 'false' taken 586 times.
|
1758 | for (int i = 0; i < ETB_COUNT; i++) { |
| 958 | 1172 | engine->etbControllers[i] = etbControllers[i]; | ||
| 959 | } | |||
| 960 | ||||
| 961 | #if EFI_PROD_CODE | |||
| 962 | addConsoleAction("etbautocal", [](){ | |||
| 963 | efiPrintf("etbAutocal invoked"); | |||
| 964 | etbAutocal(DC_Throttle1); | |||
| 965 | }); | |||
| 966 | ||||
| 967 | addConsoleAction("etbinfo", [](){ | |||
| 968 | efiPrintf("etbAutoTune=%d", engine->etbAutoTune); | |||
| 969 | efiPrintf("TPS=%.2f", Sensor::getOrZero(SensorType::Tps1)); | |||
| 970 | ||||
| 971 | efiPrintf("ETB1 duty=%.2f", | |||
| 972 | (float)engine->outputChannels.etb1DutyCycle); | |||
| 973 | ||||
| 974 | efiPrintf("ETB freq=%d", | |||
| 975 | engineConfiguration->etbFreq); | |||
| 976 | ||||
| 977 | for (int i = 0; i < ETB_COUNT; i++) { | |||
| 978 | efiPrintf("ETB%d", i); | |||
| 979 | efiPrintf(" dir1=%s", hwPortname(engineConfiguration->etbIo[i].directionPin1)); | |||
| 980 | efiPrintf(" dir2=%s", hwPortname(engineConfiguration->etbIo[i].directionPin2)); | |||
| 981 | efiPrintf(" control=%s", hwPortname(engineConfiguration->etbIo[i].controlPin)); | |||
| 982 | efiPrintf(" disable=%s", hwPortname(engineConfiguration->etbIo[i].disablePin)); | |||
| 983 | showDcMotorInfo(i); | |||
| 984 | } | |||
| 985 | }); | |||
| 986 | ||||
| 987 | #endif /* EFI_PROD_CODE */ | |||
| 988 | ||||
| 989 | 586 | pedal2tpsMap.initTable(config->pedalToTpsTable, config->pedalToTpsRpmBins, config->pedalToTpsPedalBins); | ||
| 990 | 586 | throttle2TrimTable.initTable(config->throttle2TrimTable, config->throttle2TrimRpmBins, config->throttle2TrimTpsBins); | ||
| 991 | 586 | tcEtbDropTable.initTable(engineConfiguration->tractionControlEtbDrop, engineConfiguration->tractionControlSlipBins, engineConfiguration->tractionControlSpeedBins); | ||
| 992 | ||||
| 993 | 586 | doInitElectronicThrottle(/*isStartupInit*/true); | ||
| 994 | } | |||
| 995 | ||||
| 996 | 1102 | void setEtbIdlePosition(percent_t pos) { | ||
| 997 |
2/2✓ Branch 0 taken 2204 times.
✓ Branch 1 taken 1102 times.
|
2/2✓ Decision 'true' taken 2204 times.
✓ Decision 'false' taken 1102 times.
|
3306 | for (int i = 0; i < ETB_COUNT; i++) { |
| 998 |
1/2✓ Branch 1 taken 2204 times.
✗ Branch 2 not taken.
|
1/2✓ Decision 'true' taken 2204 times.
✗ Decision 'false' not taken.
|
2204 | if (auto etb = engine->etbControllers[i]) { |
| 999 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2204 times.
|
2204 | assertNotNullVoid(etb); | |
| 1000 | 2204 | etb->setIdlePosition(pos); | ||
| 1001 | } | |||
| 1002 | } | |||
| 1003 | } | |||
| 1004 | ||||
| 1005 | 1104 | void setEtbWastegatePosition(percent_t pos) { | ||
| 1006 |
2/2✓ Branch 0 taken 2208 times.
✓ Branch 1 taken 1104 times.
|
2/2✓ Decision 'true' taken 2208 times.
✓ Decision 'false' taken 1104 times.
|
3312 | for (int i = 0; i < ETB_COUNT; i++) { |
| 1007 |
1/2✓ Branch 1 taken 2208 times.
✗ Branch 2 not taken.
|
1/2✓ Decision 'true' taken 2208 times.
✗ Decision 'false' not taken.
|
2208 | if (auto etb = engine->etbControllers[i]) { |
| 1008 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2208 times.
|
2208 | assertNotNullVoid(etb); | |
| 1009 | 2208 | etb->setWastegatePosition(pos); | ||
| 1010 | } | |||
| 1011 | } | |||
| 1012 | } | |||
| 1013 | ||||
| 1014 | ✗ | void setEtbLuaAdjustment(percent_t pos) { | ||
| 1015 | ✗ | for (int i = 0; i < ETB_COUNT; i++) { | ||
| 1016 | /* TODO: use from engine, add getFunction() to base class */ | |||
| 1017 | //if (auto etb = engine->etbControllers[i]) { | |||
| 1018 | ✗ | if (auto etb = etbControllers[i]) { | ||
| 1019 | ✗ | assertNotNullVoid(etb); | ||
| 1020 | // try to adjust all ETB | |||
| 1021 | ✗ | if (etb->getFunction() == DC_Throttle1 || etb->getFunction() == DC_Throttle2) { | ||
| 1022 | ✗ | etb->setLuaAdjustment(pos); | ||
| 1023 | } | |||
| 1024 | } | |||
| 1025 | } | |||
| 1026 | } | |||
| 1027 | ||||
| 1028 | ✗ | void setEwgLuaAdjustment(percent_t pos) { | ||
| 1029 | ✗ | for (int i = 0; i < ETB_COUNT; i++) { | ||
| 1030 | /* TODO: use from engine, add getFunction() to base class */ | |||
| 1031 | //if (auto etb = engine->etbControllers[i]) { | |||
| 1032 | ✗ | if (auto etb = etbControllers[i]) { | ||
| 1033 | ✗ | assertNotNullVoid(etb); | ||
| 1034 | // try to adjust all ETB | |||
| 1035 | ✗ | if (etb->getFunction() == DC_Wastegate) { | ||
| 1036 | ✗ | etb->setLuaAdjustment(pos); | ||
| 1037 | } | |||
| 1038 | } | |||
| 1039 | } | |||
| 1040 | } | |||
| 1041 | ||||
| 1042 | 1 | void setToyota89281_33010_pedal_position_sensor() { | ||
| 1043 | 1 | setPPSCalibration(0, 4.1, 0.73, 4.9); | ||
| 1044 | 1 | } | ||
| 1045 | ||||
| 1046 | 1 | void setHitachiEtbCalibration() { | ||
| 1047 | 1 | setToyota89281_33010_pedal_position_sensor(); | ||
| 1048 | ||||
| 1049 | 1 | setHitachiEtbBiasBins(); | ||
| 1050 | ||||
| 1051 | 1 | engineConfiguration->etb.pFactor = 2.7999; | ||
| 1052 | 1 | engineConfiguration->etb.iFactor = 25.5; | ||
| 1053 | 1 | engineConfiguration->etb.dFactor = 0.053; | ||
| 1054 | 1 | engineConfiguration->etb.offset = 0.0; | ||
| 1055 | 1 | engineConfiguration->etb.periodMs = 5.0; | ||
| 1056 | 1 | engineConfiguration->etb.minValue = -100.0; | ||
| 1057 | 1 | engineConfiguration->etb.maxValue = 100.0; | ||
| 1058 | ||||
| 1059 | // Nissan 60mm throttle | |||
| 1060 | 1 | engineConfiguration->tpsMin = engineConfiguration->tps2Min = 113; | ||
| 1061 | 1 | engineConfiguration->tpsMax = engineConfiguration->tps2Max = 846; | ||
| 1062 | 1 | engineConfiguration->tps1SecondaryMin = engineConfiguration->tps2SecondaryMin = 897; | ||
| 1063 | 1 | engineConfiguration->tps1SecondaryMax = engineConfiguration->tps2SecondaryMax = 161; | ||
| 1064 | 1 | } | ||
| 1065 | ||||
| 1066 | 1 | void setProteusHitachiEtbDefaults() { | ||
| 1067 | #if HW_PROTEUS | |||
| 1068 | 1 | setHitachiEtbCalibration(); | ||
| 1069 | ||||
| 1070 | // EFI_ADC_12: "Analog Volt 3" | |||
| 1071 | 1 | engineConfiguration->tps1_2AdcChannel = PROTEUS_IN_TPS1_2; | ||
| 1072 | // EFI_ADC_13: "Analog Volt 4" | |||
| 1073 | 1 | engineConfiguration->tps2_1AdcChannel = PROTEUS_IN_TPS2_1; | ||
| 1074 | // EFI_ADC_0: "Analog Volt 5" | |||
| 1075 | 1 | engineConfiguration->tps2_2AdcChannel = PROTEUS_IN_ANALOG_VOLT_5; | ||
| 1076 | 1 | setPPSInputs(PROTEUS_IN_ANALOG_VOLT_6, PROTEUS_IN_PPS2); | ||
| 1077 | #endif // HW_PROTEUS | |||
| 1078 | 1 | } | ||
| 1079 | ||||
| 1080 | #endif /* EFI_ELECTRONIC_THROTTLE_BODY */ | |||
| 1081 | ||||
| 1082 | // So far used by FragmentEntry (LiveData) | |||
| 1083 | template<> | |||
| 1084 | ✗ | const electronic_throttle_s* getLiveData(size_t idx) { | ||
| 1085 | #if EFI_ELECTRONIC_THROTTLE_BODY | |||
| 1086 | ✗ | if (idx >= efi::size(etbControllers)) { | ||
| 1087 | ✗ | return nullptr; | ||
| 1088 | } | |||
| 1089 | ||||
| 1090 | ✗ | return etbControllers[idx]; | ||
| 1091 | #else | |||
| 1092 | return nullptr; | |||
| 1093 | #endif | |||
| 1094 | } | |||
| 1095 |