Implement nonlinear fuel level sender (#2473)

* table function

* config fields

* sensor type

* switch consumers

* init the sensor

* ui

* 1mv resolution

Co-authored-by: Matthew Kennedy <makenne@microsoft.com>
This commit is contained in:
Matthew Kennedy 2021-03-19 05:39:08 -07:00 committed by GitHub
parent 5a2cc14a46
commit 95b08c433f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 141 additions and 27 deletions

View File

@ -574,7 +574,7 @@ void updateTunerStudioState(TunerStudioOutputChannels *tsOutputChannels DECLARE_
tsOutputChannels->injectorDutyCycle = getInjectorDutyCycle(rpm PASS_ENGINE_PARAMETER_SUFFIX);
#endif
// 148
tsOutputChannels->fuelTankLevel = engine->sensors.fuelTankLevel;
tsOutputChannels->fuelTankLevel = Sensor::get(SensorType::FuelLevel).value_or(0);
// 160
const auto& wallFuel = ENGINE(injectionEvents.elements[0].wallFuel);
tsOutputChannels->wallFuelAmount = wallFuel.getWallFuel();

View File

@ -280,14 +280,6 @@ void Engine::updateSlowSensors(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
engineState.updateSlowSensors(PASS_ENGINE_PARAMETER_SIGNATURE);
// todo: move this logic somewhere to sensors folder?
if (isAdcChannelValid(CONFIG(fuelLevelSensor))) {
float fuelLevelVoltage = getVoltageDivided("fuel", engineConfiguration->fuelLevelSensor PASS_ENGINE_PARAMETER_SUFFIX);
sensors.fuelTankLevel = interpolateMsg("fgauge", CONFIG(fuelLevelEmptyTankVoltage), 0,
CONFIG(fuelLevelFullTankVoltage), 100,
fuelLevelVoltage);
}
#if (BOARD_TLE8888_COUNT > 0)
// nasty value injection into C driver which would not be able to access Engine class
vBattForTle8888 = Sensor::get(SensorType::BatteryVoltage).value_or(VBAT_FALLBACK_VALUE);

View File

@ -1015,9 +1015,6 @@ static void setDefaultEngineConfiguration(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
engineConfiguration->knockDetectionWindowStart = 35;
engineConfiguration->knockDetectionWindowEnd = 135;
engineConfiguration->fuelLevelEmptyTankVoltage = 0;
engineConfiguration->fuelLevelFullTankVoltage = 5;
/**
* this is RPM. 10000 rpm is only 166Hz, 800 rpm is 13Hz
*/

View File

@ -35,11 +35,6 @@ public:
SensorsState();
Accelerometer accelerometer;
/**
* that's fuel in tank - just a gauge
*/
percent_t fuelTankLevel = 0;
};
class FuelConsumptionState {

View File

@ -107,7 +107,7 @@ static void populateFrame(Sensors1& msg) {
msg.aux2 = 0 + PACK_ADD_TEMPERATURE;
msg.mcuTemp = getMCUInternalTemperature();
msg.fuelLevel = engine->sensors.fuelTankLevel;
msg.fuelLevel = Sensor::get(SensorType::FuelLevel).value_or(0);
}
struct Sensors2 {

View File

@ -0,0 +1,34 @@
/**
* @author Matthew Kennedy, (c) 2021
*
* A function to convert input voltage output value based on a 2d table.
*/
#pragma once
#include "sensor_converter_func.h"
#include "interpolation.h"
#include "efi_ratio.h"
template <class TBin, class TValue, int TSize, typename TInputScale = efi::ratio<1>, typename TOutputScale = efi::ratio<1>>
class TableFunc final : public SensorConverter {
public:
TableFunc(TBin (&bins)[TSize], TValue (&values)[TSize])
: m_bins(bins)
, m_values(values)
{
}
SensorResult convert(float inputValue) const override {
// Multiply by the reciprocal instead of dividing
inputValue = inputValue * TInputScale::recip::asFloat();
return interpolate2d(inputValue, m_bins, m_values) * TOutputScale::asFloat();
}
void showInfo(Logging* /*logger*/, float /*testInputValue*/) const override { }
private:
TBin (&m_bins)[TSize];
TValue (&m_values)[TSize];
};

View File

@ -45,6 +45,8 @@ static const char* s_sensorNames[] = {
"Battery Voltage",
"Barometric Pressure",
"Fuel Level %",
};
// This struct represents one sensor in the registry.

View File

@ -66,6 +66,8 @@ enum class SensorType : unsigned char {
BarometricPressure,
FuelLevel,
// Leave me at the end!
PlaceholderLast
};

View File

@ -4,7 +4,7 @@
#pragma once
#include "globalaccess.h"
#include "engine_ptr.h"
class Logging;
@ -27,6 +27,7 @@ void initThermistors(DECLARE_CONFIG_PARAMETER_SIGNATURE);
void initCanSensors();
void initLambda(DECLARE_ENGINE_PARAMETER_SIGNATURE);
void initFlexSensor(DECLARE_CONFIG_PARAMETER_SIGNATURE);
void initFuelLevel(DECLARE_CONFIG_PARAMETER_SIGNATURE);
void initBaro();
// Sensor reconfiguration

View File

@ -9,3 +9,4 @@ INIT_SRC_CPP = $(PROJECT_DIR)/init/sensor/init_sensors.cpp \
$(PROJECT_DIR)/init/sensor/init_flex.cpp \
$(PROJECT_DIR)/init/sensor/init_vbatt.cpp \
$(PROJECT_DIR)/init/sensor/init_baro.cpp \
$(PROJECT_DIR)/init/sensor/init_fuel_level.cpp \

View File

@ -0,0 +1,36 @@
#include "init.h"
#include "adc_inputs.h"
#include "adc_subscription.h"
#include "engine_configuration.h"
#include "functional_sensor.h"
#include "table_func.h"
EXTERN_CONFIG;
static FunctionalSensor fuelSensor(SensorType::FuelLevel, /* timeout = */ MS2NT(500));
#if !EFI_UNIT_TEST
// extract the type of the elements in the bin/value arrays
using BinType = std::remove_extent_t<decltype(CONFIG(fuelLevelBins))>;
using ValueType = std::remove_extent_t<decltype(CONFIG(fuelLevelValues))>;
static TableFunc
<BinType, ValueType, FUEL_LEVEL_TABLE_COUNT,
// Bins are stored in millivolts
efi::ratio<1, PACK_MULT_VOLTAGE>,
// Values are stored in percent
efi::ratio<1>>
fuelCurve(CONFIG(fuelLevelBins), CONFIG(fuelLevelValues));
void initFuelLevel(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
adc_channel_e channel = CONFIG(fuelLevelSensor);
if (!isAdcChannelValid(channel)) {
return;
}
fuelSensor.setFunction(fuelCurve);
AdcSubscription::SubscribeSensor(fuelSensor, channel, /*lowpassCutoff =*/ 1);
fuelSensor.Register();
}
#endif // ! EFI_UNIT_TEST

View File

@ -22,6 +22,10 @@ void initNewSensors(Logging* logger DECLARE_ENGINE_PARAMETER_SUFFIX) {
initFlexSensor(PASS_CONFIG_PARAMETER_SIGNATURE);
initBaro();
#if !EFI_UNIT_TEST
initFuelLevel(PASS_CONFIG_PARAMETER_SIGNATURE);
#endif
// Init CLI functionality for sensors (mocking)
initSensorCli(logger);
}

View File

@ -159,6 +159,7 @@ struct_no_prefix engine_configuration_s
#define BOOST_RPM_COUNT 8
#define BOOST_LOAD_COUNT 8
#define PEDAL_TO_TPS_SIZE 8
#define FUEL_LEVEL_TABLE_COUNT 8
#define STFT_CELL_COUNT 4
#define STFT_BANK_COUNT 2
@ -772,8 +773,7 @@ custom adc_channel_mode_e 4 bits, U32, @OFFSET@, [0:1], "Off", "Slow", "Fas
adc_channel_e tps2_2AdcChannel;Second throttle body, second sensor.
adc_channel_e throttlePedalPositionSecondAdcChannel;Electronic throttle pedal position input\nSecond channel\nSee also tps1_1AdcChannel\nSee throttlePedalSecondaryUpVoltage and throttlePedalSecondaryWOTVoltage
float fuelLevelEmptyTankVoltage;;"V", 1, 0, 0,10, 2
float fuelLevelFullTankVoltage;;"V", 1, 0, 0,10, 2
uint8_t[FUEL_LEVEL_TABLE_COUNT] fuelLevelValues;;"%", 1, 0, 0, 100, 0
ego_sensor_e afr_type;AFR, WBO, EGO - whatever you like to call it;
float idle_antiwindupFreq;+0.1 is a good default value; "x", 1, 0.0, -1000000, 1000000, 4
@ -1030,7 +1030,9 @@ custom maf_sensor_type_e 4 bits, S32, @OFFSET@, [0:1], @@maf_sensor_type_e_enum@
pin_output_mode_e drv8860_csPinMode;
brain_pin_e drv8860_miso;
int[63] unusedAtOldBoardConfigurationEnd;;"units", 1, 0, -20, 100, 0
uint16_t[FUEL_LEVEL_TABLE_COUNT] fuelLevelBins;;"volt", {1/@@PACK_MULT_VOLTAGE@@}, 0, 0, 5, 3
int[59] unusedAtOldBoardConfigurationEnd;;"units", 1, 0, -20, 100, 0
uint16_t vehicleWeight;;"kg", 1, 0, 0, 10000, 0
brain_pin_e lps25BaroSensorScl
brain_pin_e lps25BaroSensorSda

View File

@ -283,7 +283,7 @@ enable2ndByteCanID = false
etb1Error = scalar, S16, 96, "%",{1/@@PACK_MULT_PERCENT@@}, 0
; Fuel system
fuelTankLevel = scalar, S16, 98, "amount",{1/@@PACK_MULT_PERCENT@@}, 0
fuelTankLevel = scalar, S16, 98, "%",{1/@@PACK_MULT_PERCENT@@}, 0
fuelConsumptionPerHour=scalar,F32, 100, "kPa", 1, 0.0
; Y axis values for selectable tables
@ -763,6 +763,14 @@ enable2ndByteCanID = false
yBins = crankingAdvance
gauge = RPMGauge
curve = fuelLevelCurve, "Fuel Level"
columnLabel = "Voltage", "%"
xAxis = 0, 5, 6
yAxis = 0, 100, 11
xBins = fuelLevelBins
yBins = fuelLevelValues
gauge = fuelTankLevelGauge
curve = wueAfrTargetOffsetCurve, "AFR Target Temperature Adjustment"
columnLabel = "Coolant", "AFR Offset"
xAxis = -40, 200, 9
@ -960,7 +968,7 @@ gaugeCategory = Sensors - Extra 2
egt8Gauge = egt8, "EGT#8", "C", 0, 2000
rpmAccelerationGa = rpmAcceleration, "rpm delta", "RPM/s", -2000, 2000, -2000, 2000, -2000, 2000, 0, 0
knockLevelGauge = knockLevel,"Knock level", "volts", 0, 7, 10, 10, 100, 100, 1, 2
fuelTankLevelGauge = fuelTankLevel,"Fuel level", "x", 0, 7, 10, 10, 100, 100, 1, 2
fuelTankLevelGauge = fuelTankLevel,"Fuel level", "%", 0, 100, 10, 20, 100, 100, 1, 1
speedToRpmRatioGauge = speedToRpmRatio, "speed2rpm", "", 0, 100, 0, 0, 100, 100, 4, 4
wastegatePosGauge = wastegatePositionSensor, @@GAUGE_NAME_WG_POSITION@@, "%", 0, 100, 0, 0, 100, 100, 1, 1
idlePosSensGauge = idlePositionSensor, "Idle position sensor", "%", 0, 100, 0, 0, 100, 100, 1, 1
@ -1516,6 +1524,7 @@ menuDialog = main
subMenu = speedSensor, "Vehicle speed sensor"
subMenu = oilPressureSensor, "Oil pressure"
subMenu = fuelPressureSensor, "Fuel pressure"
subMenu = fuelLevelDialog, "Fuel level sensor"
subMenu = egtInputs, "EGT" @@if_ts_show_egt
subMenu = wastegateIdlePos, "Wastegate and idle position sensors"
@ -2100,7 +2109,6 @@ cmd_set_engine_type_default = "@@TS_IO_TEST_COMMAND_char@@\x00\x31\x00\x00"
; Sensor Inputs
dialog = otherSensorInputs, "Other Sensor Inputs"
field = "Fuel level", fuelLevelSensor
field = "Clutch down switch", clutchDownPin
field = "Clutch down inverted", clutchDownPinMode
field = "Clutch up switch", clutchUpPin
@ -2111,6 +2119,10 @@ cmd_set_engine_type_default = "@@TS_IO_TEST_COMMAND_char@@\x00\x31\x00\x00"
field = "A/C switch mode", acSwitchMode
field = "Flex fuel sensor", flexSensorPin
dialog = fuelLevelDialog, "Fuel Level Sensor"
field = "Input channel", fuelLevelSensor
panel = fuelLevelCurve
dialog = triggerInputComparator, "Built-in Comparator Settings (Kinetis-only)"
field = "Comparator Center Point Voltage", triggerCompCenterVolt
field = "Comparator hysteresis voltage (Min)", triggerCompHystMin

View File

@ -8,8 +8,9 @@
#pragma once
#include <stdarg.h>
#include <stdint.h>
#include <cstdarg>
#include <cstdint>
#include <cstddef>
#define DELIMETER ","

View File

@ -9,6 +9,9 @@ private:
static constexpr int den = TDenom;
public:
// A ratio type representing the reciprocal of this type.
using recip = ratio<den, num>;
static float asFloat() {
return (float)num / den;
}

View File

@ -0,0 +1,31 @@
#include "table_func.h"
#include <gtest/gtest.h>
TEST(TableFuncTest, basic) {
float in[] = { 0, 10 };
float out[] = { 30, 40 };
TableFunc dut(in, out);
EXPECT_EQ(30, dut.convert(-10).value_or(0));
EXPECT_EQ(30, dut.convert(0).value_or(0));
EXPECT_EQ(35, dut.convert(5).value_or(0));
EXPECT_EQ(40, dut.convert(10).value_or(0));
EXPECT_EQ(40, dut.convert(20).value_or(0));
}
TEST(TableFuncTest, scaled) {
uint16_t in[] = { 0, 1000, 2000 };
uint8_t out[] = { 70, 60, 50 };
TableFunc<uint16_t, uint8_t, 3,
// Input units are 1/1000
efi::ratio<1, 1000>,
// output units are 1/100
efi::ratio<1, 100>>
dut(in, out);
EXPECT_EQ(0.65f, dut.convert(0.5f).value_or(0));
EXPECT_EQ(0.55f, dut.convert(1.5f).value_or(0));
}

View File

@ -60,6 +60,7 @@ TESTS_SRC_CPP = \
tests/sensor/func_chain.cpp \
tests/sensor/redundant.cpp \
tests/sensor/test_sensor_init.cpp \
tests/sensor/table_func.cpp \
tests/util/test_closed_loop_controller.cpp \
tests/test_stft.cpp \
tests/test_boost.cpp \