Allow scaled_channel to have both multiplier and divisor (#3468)

Disallow scaled_channel for float.  I can't think of a reason to allow it, and it gets in the way
of rounding.

Add separate template param to setTable; let the compiler sort out whether assignment can happen
between TElement and VElement without forcing them to be the same at function call time.
This commit is contained in:
Scott Smith 2021-11-05 14:34:22 -07:00 committed by GitHub
parent f6a20ca1ea
commit 6d38fe1eb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 52 additions and 21 deletions

View File

@ -96,7 +96,7 @@ typedef brain_pin_e egt_cs_array_t[EGT_CHANNEL_COUNT];
#if __cplusplus
#include "scaled_channel.h"
using ve_table_t = scaled_channel<float, 1>[FUEL_LOAD_COUNT][FUEL_RPM_COUNT];
using ve_table_t = float[FUEL_LOAD_COUNT][FUEL_RPM_COUNT];
using lambda_table_t = scaled_channel<uint8_t, PACK_MULT_LAMBDA_CFG>[FUEL_LOAD_COUNT][FUEL_RPM_COUNT];
#endif

View File

@ -152,17 +152,8 @@ void setArrayValues(TValue (&array)[TSize], TValue value) {
}
}
template <typename TElement, size_t N, size_t M>
constexpr void setTable(TElement (&dest)[N][M], const TElement value) {
for (size_t n = 0; n < N; n++) {
for (size_t m = 0; m < M; m++) {
dest[n][m] = value;
}
}
}
template <typename TElement, size_t N, size_t M, int mult = 1>
constexpr void setTable(scaled_channel<TElement, mult> (&dest)[N][M], float value) {
template <typename TElement, typename VElement, size_t N, size_t M>
constexpr void setTable(TElement (&dest)[N][M], const VElement value) {
for (size_t n = 0; n < N; n++) {
for (size_t m = 0; m < M; m++) {
dest[n][m] = value;

View File

@ -11,6 +11,7 @@
#pragma once
#include <cmath>
#include <cstdint>
#include <type_traits>
@ -26,20 +27,25 @@ static constexpr bool is_scaled_channel = std::is_base_of_v<scaled_channel_base,
// scaled_channel<uint8_t, 10> myVar;
// myVar = 2.4f; // converts to an int, stores 24
// float x = myVar; // converts back to float, returns 2.4f
template <typename T, int mult = 1>
template <typename T, int mul = 1, int div = 1>
class scaled_channel : scaled_channel_base {
using TSelf = scaled_channel<T, mult>;
using TSelf = scaled_channel<T, mul, div>;
public:
constexpr scaled_channel() : m_value(static_cast<T>(0)) { }
constexpr scaled_channel(float val)
: m_value(val * mult)
{
val *= float(mul) / div;
if (std::is_integral_v<T>) {
m_value = std::roundf(val);
} else {
m_value = val;
}
}
// Allow reading back out as a float (note: this may be lossy!)
constexpr operator float() const {
return m_value / (float)mult;
return m_value * (float(div) / mul);
}
// Postfix increment operator
@ -47,9 +53,11 @@ public:
// - base type T is an integral type (integer)
// - multiplier is equal to 1
void operator++(int) {
static_assert(mult == 1, "Increment operator only supported for non-scaled integer types");
static_assert(mul == 1 && div == 1,
"Increment operator only supported for non-scaled integer types");
static_assert(std::is_integral_v<T>, "Increment operator only supported for non-scaled integer types");
m_value++;
}

View File

@ -28,6 +28,7 @@ TESTS_SRC_CPP = \
tests/lua/test_lua_hooks.cpp \
tests/sensor/test_cj125.cpp \
tests/test_change_engine_type.cpp \
tests/util/test_scaled_channel.cpp \
tests/util/test_timer.cpp \
tests/system/test_periodic_thread_controller.cpp \
tests/test_util.cpp \
@ -89,6 +90,3 @@ TESTS_SRC_CPP = \
tests/sensor/test_frequency_sensor.cpp \
tests/sensor/test_turbocharger_speed_converter.cpp \
tests/sensor/test_vehicle_speed_converter.cpp

View File

@ -0,0 +1,34 @@
#include "pch.h"
#include "boost_control.h"
using ::testing::_;
using ::testing::StrictMock;
TEST(ScaledChannel, Basic) {
{
scaled_channel<int16_t, 10> i1;
i1 = 10; EXPECT_FLOAT_EQ(i1, 10);
i1 = 1; EXPECT_FLOAT_EQ(i1, 1);
i1 = 0.11; EXPECT_FLOAT_EQ(i1, 0.1); // round
i1 = 0.1; EXPECT_FLOAT_EQ(i1, 0.1);
i1 = 0.09; EXPECT_FLOAT_EQ(i1, 0.1); // round
i1 = 0.01; EXPECT_FLOAT_EQ(i1, 0.); // out of range
i1 = -0.09; EXPECT_FLOAT_EQ(i1, -0.1); // round
i1 = -0.1; EXPECT_FLOAT_EQ(i1, -0.1);
i1 = -0.11; EXPECT_FLOAT_EQ(i1, -0.1); // round
}
{
scaled_channel<int16_t, 1, 10> i2;
i2 = 1000; EXPECT_FLOAT_EQ(i2, 1000);
i2 = 100; EXPECT_FLOAT_EQ(i2, 100);
i2 = 11; EXPECT_FLOAT_EQ(i2, 10); // round
i2 = 10; EXPECT_FLOAT_EQ(i2, 10);
i2 = 9; EXPECT_FLOAT_EQ(i2, 10); // round
i2 = 1; EXPECT_FLOAT_EQ(i2, 0); // out of range
i2 = -9; EXPECT_FLOAT_EQ(i2, -10); // round
i2 = -10; EXPECT_FLOAT_EQ(i2, -10);
i2 = -11; EXPECT_FLOAT_EQ(i2, -10); // round
}
}