/** * @file table_helper.h * * @date Jul 6, 2014 * @author Andrey Belomutskiy, (c) 2012-2020 */ #pragma once #include #include "interpolation.h" #include "efilib.h" #include "efi_ratio.h" #include "scaled_channel.h" // popular left edge of CLT-based correction curves #define CLT_CURVE_RANGE_FROM -40 class ValueProvider3D { public: virtual float getValue(float xColumn, float yRow) const = 0; }; /** * this helper class brings together 3D table with two 2D axis curves */ template class Map3D : public ValueProvider3D { public: template 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 final { if (!m_values) { // not initialized, return 0 return 0; } return interpolate3d(*m_values, *m_rowBins, yRow * m_rowMult, *m_columnBins, xColumn * m_colMult) * m_valueMult; } void setAll(TValue value) { efiAssertVoid(CUSTOM_ERR_6573, m_values, "map not initialized"); for (size_t r = 0; r < TRowNum; r++) { for (size_t c = 0; c < TColNum; c++) { (*m_values)[r][c] = value / m_valueMult; } } } private: template void initValues(scaled_channel (&table)[TRowNum][TColNum]) { m_values = reinterpret_cast(&table); m_valueMult = 1 / efi::ratio::asFloat(); } void initValues(TValue (&table)[TRowNum][TColNum]) { m_values = &table; m_valueMult = 1; } template void initRows(const scaled_channel (&rowBins)[TRowNum]) { m_rowBins = reinterpret_cast(&rowBins); m_rowMult = efi::ratio::asFloat(); } void initRows(const TRow (&rowBins)[TRowNum]) { m_rowBins = &rowBins; m_rowMult = 1; } template void initCols(const scaled_channel (&columnBins)[TColNum]) { m_columnBins = reinterpret_cast(&columnBins); m_colMult = efi::ratio::asFloat(); } void initCols(const TColumn (&columnBins)[TColNum]) { m_columnBins = &columnBins; 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 // indicies count right, then up return row * TColNum + column; } TValue getValueAtPosition(size_t row, size_t column) const { auto idx = getIndexForCoordinates(row, column); return m_values[idx]; } // TODO: should be const /*const*/ TValue (*m_values)[TRowNum][TColNum] = nullptr; const TRow (*m_rowBins)[TRowNum] = nullptr; const TColumn (*m_columnBins)[TColNum] = nullptr; float m_rowMult = 1; float m_colMult = 1; float m_valueMult = 1; }; typedef Map3D lambda_Map3D_t; typedef Map3D fuel_Map3D_t; typedef Map3D baroCorr_Map3D_t; typedef Map3D pedal2tps_t; typedef Map3D boostOpenLoop_Map3D_t; typedef Map3D boostClosedLoop_Map3D_t; typedef Map3D gppwm_Map3D_t; typedef Map3D mapEstimate_Map3D_t; /** * @param precision for example '0.1' for one digit fractional part. Default to 0.01, two digits. */ template void setLinearCurve(TValue (&array)[TSize], float from, float to, float precision = 0.01f) { for (int i = 0; i < TSize; i++) { float value = interpolateMsg("setLinearCurve", 0, from, TSize - 1, to, i); /** * rounded values look nicer, also we want to avoid precision mismatch with Tuner Studio */ array[i] = efiRound(value, precision); } } template void setArrayValues(TValue (&array)[TSize], float value) { for (int i = 0; i < TSize; i++) { array[i] = value; } } template constexpr void setTable(TElement (&dest)[N][M], const VElement value) { for (size_t n = 0; n < N; n++) { for (size_t m = 0; m < M; m++) { dest[n][m] = value; } } } template constexpr void copyTable(TDest (&dest)[N][M], const TSource (&source)[N][M], float multiply = 1.0f) { for (size_t n = 0; n < N; n++) { for (size_t m = 0; m < M; m++) { dest[n][m] = source[n][m] * multiply; } } } // specialization that can use memcpy when src and dest types match template constexpr void copyTable(scaled_channel (&dest)[N][M], const TDest (&source)[N][M]) { memcpy(dest, source, N * M * sizeof(TDest)); } template constexpr void copyTable(TDest (&dest)[N][M], const TDest (&source)[N][M]) { memcpy(dest, source, N * M * sizeof(TDest)); } template void setRpmBin(kType array[], int size, float idleRpm, float topRpm) { array[0] = idleRpm - 150; int rpmStep = (int)(efiRound((topRpm - idleRpm) / (size - 2), 50) - 150); for (int i = 1; i < size - 1;i++) array[i] = idleRpm + rpmStep * (i - 1); array[size - 1] = topRpm; } /** * initialize RPM table axis using default RPM range */ template void setRpmTableBin(kType array[], int size) { setRpmBin(array, size, 800, 7000); }