From 650d148008d8148ea3fcc1fdf0476cce0d30882f Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Fri, 19 Mar 2021 14:04:42 -0700 Subject: [PATCH] Usable fuel consumption data/gauges (#2474) * fuel consumption * gauge names * consumers * obd * binary logging * doesn't need explicit constructor * getters * it works * correct for injections per cycle * datalog Co-authored-by: Matthew Kennedy --- firmware/console/binary/tunerstudio_outputs.h | 3 +- .../console/binary_log/binary_logging.cpp | 2 + firmware/console/status_loop.cpp | 3 +- firmware/controllers/algo/engine2.cpp | 38 +++++++------------ firmware/controllers/algo/engine_parts.h | 21 +++++----- firmware/controllers/algo/fuel_math.cpp | 10 ----- firmware/controllers/algo/fuel_math.h | 3 -- firmware/controllers/can/obd2.cpp | 9 +++-- .../engine_cycle/main_trigger_callback.cpp | 14 +++---- firmware/integration/rusefi_config.txt | 3 ++ firmware/tunerstudio/rusefi.input | 9 ++++- 11 files changed, 52 insertions(+), 63 deletions(-) diff --git a/firmware/console/binary/tunerstudio_outputs.h b/firmware/console/binary/tunerstudio_outputs.h index f4c34ad2e5..41a260311a 100644 --- a/firmware/console/binary/tunerstudio_outputs.h +++ b/firmware/console/binary/tunerstudio_outputs.h @@ -158,7 +158,8 @@ typedef struct { // Fuel system scaled_percent fuelTankLevel; // 98 - float fuelConsumptionPerHour; // 100 + scaled_channel totalFuelConsumption; // 100 + scaled_channel fuelFlowRate; // 102 // Y axis values for selectable tables scaled_channel veTableYAxis; // 104 diff --git a/firmware/console/binary_log/binary_logging.cpp b/firmware/console/binary_log/binary_logging.cpp index fdee01c048..35e4b59954 100644 --- a/firmware/console/binary_log/binary_logging.cpp +++ b/firmware/console/binary_log/binary_logging.cpp @@ -64,6 +64,8 @@ static const LogField fields[] = { {tsOutputChannels.ignitionLoad, GAUGE_NAME_IGNITION_LOAD, "%", 1}, {tsOutputChannels.massAirFlow, GAUGE_NAME_AIR_FLOW, "kg/h", 1}, {tsOutputChannels.flexPercent, GAUGE_NAME_FLEX, "%", 1}, + {tsOutputChannels.fuelFlowRate, GAUGE_NAME_FUEL_FLOW, "g/s", 3}, + {tsOutputChannels.totalFuelConsumption, GAUGE_NAME_FUEL_CONSUMPTION, "g", 1}, }; void writeHeader(Writer& outBuffer) { diff --git a/firmware/console/status_loop.cpp b/firmware/console/status_loop.cpp index 955cd2ed51..c92863908f 100644 --- a/firmware/console/status_loop.cpp +++ b/firmware/console/status_loop.cpp @@ -705,7 +705,8 @@ void updateTunerStudioState(TunerStudioOutputChannels *tsOutputChannels DECLARE_ #endif /* EFI_VEHICLE_SPEED */ #endif /* EFI_PROD_CODE */ - tsOutputChannels->fuelConsumptionPerHour = engine->engineState.fuelConsumption.perSecondConsumption; + tsOutputChannels->fuelFlowRate = engine->engineState.fuelConsumption.getConsumptionGramPerSecond(); + tsOutputChannels->totalFuelConsumption = engine->engineState.fuelConsumption.getConsumedGrams(); tsOutputChannels->warningCounter = engine->engineState.warnings.warningCounter; tsOutputChannels->lastErrorCode = engine->engineState.warnings.lastErrorCode; diff --git a/firmware/controllers/algo/engine2.cpp b/firmware/controllers/algo/engine2.cpp index cda79921c8..ecc19b04f0 100644 --- a/firmware/controllers/algo/engine2.cpp +++ b/firmware/controllers/algo/engine2.cpp @@ -76,34 +76,25 @@ void MockAdcState::setMockVoltage(int hwChannel, float voltage DECLARE_ENGINE_PA } #endif /* EFI_ENABLE_MOCK_ADC */ -FuelConsumptionState::FuelConsumptionState() { - accumulatedSecondPrevNt = accumulatedMinutePrevNt = getTimeNowNt(); -} +void FuelConsumptionState::consumeFuel(float grams, efitick_t nowNt) { + m_consumedGrams += grams; -void FuelConsumptionState::addData(float durationMs) { - if (durationMs > 0.0f) { - perSecondAccumulator += durationMs; - perMinuteAccumulator += durationMs; + float elapsedSecond = m_timer.getElapsedSecondsAndReset(nowNt); + + // If it's been a long time since last injection, ignore this pulse + if (elapsedSecond > 0.2f) { + m_rate = 0; + } else { + m_rate = grams / elapsedSecond; } } -void FuelConsumptionState::update(efitick_t nowNt DECLARE_ENGINE_PARAMETER_SUFFIX) { - efitick_t deltaNt = nowNt - accumulatedSecondPrevNt; - if (deltaNt >= NT_PER_SECOND) { - perSecondConsumption = getFuelRate(perSecondAccumulator, deltaNt PASS_ENGINE_PARAMETER_SUFFIX); - perSecondAccumulator = 0; - accumulatedSecondPrevNt = nowNt; - } - - deltaNt = nowNt - accumulatedMinutePrevNt; - if (deltaNt >= NT_PER_SECOND * 60) { - perMinuteConsumption = getFuelRate(perMinuteAccumulator, deltaNt PASS_ENGINE_PARAMETER_SUFFIX); - perMinuteAccumulator = 0; - accumulatedMinutePrevNt = nowNt; - } +float FuelConsumptionState::getConsumedGrams() const { + return m_consumedGrams; } -TransmissionState::TransmissionState() { +float FuelConsumptionState::getConsumptionGramPerSecond() const { + return m_rate; } EngineState::EngineState() { @@ -144,9 +135,6 @@ void EngineState::periodicFastCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) { auto clResult = fuelClosedLoopCorrection(PASS_ENGINE_PARAMETER_SIGNATURE); running.pidCorrection = clResult.banks[0]; - // update fuel consumption states - fuelConsumption.update(nowNt PASS_ENGINE_PARAMETER_SUFFIX); - // Fuel cut-off isn't just 0 or 1, it can be tapered fuelCutoffCorrection = getFuelCutOffCorrection(nowNt, rpm PASS_ENGINE_PARAMETER_SUFFIX); diff --git a/firmware/controllers/algo/engine_parts.h b/firmware/controllers/algo/engine_parts.h index 9f85ca6236..297ae2f518 100644 --- a/firmware/controllers/algo/engine_parts.h +++ b/firmware/controllers/algo/engine_parts.h @@ -10,6 +10,7 @@ #include "global.h" #include "engine_configuration_generated_structures.h" #include "cyclic_buffer.h" +#include "timer.h" #define MOCK_ADC_SIZE 26 @@ -39,20 +40,20 @@ public: class FuelConsumptionState { public: - FuelConsumptionState(); - void addData(float durationMs); - void update(efitick_t nowNt DECLARE_ENGINE_PARAMETER_SUFFIX); - float perSecondConsumption = 0; - float perMinuteConsumption = 0; - float perSecondAccumulator = 0; - float perMinuteAccumulator = 0; - efitick_t accumulatedSecondPrevNt; - efitick_t accumulatedMinutePrevNt; + void consumeFuel(float grams, efitick_t nowNt); + + float getConsumedGrams() const; + float getConsumptionGramPerSecond() const; + +private: + float m_consumedGrams = 0; + float m_rate = 0; + + Timer m_timer; }; class TransmissionState { public: - TransmissionState(); gear_e gearSelectorPosition; }; diff --git a/firmware/controllers/algo/fuel_math.cpp b/firmware/controllers/algo/fuel_math.cpp index 9d4644d74e..95131681ce 100644 --- a/firmware/controllers/algo/fuel_math.cpp +++ b/firmware/controllers/algo/fuel_math.cpp @@ -477,14 +477,4 @@ float getStandardAirCharge(DECLARE_ENGINE_PARAMETER_SIGNATURE) { } #endif - -float getFuelRate(floatms_t totalInjDuration, efitick_t timePeriod DECLARE_ENGINE_PARAMETER_SUFFIX) { - if (timePeriod <= 0.0f) - return 0.0f; - float timePeriodMs = (float)NT2US(timePeriod) / 1000.0f; - float fuelRate = totalInjDuration / timePeriodMs; - const float cc_min_to_L_h = 60.0f / 1000.0f; - return fuelRate * CONFIG(injector.flow) * cc_min_to_L_h; -} - #endif diff --git a/firmware/controllers/algo/fuel_math.h b/firmware/controllers/algo/fuel_math.h index 54e6b48ce4..f124b1a441 100644 --- a/firmware/controllers/algo/fuel_math.h +++ b/firmware/controllers/algo/fuel_math.h @@ -31,6 +31,3 @@ floatms_t getInjectionMass(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX); percent_t getInjectorDutyCycle(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX); float getStandardAirCharge(DECLARE_ENGINE_PARAMETER_SIGNATURE); - -// convert injection duration (Ms/Nt) to fuel rate (L/h) -float getFuelRate(floatms_t totalInjDuration, efitick_t timePeriod DECLARE_ENGINE_PARAMETER_SUFFIX); diff --git a/firmware/controllers/can/obd2.cpp b/firmware/controllers/can/obd2.cpp index a968c5325e..209655de8a 100644 --- a/firmware/controllers/can/obd2.cpp +++ b/firmware/controllers/can/obd2.cpp @@ -171,10 +171,13 @@ static void handleGetDataRequest(const CANRxFrame& rx) { obdSendPacket(1, pid, 4, scaled << 16); break; - } case PID_FUEL_RATE: - obdSendValue(_1_MODE, pid, 2, engine->engineState.fuelConsumption.perSecondConsumption * 20.0f); // L/h. (A*256+B)/20 + } case PID_FUEL_RATE: { + float gPerSecond = engine->engineState.fuelConsumption.getConsumptionGramPerSecond(); + float gPerHour = gPerSecond * 3600; + float literPerHour = gPerHour * 0.00139f; + obdSendValue(_1_MODE, pid, 2, literPerHour * 20.0f); // L/h. (A*256+B)/20 break; - default: + } default: // ignore unhandled PIDs break; } diff --git a/firmware/controllers/engine_cycle/main_trigger_callback.cpp b/firmware/controllers/engine_cycle/main_trigger_callback.cpp index 252997d75a..023bdf3eef 100644 --- a/firmware/controllers/engine_cycle/main_trigger_callback.cpp +++ b/firmware/controllers/engine_cycle/main_trigger_callback.cpp @@ -206,8 +206,8 @@ void InjectionEvent::onTriggerTooth(size_t trgEventIndex, int rpm, efitick_t now // Perform wall wetting adjustment on fuel mass, not duration, so that // it's correct during fuel pressure (injector flow) or battery voltage (deadtime) transients - const float injectionMass = wallFuel.adjust(ENGINE(injectionMass) PASS_ENGINE_PARAMETER_SUFFIX); - const floatms_t injectionDuration = ENGINE(injectorModel)->getInjectionDuration(injectionMass); + const float injectionMassGrams = wallFuel.adjust(ENGINE(injectionMass) PASS_ENGINE_PARAMETER_SUFFIX); + const floatms_t injectionDuration = ENGINE(injectorModel)->getInjectionDuration(injectionMassGrams); #if EFI_PRINTF_FUEL_DETAILS if (printFuelDebug) { @@ -223,15 +223,13 @@ void InjectionEvent::onTriggerTooth(size_t trgEventIndex, int rpm, efitick_t now * todo: pre-calculate 'numberOfInjections' * see also injectorDutyCycle */ - if (!isCranking && injectionDuration * getNumberOfInjections(engineConfiguration->injectionMode PASS_ENGINE_PARAMETER_SUFFIX) > getEngineCycleDuration(rpm PASS_ENGINE_PARAMETER_SUFFIX)) { + int numberOfInjections = isCranking ? getNumberOfInjections(engineConfiguration->crankingInjectionMode PASS_ENGINE_PARAMETER_SUFFIX) : getNumberOfInjections(engineConfiguration->injectionMode PASS_ENGINE_PARAMETER_SUFFIX); + if (injectionDuration * numberOfInjections > getEngineCycleDuration(rpm PASS_ENGINE_PARAMETER_SUFFIX)) { warning(CUSTOM_TOO_LONG_FUEL_INJECTION, "Too long fuel injection %.2fms", injectionDuration); - } else if (isCranking && injectionDuration * getNumberOfInjections(engineConfiguration->crankingInjectionMode PASS_ENGINE_PARAMETER_SUFFIX) > getEngineCycleDuration(rpm PASS_ENGINE_PARAMETER_SUFFIX)) { - warning(CUSTOM_TOO_LONG_CRANKING_FUEL_INJECTION, "Too long cranking fuel injection %.2fms", injectionDuration); } - // Store 'pure' injection duration (w/o injector lag) for fuel rate calc. - engine->engineState.fuelConsumption.addData(injectionDuration - ENGINE(engineState.running.injectorLag)); - + ENGINE(engineState.fuelConsumption).consumeFuel(injectionMassGrams * numberOfInjections, nowNt); + ENGINE(actualLastInjection) = injectionDuration; if (cisnan(injectionDuration)) { warning(CUSTOM_OBD_NAN_INJECTION, "NaN injection pulse"); diff --git a/firmware/integration/rusefi_config.txt b/firmware/integration/rusefi_config.txt index b349a7c31f..71ad1dee05 100644 --- a/firmware/integration/rusefi_config.txt +++ b/firmware/integration/rusefi_config.txt @@ -194,6 +194,7 @@ struct_no_prefix engine_configuration_s #define PACK_MULT_AFR_CFG 10 #define PACK_MULT_LAMBDA_CFG 147 #define PACK_MULT_FUEL_MASS 100 +#define PACK_MULT_FUEL_FLOW 200 #define FSIO_TABLE_8 8 #define FSIO_CURVE_8 8 @@ -1716,6 +1717,8 @@ end_struct #define GAUGE_NAME_FUEL_WALL_AMOUNT "fuel: wall amount" #define GAUGE_NAME_FUEL_WALL_CORRECTION "fuel: wall corr ms" #define GAUGE_NAME_FUEL_LOAD "fuel: load" +#define GAUGE_NAME_FUEL_CONSUMPTION "fuel: Total consumed" +#define GAUGE_NAME_FUEL_FLOW "fuel: Flow rate" #define GAUGE_NAME_FUEL_INJ_DUTY "fuel: injector duty cycle" #define GAUGE_NAME_TCHARGE "fuel: SD tCharge" diff --git a/firmware/tunerstudio/rusefi.input b/firmware/tunerstudio/rusefi.input index 1de909b043..981fa427c4 100644 --- a/firmware/tunerstudio/rusefi.input +++ b/firmware/tunerstudio/rusefi.input @@ -283,8 +283,9 @@ enable2ndByteCanID = false etb1Error = scalar, S16, 96, "%",{1/@@PACK_MULT_PERCENT@@}, 0 ; Fuel system - fuelTankLevel = scalar, S16, 98, "%",{1/@@PACK_MULT_PERCENT@@}, 0 - fuelConsumptionPerHour=scalar,F32, 100, "kPa", 1, 0.0 + fuelTankLevel = scalar, U16, 98, "%",{1/@@PACK_MULT_PERCENT@@}, 0 + totalFuelConsumption=scalar,U16, 100, "grams", 1, 0 + fuelFlowRate = scalar, U16, 102, "gram/s",{1/@@PACK_MULT_FUEL_FLOW@@}, 0 ; Y axis values for selectable tables veTableYAxis = scalar, U16, 104, "%", 0.01, 0 @@ -1064,6 +1065,8 @@ gaugeCategory = Fueling baseFuelGauge = baseFuel, @@GAUGE_NAME_FUEL_BASE@@, "mg", 0, 100, 0, 0, 100, 100, 2, 0 fuelPidCorrectionGauge = fuelPidCorrection, @@GAUGE_NAME_FUEL_PID_CORR@@, "%", -10, 10, -8, -5, 5, 8, 3, 1 fuelingLoadGauge = fuelingLoad, @@GAUGE_NAME_FUEL_LOAD@@, "%", 0, 300, 0, 0, 300, 300, 1, 1 + totalFuelConsumptionGauge = totalFuelConsumption, @@GAUGE_NAME_FUEL_CONSUMPTION@@, "g", 0, 10000, 0, 0, 10000, 10000, 0, 0 + fuelFlowRateGauge = fuelFlowRate, @@GAUGE_NAME_FUEL_FLOW@@, "g/s", 0, 50, 0, 0, 50, 50, 2, 0 gaugeCategory = Throttle Body (incl. ETB) pedalPositionGauge = throttlePedalPosition, @@GAUGE_NAME_THROTTLE_PEDAL@@, "%", 0, 120, 0, 0, 100, 100, 1, 1 @@ -1258,6 +1261,8 @@ gaugeCategory = DynoView entry = coilDutyCycle, @@GAUGE_NAME_DWELL_DUTY@@, float,"%.3f" entry = currentTargetAfr,@@GAUGE_NAME_TARGET_AFR@@, float,"%.3f" entry = targetLambda, @@GAUGE_NAME_TARGET_LAMBDA@@, float,"%.4f" + entry = totalFuelConsumption, @@GAUGE_NAME_FUEL_CONSUMPTION@@, int, "%d" + entry = fuelFlowRate, @@GAUGE_NAME_FUEL_FLOW@@, float, "%.2f" entry = accelerationX, @@GAUGE_NAME_ACCEL_X@@, float,"%.2f", { LIS302DLCsPin != 0 } entry = accelerationY, @@GAUGE_NAME_ACCEL_Y@@, float,"%.2f", { LIS302DLCsPin != 0 }