firmware/libfirmware/util/include/rusefi/scaled_channel.h
| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /** | |||
| 2 | * @file scaled_channel.h | |||
| 3 | */ | |||
| 4 | ||||
| 5 | #pragma once | |||
| 6 | ||||
| 7 | #include <cmath> | |||
| 8 | #include <cstdint> | |||
| 9 | #include <type_traits> | |||
| 10 | ||||
| 11 | struct scaled_channel_base { }; | |||
| 12 | ||||
| 13 | template <typename TTest> | |||
| 14 | static constexpr bool is_scaled_channel = std::is_base_of_v<scaled_channel_base, TTest>; | |||
| 15 | ||||
| 16 | // This class lets us transparently store something at a ratio inside an integer type | |||
| 17 | // Just use it like a float - you can read and write to it, like this: | |||
| 18 | // scaled_channel<uint8_t, 10> myVar; | |||
| 19 | // myVar = 2.4f; // converts to an int, stores 24 | |||
| 20 | // float x = myVar; // converts back to float, returns 2.4f | |||
| 21 | template <typename T, int mul = 1, int div = 1> | |||
| 22 | class scaled_channel : scaled_channel_base { | |||
| 23 | using TSelf = scaled_channel<T, mul, div>; | |||
| 24 | ||||
| 25 | public: | |||
| 26 | struct IncompleteType; | |||
| 27 | ||||
| 28 | 54590 | constexpr scaled_channel() : m_value(static_cast<T>(0)) { } | ||
| 29 | ||||
| 30 | // Only allow conversion directly to T when mul/div are both 1, otherwise this constructor doesn't exist and the float conversion is used. | |||
| 31 | 36582 | constexpr scaled_channel(std::conditional_t<mul == 1 && div == 1, T, IncompleteType> val) { | ||
| 32 | 36582 | m_value = val; | ||
| 33 | 36582 | } | ||
| 34 | ||||
| 35 | // Scale the float in to our scaled channel | |||
| 36 | 54339279 | constexpr scaled_channel(std::conditional_t<mul != 1 || div != 1, float, IncompleteType> val) { | ||
| 37 | 54339279 | m_value = std::roundf(val * float(mul) / div); | ||
| 38 | 54339279 | } | ||
| 39 | ||||
| 40 | // Only allow conversion directly to T when mul/div are both 1, otherwise this operator doesn't exist and the float conversion is used. | |||
| 41 | 517265 | constexpr operator typename std::conditional_t<mul == 1 && div == 1, T, IncompleteType>() const { | ||
| 42 | 517265 | return m_value; | ||
| 43 | } | |||
| 44 | ||||
| 45 | // Allow reading back out as a float (note: this may be lossy!) | |||
| 46 | 10310033 | constexpr operator typename std::conditional_t<mul != 1 || div != 1, float, IncompleteType>() const { | ||
| 47 | 10310033 | return m_value * (float(div) / mul); | ||
| 48 | } | |||
| 49 | ||||
| 50 | // Postfix increment operator | |||
| 51 | // only enable if: | |||
| 52 | // - base type T is an integral type (integer) | |||
| 53 | // - multiplier is equal to 1 | |||
| 54 | void operator++(int) { | |||
| 55 | static_assert(mul == 1 && div == 1, | |||
| 56 | "Increment operator only supported for non-scaled integer types"); | |||
| 57 | static_assert(std::is_integral_v<T>, "Increment operator only supported for non-scaled integer types"); | |||
| 58 | ||||
| 59 | m_value++; | |||
| 60 | } | |||
| 61 | ||||
| 62 | 235 | constexpr const char* getFirstByteAddr() const { | ||
| 63 | 235 | return &m_firstByte; | ||
| 64 | } | |||
| 65 | ||||
| 66 | private: | |||
| 67 | union { | |||
| 68 | T m_value; | |||
| 69 | char m_firstByte; | |||
| 70 | }; | |||
| 71 | }; | |||
| 72 | ||||
| 73 | // We need to guarantee that scaled values containing some type are the same size | |||
| 74 | // as that underlying type. We rely on the class only having a single member for | |||
| 75 | // this trick to work. | |||
| 76 | static_assert(sizeof(scaled_channel<uint8_t>) == 1); | |||
| 77 | static_assert(sizeof(scaled_channel<uint16_t>) == 2); | |||
| 78 | static_assert(sizeof(scaled_channel<uint32_t>) == 4); | |||
| 79 | static_assert(sizeof(scaled_channel<float>) == 4); | |||
| 80 | ||||
| 81 | // make sure the scaled channel detector works | |||
| 82 | static_assert(!is_scaled_channel<int>); | |||
| 83 | static_assert(is_scaled_channel<scaled_channel<int, 5>>); | |||
| 84 |