diff --git a/README.md b/README.md index 224d853..69bd2fe 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ -Modules: +## Modules: - `util`: General purpose math functions, interpolation, array handling, etc. +## Including modules in your project: + Set variable `$(RUSEFI_LIB)` to the path to the folder that contains this readme. Include the mk files of the modules that you want, then add: @@ -8,3 +10,7 @@ Include the mk files of the modules that you want, then add: - `$(RUSEFI_LIB_CPP)` to your list of c++ input files Currently, C++17 is required to compile these libraries. + +## Unit tests: + +TODO diff --git a/util/include/rusefi/math.h b/util/include/rusefi/math.h new file mode 100644 index 0000000..0310cbf --- /dev/null +++ b/util/include/rusefi/math.h @@ -0,0 +1,27 @@ +// Various math utility functions, implemented in microcontroller friendly ways. + +#pragma once + +// absolute value +int absI(int value); +float absF(float value); + +// Min/max +int maxI(int i1, int i2); +int minI(int i1, int i2); +float maxF(float i1, float i2); +float minF(float i1, float i2); + +// Clamping +float clampF(float min, float clamp, float max); + +// Returns if two floats are within 0.0001 +bool isSameF(float a, float b); + +// @brief Compute e^x using a 4th order taylor expansion centered at x=-1. Provides +// bogus results outside the range -2 < x < 0. +float expf_taylor(float x); + +// @brief Compute tan(theta) using a ratio of the Taylor series for sin and cos +// Valid for the range [0, pi/2 - 0.01] +float tanf_taylor(float theta); diff --git a/util/src/math.cpp b/util/src/math.cpp new file mode 100644 index 0000000..c9af3dc --- /dev/null +++ b/util/src/math.cpp @@ -0,0 +1,95 @@ +#include + +#include + +float absF(float value) { + return value > 0 ? value : -value; +} + +int absI(int value) { + return value >= 0 ? value : -value; +} + +int maxI(int i1, int i2) { + return i1 > i2 ? i1 : i2; +} + +int minI(int i1, int i2) { + return i1 < i2 ? i1 : i2; +} + +float maxF(float i1, float i2) { + return i1 > i2 ? i1 : i2; +} + +float minF(float i1, float i2) { + return i1 < i2 ? i1 : i2; +} + +float clampF(float min, float clamp, float max) { + return maxF(min, minF(clamp, max)); +} + +bool isSameF(float a, float b) { + return absF(a - b) < 0.0001; +} + +constexpr float expf_taylor_impl(float x, uint8_t n) +{ + if (x < -2) + { + return 0.818f; + } + else if (x > 0) + { + return 1; + } + + x = x + 1; + + float x_power = x; + int fac = 1; + float sum = 1; + + for (int i = 1; i <= n; i++) + { + fac *= i; + sum += x_power / fac; + + x_power *= x; + } + + constexpr const float constant_e = 2.71828f; + return sum / constant_e; +} + +float expf_taylor(float x) +{ + return expf_taylor_impl(x, 4); +} + +float tanf_taylor(float x) { + // This exists because the "normal" implementation, tanf, pulls in like 6kb of + // code and loookup tables + + // This is only specified from [0, pi/2 - 0.01) + // Inside that range it has an error of less than 0.1%, and it gets worse as theta -> pi/2 + + // Precompute some exponents of x + float x2 = x * x; + float x3 = x2 * x; + float x4 = x3 * x; + float x5 = x4 * x; + float x6 = x5 * x; + // x7 not used + float x8 = x6 * x2; + + // 3-term Taylor Series for sin(theta) + float sin_val = x - (x3 / 6) + (x5 / 120); + + // 5-term Taylor Series for cos(theta) + float cos_val = 1 - (x2 / 2) + (x4 / 24) - (x6 / 720) + (x8 / 40320); + + // tan = sin / cos + return sin_val / cos_val; +} diff --git a/util/test/test_math.cpp b/util/test/test_math.cpp new file mode 100644 index 0000000..139d472 --- /dev/null +++ b/util/test/test_math.cpp @@ -0,0 +1,39 @@ +#include + +#include +#include + +TEST(Util_Math, ExpTaylor) +{ + float x = -2; + + // test from -2 < x < 0 + for(float x = -2; x < 0; x += 0.05) + { + // Compare taylor to libc implementation + EXPECT_NEAR(expf_taylor(x), expf(x), 0.01f); + } +} + +TEST(Util_Math, clampf) { + // off scale low + EXPECT_EQ(clampF(10, 5, 20), 10); + EXPECT_EQ(clampF(-10, -50, 10), -10); + + // in range (unclamped) + EXPECT_EQ(clampF(10, 15, 20), 15); + EXPECT_EQ(clampF(-10, -5, 10), -5); + + // off scale high + EXPECT_EQ(clampF(10, 25, 20), 20); + EXPECT_EQ(clampF(-10, 50, 10), 10); +} + +TEST(Util_Math, tanf_taylor) { + // Function is only specified from [0, pi/2) ish, so test that range + for (float i = 0; i < 1.5; i += 0.1f) + { + // Compare to libc implementation + EXPECT_NEAR(tanf_taylor(i), tanf(i), 0.05f) << "I = " << i; + } +} diff --git a/util/util.mk b/util/util.mk index 100488a..963a2e1 100644 --- a/util/util.mk +++ b/util/util.mk @@ -3,4 +3,8 @@ RUSEFI_LIB_INC += $(RUSEFI_LIB)/util/include RUSEFI_LIB_CPP += \ $(RUSEFI_LIB)/util/src/util_dummy.cpp \ $(RUSEFI_LIB)/util/src/crc.cpp \ + $(RUSEFI_LIB)/util/src/math.cpp \ + +RUSEFI_LIB_CPP_TEST += \ + $(RUSEFI_LIB)/util/test/test_math.cpp \