Speeduino
Loading...
Searching...
No Matches
table2d.h
Go to the documentation of this file.
1/*
2This file is used for everything related to maps/tables including their definition, functions etc
3*/
4#ifndef TABLE_H
5#define TABLE_H
6
8#include <algorithm>
9#include <stdint.h>
10#include <Arduino.h>
11#include "preprocessor.h"
12
14// private to table2D implementation
15
16namespace _table2d_detail {
17
18// The 2D table cache
19template <typename axis_t, typename value_t>
20struct Table2DCache
21{
22 // Store the upper index of the bin we last found. This is used to make the next check faster
23 // Since this is the *upper* index, it can never be 0.
24 uint8_t lastBinUpperIndex = 1U; // The axis bin search algo relies on this being 1 initially
25
26 //Store the last input and output for caching
27 axis_t lastInput = 0;
28 value_t lastOutput = 0;
29 uint8_t cacheTime = 0U; //Tracks when the last cache value was set so it can expire after x seconds. A timeout is required to pickup when a tuning value is changed, otherwise the old cached value will continue to be returned as the X value isn't changing.
30
31 constexpr Table2DCache(void) = default;
32};
33
34uint8_t getCacheTime(void);
35
36// LCOV_EXCL_START
37template <typename axis_t, typename value_t>
38static inline bool cacheExpired(const Table2DCache<axis_t, value_t> &cache) {
39 return (cache.cacheTime != getCacheTime());
40}
41// LCOV_EXCL_STOP
42
43template <typename T>
44struct Bin {
45 constexpr Bin(const T *array, uint8_t binUpperIndex)
46 : upperIndex(binUpperIndex)
47 , _upperValue(array[binUpperIndex])
48 , _lowerValue(array[binUpperIndex-1U])
49 {
50 }
51 constexpr Bin(uint8_t binUpperIndex, const T upperValue, const T lowerValue)
52 : upperIndex(binUpperIndex)
53 , _upperValue(upperValue)
54 , _lowerValue(lowerValue)
55 {
56 }
57
58 const T upperValue(void) const noexcept { return _upperValue; }
59 const T lowerValue(void) const noexcept { return _lowerValue; }
60
61 static bool withinBin(const T &value, const T &min, const T&max) noexcept {
62 return (value <= max) && (value > min);
63 }
64 bool withinBin(const T value) const noexcept {
65 return withinBin(value, lowerValue(), upperValue());
66 }
67
68 uint8_t upperIndex;
69 T _upperValue;
70 T _lowerValue;
71};
72
73
74template <typename TIter, typename TValue>
75static inline uint8_t findBinUpperIndex(TIter pStart, TIter pEnd, const TValue &value)
76{
77 auto it = std::lower_bound(pStart, pEnd, value);
78
79 if (it == pStart) {
80 ++it;
81 }
82 if (it != pEnd) {
83 return std::distance(pStart, it);
84 }
85 return std::distance(pStart, pEnd)-1U;
86}
87
88template <typename TIter, typename TValue>
89static inline Bin<TValue> findCachedBin(uint8_t cachedUpperIndex, TIter pStart, TIter pEnd, const TValue &value)
90{
91 // Ignore the cache during tests
92#if !defined(UNIT_TEST)
93 // LCOV_EXCL_START
94 auto bin = Bin<TValue>(pStart, cachedUpperIndex);
95 if (bin.withinBin(value))
96 {
97 return bin;
98 }
99 // LCOV_EXCL_STOP
100#else
101 UNUSED(cachedUpperIndex);
102#endif
103
104 // If we're not in the same bin, search
105 return Bin<TValue>(pStart, findBinUpperIndex(pStart, pEnd, value));
106}
107
108// Generic interpolation
109// Exclude from code coverage - it's just a wrapper around map()
110// LCOV_EXCL_START
111template <typename axis_t, typename value_t>
112static inline value_t interpolate(const axis_t axisValue, const Bin<axis_t> &axisBin, const Bin<value_t> &valueBin)
113{
114 return map(axisValue, axisBin.lowerValue(), axisBin.upperValue(), valueBin.lowerValue(), valueBin.upperValue());
115}
116// LCOV_EXCL_STOP
117
118// Specialized interpolation of uint8_t for performance
119uint8_t interpolate(const uint8_t axisValue, const Bin<uint8_t> &axisBin, const Bin<uint8_t> &valueBin);
120
121} // _table2d_detail
123
135template <typename axis_t, typename value_t, uint8_t sizeT>
137{
138 using size_type = uint8_t;
139
140 value_t (&values)[sizeT];
141 axis_t (&axis)[sizeT];
142
143 mutable _table2d_detail::Table2DCache<axis_t, value_t> cache;
144
145 constexpr table2D(axis_t (*pAxisBin)[sizeT], value_t (*pCurve)[sizeT])
146 : values(*pCurve) //cppcheck-suppress misra-c2012-10.4
147 , axis(*pAxisBin)
148 {
149 }
150
151 static constexpr size_type size(void) { return sizeT; }
152
153 value_t getValue(const axis_t axisValue) const noexcept {
154// Turn off caching during unit tests
155#if !defined(UNIT_TEST)
156 // LCOV_EXCL_START
157 // Check whether the X input is the same as last time this ran
158 if( (axisValue == cache.lastInput) && (!cacheExpired(cache)) )
159 {
160 return cache.lastOutput;
161 }
162 // LCOV_EXCL_STOP
163#endif
164
165 // Test if above the max axis value, clip to max data value
166 if(axisValue >= axis[sizeT-1U])
167 {
168 cache.lastOutput = values[sizeT-1U];
169 cache.lastBinUpperIndex = sizeT-1U;
170 }
171 // Test if below the min axis value, clip to min data value
172 else if (axisValue <= axis[0])
173 {
174 cache.lastOutput = values[0];
175 cache.lastBinUpperIndex = 1U;
176 }
177 else
178 {
179 // None of the cached or last values match, so we need to find the new value
180 auto xBin = _table2d_detail::findCachedBin(cache.lastBinUpperIndex, axis, axis+sizeT, axisValue);
181
182 // We are exactly at the bin upper bound, so no need to interpolate
183 if (axisValue==xBin.upperValue())
184 {
185 cache.lastOutput = values[xBin.upperIndex];
186 cache.lastBinUpperIndex = xBin.upperIndex;
187 }
188 else // Must be within the bin, interpolate
189 {
190 // LCOV_EXCL_BR_START
191 cache.lastOutput = _table2d_detail::interpolate(axisValue, xBin, _table2d_detail::Bin<value_t>(values, xBin.upperIndex));
192 // LCOV_EXCL_BR_STOP
193 cache.lastBinUpperIndex = xBin.upperIndex;
194 }
195 // Note: we cannot be at the bin lower bound here, as that would violate the bin definition of a non-inclusive lower bound
196 }
197 cache.cacheTime = _table2d_detail::getCacheTime(); //As we're not using the cache value, set the current secl value to track when this new value was calculated
198 cache.lastInput = axisValue;
199
200 return cache.lastOutput;
201 }
202};
203
214template <typename axis_t, typename value_t, uint8_t sizeT>
215static inline value_t table2D_getValue(const table2D<axis_t, value_t, sizeT> *fromTable, const axis_t axisValue) noexcept
216{
217 // LCOV_EXCL_START
218 return fromTable->getValue(axisValue);
219 // LCOV_EXCL_STOP
220}
221
222// Hide use of template in the header file
233
234#endif // TABLE_H
Definition table2d.cpp:13
uint8_t getCacheTime(void)
Definition table2d.cpp:15
uint8_t interpolate(const uint8_t axisValue, const Bin< uint8_t > &axisBin, const Bin< uint8_t > &valueBin)
Definition table2d.cpp:24
Workaround when min() & max() are defined as macros, which generates compile errors in most C++ stand...
#define UNUSED(x)
Used to suppress unused parameter compiler warnings.
Definition preprocessor.h:10
A 2D table.
Definition table2d.h:137
_table2d_detail::Table2DCache< axis_t, value_t > cache
Definition table2d.h:143
static constexpr size_type size(void)
Definition table2d.h:151
constexpr table2D(axis_t(*pAxisBin)[sizeT], value_t(*pCurve)[sizeT])
Definition table2d.h:145
axis_t(& axis)[sizeT]
Definition table2d.h:141
uint8_t size_type
Definition table2d.h:138
value_t(& values)[sizeT]
Definition table2d.h:140
value_t getValue(const axis_t axisValue) const noexcept
Definition table2d.h:153
static value_t table2D_getValue(const table2D< axis_t, value_t, sizeT > *fromTable, const axis_t axisValue) noexcept
Interpolate a value from a 2d table.
Definition table2d.h:215