diff --git a/util/include/rusefi/arrays.h b/util/include/rusefi/arrays.h index 74e27de..fd8f6bb 100644 --- a/util/include/rusefi/arrays.h +++ b/util/include/rusefi/arrays.h @@ -2,6 +2,7 @@ #include #include +#include #include "scaled_channel.h" #include "critical_error.h" @@ -41,6 +42,48 @@ constexpr void copyArrayPartial(TElement (&dest)[NDest], const TElement (&src)[N } } +/** + * Copies an array from src to dest. The lengths can be different + * if dest is larger, the array is interpolated, otherwise, values are stepped to preserve the range + * on interpolation we use float and then cast to DElement + */ +template +constexpr void copyArrayInterpolated(DElement (&dest)[NDest], const SElement (&src)[NSrc]) { + if constexpr (NDest == NSrc) { + // Same size - direct copy + copyArray(dest, src); + } else if constexpr (NDest > NSrc) { + // Destination larger - interpolate + const float roundScale = pow(10, roundDigits); + constexpr float step = static_cast(NSrc - 1) / (NDest - 1); + + for (size_t i = 0; i < NDest; i++) { + const float currentSrcPos = static_cast(i) * step; + auto srcIdx = static_cast(currentSrcPos); + const float frac = currentSrcPos - static_cast(srcIdx); + + if (srcIdx >= NSrc - 1) { + dest[i] = src[NSrc - 1]; + } else { + float interpolated = static_cast(src[srcIdx]) * (1.0f - frac) + static_cast(src[srcIdx + 1]) * frac; + if constexpr (roundDigits >= 0) { + // Round to specified decimal places + float rounded = static_cast(static_cast(interpolated * roundScale + 0.5f)) / roundScale; + dest[i] = static_cast(rounded); + } else { + dest[i] = static_cast(interpolated); + } + } + } + } else { + // Destination smaller - step through source to preserve range + for (size_t i = 0; i < NDest; i++) { + size_t srcIdx = i * (NSrc - 1) / (NDest - 1); + dest[i] = src[srcIdx]; + } + } +} + namespace efi { template diff --git a/util/test/test_arrays.cpp b/util/test/test_arrays.cpp index c18cd10..606342f 100644 --- a/util/test/test_arrays.cpp +++ b/util/test/test_arrays.cpp @@ -42,3 +42,91 @@ TEST(Util_Arrays, Size) { ASSERT_EQ(17u, efi::size(arr2)); ASSERT_EQ(21u, efi::size(arr3)); } + +#define ARRAY_INTERPOLATION_ERROR 0.01f + +TEST(Util_Arrays, copyArrayInterpolated){ + // direct copy + { + float src[] = { 1.0f, 2.0f, 3.0f }; + float dest[3]; + copyArrayInterpolated(dest, src); + ASSERT_THAT(dest, ElementsAre(1.0f, 2.0f, 3.0f)); + } + + // Test upscaling with interpolation (2 -> 5) + { + float src[] = { 0.0f, 10.0f }; + float dest[5]; + copyArrayInterpolated(dest, src); + ASSERT_NEAR(dest[0], 0.0f, ARRAY_INTERPOLATION_ERROR); + ASSERT_NEAR(dest[1], 2.5f, ARRAY_INTERPOLATION_ERROR); + ASSERT_NEAR(dest[2], 5.0f, ARRAY_INTERPOLATION_ERROR); + ASSERT_NEAR(dest[3], 7.5f, ARRAY_INTERPOLATION_ERROR); + ASSERT_NEAR(dest[4], 10.0f, ARRAY_INTERPOLATION_ERROR); + } + + // Test upscaling with interpolation (3 -> 7) + { + float src[] = { 0.0f, 6.0f, 12.0f }; + float dest[7]; + copyArrayInterpolated(dest, src); + ASSERT_NEAR(dest[0], 0.0f, ARRAY_INTERPOLATION_ERROR); + ASSERT_NEAR(dest[1], 2.0f, ARRAY_INTERPOLATION_ERROR); + ASSERT_NEAR(dest[2], 4.0f, ARRAY_INTERPOLATION_ERROR); + ASSERT_NEAR(dest[3], 6.0f, ARRAY_INTERPOLATION_ERROR); + ASSERT_NEAR(dest[4], 8.0f, ARRAY_INTERPOLATION_ERROR); + ASSERT_NEAR(dest[5], 10.0f, ARRAY_INTERPOLATION_ERROR); + ASSERT_NEAR(dest[6], 12.0f, ARRAY_INTERPOLATION_ERROR); + } + + // Test downscaling (5 -> 3) + { + float src[] = { 0.0f, 2.5f, 5.0f, 7.5f, 10.0f }; + float dest[3]; + copyArrayInterpolated(dest, src); + ASSERT_THAT(dest, ElementsAre(0.0f, 5.0f, 10.0f)); + } + + // Test rounding with default (2 digits) + { + float src[] = { 0.0f, 1.0f }; + float dest[3]; + copyArrayInterpolated(dest, src); + // Middle value should be 0.5, rounded to 2 digits + EXPECT_FLOAT_EQ(dest[0], 0.0f); + EXPECT_FLOAT_EQ(dest[1], 0.5f); + EXPECT_FLOAT_EQ(dest[2], 1.0f); + } + + // Test rounding with 1 digit + { + float src[] = { 0.0f, 1.0f }; + float dest[4]; + copyArrayInterpolated(dest, src); + EXPECT_FLOAT_EQ(dest[0], 0.0f); + EXPECT_NEAR(dest[1], 0.3f, 0.05f); + EXPECT_NEAR(dest[2], 0.7f, 0.05f); + EXPECT_FLOAT_EQ(dest[3], 1.0f); + } + + // Test with integer types + { + int src[] = { 0, 100 }; + int dest[5]; + copyArrayInterpolated(dest, src); + ASSERT_THAT(dest, ElementsAre(0, 25, 50, 75, 100)); + } + + // Test different src/dest types + { + int src[] = { 0, 10, 20 }; + float dest[5]; + copyArrayInterpolated(dest, src); + ASSERT_NEAR(dest[0], 0.0f, ARRAY_INTERPOLATION_ERROR); + ASSERT_NEAR(dest[1], 5.0f, ARRAY_INTERPOLATION_ERROR); + ASSERT_NEAR(dest[2], 10.0f, ARRAY_INTERPOLATION_ERROR); + ASSERT_NEAR(dest[3], 15.0f, ARRAY_INTERPOLATION_ERROR); + ASSERT_NEAR(dest[4], 20.0f, ARRAY_INTERPOLATION_ERROR); + } +} \ No newline at end of file