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 | 1126 | 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 1125 times.
✓ Branch 1 taken 1 time.
|
2/2✓ Decision 'true' taken 1125 times.
✓ Decision 'false' taken 1 time.
|
1126 | if (engineConfiguration->tChargeMode == TCHARGE_MODE_RPM_TPS) { |
27 | 1125 | float minRpmKcurrentTPS = interpolateMsg("minRpm", tpMin, | ||
28 | 1125 | engineConfiguration->tChargeMinRpmMinTps, tpMax, | ||
29 | 1125 | engineConfiguration->tChargeMinRpmMaxTps, tps); | ||
30 | 1125 | float maxRpmKcurrentTPS = interpolateMsg("maxRpm", tpMin, | ||
31 | 1125 | engineConfiguration->tChargeMaxRpmMinTps, tpMax, | ||
32 | 1125 | engineConfiguration->tChargeMaxRpmMaxTps, tps); | ||
33 | ||||
34 | 1125 | 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 | 1126 | temperature_t IFuelComputer::getTCharge(float rpm, float tps) { | ||
66 |
1/1✓ Branch 2 taken 1126 times.
|
1126 | const auto clt = Sensor::get(SensorType::Clt); | |
67 |
1/1✓ Branch 2 taken 1126 times.
|
1126 | 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 1126 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1126 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1126 times.
|
1126 | if (!iat && !clt) { |
73 | ✗ | return 0; | ||
74 |
2/6✗ Branch 1 not taken.
✓ Branch 2 taken 1126 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1126 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1126 times.
|
1126 | } 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 1126 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1126 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1126 times.
|
1126 | } 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 | 1126 | airTemp = iat.Value; | ||
83 | } | |||
84 | ||||
85 | 1126 | float coolantTemp = clt.Value; | ||
86 | ||||
87 |
1/1✓ Branch 1 taken 1126 times.
|
1126 | sdTcharge_coff = getTChargeCoefficient(rpm, tps); | |
88 | ||||
89 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1126 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1126 times.
|
1126 | 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 1126 times.
|
1126 | float Tcharge = interpolateClamped(0.0f, coolantTemp, 1.0f, airTemp, sdTcharge_coff); | |
98 | ||||
99 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1126 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1126 times.
|
1126 | 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 | 1126 | return Tcharge; | ||
106 | } | |||
107 | ||||
108 | 585 | void initSpeedDensity() { | ||
109 | 585 | veMap.initTable(config->veTable, config->veRpmBins, config->veLoadBins); | ||
110 | 585 | } | ||
111 |