WPILibC++ 2024.3.2
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 <wpi/SymbolExports.h>
11
12#include "units/angle.h"
13#include "units/base.h"
14#include "units/math.h"
15
16namespace frc {
17
18/**
19 * Returns 0.0 if the given value is within the specified range around zero. The
20 * remaining range between the deadband and the maximum magnitude is scaled from
21 * 0.0 to the maximum magnitude.
22 *
23 * @param value Value to clip.
24 * @param deadband Range around zero.
25 * @param maxMagnitude The maximum magnitude of the input (defaults to 1). Can
26 * be infinite.
27 * @return The value after the deadband is applied.
28 */
29template <typename T>
30 requires std::is_arithmetic_v<T> || units::traits::is_unit_t_v<T>
31T ApplyDeadband(T value, T deadband, T maxMagnitude = T{1.0}) {
32 T magnitude;
33 if constexpr (std::is_arithmetic_v<T>) {
34 magnitude = std::abs(value);
35 } else {
36 magnitude = units::math::abs(value);
37 }
38
39 if (magnitude > deadband) {
40 if (maxMagnitude / deadband > 1.0E12) {
41 // If max magnitude is sufficiently large, the implementation encounters
42 // roundoff error. Implementing the limiting behavior directly avoids
43 // the problem.
44 return value > T{0.0} ? value - deadband : value + deadband;
45 }
46 if (value > T{0.0}) {
47 // Map deadband to 0 and map max to max.
48 //
49 // y - y₁ = m(x - x₁)
50 // y - y₁ = (y₂ - y₁)/(x₂ - x₁) (x - x₁)
51 // y = (y₂ - y₁)/(x₂ - x₁) (x - x₁) + y₁
52 //
53 // (x₁, y₁) = (deadband, 0) and (x₂, y₂) = (max, max).
54 // x₁ = deadband
55 // y₁ = 0
56 // x₂ = max
57 // y₂ = max
58 //
59 // y = (max - 0)/(max - deadband) (x - deadband) + 0
60 // y = max/(max - deadband) (x - deadband)
61 // y = max (x - deadband)/(max - deadband)
62 return maxMagnitude * (value - deadband) / (maxMagnitude - deadband);
63 } else {
64 // Map -deadband to 0 and map -max to -max.
65 //
66 // y - y₁ = m(x - x₁)
67 // y - y₁ = (y₂ - y₁)/(x₂ - x₁) (x - x₁)
68 // y = (y₂ - y₁)/(x₂ - x₁) (x - x₁) + y₁
69 //
70 // (x₁, y₁) = (-deadband, 0) and (x₂, y₂) = (-max, -max).
71 // x₁ = -deadband
72 // y₁ = 0
73 // x₂ = -max
74 // y₂ = -max
75 //
76 // y = (-max - 0)/(-max + deadband) (x + deadband) + 0
77 // y = max/(max - deadband) (x + deadband)
78 // y = max (x + deadband)/(max - deadband)
79 return maxMagnitude * (value + deadband) / (maxMagnitude - deadband);
80 }
81 } else {
82 return T{0.0};
83 }
84}
85
86/**
87 * Returns modulus of input.
88 *
89 * @param input Input value to wrap.
90 * @param minimumInput The minimum value expected from the input.
91 * @param maximumInput The maximum value expected from the input.
92 */
93template <typename T>
94constexpr T InputModulus(T input, T minimumInput, T maximumInput) {
95 T modulus = maximumInput - minimumInput;
96
97 // Wrap input if it's above the maximum input
98 int numMax = (input - minimumInput) / modulus;
99 input -= numMax * modulus;
100
101 // Wrap input if it's below the minimum input
102 int numMin = (input - maximumInput) / modulus;
103 input -= numMin * modulus;
104
105 return input;
106}
107
108/**
109 * Checks if the given value matches an expected value within a certain
110 * tolerance.
111 *
112 * @param expected The expected value
113 * @param actual The actual value
114 * @param tolerance The allowed difference between the actual and the expected
115 * value
116 * @return Whether or not the actual value is within the allowed tolerance
117 */
118template <typename T>
119 requires std::is_arithmetic_v<T> || units::traits::is_unit_t_v<T>
120constexpr bool IsNear(T expected, T actual, T tolerance) {
121 if constexpr (std::is_arithmetic_v<T>) {
122 return std::abs(expected - actual) < tolerance;
123 } else {
124 return units::math::abs(expected - actual) < tolerance;
125 }
126}
127
128/**
129 * Checks if the given value matches an expected value within a certain
130 * tolerance. Supports continuous input for cases like absolute encoders.
131 *
132 * Continuous input means that the min and max value are considered to be the
133 * same point, and tolerances can be checked across them. A common example
134 * would be for absolute encoders: calling isNear(2, 359, 5, 0, 360) returns
135 * true because 359 is 1 away from 360 (which is treated as the same as 0) and
136 * 2 is 2 away from 0, adding up to an error of 3 degrees, which is within the
137 * given tolerance of 5.
138 *
139 * @param expected The expected value
140 * @param actual The actual value
141 * @param tolerance The allowed difference between the actual and the expected
142 * value
143 * @param min Smallest value before wrapping around to the largest value
144 * @param max Largest value before wrapping around to the smallest value
145 * @return Whether or not the actual value is within the allowed tolerance
146 */
147template <typename T>
148 requires std::is_arithmetic_v<T> || units::traits::is_unit_t_v<T>
149constexpr bool IsNear(T expected, T actual, T tolerance, T min, T max) {
150 T errorBound = (max - min) / 2.0;
151 T error = frc::InputModulus<T>(expected - actual, -errorBound, errorBound);
152
153 if constexpr (std::is_arithmetic_v<T>) {
154 return std::abs(error) < tolerance;
155 } else {
156 return units::math::abs(error) < tolerance;
157 }
158}
159
160/**
161 * Wraps an angle to the range -pi to pi radians (-180 to 180 degrees).
162 *
163 * @param angle Angle to wrap.
164 */
166constexpr units::radian_t AngleModulus(units::radian_t angle) {
167 return InputModulus<units::radian_t>(angle,
168 units::radian_t{-std::numbers::pi},
169 units::radian_t{std::numbers::pi});
170}
171
172} // namespace frc
#define WPILIB_DLLEXPORT
Definition: SymbolExports.h:36
UnitType abs(const UnitType x) noexcept
Compute absolute value.
Definition: math.h:721
@ value
the parser finished reading a JSON value
Definition: AprilTagPoseEstimator.h:15
WPILIB_DLLEXPORT constexpr units::radian_t AngleModulus(units::radian_t angle)
Wraps an angle to the range -pi to pi radians (-180 to 180 degrees).
Definition: MathUtil.h:166
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:120
constexpr T InputModulus(T input, T minimumInput, T maximumInput)
Returns modulus of input.
Definition: MathUtil.h:94
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:31
static constexpr const unit_t< PI > pi(1)
Ratio of a circle's circumference to its diameter.
UnitTypeLhs() max(const UnitTypeLhs &lhs, const UnitTypeRhs &rhs)
Definition: base.h:3417
UnitTypeLhs() min(const UnitTypeLhs &lhs, const UnitTypeRhs &rhs)
Definition: base.h:3409