Skip to content

Commit 8239995

Browse files
committed
Add MathUtil tests
1 parent 5905b77 commit 8239995

2 files changed

Lines changed: 76 additions & 5 deletions

File tree

wpimath/src/main/native/include/wpi/math/util/MathUtil.hpp

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ namespace wpi::math {
3535
* @return The value after the deadband is applied.
3636
*/
3737
template <typename T>
38-
requires std::is_arithmetic_v<T> || wpi::units::traits::is_unit_v<T>
38+
requires std::is_arithmetic_v<T> || wpi::units::DimensionedUnitType<T>
3939
constexpr T ApplyDeadband(T value, T deadband, T maxMagnitude = T{1.0}) {
4040
T magnitude;
4141
if constexpr (std::is_arithmetic_v<T>) {
@@ -95,6 +95,24 @@ constexpr T ApplyDeadband(T value, T deadband, T maxMagnitude = T{1.0}) {
9595
}
9696
}
9797

98+
/**
99+
* Returns 0.0 if the given value is within the specified range around zero. The
100+
* remaining range between the deadband and the maximum magnitude is scaled from
101+
* 0.0 to the maximum magnitude.
102+
*
103+
* @param value Value to clip.
104+
* @param deadband Range around zero.
105+
* @param maxMagnitude The maximum magnitude of the input (defaults to 1). Can
106+
* be infinite.
107+
* @return The value after the deadband is applied.
108+
*/
109+
template <typename T>
110+
requires wpi::units::DimensionlessUnitType<T>
111+
constexpr T ApplyDeadband(T value, T deadband,
112+
T maxMagnitude = wpi::units::dimensionless<>{1.0}) {
113+
return ApplyDeadband(value.raw(), deadband.raw(), maxMagnitude.raw());
114+
}
115+
98116
/**
99117
* Returns a zero vector if the given vector is within the specified
100118
* distance from the origin. The remaining distance between the deadband and the
@@ -107,7 +125,7 @@ constexpr T ApplyDeadband(T value, T deadband, T maxMagnitude = T{1.0}) {
107125
* @return The value after the deadband is applied.
108126
*/
109127
template <typename T, int N>
110-
requires std::is_arithmetic_v<T> || wpi::units::traits::is_unit_v<T>
128+
requires std::is_arithmetic_v<T> || wpi::units::DimensionedUnitType<T>
111129
Eigen::Vector<T, N> ApplyDeadband(const Eigen::Vector<T, N>& value, T deadband,
112130
T maxMagnitude = T{1.0}) {
113131
if constexpr (std::is_arithmetic_v<T>) {
@@ -143,7 +161,7 @@ Eigen::Vector<T, N> ApplyDeadband(const Eigen::Vector<T, N>& value, T deadband,
143161
* range.
144162
*/
145163
template <typename T>
146-
requires std::is_arithmetic_v<T> || wpi::units::traits::is_unit_v<T>
164+
requires std::is_arithmetic_v<T> || wpi::units::DimensionedUnitType<T>
147165
constexpr T CopyDirectionPow(T value, double exponent,
148166
T maxMagnitude = T{1.0}) {
149167
if constexpr (std::is_arithmetic_v<T>) {
@@ -152,12 +170,37 @@ constexpr T CopyDirectionPow(T value, double exponent,
152170
value);
153171
} else {
154172
return wpi::units::copysign(
155-
gcem::pow((wpi::units::abs(value) / maxMagnitude).value(), exponent) *
173+
gcem::pow((wpi::units::abs(value) / maxMagnitude).raw(), exponent) *
156174
maxMagnitude,
157175
value);
158176
}
159177
}
160178

179+
/**
180+
* Raises the input to the power of the given exponent while preserving its
181+
* sign.
182+
*
183+
* The function normalizes the input value to the range [0, 1] based on the
184+
* maximum magnitude so that the output stays in the range.
185+
*
186+
* This is useful for applying smoother or more aggressive control response
187+
* curves (e.g. joystick input shaping).
188+
*
189+
* @param value The input value to transform.
190+
* @param exponent The exponent to apply (e.g. 1.0 = linear, 2.0 = squared
191+
* curve). Must be positive.
192+
* @param maxMagnitude The maximum expected absolute value of input (defaults to
193+
* 1). Must be positive.
194+
* @return The transformed value with the same sign and scaled to the input
195+
* range.
196+
*/
197+
template <typename T>
198+
requires wpi::units::DimensionlessUnitType<T>
199+
constexpr T CopyDirectionPow(T value, double exponent,
200+
T maxMagnitude = wpi::units::dimensionless{1.0}) {
201+
return CopyDirectionPow(value.raw(), exponent, maxMagnitude.raw());
202+
}
203+
161204
/**
162205
* Raises the norm of the input to the power of the given exponent while
163206
* preserving its direction.
@@ -177,7 +220,7 @@ constexpr T CopyDirectionPow(T value, double exponent,
177220
* the input range.
178221
*/
179222
template <typename T, int N>
180-
requires std::is_arithmetic_v<T> || wpi::units::traits::is_unit_v<T>
223+
requires std::is_arithmetic_v<T> || wpi::units::DimensionedUnitType<T>
181224
Eigen::Vector<T, N> CopyDirectionPow(const Eigen::Vector<T, N>& value,
182225
double exponent, T maxMagnitude = T{1.0}) {
183226
if constexpr (std::is_arithmetic_v<T>) {

wpimath/src/test/native/cpp/MathUtilTest.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "wpi/math/geometry/Translation2d.hpp"
1616
#include "wpi/math/geometry/Translation3d.hpp"
1717
#include "wpi/units/angle.hpp"
18+
#include "wpi/units/concentration.hpp"
1819
#include "wpi/units/length.hpp"
1920
#include "wpi/units/time.hpp"
2021
#include "wpi/units/velocity.hpp"
@@ -59,6 +60,17 @@ TEST_CASE("MathUtilTest ApplyDeadbandUnits", "[wpimath]") {
5960
-20_rad, 1_rad, 20_rad));
6061
}
6162

63+
TEST_CASE("MathUtilTest ApplyDeadbandDimensionlessUnits", "[wpimath]") {
64+
// < 0
65+
CHECK_UNITS_EQ(-100_pct, wpi::math::ApplyDeadband<wpi::units::percent<>>(
66+
-100_pct, 1_pct, 100_pct));
67+
CHECK_UNITS_EQ(-100_pct, wpi::math::ApplyDeadband<wpi::units::percent<>>(
68+
-100_pct, 1_pct));
69+
70+
CHECK_UNITS_EQ(25_pct, wpi::math::ApplyDeadband<wpi::units::percent<>>(
71+
25_pct, 2_pct, 25_pct));
72+
}
73+
6274
TEST_CASE("MathUtilTest ApplyDeadbandLargeMaxMagnitude", "[wpimath]") {
6375
CHECK_DOUBLE_EQ(
6476
80.0, wpi::math::ApplyDeadband(100.0, 20.0,
@@ -194,6 +206,22 @@ TEST_CASE("MathUtilTest CopyDirectionPowWithUnits", "[wpimath]") {
194206
-5_mps, 2.0, 10_mps));
195207
}
196208

209+
TEST_CASE("MathUtilTest CopyDirectionPowWithDimensionUnits", "[wpimath]") {
210+
CHECK_UNITS_EQ(
211+
0_pct, wpi::math::CopyDirectionPow<wpi::units::percent<>>(0_pct, 2.0));
212+
CHECK_UNITS_EQ(100_pct, wpi::math::CopyDirectionPow<wpi::units::percent<>>(
213+
100_pct, 2.0));
214+
CHECK_UNITS_EQ(-100_pct, wpi::math::CopyDirectionPow<wpi::units::percent<>>(
215+
-100_pct, 2.0));
216+
217+
CHECK_UNITS_EQ(
218+
wpi::units::percent<>{0.5 * 0.5 * 10},
219+
wpi::math::CopyDirectionPow<wpi::units::percent<>>(5_pct, 2.0, 10_pct));
220+
CHECK_UNITS_EQ(
221+
wpi::units::percent<>{-0.5 * 0.5 * 10},
222+
wpi::math::CopyDirectionPow<wpi::units::percent<>>(-5_pct, 2.0, 10_pct));
223+
}
224+
197225
TEST_CASE("MathUtilTest CopyDirectionPow2d", "[wpimath]") {
198226
CHECK((Eigen::Vector2d{{0.5}, {0.0}}) ==
199227
wpi::math::CopyDirectionPow(Eigen::Vector2d{{0.5}, {0.0}}, 1.0));

0 commit comments

Comments
 (0)