support autoscale on table axes (#3452)

* scale map Y axis

* allow different row/col types

* scaled channel detector

* interpolation

* looks like this actually works

* tests, no manual scaling

* comment
This commit is contained in:
Matthew Kennedy 2021-11-03 16:53:26 -07:00 committed by GitHub
parent e1b085cf32
commit 24224729a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 118 additions and 62 deletions

View File

@ -14,7 +14,7 @@
#include "wall_fuel_state_generated.h"
#include "tps_accel_state_generated.h"
typedef Map3D<TPS_TPS_ACCEL_TABLE, TPS_TPS_ACCEL_TABLE, float, float> tps_tps_Map3D_t;
typedef Map3D<TPS_TPS_ACCEL_TABLE, TPS_TPS_ACCEL_TABLE, float, float, float> tps_tps_Map3D_t;
/**
* this object is used for MAP rate-of-change and TPS rate-of-change corrections

View File

@ -41,7 +41,7 @@ float SpeedDensityAirmass::getMap(int rpm) const {
float fallbackMap;
if (CONFIG(enableMapEstimationTableFallback)) {
// if the map estimation table is enabled, estimate map based on the TPS and RPM
fallbackMap = m_mapEstimationTable->getValue(rpm, TPS_2_BYTE_PACKING_MULT * Sensor::getOrZero(SensorType::Tps1));
fallbackMap = m_mapEstimationTable->getValue(rpm, Sensor::getOrZero(SensorType::Tps1));
} else {
fallbackMap = CONFIG(failedMapFallback);
}

View File

@ -17,8 +17,8 @@
// see useFSIO5ForCriticalIssueEngineStop
#define MAGIC_OFFSET_FOR_CRITICAL_ENGINE 5
typedef Map3D<FSIO_TABLE_8, FSIO_TABLE_8, float, float> fsio8_Map3D_f32t;
typedef Map3D<FSIO_TABLE_8, FSIO_TABLE_8, uint8_t, float> fsio8_Map3D_u8t;
typedef Map3D<FSIO_TABLE_8, FSIO_TABLE_8, float, float, float> fsio8_Map3D_f32t;
typedef Map3D<FSIO_TABLE_8, FSIO_TABLE_8, uint8_t, float, float> fsio8_Map3D_u8t;
expected<float> getEngineValue(le_action_e action DECLARE_ENGINE_PARAMETER_SUFFIX);

View File

@ -10,7 +10,7 @@
#include "interpolation.h"
#include "efi_ratio.h"
template <class TBin, class TValue, int TSize, typename TInputScale = efi::ratio<1>, typename TOutputScale = efi::ratio<1>>
template <class TBin, class TValue, int TSize, typename TOutputScale = efi::ratio<1>>
class TableFunc final : public SensorConverter {
public:
TableFunc(TBin (&bins)[TSize], TValue (&values)[TSize])
@ -20,9 +20,6 @@ public:
}
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();
}

View File

@ -14,8 +14,6 @@ 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));

View File

@ -13,9 +13,7 @@ using BinType = std::remove_extent_t<decltype(config->mafDecodingBins)>;
using ValueType = std::remove_extent_t<decltype(config->mafDecoding)>;
// This function converts volts -> kg/h
static TableFunc
<BinType, ValueType, MAF_DECODING_COUNT>
mafCurve(config->mafDecodingBins, config->mafDecoding);
static TableFunc mafCurve(config->mafDecodingBins, config->mafDecoding);
void initMaf(DECLARE_CONFIG_PARAMETER_SIGNATURE) {
adc_channel_e channel = CONFIG(mafAdcChannel);

View File

@ -977,7 +977,7 @@ 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;
uint16_t[FUEL_LEVEL_TABLE_COUNT] fuelLevelBins;;"volt", {1/@@PACK_MULT_VOLTAGE@@}, 0, 0, 5, 3
uint16_t[FUEL_LEVEL_TABLE_COUNT] autoscale<PACK_MULT_VOLTAGE> fuelLevelBins;;"volt", {1/@@PACK_MULT_VOLTAGE@@}, 0, 0, 5, 3
output_pin_e[LUA_PWM_COUNT iterate] luaOutputPins
@ -1558,7 +1558,7 @@ tcubinary_table_t tcuSolenoidTable;
float vssFilterReciprocal;+Good example: number of tooth on wheel, For Can 10 is a good number.;"Hz", 1, 0, 2, 20, 2
map_estimate_table_t mapEstimateTable;
uint16_t[FUEL_LOAD_COUNT] mapEstimateTpsBins;;"% TPS", {1/@@TPS_2_BYTE_PACKING_MULT@@}, 0, 0, 100, 1
uint16_t[FUEL_LOAD_COUNT] autoscale<TPS_2_BYTE_PACKING_MULT> mapEstimateTpsBins;;"% TPS", {1/@@TPS_2_BYTE_PACKING_MULT@@}, 0, 0, 100, 1
uint16_t[FUEL_RPM_COUNT] mapEstimateRpmBins;;"RPM", 1, 0, 0, 18000, 0
fsio_table_8x8_u8t vvtTable1;

View File

@ -25,25 +25,16 @@ public:
/**
* this helper class brings together 3D table with two 2D axis curves
*/
template<int TColNum, int TRowNum, typename vType, typename kType, typename TValueMultiplier = efi::ratio<1>>
template<int TColNum, int TRowNum, typename TValue, typename TColumn, typename TRow, typename TValueMultiplier = efi::ratio<1>>
class Map3D : public ValueProvider3D {
public:
template<int mult>
void init(scaled_channel<vType, mult> table[TRowNum][TColNum], const kType rowBins[TRowNum], const kType columnBins[TColNum]) {
static_assert(TValueMultiplier::den == mult);
static_assert(TValueMultiplier::num == 1);
m_values = reinterpret_cast<vType*>(&table[0][0]);
m_rowBins = rowBins;
m_columnBins = columnBins;
}
void init(vType table[TRowNum][TColNum], const kType rowBins[TRowNum], const kType columnBins[TColNum]) {
m_values = &table[0][0];
m_rowBins = rowBins;
m_columnBins = columnBins;
template <typename TValueInit, typename TRowInit, typename TColumnInit>
void init(TValueInit table[TRowNum][TColNum], const TRowInit rowBins[TRowNum], const TColumnInit columnBins[TColNum]) {
// This splits out here so that we don't need one overload of init per possible combination of table/rows/columns types/dimensions
// Overload resolution figures out the correct versions of the functions below to call, some of which have assertions about what's allowed
initValues(table);
initRows(rowBins);
initCols(columnBins);
}
float getValue(float xColumn, float yRow) const override {
@ -52,8 +43,8 @@ public:
return 0;
}
auto row = priv::getBinPtr<kType, TRowNum>(yRow, m_rowBins);
auto col = priv::getBinPtr<kType, TColNum>(xColumn, m_columnBins);
auto row = priv::getBinPtr<TRow, TRowNum>(yRow * m_rowMult, m_rowBins);
auto col = priv::getBinPtr<TColumn, TColNum>(xColumn * m_colMult, m_columnBins);
// Orient the table such that (0, 0) is the bottom left corner,
// then the following variable names will make sense
@ -73,7 +64,7 @@ public:
return tableValue * TValueMultiplier::asFloat();
}
void setAll(vType value) {
void setAll(TValue value) {
efiAssertVoid(CUSTOM_ERR_6573, m_values, "map not initialized");
for (size_t i = 0; i < TRowNum * TColNum; i++) {
@ -82,6 +73,40 @@ public:
}
private:
template <int TMult>
void initValues(scaled_channel<TValue, TMult> table[TRowNum][TColNum]) {
static_assert(TValueMultiplier::den == TMult);
static_assert(TValueMultiplier::num == 1);
m_values = reinterpret_cast<TValue*>(&table[0][0]);
}
void initValues(TValue table[TRowNum][TColNum]) {
m_values = &table[0][0];
}
template <int TRowMult>
void initRows(const scaled_channel<TRow, TRowMult> rowBins[TRowNum]) {
m_rowBins = reinterpret_cast<const TRow*>(&rowBins[0]);
m_rowMult = TRowMult;
}
void initRows(const TRow rowBins[TRowNum]) {
m_rowBins = &rowBins[0];
m_rowMult = 1;
}
template <int TColMult>
void initCols(const scaled_channel<TColumn, TColMult> columnBins[TColNum]) {
m_columnBins = reinterpret_cast<const TColumn*>(&columnBins[0]);
m_colMult = TColMult;
}
void initCols(const TColumn columnBins[TColNum]) {
m_columnBins = &columnBins[0];
m_colMult = 1;
}
static size_t getIndexForCoordinates(size_t row, size_t column) {
// Index 0 is bottom left corner
// Index TColNum - 1 is bottom right corner
@ -89,28 +114,31 @@ private:
return row * TColNum + column;
}
vType getValueAtPosition(size_t row, size_t column) const {
TValue getValueAtPosition(size_t row, size_t column) const {
auto idx = getIndexForCoordinates(row, column);
return m_values[idx];
}
// TODO: should be const
/*const*/ vType* m_values = nullptr;
/*const*/ TValue* m_values = nullptr;
const kType *m_rowBins = nullptr;
const kType *m_columnBins = nullptr;
const TRow *m_rowBins = nullptr;
const TColumn *m_columnBins = nullptr;
float m_rowMult = 1;
float m_colMult = 1;
};
typedef Map3D<FUEL_RPM_COUNT, FUEL_LOAD_COUNT, uint8_t, float, efi::ratio<1, PACK_MULT_LAMBDA_CFG>> lambda_Map3D_t;
typedef Map3D<IGN_RPM_COUNT, IGN_LOAD_COUNT, float, float> ign_Map3D_t;
typedef Map3D<FUEL_RPM_COUNT, FUEL_LOAD_COUNT, float, float> fuel_Map3D_t;
typedef Map3D<BARO_CORR_SIZE, BARO_CORR_SIZE, float, float> baroCorr_Map3D_t;
typedef Map3D<PEDAL_TO_TPS_SIZE, PEDAL_TO_TPS_SIZE, uint8_t, uint8_t> pedal2tps_t;
typedef Map3D<BOOST_RPM_COUNT, BOOST_LOAD_COUNT, uint8_t, uint8_t, efi::ratio<LOAD_1_BYTE_PACKING_MULT>> boostOpenLoop_Map3D_t;
typedef Map3D<BOOST_RPM_COUNT, BOOST_LOAD_COUNT, uint8_t, uint8_t> boostClosedLoop_Map3D_t;
typedef Map3D<IAC_PID_MULT_SIZE, IAC_PID_MULT_SIZE, uint8_t, uint8_t> iacPidMultiplier_t;
typedef Map3D<GPPWM_RPM_COUNT, GPPWM_LOAD_COUNT, uint8_t, uint8_t> gppwm_Map3D_t;
typedef Map3D<FUEL_RPM_COUNT, FUEL_LOAD_COUNT, uint16_t, uint16_t, efi::ratio<1, PACK_MULT_MAP_ESTIMATE>> mapEstimate_Map3D_t;
typedef Map3D<FUEL_RPM_COUNT, FUEL_LOAD_COUNT, uint8_t, float, float, efi::ratio<1, PACK_MULT_LAMBDA_CFG>> lambda_Map3D_t;
typedef Map3D<IGN_RPM_COUNT, IGN_LOAD_COUNT, float, float, float> ign_Map3D_t;
typedef Map3D<FUEL_RPM_COUNT, FUEL_LOAD_COUNT, float, float, float> fuel_Map3D_t;
typedef Map3D<BARO_CORR_SIZE, BARO_CORR_SIZE, float, float, float> baroCorr_Map3D_t;
typedef Map3D<PEDAL_TO_TPS_SIZE, PEDAL_TO_TPS_SIZE, uint8_t, uint8_t, uint8_t> pedal2tps_t;
typedef Map3D<BOOST_RPM_COUNT, BOOST_LOAD_COUNT, uint8_t, uint8_t, uint8_t, efi::ratio<LOAD_1_BYTE_PACKING_MULT>> boostOpenLoop_Map3D_t;
typedef Map3D<BOOST_RPM_COUNT, BOOST_LOAD_COUNT, uint8_t, uint8_t, uint8_t> boostClosedLoop_Map3D_t;
typedef Map3D<IAC_PID_MULT_SIZE, IAC_PID_MULT_SIZE, uint8_t, uint8_t, uint8_t> iacPidMultiplier_t;
typedef Map3D<GPPWM_RPM_COUNT, GPPWM_LOAD_COUNT, uint8_t, uint8_t, uint8_t> gppwm_Map3D_t;
typedef Map3D<FUEL_RPM_COUNT, FUEL_LOAD_COUNT, uint16_t, uint16_t, uint16_t, efi::ratio<1, PACK_MULT_MAP_ESTIMATE>> mapEstimate_Map3D_t;
void setRpmBin(float array[], int size, float idleRpm, float topRpm);

View File

@ -94,11 +94,23 @@ BinResult getBinPtr(float value, const TBin* bins) {
return { idx, fraction };
}
template<class TBin, int TSize, int TMult>
BinResult getBinPtr(float value, const scaled_channel<TBin, TMult>* bins) {
// Strip off the scaled_channel, and perform the scaling before searching the array
auto binPtrRaw = reinterpret_cast<const TBin*>(bins);
return getBinPtr<TBin, TSize>(value * TMult, binPtrRaw);
}
template<class TBin, int TSize>
BinResult getBin(float value, const TBin (&bins)[TSize]) {
return getBinPtr<TBin, TSize>(value, &bins[0]);
}
template<class TBin, int TSize, int TMult>
BinResult getBin(float value, const scaled_channel<TBin, TMult> (&bins)[TSize]) {
return getBinPtr<TBin, TSize, TMult>(value, &bins[0]);
}
static float linterp(float low, float high, float frac)
{
return high * frac + low * (1 - frac);
@ -108,7 +120,7 @@ static float linterp(float low, float high, float frac)
template <class TBin, class TValue, int TSize>
float interpolate2d(const float value, const TBin (&bin)[TSize], const TValue (&values)[TSize]) {
// Enforce numeric only (int, float, uintx_t, etc)
static_assert(std::is_arithmetic_v<TBin>, "Table values must be an arithmetic type");
static_assert(std::is_arithmetic_v<TBin> || is_scaled_channel<TBin>, "Table values must be an arithmetic type or scaled channel");
auto b = priv::getBin(value, bin);

View File

@ -16,13 +16,18 @@
#include "rusefi_generated.h"
struct scaled_channel_base { };
template <typename TTest>
static constexpr bool is_scaled_channel = std::is_base_of_v<scaled_channel_base, TTest>;
// This class lets us transparently store something at a ratio inside an integer type
// Just use it like a float - you can read and write to it, like this:
// scaled_channel<uint8_t, 10> myVar;
// myVar = 2.4f; // converts to an int, stores 24
// float x = myVar; // converts back to float, returns 2.4f
template <typename T, int mult = 1>
class scaled_channel {
class scaled_channel : scaled_channel_base {
using TSelf = scaled_channel<T, mult>;
public:
@ -78,3 +83,7 @@ using scaled_voltage = scaled_channel<uint16_t, PACK_MULT_VOLTAGE>; // 0-65v at
using scaled_afr = scaled_channel<uint16_t, PACK_MULT_AFR>; // 0-65afr at 0.001 resolution
using scaled_lambda = scaled_channel<uint16_t, PACK_MULT_LAMBDA>; // 0-6.5 lambda at 0.0001 resolution
using scaled_fuel_mass_mg = scaled_channel<uint16_t, PACK_MULT_FUEL_MASS>; // 0 - 655.35 milligrams, 0.01mg resolution
// make sure the scaled channel detector works
static_assert(!is_scaled_channel<int>);
static_assert(is_scaled_channel<scaled_channel<int, 5>>);

View File

@ -14,6 +14,8 @@
float rpmBins[5] = { 100, 200, 300, 400, 500 };
float mafBins[4] = { 1, 2, 3, 4 };
scaled_channel<int, 10> mafBins2[4] = { 1, 2, 3, 4 };
float map[4][5] = {
{ 1, 2, 3, 4, 4},
{ 2, 3, 4, 200, 200 },
@ -21,15 +23,27 @@ float map[4][5] = {
{ 4, 5, 300, 600, 600 },
};
static float getValue(float rpm, float maf) {
Map3D<5, 4, float, float> x;
x.init(map, mafBins, rpmBins);
return x.getValue(rpm, maf);
}
#define EXPECT_NEAR_M4(a, b) EXPECT_NEAR(a, b, 1e-4)
static float getValue(float rpm, float maf) {
float result1, result2;
Map3D<5, 4, float, float, float> x1;
x1.init(map, mafBins, rpmBins);
result1 = x1.getValue(rpm, maf);
Map3D<5, 4, float, float, int> x2;
x2.init(map, mafBins2, rpmBins);
result2 = x2.getValue(rpm, maf);
EXPECT_NEAR_M4(result1, result2);
return result1;
}
static void newTestToComfirmInterpolation() {
// here's how the table loos like:
//

View File

@ -16,12 +16,12 @@ TEST(TableFuncTest, basic) {
}
TEST(TableFuncTest, scaled) {
uint16_t in[] = { 0, 1000, 2000 };
scaled_channel<uint16_t, 1000> in[] = { 0, 1, 2 };
uint8_t out[] = { 70, 60, 50 };
TableFunc<uint16_t, uint8_t, 3,
// Input units are 1/1000
efi::ratio<1, 1000>,
using BinType = std::remove_extent_t<decltype(in)>;
TableFunc<BinType, uint8_t, 3,
// output units are 1/100
efi::ratio<1, 100>>
dut(in, out);