Speeduino
Loading...
Searching...
No Matches
maths.h
Go to the documentation of this file.
1#ifndef MATH_H
2#define MATH_H
3
4#include <stdint.h>
5#include <avr-fast-shift.h>
6#include <avr-fast-div.h>
7#ifdef USE_LIBDIVIDE
8// We use pre-computed constant parameters with libdivide where possible.
9// Using predefined constants saves flash and RAM (.bss) versus calling the
10// libdivide generator functions (E.g. libdivide_s32_gen)
11// 32-bit constants generated here: https://godbolt.org/z/vP8Kfejo9
12#include <libdivide.h>
13#endif
14#include "unit_testing.h"
15
16uint8_t random1to100(void) noexcept;
17
19static constexpr uint32_t MICROS_PER_SEC = UINT32_C(1000000);
20
22static constexpr uint32_t MICROS_PER_MIN = MICROS_PER_SEC*60U;
23
26
28static constexpr uint32_t MILLI_PER_SEC = MICROS_PER_SEC/1000;
29
32
50#define DIV_ROUND_DOWN -1
51
53#define DIV_ROUND_UP 1
54
60#define DIV_ROUND_NEAREST 0
61
63#define DIV_ROUND_BEHAVIOR DIV_ROUND_NEAREST
64// (Unit tests expect DIV_ROUND_NEAREST behavior)
65
73#define DIV_ROUND_CORRECT(d, t) ((t)(((d)>>1U)+(t)DIV_ROUND_BEHAVIOR))
75
90#define DIV_ROUND_CLOSEST(n, d, t) ( \
91 (((n) < (t)(0)) ^ ((d) < (t)(0))) ? \
92 ((t)((n) - DIV_ROUND_CORRECT(d, t))/(t)(d)) : \
93 ((t)((n) + DIV_ROUND_CORRECT(d, t))/(t)(d)))
94
107#define UDIV_ROUND_CLOSEST(n, d, t) ((t)((n) + DIV_ROUND_CORRECT(d, t))/(t)(d))
108
116template <uint16_t divisor>
118 // This is a compile time version of UDIV_ROUND_CLOSEST
119 //
120 // As of avr-gcc 5.4.0, the compiler will optimize this to a multiply/shift
121 // assuming d is a constant.
123}
124
126#define UDIV_ROUND_UP(n, d, t) ((t)((n) + (t)((d)+1U))/(t)(d))
127
129
131#define IS_INTEGER(d) ((d) == (int32_t)(d))
132
142static inline uint16_t div100(uint16_t n) {
143 // As of avr-gcc 5.4.0, the compiler will optimize this to a multiply/shift
144 // (unlike the signed integer overload, where __divmodhi4 is still called
145 // see https://godbolt.org/z/c5bs5noT1)
146#ifdef USE_LIBDIVIDE
147 constexpr libdivide::libdivide_u16_t libdiv_u16_100 = { .magic = 18351, .more = 70 };
148 // LCOV_EXCL_BR_START
150 // LCOV_EXCL_BR_STOP
151#else
152 return UDIV_ROUND_CLOSEST(n, UINT16_C(100), uint16_t);
153#endif
154}
155
156static inline int16_t div100(int16_t n) {
157#ifdef USE_LIBDIVIDE
158 // Try faster unsigned path first
159 if (n>0) {
160 return div100((uint16_t)n);
161 }
162 // Negative values here, so adjust pre-division to get same
163 // behavior as roundf(float)
164 constexpr libdivide::libdivide_s16_t libdiv_s16_100 = { .magic = 20972, .more = 5 };
165 // LCOV_EXCL_BR_START
167 // LCOV_EXCL_BR_STOP
168#else
169 return DIV_ROUND_CLOSEST(n, UINT16_C(100), int16_t);
170#endif
171}
172
173static inline uint32_t div100(uint32_t n) {
174#ifdef USE_LIBDIVIDE
175 if (n<=(uint32_t)UINT16_MAX) {
176 return div100((uint16_t)n);
177 }
178 constexpr libdivide::libdivide_u32_t libdiv_u32_100 = { .magic = 2748779070, .more = 6 };
179 // LCOV_EXCL_BR_START
181 // LCOV_EXCL_BR_STOP
182#else
183 return UDIV_ROUND_CLOSEST(n, UINT32_C(100), uint32_t);
184#endif
185}
186
187static inline int32_t div100(int32_t n) {
188#ifdef USE_LIBDIVIDE
190 return div100((int16_t)n);
191 }
192 constexpr libdivide::libdivide_s32_t libdiv_s32_100 = { .magic = 1374389535, .more = 5 };
193 // LCOV_EXCL_BR_START
195 // LCOV_EXCL_BR_STOP
196#else
197 return DIV_ROUND_CLOSEST(n, INT32_C(100), int32_t);
198#endif
199}
201
208static inline uint32_t div360(uint32_t n) {
209#ifdef USE_LIBDIVIDE
210 constexpr libdivide::libdivide_u32_t libdiv_u32_360 = { .magic = 1813430637, .more = 72 };
211 // LCOV_EXCL_BR_START
213 // LCOV_EXCL_BR_STOP
214#else
216#endif
217}
218
231template <uint8_t b>
233 constexpr uint8_t CORRECTION_SHIFT = b-1U; // cppcheck-suppress misra-c2012-10.4
234 constexpr uint32_t CORRECTION = 1UL<<CORRECTION_SHIFT;
235 return rshift<b>((uint32_t)(a+CORRECTION));
236}
237
239
244template <uint8_t bitsPrecision>
248}
249
251
272 // To keep the percentage within 16-bits (for performance), we have to scale the precision based on the percentage.
273 // I.e. the larger the percentage, the smaller the precision has to be (and vice-versa).
274 //
275 // We could use __builtin_clz() and use the leading zero count as the precision, but that is slow:
276 // * AVR doesn't have a clz ASM instruction, so __builtin_clz() is implemented in software
277 // * It would require removing some compile time optimizations
278 #define TEST_AND_APPLY(precision) \
279 if (percent<(UINT16_C(1)<<(UINT16_C(16)-(precision)))) { \
280 return _percentageApprox<(precision)>(percent, value); \
281 }
282
283 TEST_AND_APPLY(9) // Percent<128
284 TEST_AND_APPLY(8) // Percent<256
285 TEST_AND_APPLY(7) // Percent<512
286 TEST_AND_APPLY(6) // Percent<1024
287
288 #undef TEST_AND_APPLY
289
290 // Percent<2048
292}
293
298 if (percent<(UINT8_C(1)<<UINT8_C(7))) {
300 }
302}
303
309static constexpr uint8_t ONE_HUNDRED_PCT = 100U;
310
321
322
332#ifdef USE_LIBDIVIDE
333 constexpr libdivide::libdivide_u32_t libdiv_u32_200 = { .magic = 2748779070, .more = 7 };
334 // LCOV_EXCL_BR_START
336 // LCOV_EXCL_BR_STOP
337#else
339#endif
340}
341
352{
353 if (value<min) { return value + nudgeAmount; }
354 if (value>max) { return value - nudgeAmount; }
355 return value;
356}
357
364template <typename TDividend, typename TDivisor>
368
380// LCOV_EXCL_START
381template<class T>
382TESTABLE_STATIC_CONSTEXPR const T& clamp(const T& v, const T& lo, const T& hi){
383 return v<lo ? lo : hi<v ? hi : v;
384}
385// LCOV_EXCL_STOP
386
388
389template <typename T, typename TPrime>
390static inline T LOW_PASS_FILTER_8BIT(T input, uint8_t alpha, T prior) {
391 // Intermediate steps are for MISRA compliance
392 // Equivalent to: (input * (256 - alpha) + (prior * alpha)) >> 8
393 static constexpr uint16_t ALPHA_MAX_SHIFT = 8U;
394 static constexpr uint16_t ALPHA_MAX = 2U << (ALPHA_MAX_SHIFT-1U);
398 return (T)(preshift >> (TPrime)ALPHA_MAX_SHIFT);
399}
400
402
417
422
434static inline uint8_t scale(const uint8_t from, const uint8_t fromRange, const uint8_t toRange) {
435 // Using uint16_t to avoid overflow when calculating the result
436 return fromRange==0U ? 0U : (((uint16_t)from * (uint16_t)toRange) / (uint16_t)fromRange);
437}
438
452static inline uint8_t fast_map(const uint8_t from, const uint8_t fromLow, const uint8_t fromHigh, const uint8_t toLow, const uint8_t toHigh) {
453 // Stick to unsigned math for performance, so need to check for output range inversion
454 if (toLow>toHigh) {
456 } else {
458 }
459}
460
461#endif
#define DIV_ROUND_CORRECT(d, t)
Computes the denominator correction for rounding division based on our rounding behavior.
Definition maths.h:73
TESTABLE_STATIC_CONSTEXPR uint16_t div_round_closest_u16(uint16_t n)
Rounded unsigned integer division optimized for compile time constants.
Definition maths.h:117
#define DIV_ROUND_CLOSEST(n, d, t)
Rounded integer division.
Definition maths.h:90
#define UDIV_ROUND_CLOSEST(n, d, t)
Rounded unsigned integer division.
Definition maths.h:107
static uint8_t a
Definition maths.cpp:8
uint8_t random1to100(void) noexcept
Definition maths.cpp:9
static constexpr uint32_t MICROS_PER_MIN
Self-explanatory.
Definition maths.h:22
static constexpr uint32_t MICROS_PER_SEC
Self-explanatory.
Definition maths.h:19
#define TEST_AND_APPLY(precision)
static constexpr uint32_t MILLI_PER_SEC
Self-explanatory.
Definition maths.h:28
static uint16_t LOW_PASS_FILTER(uint16_t input, uint8_t alpha, uint16_t prior)
Simple low pass IIR filter 16-bit values.
Definition maths.h:414
TESTABLE_STATIC_CONSTEXPR TDividend fast_div_closest(TDividend dividend, TDivisor divisor)
Same as fast_div(), except this will round to nearest integer instead of truncating.
Definition maths.h:365
static constexpr uint32_t MICROS_PER_HOUR
Self-explanatory.
Definition maths.h:25
uint16_t pwmFreqToTicks(uint16_t frequency)
Convert a frequency in Hz to the equivalent number of PWM timer ticks.
Definition maths.cpp:32
static uint32_t percentage(uint16_t percent, uint32_t value)
Integer based percentage calculation.
Definition maths.h:317
static uint32_t div360(uint32_t n)
Optimised integer division by 360.
Definition maths.h:208
static uint8_t scale(const uint8_t from, const uint8_t fromRange, const uint8_t toRange)
Scale a value from one range to another.
Definition maths.h:434
static uint8_t fast_map(const uint8_t from, const uint8_t fromLow, const uint8_t fromHigh, const uint8_t toLow, const uint8_t toHigh)
Specialist version of map(long, long, long, long, long) for performance.
Definition maths.h:452
static uint32_t percentageApprox(uint16_t percent, uint32_t value)
Integer based percentage calculation: faster, but less accurate, than percentage()
Definition maths.h:271
static constexpr uint8_t ONE_HUNDRED_PCT
This is only here to eliminate magic numbers.
Definition maths.h:309
static uint32_t rshift_round(uint32_t a)
Rounded arithmetic right shift.
Definition maths.h:232
TESTABLE_STATIC_CONSTEXPR const T & clamp(const T &v, const T &lo, const T &hi)
clamps a given value between the minimum and maximum thresholds.
Definition maths.h:382
static int16_t nudge(int16_t min, int16_t max, int16_t value, int16_t nudgeAmount)
Make one pass at correcting the value into the range [min, max)
Definition maths.h:351
static uint16_t halfPercentage(uint8_t percent, uint16_t value)
Integer based half-percentage calculation.
Definition maths.h:330
static uint16_t div100(uint16_t n)
Performance optimised integer division by 100. I.e. same as n/100.
Definition maths.h:142
Definition array.h:14
constexpr array()=default
Unit testability support.
#define TESTABLE_STATIC_CONSTEXPR
Mark a function with constexpr, unless a unit test is in progress - then the entity is inlined.
Definition unit_testing.h:56