blob: d7bbee43dc0ef088cc98a087ce2cd99977acbfbf [file] [log] [blame]
Brian Silverman8d3816a2017-07-03 18:52:15 -07001#ifndef MOTORS_MATH_H_
2#define MOTORS_MATH_H_
3
4#include <limits.h>
5
Brian Silverman9ccec6e2018-10-21 22:06:35 -07006#include <array>
Brian Silverman8d3816a2017-07-03 18:52:15 -07007#include <complex>
8#include <ratio>
9
10// This file has some specialized math functions useful for implementing our
11// controls in a minimal number of cycles.
12
13namespace frc971 {
Brian Silvermana96c1a42018-05-12 12:11:31 -070014namespace motors {
Brian Silverman8d3816a2017-07-03 18:52:15 -070015
16inline constexpr unsigned int Log2RoundUp(unsigned int x) {
17 return (x < 2) ? x : (1 + Log2RoundUp(x / 2));
18}
19
20template <typename T>
21inline constexpr const T &ConstexprMax(const T &a, const T &b) {
22 return (a < b) ? b : a;
23}
24
25namespace math_internal {
26
Brian Silverman9ccec6e2018-10-21 22:06:35 -070027constexpr uint32_t SinCosFloatTableSize() { return 2048; }
Brian Silverman8d3816a2017-07-03 18:52:15 -070028
29constexpr float FloatMaxMagnitude() { return 1.0f; }
30
31constexpr bool IsPowerOf2(uint32_t value) {
32 return value == (1u << (Log2RoundUp(value) - 1));
33}
34
Brian Silverman9ccec6e2018-10-21 22:06:35 -070035static_assert(IsPowerOf2(SinCosFloatTableSize()),
36 "Tables need to be a power of 2");
Brian Silverman8d3816a2017-07-03 18:52:15 -070037
Brian Silverman9ccec6e2018-10-21 22:06:35 -070038extern float sin_float_table[SinCosFloatTableSize() + 1];
39extern float cos_float_table[SinCosFloatTableSize() + 1];
Brian Silverman8d3816a2017-07-03 18:52:15 -070040
Brian Silverman9ccec6e2018-10-21 22:06:35 -070041template <class Rotation, int kTableSize>
Brian Silverman8d3816a2017-07-03 18:52:15 -070042float FastTableLookupInt(uint32_t theta, const float *table) {
43 static_assert(IsPowerOf2(Rotation::den),
44 "Denominator needs to be a power of 2");
45
46 // Don't need to worry about the sizes of intermediates given this constraint.
47 static_assert(
Brian Silverman9ccec6e2018-10-21 22:06:35 -070048 ConstexprMax<uint32_t>(Rotation::den, kTableSize) * Rotation::num <
Brian Silverman8d3816a2017-07-03 18:52:15 -070049 UINT32_MAX,
50 "Numerator and denominator are too big");
51
52 // Rounding/truncating here isn't supported.
Brian Silverman9ccec6e2018-10-21 22:06:35 -070053 static_assert(Rotation::den <= kTableSize, "Tables need to be bigger");
Brian Silverman8d3816a2017-07-03 18:52:15 -070054
55 // Don't feel like thinking through the consequences of this not being true.
56 static_assert(Rotation::num > 0 && Rotation::den > 0,
57 "Need a positive ratio");
58
Brian Silverman9ccec6e2018-10-21 22:06:35 -070059 constexpr uint32_t kDenominatorRatio = kTableSize / Rotation::den;
Brian Silverman8d3816a2017-07-03 18:52:15 -070060
61 // These should always be true given the other constraints.
Brian Silverman9ccec6e2018-10-21 22:06:35 -070062 static_assert(kDenominatorRatio * Rotation::den == kTableSize,
Brian Silverman8d3816a2017-07-03 18:52:15 -070063 "Math is broken");
64 static_assert(IsPowerOf2(kDenominatorRatio), "Math is broken");
65
66 return table[(theta * kDenominatorRatio * Rotation::num +
67 kDenominatorRatio * Rotation::num / 2) %
Brian Silverman9ccec6e2018-10-21 22:06:35 -070068 kTableSize];
Brian Silverman8d3816a2017-07-03 18:52:15 -070069}
70
71inline float FastTableLookupFloat(float theta, const float *table) {
Brian Silvermana6e53b42018-01-03 20:33:15 -080072 static constexpr float kScalar =
Brian Silverman9ccec6e2018-10-21 22:06:35 -070073 (SinCosFloatTableSize() / 2) / FloatMaxMagnitude();
Brian Silvermana6e53b42018-01-03 20:33:15 -080074 const int index =
Brian Silverman9ccec6e2018-10-21 22:06:35 -070075 (SinCosFloatTableSize() / 2) + static_cast<int32_t>(theta * kScalar);
Brian Silverman8d3816a2017-07-03 18:52:15 -070076 return table[index];
77}
78
Brian Silverman9ccec6e2018-10-21 22:06:35 -070079// A simple parent class for arranging to initialize all the templates.
80//
81// This will be lower overhead than ::std::function and also adds less
82// complicated stuff around static initialization time.
83class GenericInitializer {
84 public:
85 virtual void Initialize() = 0;
86
87 protected:
88 // Don't destroy instances via pointers to this type. Do it through subclass
89 // pointers instead.
90 ~GenericInitializer() = default;
91};
92
93// We build up this list at static construction time so we can call all the
94// contained objects in MathInit(). The contained pointers are not owned by this
95// list.
96//
97// This has to be big enough to hold all the different sizes of integer tables
98// somebody might use in one program. If it overflows, there will be a
99// __builtin_abort during static initialization.
100extern ::std::array<GenericInitializer *, 10> global_initializers;
101
102// Manages initializing and access to an integer table of a single size.
103template<int kTableSize>
104class SinCosIntTable {
105 public:
106 static const float *sin_int_table() {
107 // Empirically, this is enough to get GCC to actually instantiate and link
108 // in the variable, without any runtime overhead.
109 (void)add_my_initializer;
110 return &static_sin_int_table[0];
111 }
112 static const float *cos_int_table() {
113 (void)add_my_initializer;
114 return &static_cos_int_table[0];
115 }
116
117 private:
118 // An object which adds a MyInitializer to global_initializers at static
119 // construction time.
120 class AddMyInitializer {
121 public:
122 AddMyInitializer() {
123 static MyInitializer initializer;
124 for (size_t i = 0; i < global_initializers.size(); ++i) {
125 if (global_initializers[i] == nullptr) {
126 global_initializers[i] = &initializer;
127 return;
128 }
129 }
130 __builtin_trap();
131 }
132 };
133
134 class MyInitializer : public GenericInitializer {
135 void Initialize() override {
136 for (uint32_t i = 0; i < kTableSize; ++i) {
137 const double int_theta =
138 ((static_cast<double>(i) + 0.5) / kTableSize) * 2.0 * M_PI;
139 static_sin_int_table[i] = sin(int_theta);
140 static_cos_int_table[i] = cos(int_theta);
141 }
142 }
143 };
144
145 static AddMyInitializer add_my_initializer;
146
147 static float static_sin_int_table[kTableSize];
148 static float static_cos_int_table[kTableSize];
149};
150
151template <int kTableSize>
152typename SinCosIntTable<kTableSize>::AddMyInitializer
153 SinCosIntTable<kTableSize>::add_my_initializer;
154
155template <int kTableSize>
156float SinCosIntTable<kTableSize>::static_sin_int_table[kTableSize];
157template <int kTableSize>
158float SinCosIntTable<kTableSize>::static_cos_int_table[kTableSize];
159
Brian Silverman8d3816a2017-07-03 18:52:15 -0700160} // namespace math_internal
161
Brian Silverman9ccec6e2018-10-21 22:06:35 -0700162// theta must be in [-0.2, 0.2].
Brian Silverman8d3816a2017-07-03 18:52:15 -0700163inline float FastSinFloat(float theta) {
164 return math_internal::FastTableLookupFloat(theta,
165 math_internal::sin_float_table);
166}
167
Brian Silverman9ccec6e2018-10-21 22:06:35 -0700168// theta must be in [-0.2, 0.2].
Brian Silverman8d3816a2017-07-03 18:52:15 -0700169inline float FastCosFloat(float theta) {
170 return math_internal::FastTableLookupFloat(theta,
171 math_internal::cos_float_table);
172}
173
Brian Silverman9ccec6e2018-10-21 22:06:35 -0700174// theta must be in [-0.2, 0.2].
Brian Silverman8d3816a2017-07-03 18:52:15 -0700175inline ::std::complex<float> ImaginaryExpFloat(float theta) {
176 return ::std::complex<float>(FastCosFloat(theta), FastSinFloat(theta));
177}
178
179// The integer-based sin/cos functions all have a Rotation template argument,
180// which should be a ::std::ratio. The real argument to the trigonometric
181// function is this ratio multiplied by the theta argument.
182//
183// Specifically, they return the function evaluated at
184// (Rotation * (theta + 0.5)).
185//
186// All theta arguments must be in [0, Rotation::den).
187//
188// All denominators must be powers of 2.
Brian Silverman9ccec6e2018-10-21 22:06:35 -0700189//
190// kTableSize is the size table it will actually use. This must be a power of 2
191// >= Rotation::den. The default of Rotation::den makes the most sense, unless
192// multiple denominators are needed in the same program, in which case using the
193// biggest denominator for all of them will use the least memory.
Brian Silverman8d3816a2017-07-03 18:52:15 -0700194
Brian Silverman9ccec6e2018-10-21 22:06:35 -0700195template<class Rotation, int kTableSize = Rotation::den>
Brian Silverman8d3816a2017-07-03 18:52:15 -0700196float FastSinInt(uint32_t theta) {
Brian Silverman9ccec6e2018-10-21 22:06:35 -0700197 return math_internal::FastTableLookupInt<Rotation, kTableSize>(
198 theta, math_internal::SinCosIntTable<kTableSize>::sin_int_table());
Brian Silverman8d3816a2017-07-03 18:52:15 -0700199}
200
Brian Silverman9ccec6e2018-10-21 22:06:35 -0700201template <class Rotation, int kTableSize = Rotation::den>
Brian Silverman8d3816a2017-07-03 18:52:15 -0700202float FastCosInt(uint32_t theta) {
Brian Silverman9ccec6e2018-10-21 22:06:35 -0700203 return math_internal::FastTableLookupInt<Rotation, kTableSize>(
204 theta, math_internal::SinCosIntTable<kTableSize>::cos_int_table());
Brian Silverman8d3816a2017-07-03 18:52:15 -0700205}
206
207template<class Rotation>
208::std::complex<float> ImaginaryExpInt(uint32_t theta) {
209 return ::std::complex<float>(FastCosInt<Rotation>(theta),
210 FastSinInt<Rotation>(theta));
211}
212
Brian Silverman9ccec6e2018-10-21 22:06:35 -0700213// This must be called before any of the other functions.
Brian Silverman8d3816a2017-07-03 18:52:15 -0700214void MathInit();
215
Brian Silvermana96c1a42018-05-12 12:11:31 -0700216} // namespace motors
Brian Silverman8d3816a2017-07-03 18:52:15 -0700217} // namespace frc971
218
219#endif // MOTORS_MATH_H_