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
7#include <stdint.h>
8#include <Arduino.h>
9
11// private to table2D implementation
12
13namespace _table2d_detail {
14
15// The 2D table cache
16template <typename axis_t, typename value_t>
17struct Table2DCache
18{
19 // Store the upper index of the bin we last found. This is used to make the next check faster
20 // Since this is the *upper* index, it can never be 0.
21 uint8_t lastBinUpperIndex = 1U; // The axis bin search algo relies on this being 1 initially
22
23 //Store the last input and output for caching
24 axis_t lastInput = 0;
25 value_t lastOutput = 0;
26 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.
27
28 constexpr Table2DCache(void) = default;
29};
30
31extern uint8_t getCacheTime(void);
32
33template <typename axis_t, typename value_t>
34static inline bool cacheExpired(const Table2DCache<axis_t, value_t> &cache) {
35 return (cache.cacheTime != getCacheTime());
36}
37
38} // _table2d_detail
40
41
53template <typename axis_t, typename value_t, uint8_t sizeT>
54struct table2D
55{
57
60
61 mutable _table2d_detail::Table2DCache<axis_t, value_t> cache;
62
64 : values(*pCurve) //cppcheck-suppress misra-c2012-10.4
65 , axis(*pAxisBin)
66 {
67 }
68
69 static constexpr size_type size(void) { return sizeT; }
70};
71
72
74// private to table2D implementation
75
76namespace _table2d_detail {
77
78template <typename T>
79struct Bin {
80 constexpr Bin(const T *array, uint8_t binUpperIndex)
84 {
85 }
86 constexpr Bin(uint8_t binUpperIndex, const T upperValue, const T lowerValue)
90 {
91 }
92
93 const T upperValue(void) const noexcept { return _upperValue; }
94 const T lowerValue(void) const noexcept { return _lowerValue; }
95
96 static bool withinBin(const T &value, const T &min, const T&max) noexcept {
97 return (value <= max) && (value > min);
98 }
99 bool withinBin(const T value) const noexcept {
100 return withinBin(value, lowerValue(), upperValue());
101 }
102
106};
107
108
109template <typename T, uint8_t sizeT>
110static inline Bin<T> findBin(const T *const array, const T value)
111{
112 // Loop from the upper end of the axis back down to the 1st bin [0,1]
113 // Assume array is ordered [min...max]
114 const T *binLower = array+sizeT-2U;
115 while ( (*binLower >= value) && (binLower != array) ) { --binLower; }
116 return Bin<T>(binLower-array+1U, *(binLower+1U), *binLower);
117}
118
119// Generic interpolation
120template <typename axis_t, typename value_t>
121static inline value_t interpolate(const axis_t axisValue, const Bin<axis_t> &axisBin, const Bin<value_t> &valueBin)
122{
123 return map(axisValue, axisBin.lowerValue(), axisBin.upperValue(), valueBin.lowerValue(), valueBin.upperValue());
124}
125
126// Specialized interpolation of uint8_t for performance
128
129} // _table2d_detail
130
132
143template <typename axis_t, typename value_t, uint8_t sizeT>
145{
146 //Check whether the X input is the same as last time this ran
147 if( (axisValue == fromTable->cache.lastInput) && (!cacheExpired(fromTable->cache)) ) { return fromTable->cache.lastOutput; }
148 else if(axisValue >= fromTable->axis[sizeT-1]) // Test if above the max axis value, clip to max data value
149 {
150 fromTable->cache.lastOutput = fromTable->values[sizeT-1];
151 fromTable->cache.lastBinUpperIndex = sizeT-1;
152 }
153 else if (axisValue <= fromTable->axis[0]) // Test if below the min axis value, clip to min data value
154 {
155 fromTable->cache.lastOutput = fromTable->values[0];
156 fromTable->cache.lastBinUpperIndex = 1U;
157 }
158 else
159 {
160 // None of the cached or last values match, so we need to find the new value
161 // 1st check is whether we're still in the same X bin as last time
162 _table2d_detail::Bin<axis_t> xBin = _table2d_detail::Bin<axis_t>(fromTable->axis, fromTable->cache.lastBinUpperIndex);
163 if (!xBin.withinBin(axisValue))
164 {
165 //If we're not in the same bin, search
166 xBin = _table2d_detail::findBin<axis_t, sizeT>(fromTable->axis, axisValue);
167 }
168
169 // We are exactly at the bin upper bound, so no need to interpolate
170 if (axisValue==xBin.upperValue())
171 {
172 fromTable->cache.lastOutput = fromTable->values[xBin.upperIndex];
173 fromTable->cache.lastBinUpperIndex = xBin.upperIndex;
174 }
175 else // Must be within the bin, interpolate
176 {
177 fromTable->cache.lastOutput = _table2d_detail::interpolate(axisValue, xBin, _table2d_detail::Bin<value_t>(fromTable->values, xBin.upperIndex));
178 fromTable->cache.lastBinUpperIndex = xBin.upperIndex;
179 }
180 // Note: we cannot be at the bin lower bound here, as that would violate the bin definition of a non-inclusive lower bound
181 }
182 fromTable->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
183 fromTable->cache.lastInput = axisValue;
184
185 return fromTable->cache.lastOutput;
186}
187
188// Hide use of template in the header file
199
200#endif // TABLE_H
static uint32_t rshift(uint32_t a)
Bitwise right shift - generic, unoptimized, case.
Definition bit_shifts.h:348
A 2D table.
Definition table2d.h:55
_table2d_detail::Table2DCache< axis_t, value_t > cache
Definition table2d.h:61
static constexpr size_type size(void)
Definition table2d.h:69
constexpr table2D(axis_t(*pAxisBin)[sizeT], value_t(*pCurve)[sizeT])
Definition table2d.h:63
axis_t(& axis)[sizeT]
Definition table2d.h:59
uint8_t size_type
Definition table2d.h:56
value_t(& values)[sizeT]
Definition table2d.h:58
static value_t table2D_getValue(const table2D< axis_t, value_t, sizeT > *fromTable, const axis_t axisValue)
Interpolate a value from a 2d table.
Definition table2d.h:144