Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 1 | #ifndef MOTORS_MATH_H_ |
| 2 | #define MOTORS_MATH_H_ |
| 3 | |
| 4 | #include <limits.h> |
| 5 | |
| 6 | #include <complex> |
| 7 | #include <ratio> |
| 8 | |
| 9 | // This file has some specialized math functions useful for implementing our |
| 10 | // controls in a minimal number of cycles. |
| 11 | |
| 12 | namespace frc971 { |
| 13 | namespace salsa { |
| 14 | |
| 15 | inline constexpr unsigned int Log2RoundUp(unsigned int x) { |
| 16 | return (x < 2) ? x : (1 + Log2RoundUp(x / 2)); |
| 17 | } |
| 18 | |
| 19 | template <typename T> |
| 20 | inline constexpr const T &ConstexprMax(const T &a, const T &b) { |
| 21 | return (a < b) ? b : a; |
| 22 | } |
| 23 | |
| 24 | namespace math_internal { |
| 25 | |
Brian Silverman | a6e53b4 | 2018-01-03 20:33:15 -0800 | [diff] [blame^] | 26 | constexpr uint32_t SinCosTableSize() { return 4096; } |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 27 | |
| 28 | constexpr float FloatMaxMagnitude() { return 1.0f; } |
| 29 | |
| 30 | constexpr bool IsPowerOf2(uint32_t value) { |
| 31 | return value == (1u << (Log2RoundUp(value) - 1)); |
| 32 | } |
| 33 | |
| 34 | static_assert(IsPowerOf2(SinCosTableSize()), "Tables need to be a power of 2"); |
| 35 | |
| 36 | extern float sin_int_table[SinCosTableSize()]; |
| 37 | extern float cos_int_table[SinCosTableSize()]; |
| 38 | extern float sin_float_table[SinCosTableSize() + 1]; |
| 39 | extern float cos_float_table[SinCosTableSize() + 1]; |
| 40 | |
| 41 | template <class Rotation> |
| 42 | float 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( |
| 48 | ConstexprMax<uint32_t>(Rotation::den, SinCosTableSize()) * Rotation::num < |
| 49 | UINT32_MAX, |
| 50 | "Numerator and denominator are too big"); |
| 51 | |
| 52 | // Rounding/truncating here isn't supported. |
| 53 | static_assert(Rotation::den <= SinCosTableSize(), |
| 54 | "Tables need to be bigger"); |
| 55 | |
| 56 | // Don't feel like thinking through the consequences of this not being true. |
| 57 | static_assert(Rotation::num > 0 && Rotation::den > 0, |
| 58 | "Need a positive ratio"); |
| 59 | |
| 60 | constexpr uint32_t kDenominatorRatio = SinCosTableSize() / Rotation::den; |
| 61 | |
| 62 | // These should always be true given the other constraints. |
| 63 | static_assert(kDenominatorRatio * Rotation::den == SinCosTableSize(), |
| 64 | "Math is broken"); |
| 65 | static_assert(IsPowerOf2(kDenominatorRatio), "Math is broken"); |
| 66 | |
| 67 | return table[(theta * kDenominatorRatio * Rotation::num + |
| 68 | kDenominatorRatio * Rotation::num / 2) % |
| 69 | SinCosTableSize()]; |
| 70 | } |
| 71 | |
| 72 | inline float FastTableLookupFloat(float theta, const float *table) { |
Brian Silverman | a6e53b4 | 2018-01-03 20:33:15 -0800 | [diff] [blame^] | 73 | static constexpr float kScalar = |
| 74 | (SinCosTableSize() / 2) / FloatMaxMagnitude(); |
| 75 | const int index = |
| 76 | (SinCosTableSize() / 2) + static_cast<int32_t>(theta * kScalar); |
Brian Silverman | 8d3816a | 2017-07-03 18:52:15 -0700 | [diff] [blame] | 77 | return table[index]; |
| 78 | } |
| 79 | |
| 80 | } // namespace math_internal |
| 81 | |
| 82 | // All theta arguments to the float-based functions must be in [-0.2, 0.2]. |
| 83 | |
| 84 | inline float FastSinFloat(float theta) { |
| 85 | return math_internal::FastTableLookupFloat(theta, |
| 86 | math_internal::sin_float_table); |
| 87 | } |
| 88 | |
| 89 | inline float FastCosFloat(float theta) { |
| 90 | return math_internal::FastTableLookupFloat(theta, |
| 91 | math_internal::cos_float_table); |
| 92 | } |
| 93 | |
| 94 | inline ::std::complex<float> ImaginaryExpFloat(float theta) { |
| 95 | return ::std::complex<float>(FastCosFloat(theta), FastSinFloat(theta)); |
| 96 | } |
| 97 | |
| 98 | // The integer-based sin/cos functions all have a Rotation template argument, |
| 99 | // which should be a ::std::ratio. The real argument to the trigonometric |
| 100 | // function is this ratio multiplied by the theta argument. |
| 101 | // |
| 102 | // Specifically, they return the function evaluated at |
| 103 | // (Rotation * (theta + 0.5)). |
| 104 | // |
| 105 | // All theta arguments must be in [0, Rotation::den). |
| 106 | // |
| 107 | // All denominators must be powers of 2. |
| 108 | |
| 109 | template<class Rotation> |
| 110 | float FastSinInt(uint32_t theta) { |
| 111 | return math_internal::FastTableLookupInt<Rotation>( |
| 112 | theta, math_internal::sin_int_table); |
| 113 | } |
| 114 | |
| 115 | template<class Rotation> |
| 116 | float FastCosInt(uint32_t theta) { |
| 117 | return math_internal::FastTableLookupInt<Rotation>( |
| 118 | theta, math_internal::cos_int_table); |
| 119 | } |
| 120 | |
| 121 | template<class Rotation> |
| 122 | ::std::complex<float> ImaginaryExpInt(uint32_t theta) { |
| 123 | return ::std::complex<float>(FastCosInt<Rotation>(theta), |
| 124 | FastSinInt<Rotation>(theta)); |
| 125 | } |
| 126 | |
| 127 | void MathInit(); |
| 128 | |
| 129 | } // namespace salsa |
| 130 | } // namespace frc971 |
| 131 | |
| 132 | #endif // MOTORS_MATH_H_ |