WPILibC++ 2025.2.1
Loading...
Searching...
No Matches
MathUtil.h
Go to the documentation of this file.
1// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#pragma once
6
7#include <numbers>
8#include <type_traits>
9
10#include <gcem.hpp>
11#include <wpi/SymbolExports.h>
12
13#include "units/angle.h"
14#include "units/base.h"
15#include "units/math.h"
16
17namespace frc {
18
19/**
20 * Returns 0.0 if the given value is within the specified range around zero. The
21 * remaining range between the deadband and the maximum magnitude is scaled from
22 * 0.0 to the maximum magnitude.
23 *
24 * @param value Value to clip.
25 * @param deadband Range around zero.
26 * @param maxMagnitude The maximum magnitude of the input (defaults to 1). Can
27 * be infinite.
28 * @return The value after the deadband is applied.
29 */
30template <typename T>
31 requires std::is_arithmetic_v<T> || units::traits::is_unit_t_v<T>
32constexpr T ApplyDeadband(T value, T deadband, T maxMagnitude = T{1.0}) {
33 T magnitude;
34 if constexpr (std::is_arithmetic_v<T>) {
35 magnitude = gcem::abs(value);
36 } else {
37 magnitude = units::math::abs(value);
38 }
39
40 if (magnitude > deadband) {
41 if (maxMagnitude / deadband > 1.0E12) {
42 // If max magnitude is sufficiently large, the implementation encounters
43 // roundoff error. Implementing the limiting behavior directly avoids
44 // the problem.
45 return value > T{0.0} ? value - deadband : value + deadband;
46 }
47 if (value > T{0.0}) {
48 // Map deadband to 0 and map max to max.
49 //
50 // y - y₁ = m(x - x₁)
51 // y - y₁ = (y₂ - y₁)/(x₂ - x₁) (x - x₁)
52 // y = (y₂ - y₁)/(x₂ - x₁) (x - x₁) + y₁
53 //
54 // (x₁, y₁) = (deadband, 0) and (x₂, y₂) = (max, max).
55 // x₁ = deadband
56 // y₁ = 0
57 // x₂ = max
58 // y₂ = max
59 //
60 // y = (max - 0)/(max - deadband) (x - deadband) + 0
61 // y = max/(max - deadband) (x - deadband)
62 // y = max (x - deadband)/(max - deadband)
63 return maxMagnitude * (value - deadband) / (maxMagnitude - deadband);
64 } else {
65 // Map -deadband to 0 and map -max to -max.
66 //
67 // y - y₁ = m(x - x₁)
68 // y - y₁ = (y₂ - y₁)/(x₂ - x₁) (x - x₁)
69 // y = (y₂ - y₁)/(x₂ - x₁) (x - x₁) + y₁
70 //
71 // (x₁, y₁) = (-deadband, 0) and (x₂, y₂) = (-max, -max).
72 // x₁ = -deadband
73 // y₁ = 0
74 // x₂ = -max
75 // y₂ = -max
76 //
77 // y = (-max - 0)/(-max + deadband) (x + deadband) + 0
78 // y = max/(max - deadband) (x + deadband)
79 // y = max (x + deadband)/(max - deadband)
80 return maxMagnitude * (value + deadband) / (maxMagnitude - deadband);
81 }
82 } else {
83 return T{0.0};
84 }
85}
86
87/**
88 * Returns modulus of input.
89 *
90 * @param input Input value to wrap.
91 * @param minimumInput The minimum value expected from the input.
92 * @param maximumInput The maximum value expected from the input.
93 */
94template <typename T>
95constexpr T InputModulus(T input, T minimumInput, T maximumInput) {
96 T modulus = maximumInput - minimumInput;
97
98 // Wrap input if it's above the maximum input
99 int numMax = (input - minimumInput) / modulus;
100 input -= numMax * modulus;
101
102 // Wrap input if it's below the minimum input
103 int numMin = (input - maximumInput) / modulus;
104 input -= numMin * modulus;
105
106 return input;
107}
108
109/**
110 * Checks if the given value matches an expected value within a certain
111 * tolerance.
112 *
113 * @param expected The expected value
114 * @param actual The actual value
115 * @param tolerance The allowed difference between the actual and the expected
116 * value
117 * @return Whether or not the actual value is within the allowed tolerance
118 */
119template <typename T>
120 requires std::is_arithmetic_v<T> || units::traits::is_unit_t_v<T>
121constexpr bool IsNear(T expected, T actual, T tolerance) {
122 if constexpr (std::is_arithmetic_v<T>) {
123 return std::abs(expected - actual) < tolerance;
124 } else {
125 return units::math::abs(expected - actual) < tolerance;
126 }
127}
128
129/**
130 * Checks if the given value matches an expected value within a certain
131 * tolerance. Supports continuous input for cases like absolute encoders.
132 *
133 * Continuous input means that the min and max value are considered to be the
134 * same point, and tolerances can be checked across them. A common example
135 * would be for absolute encoders: calling isNear(2, 359, 5, 0, 360) returns
136 * true because 359 is 1 away from 360 (which is treated as the same as 0) and
137 * 2 is 2 away from 0, adding up to an error of 3 degrees, which is within the
138 * given tolerance of 5.
139 *
140 * @param expected The expected value
141 * @param actual The actual value
142 * @param tolerance The allowed difference between the actual and the expected
143 * value
144 * @param min Smallest value before wrapping around to the largest value
145 * @param max Largest value before wrapping around to the smallest value
146 * @return Whether or not the actual value is within the allowed tolerance
147 */
148template <typename T>
149 requires std::is_arithmetic_v<T> || units::traits::is_unit_t_v<T>
150constexpr bool IsNear(T expected, T actual, T tolerance, T min, T max) {
151 T errorBound = (max - min) / 2.0;
152 T error = frc::InputModulus<T>(expected - actual, -errorBound, errorBound);
153
154 if constexpr (std::is_arithmetic_v<T>) {
155 return std::abs(error) < tolerance;
156 } else {
157 return units::math::abs(error) < tolerance;
158 }
159}
160
161/**
162 * Wraps an angle to the range -π to π radians (-180 to 180 degrees).
163 *
164 * @param angle Angle to wrap.
165 */
167constexpr units::radian_t AngleModulus(units::radian_t angle) {
169 units::radian_t{-std::numbers::pi},
170 units::radian_t{std::numbers::pi});
171}
172
173// floorDiv and floorMod algorithms taken from Java
174
175/**
176 * Returns the largest (closest to positive infinity)
177 * {@code int} value that is less than or equal to the algebraic quotient.
178 *
179 * @param x the dividend
180 * @param y the divisor
181 * @return the largest (closest to positive infinity)
182 * {@code int} value that is less than or equal to the algebraic quotient.
183 */
184constexpr std::signed_integral auto FloorDiv(std::signed_integral auto x,
185 std::signed_integral auto y) {
186 auto quot = x / y;
187 auto rem = x % y;
188 // if the signs are different and modulo not zero, round down
189 if ((x < 0) != (y < 0) && rem != 0) {
190 --quot;
191 }
192 return quot;
193}
194
195/**
196 * Returns the floor modulus of the {@code int} arguments.
197 * <p>
198 * The floor modulus is {@code r = x - (floorDiv(x, y) * y)},
199 * has the same sign as the divisor {@code y} or is zero, and
200 * is in the range of {@code -std::abs(y) < r < +std::abs(y)}.
201 *
202 * @param x the dividend
203 * @param y the divisor
204 * @return the floor modulus {@code x - (floorDiv(x, y) * y)}
205 */
206constexpr std::signed_integral auto FloorMod(std::signed_integral auto x,
207 std::signed_integral auto y) {
208 return x - FloorDiv(x, y) * y;
209}
210} // namespace frc
#define WPILIB_DLLEXPORT
Definition SymbolExports.h:36
constexpr UnitType abs(const UnitType x) noexcept
Compute absolute value.
Definition math.h:726
@ value
the parser finished reading a JSON value
Definition CAN.h:11
constexpr T ApplyDeadband(T value, T deadband, T maxMagnitude=T{1.0})
Returns 0.0 if the given value is within the specified range around zero.
Definition MathUtil.h:32
constexpr std::signed_integral auto FloorMod(std::signed_integral auto x, std::signed_integral auto y)
Returns the floor modulus of the int arguments.
Definition MathUtil.h:206
WPILIB_DLLEXPORT constexpr units::radian_t AngleModulus(units::radian_t angle)
Wraps an angle to the range -π to π radians (-180 to 180 degrees).
Definition MathUtil.h:167
constexpr bool IsNear(T expected, T actual, T tolerance)
Checks if the given value matches an expected value within a certain tolerance.
Definition MathUtil.h:121
constexpr std::signed_integral auto FloorDiv(std::signed_integral auto x, std::signed_integral auto y)
Returns the largest (closest to positive infinity) int value that is less than or equal to the algebr...
Definition MathUtil.h:184
constexpr T InputModulus(T input, T minimumInput, T maximumInput)
Returns modulus of input.
Definition MathUtil.h:95
constexpr T abs(const T x) noexcept
Compile-time absolute value function.
Definition abs.hpp:40
constexpr bool is_unit_t_v
Definition base.h:1870