| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /** | |||
| 2 | * @file speed_density.cpp | |||
| 3 | * | |||
| 4 | * See http://rusefi.com/wiki/index.php?title=Manual:Software:Fuel_Control#Speed_Density for details | |||
| 5 | * | |||
| 6 | * @date May 29, 2014 | |||
| 7 | * @author Andrey Belomutskiy, (c) 2012-2020 | |||
| 8 | */ | |||
| 9 | ||||
| 10 | #include "pch.h" | |||
| 11 | ||||
| 12 | #if defined(HAS_OS_ACCESS) | |||
| 13 | #error "Unexpected OS ACCESS HERE" | |||
| 14 | #endif | |||
| 15 | ||||
| 16 | #define rpmMin 500 | |||
| 17 | #define rpmMax 8000 | |||
| 18 | ||||
| 19 | ve_Map3D_t veMap{"ve"}; | |||
| 20 | ||||
| 21 | #define tpMin 0 | |||
| 22 | #define tpMax 100 | |||
| 23 | ||||
| 24 | 1107 | float IFuelComputer::getTChargeCoefficient(float rpm, float tps) { | ||
| 25 | // First, do TPS mode since it doesn't need any of the airflow math. | |||
| 26 |
2/2✓ Branch 0 taken 1106 times.
✓ Branch 1 taken 1 time.
|
2/2✓ Decision 'true' taken 1106 times.
✓ Decision 'false' taken 1 time.
|
1107 | if (engineConfiguration->tChargeMode == TCHARGE_MODE_RPM_TPS) { |
| 27 | 1106 | float minRpmKcurrentTPS = interpolateMsg("minRpm", tpMin, | ||
| 28 | 1106 | engineConfiguration->tChargeMinRpmMinTps, tpMax, | ||
| 29 | 1106 | engineConfiguration->tChargeMinRpmMaxTps, tps); | ||
| 30 | 1106 | float maxRpmKcurrentTPS = interpolateMsg("maxRpm", tpMin, | ||
| 31 | 1106 | engineConfiguration->tChargeMaxRpmMinTps, tpMax, | ||
| 32 | 1106 | engineConfiguration->tChargeMaxRpmMaxTps, tps); | ||
| 33 | ||||
| 34 | 1106 | return interpolateMsg("Kcurr", rpmMin, minRpmKcurrentTPS, rpmMax, maxRpmKcurrentTPS, rpm); | ||
| 35 | } | |||
| 36 | ||||
| 37 | 1 | constexpr floatms_t gramsPerMsToKgPerHour = (3600.0f * 1000.0f) / 1000.0f; | ||
| 38 | // We're actually using an 'old' airMass calculated for the previous cycle, but it's ok, we're not having any self-excitaton issues | |||
| 39 | 1 | floatms_t airMassForEngine = sdAirMassInOneCylinder * engineConfiguration->cylindersCount; | ||
| 40 | // airMass is in grams per 1 cycle for 1 cyl. Convert it to airFlow in kg/h for the engine. | |||
| 41 | // And if the engine is stopped (0 rpm), then airFlow is also zero (avoiding NaN division) | |||
| 42 |
1/2✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
|
1 | floatms_t airFlow = (rpm == 0) ? 0 : airMassForEngine * gramsPerMsToKgPerHour / getEngineCycleDuration(rpm); | |
| 43 | ||||
| 44 |
1/2✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 1 time.
✗ Decision 'false' not taken.
|
1 | if (engineConfiguration->tChargeMode == TCHARGE_MODE_AIR_INTERP) { |
| 45 | // just interpolate between user-specified min and max coefs, based on the max airFlow value | |||
| 46 | 1 | return interpolateClamped( | ||
| 47 | 0.0, engineConfiguration->tChargeAirCoefMin, | |||
| 48 | engineConfiguration->tChargeAirFlowMax, engineConfiguration->tChargeAirCoefMax, | |||
| 49 | airFlow | |||
| 50 | 1 | ); | ||
| 51 | ✗ | } else if (engineConfiguration->tChargeMode == TCHARGE_MODE_AIR_INTERP_TABLE) { | ||
| 52 | ✗ | return interpolate2d( | ||
| 53 | airFlow, | |||
| 54 | ✗ | engineConfiguration->tchargeBins, | ||
| 55 | ✗ | engineConfiguration->tchargeValues | ||
| 56 | ✗ | ); | ||
| 57 | } else { | |||
| 58 | ✗ | criticalError("Unexpected tChargeMode: %d", engineConfiguration->tChargeMode); | ||
| 59 | ✗ | return 0; | ||
| 60 | } | |||
| 61 | } | |||
| 62 | ||||
| 63 | // http://rusefi.com/math/t_charge.html | |||
| 64 | /***panel:Charge Temperature*/ | |||
| 65 | 1107 | temperature_t IFuelComputer::getTCharge(float rpm, float tps) { | ||
| 66 |
1/1✓ Branch 2 taken 1107 times.
|
1107 | const auto clt = Sensor::get(SensorType::Clt); | |
| 67 |
1/1✓ Branch 2 taken 1107 times.
|
1107 | const auto iat = Sensor::get(SensorType::Iat); | |
| 68 | ||||
| 69 | float airTemp; | |||
| 70 | ||||
| 71 | // Without either valid, return 0C. It's wrong, but it'll pretend to be nice and dense, so at least you won't go lean. | |||
| 72 |
2/6✗ Branch 1 not taken.
✓ Branch 2 taken 1107 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1107 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1107 times.
|
1107 | if (!iat && !clt) { |
| 73 | ✗ | return 0; | ||
| 74 |
2/6✗ Branch 1 not taken.
✓ Branch 2 taken 1107 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1107 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1107 times.
|
1107 | } else if (!clt && iat) { |
| 75 | // Intake temperature will almost always be colder (richer) than CLT - use that | |||
| 76 | ✗ | return iat.Value; | ||
| 77 |
2/6✗ Branch 1 not taken.
✓ Branch 2 taken 1107 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1107 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1107 times.
|
1107 | } else if (!iat && clt) { |
| 78 | // Without valid intake temperature, assume intake temp is 0C, and interpolate anyway | |||
| 79 | ✗ | airTemp = 0; | ||
| 80 | } else { | |||
| 81 | // All is well - use real air temp | |||
| 82 | 1107 | airTemp = iat.Value; | ||
| 83 | } | |||
| 84 | ||||
| 85 | 1107 | float coolantTemp = clt.Value; | ||
| 86 | ||||
| 87 |
1/1✓ Branch 1 taken 1107 times.
|
1107 | sdTcharge_coff = getTChargeCoefficient(rpm, tps); | |
| 88 | ||||
| 89 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1107 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1107 times.
|
1107 | if (std::isnan(sdTcharge_coff)) { |
| 90 | ✗ | warning(ObdCode::CUSTOM_ERR_T2_CHARGE, "t2-getTCharge NaN"); | ||
| 91 | ✗ | return coolantTemp; | ||
| 92 | } | |||
| 93 | ||||
| 94 | // Interpolate between CLT and IAT: | |||
| 95 | // 0.0 coefficient -> use CLT (full heat transfer) | |||
| 96 | // 1.0 coefficient -> use IAT (no heat transfer) | |||
| 97 |
1/1✓ Branch 1 taken 1107 times.
|
1107 | float Tcharge = interpolateClamped(0.0f, coolantTemp, 1.0f, airTemp, sdTcharge_coff); | |
| 98 | ||||
| 99 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1107 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1107 times.
|
1107 | if (std::isnan(Tcharge)) { |
| 100 | // we can probably end up here while resetting engine state - interpolation would fail | |||
| 101 | ✗ | warning(ObdCode::CUSTOM_ERR_TCHARGE_NOT_READY, "getTCharge NaN"); | ||
| 102 | ✗ | return coolantTemp; | ||
| 103 | } | |||
| 104 | ||||
| 105 | 1107 | return Tcharge; | ||
| 106 | } | |||
| 107 | ||||
| 108 | 586 | void initSpeedDensity() { | ||
| 109 | 586 | veMap.initTable(config->veTable, config->veRpmBins, config->veLoadBins); | ||
| 110 | 586 | } | ||
| 111 |