rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
table_helper.h
Go to the documentation of this file.
1/**
2 * @file table_helper.h
3 *
4 * @date Jul 6, 2014
5 * @author Andrey Belomutskiy, (c) 2012-2020
6 */
7
8#pragma once
9
10#include <cmath>
11#include <array>
12#include "efi_interpolation.h"
13#include "efilib.h"
14#include "efi_ratio.h"
15#include "efi_scaled_channel.h"
16#include <rusefi/interpolation.h>
17
18#if EFI_UNIT_TEST
19#include <stdexcept>
20#endif
21
22// popular left edge of CLT-based correction curves
23#define CLT_CURVE_RANGE_FROM -40
24
26public:
27 virtual float getValue(float xColumn, float yRow) const = 0;
28};
29
30
31/**
32 * this helper class brings together 3D table with two 2D axis curves
33 * TODO: explicitly spell out why do we have this template and when exactly is it useful (useful with scaled content?)
34 * todo: improve interpolate3d to handle scaling?
35 * A recommended use is if a class depends on a table and this can be shared with several classes but only one is instantiated, see for example airmass.h
36 * It can also be used if you need to test the behavior of a calculated X/Y => table value, but it is advisable to do those expects externally and/or only test the result of the get,
37 * since interpotalate3d is easier to use and does not require initiation.
38 * *** WARNING *** https://en.wikipedia.org/wiki/KISS_principle
39 * *** WARNING *** this helper requires initialization, make sure that helper is useful any time you consider using it
40 * *** WARNING *** we had too many bugs where we were not initializing, often just using the underlying interpolate3d is the way to go
41 */
42template<int TColNum, int TRowNum, typename TValue, typename TXColumn, typename TRow>
43class Map3D : public ValueProvider3D {
44public:
45 Map3D(const char *name) {
46 m_name = name;
47 }
48 template <typename TValueInit, typename TXColumnInit, typename TRowInit>
49 void initTable(TValueInit (&table)[TRowNum][TColNum],
50 const TXColumnInit (&columnBins)[TColNum], const TRowInit (&rowBins)[TRowNum]) {
51 // This splits out here so that we don't need one overload of init per possible combination of table/rows/columns types/dimensions
52 // Overload resolution figures out the correct versions of the functions below to call, some of which have assertions about what's allowed
53 initValues(table);
54 initRows(rowBins);
55 initCols(columnBins);
56 }
57
58 // RPM is usually X/Column
59 float getValue(float xColumn, float yRow) const final {
60 if (!m_values) {
61 criticalError("Access to uninitialized table: %s", m_name);
62 return 0;
63 }
64
65 return interpolate3d(*m_values,
66 *m_rowBins, yRow * m_rowMult,
67 *m_columnBins, xColumn * m_colMult) *
69 }
70
71 void setAll(TValue value) {
72 efiAssertVoid(ObdCode::CUSTOM_ERR_6573, m_values, "map not initialized");
73
74 for (size_t r = 0; r < TRowNum; r++) {
75 for (size_t c = 0; c < TColNum; c++) {
76 (*m_values)[r][c] = value / m_valueMult;
77 }
78 }
79 }
80
81private:
82 template <int TMult, int TDiv>
83 void initValues(scaled_channel<TValue, TMult, TDiv> (&table)[TRowNum][TColNum]) {
84 m_values = reinterpret_cast<TValue (*)[TRowNum][TColNum]>(&table);
86 }
87
88 void initValues(TValue (&table)[TRowNum][TColNum]) {
89 m_values = &table;
90 m_valueMult = 1;
91 }
92
93 template <int TRowMult, int TRowDiv>
94 void initRows(const scaled_channel<TRow, TRowMult, TRowDiv> (&rowBins)[TRowNum]) {
95 m_rowBins = reinterpret_cast<const TRow (*)[TRowNum]>(&rowBins);
97 }
98
99 void initRows(const TRow (&rowBins)[TRowNum]) {
100 m_rowBins = &rowBins;
101 m_rowMult = 1;
102 }
103
104 template <int TColMult, int TColDiv>
105 void initCols(const scaled_channel<TXColumn, TColMult, TColDiv> (&columnBins)[TColNum]) {
106 m_columnBins = reinterpret_cast<const TXColumn (*)[TColNum]>(&columnBins);
108 }
109
110 void initCols(const TXColumn (&columnBins)[TColNum]) {
111 m_columnBins = &columnBins;
112 m_colMult = 1;
113 }
114
115 static size_t getIndexForCoordinates(size_t row, size_t column) {
116 // Index 0 is bottom left corner
117 // Index TColNum - 1 is bottom right corner
118 // indicies count right, then up
119 return row * TColNum + column;
120 }
121
122 TValue getValueAtPosition(size_t row, size_t column) const {
123 auto idx = getIndexForCoordinates(row, column);
124 return m_values[idx];
125 }
126
127 // TODO: should be const
128 /*const*/ TValue (*m_values)[TRowNum][TColNum] = nullptr;
129
130 const TRow (*m_rowBins)[TRowNum] = nullptr;
131 const TXColumn (*m_columnBins)[TColNum] = nullptr;
132
133 float m_rowMult = 1;
134 float m_colMult = 1;
135 float m_valueMult = 1;
136 const char *m_name;
137};
138
142
143/**
144 * @param precision for example '0.1' for one digit fractional part. Default to 0.01, two digits.
145 * see also: ensureArrayIsAscending
146 */
147template<typename TValue, int TSize>
148void setLinearCurve(TValue (&array)[TSize], float from, float to, float precision = 0.01f) {
149 for (int i = 0; i < TSize; i++) {
150 float value = interpolateMsg("setLinearCurve", 0, from, TSize - 1, to, i);
151
152 /**
153 * rounded values look nicer, also we want to avoid precision mismatch with Tuner Studio
154 */
155 array[i] = efiRound(value, precision);
156 }
157}
158
159template<typename TValue, int TSize>
160void setArrayValues(TValue (&array)[TSize], float value) {
161 for (int i = 0; i < TSize; i++) {
162 array[i] = value;
163 }
164}
165
166template <typename TElement, typename VElement, size_t N, size_t M>
167constexpr void setTable(TElement (&dest)[N][M], const VElement value) {
168 for (size_t n = 0; n < N; n++) {
169 for (size_t m = 0; m < M; m++) {
170 dest[n][m] = value;
171 }
172 }
173}
174
175template <typename TDest, typename TSource, size_t N, size_t M>
176constexpr void copyTable(TDest (&dest)[N][M], const TSource (&source)[N][M], float multiply = 1.0f) {
177 for (size_t n = 0; n < N; n++) {
178 for (size_t m = 0; m < M; m++) {
179 dest[n][m] = source[n][m] * multiply;
180 }
181 }
182}
183
184template <typename TDest, typename TSource, size_t N, size_t M>
185constexpr void copyTable(
186 TDest (&dest)[N][M],
187 const std::array<std::array<TSource, M>, N>& source,
188 float multiply = 1.0f
189) {
190 for (size_t n = 0; n < N; n++) {
191 for (size_t m = 0; m < M; m++) {
192 dest[n][m] = source[n][m] * multiply;
193 }
194 }
195}
196
197// specialization that can use memcpy when src and dest types match
198template <typename TDest, size_t N, size_t M>
199constexpr void copyTable(scaled_channel<TDest, 1, 1> (&dest)[N][M], const TDest (&source)[N][M]) {
200 memcpy(dest, source, N * M * sizeof(TDest));
201}
202
203template <typename TDest, size_t N, size_t M>
204constexpr void copyTable(TDest (&dest)[N][M], const TDest (&source)[N][M]) {
205 memcpy(dest, source, N * M * sizeof(TDest));
206}
207
208template<typename kType>
209void setRpmBin(kType array[], int size, float idleRpm, float topRpm) {
210 array[0] = idleRpm - 150;
211 int rpmStep = (int)(efiRound((topRpm - idleRpm) / (size - 2), 50) - 150);
212 for (int i = 1; i < size - 1;i++)
213 array[i] = idleRpm + rpmStep * (i - 1);
214 array[size - 1] = topRpm;
215}
216
217/**
218 * initialize RPM table axis using default RPM range
219 */
220template<typename TValue, int TSize>
221void setRpmTableBin(TValue (&array)[TSize]) {
222 setRpmBin(array, TSize, 800, DEFAULT_RPM_AXIS_HIGH_VALUE);
223}
static size_t getIndexForCoordinates(size_t row, size_t column)
Map3D(const char *name)
void initCols(const scaled_channel< TXColumn, TColMult, TColDiv >(&columnBins)[TColNum])
float m_rowMult
void initValues(scaled_channel< TValue, TMult, TDiv >(&table)[TRowNum][TColNum])
float getValue(float xColumn, float yRow) const final
TValue getValueAtPosition(size_t row, size_t column) const
const TXColumn(* m_columnBins)[TColNum]
float m_valueMult
void initRows(const scaled_channel< TRow, TRowMult, TRowDiv >(&rowBins)[TRowNum])
void initValues(TValue(&table)[TRowNum][TColNum])
const char * m_name
float m_colMult
void initTable(TValueInit(&table)[TRowNum][TColNum], const TXColumnInit(&columnBins)[TColNum], const TRowInit(&rowBins)[TRowNum])
TValue(* m_values)[TRowNum][TColNum]
const TRow(* m_rowBins)[TRowNum]
void setAll(TValue value)
void initRows(const TRow(&rowBins)[TRowNum])
void initCols(const TXColumn(&columnBins)[TColNum])
virtual float getValue(float xColumn, float yRow) const =0
float interpolateMsg(const char *msg, float x1, float y1, float x2, float y2, float x)
Linear interpolation by two points.
float efiRound(float value, float precision)
Definition efilib.cpp:34
@ CUSTOM_ERR_6573
static constexpr float asFloat()
Definition efi_ratio.h:13
constexpr void copyTable(TDest(&dest)[N][M], const TSource(&source)[N][M], float multiply=1.0f)
Map3D< PEDAL_TO_TPS_RPM_SIZE, PEDAL_TO_TPS_SIZE, uint8_t, uint8_t, uint8_t > pedal2tps_t
Map3D< MAP_EST_RPM_COUNT, MAP_EST_LOAD_COUNT, uint16_t, uint16_t, uint16_t > mapEstimate_Map3D_t
constexpr void setTable(TElement(&dest)[N][M], const VElement value)
void setRpmBin(kType array[], int size, float idleRpm, float topRpm)
void setRpmTableBin(TValue(&array)[TSize])
Map3D< VE_RPM_COUNT, VE_LOAD_COUNT, uint16_t, uint16_t, uint16_t > ve_Map3D_t
void setLinearCurve(TValue(&array)[TSize], float from, float to, float precision=0.01f)
void setArrayValues(TValue(&array)[TSize], float value)
composite packet size