Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions util/include/rusefi/arrays.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <cstddef>
#include <cstring>
#include <cmath>

#include "scaled_channel.h"
#include "critical_error.h"
Expand Down Expand Up @@ -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 <typename DElement, typename SElement, size_t NDest, size_t NSrc, int roundDigits = 2>
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<float>(NSrc - 1) / (NDest - 1);

for (size_t i = 0; i < NDest; i++) {
const float currentSrcPos = static_cast<float>(i) * step;
auto srcIdx = static_cast<size_t>(currentSrcPos);
const float frac = currentSrcPos - static_cast<float>(srcIdx);

if (srcIdx >= NSrc - 1) {
dest[i] = src[NSrc - 1];
} else {
float interpolated = static_cast<float>(src[srcIdx]) * (1.0f - frac) + static_cast<float>(src[srcIdx + 1]) * frac;
if constexpr (roundDigits >= 0) {
// Round to specified decimal places
float rounded = static_cast<float>(static_cast<int>(interpolated * roundScale + 0.5f)) / roundScale;
dest[i] = static_cast<DElement>(rounded);
} else {
dest[i] = static_cast<DElement>(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 <typename T, size_t N>
Expand Down
88 changes: 88 additions & 0 deletions util/test/test_arrays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<float, float, 4, 2, 1>(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);
}
}