GCC Code Coverage Report


Directory: ./
File: firmware/libfirmware/util/include/rusefi/scaled_channel.h
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 87.6% 183 0 209
Functions: 87.4% 76 0 87
Branches: -% 0 0 0
Decisions: -% 0 - 0

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 54395 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 33950 constexpr scaled_channel(std::conditional_t<mul == 1 && div == 1, T, IncompleteType> val) {
32 33950 m_value = val;
33 33950 }
34
35 // Scale the float in to our scaled channel
36 50009951 constexpr scaled_channel(std::conditional_t<mul != 1 || div != 1, float, IncompleteType> val) {
37 50009951 m_value = std::roundf(val * float(mul) / div);
38 50009951 }
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 78880 constexpr operator typename std::conditional_t<mul == 1 && div == 1, T, IncompleteType>() const {
42 78880 return m_value;
43 }
44
45 // Allow reading back out as a float (note: this may be lossy!)
46 44004 constexpr operator typename std::conditional_t<mul != 1 || div != 1, float, IncompleteType>() const {
47 44004 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 232 constexpr const char* getFirstByteAddr() const {
63 232 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