OpenKalman
Angle.hpp
Go to the documentation of this file.
1 /* This file is part of OpenKalman, a header-only C++ library for
2  * Kalman filters and other recursive filters.
3  *
4  * Copyright (c) 2018-2025 Christopher Lee Ogden <ogden@gatech.edu>
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9  */
10 
16 #ifndef OPENKALMAN_ANGLE_HPP
17 #define OPENKALMAN_ANGLE_HPP
18 
19 #include <type_traits>
20 #include <cmath>
21 #ifdef __cpp_lib_ranges
22 #include <ranges>
23 #else
25 #endif
30 #include "values/functions/internal/update_real_part.hpp"
31 #include "values/math/sin.hpp"
32 #include "values/math/cos.hpp"
33 #include "values/math/atan2.hpp"
39 #include "linear-algebra/coordinates/interfaces/coordinate_descriptor_traits.hpp"
40 
42 {
53 #ifdef __cpp_concepts
54  template<values::fixed Min = values::fixed_minus_pi<long double>, values::fixed Max = values::fixed_pi<long double>>
55  requires (values::fixed_number_of_v<Min> <= 0) and (values::fixed_number_of_v<Max> > 0) and
56  std::common_with<long double, values::number_type_of_t<Min>> and
57  std::common_with<long double, values::number_type_of_t<Max>> and
58  std::common_with<values::number_type_of_t<Min>, values::number_type_of_t<Max>>
59 #else
60 template<typename Min = values::fixed_minus_pi<long double>, typename Max = values::fixed_pi<long double>>
61 #endif
62  struct Angle
63  {
64 #ifndef __cpp_concepts
65  static_assert(values::fixed<Min>);
66  static_assert(values::fixed<Max>);
67  static_assert(values::fixed_number_of_v<Min> <= 0);
68  static_assert(values::fixed_number_of_v<Max> > 0);
69 #endif
70  };
71 
72 
74  namespace angle
75  {
77  using Radians = Angle<>;
78 
79 
82 
83 
86 
87 
90 
91 
94 
95 
96  namespace detail
97  {
98  template<typename T>
99  struct is_angle : std::false_type {};
100 
101  template<typename Min, typename Max>
102  struct is_angle<Angle<Min, Max>> : std::true_type {};
103  }
104 
105 
109  template<typename T>
110 #ifdef __cpp_concepts
111  concept angle =
112 #else
113  static constexpr bool angle =
114 #endif
116 
117  } // namespace angle
118 
119 
120 } // OpenKalman::coordinates
121 
122 
123 namespace OpenKalman::interface
124 {
125 
130  template<typename Min, typename Max>
131  struct coordinate_descriptor_traits<coordinates::Angle<Min, Max>>
132  {
133  private:
134 
136  static constexpr auto min = values::fixed_number_of_v<Min>;
137  static constexpr auto max = values::fixed_number_of_v<Max>;
138 
139 
140  public:
141 
142  static constexpr bool is_specialized = true;
143 
144 
145  static constexpr auto dimension = [](const T&) { return std::integral_constant<std::size_t, 1>{}; };
146 
147 
148  static constexpr auto stat_dimension = [](const T&) { return std::integral_constant<std::size_t, 2>{}; };
149 
150 
151  static constexpr auto is_euclidean = [](const T&) { return std::false_type{}; };
152 
153 
154  static constexpr auto hash_code = [](const T&) -> std::size_t
155  {
156 
157  constexpr auto min_float = static_cast<float>(min);
158  constexpr auto max_float = static_cast<float>(max);
159  constexpr float a = (min_float * 3.f + max_float * 2.f) / (max_float - min_float);
160  constexpr auto bits = std::numeric_limits<std::size_t>::digits;
161  if constexpr (bits < 32) return 0x62BB_uz + static_cast<std::size_t>(a * a * 0x1.p2f);
162  else if constexpr (bits < 64) return 0x62BB0D37_uz + static_cast<std::size_t>(a * a * 0x1.p4f);
163  else return 0x62BB0D37A58D6F96_uz + static_cast<std::size_t>(a * a * 0x1.p8f);
164  };
165 
166  private:
167 
168  template<typename Scalar>
169  struct to_stat_collection
170  {
171  to_stat_collection() = default;
172  explicit constexpr to_stat_collection(Scalar theta) : my_theta {std::move(theta)} {};
173  constexpr auto operator()(std::size_t i) const { return i == 0 ? values::cos(my_theta) : values::sin(my_theta); };
174  private:
175  Scalar my_theta;
176  };
177 
178  public:
179 
180  /*
181  * \details Maps the angle to corresponding x and y coordinates on a unit circle.
182  * By convention, the minimum angle limit Limits<Scalar::min corresponds to the point (-1,0) in Euclidean statistical space,
183  * and the angle is scaled so that the difference between Limits<Scalar>::min and Limits<<Scalar>::max is 2&pi;,
184  * so Limits<Scalar>::max wraps back to the point (-1, 0).
185  */
186  static constexpr auto
187  to_stat_space =
188 #ifdef __cpp_concepts
189  [](const T&, const collections::collection_view auto& data_view) noexcept
190 #else
191  [](const T&, const auto& data_view) noexcept
192 #endif
193  {
196  Scalar cf {2 * numbers::pi_v<R> / (max - min)};
197  Scalar mid { R{max + min} * R{0.5}};
198  Scalar a = collections::get(data_view, std::integral_constant<std::size_t, 0>{});
199  return collections::views::generate(to_stat_collection {cf * (a - mid)}, std::integral_constant<std::size_t, 2>{});
200  };
201 
202 
203  /*
204  * \details Maps x and y coordinates on Euclidean space back to an angle.
205  */
206  static constexpr auto
207  from_stat_space =
208 #ifdef __cpp_concepts
209  [](const T&, const collections::collection_view auto& data_view) noexcept
210 #else
211  [](const T&, const auto& data_view) noexcept
212 #endif
213  {
216  Scalar cf {2 * numbers::pi_v<R> / (max - min)};
217  Scalar mid { R{max + min} * R{0.5}};
218  Scalar x = collections::get(data_view, std::integral_constant<std::size_t, 0>{});
219  Scalar y = collections::get(data_view, std::integral_constant<std::size_t, 1>{});
220 #ifdef __cpp_lib_ranges
221  return std::views::single(values::atan2(y, x) / cf + mid);
222 #else
223  return ranges::views::single(values::atan2(y, x) / cf + mid);
224 #endif
225  };
226 
227 
228  private:
229 
230 #ifdef __cpp_concepts
231  static constexpr auto
232  wrap_impl(auto&& a) -> std::decay_t<decltype(a)>
233 #else
234  template<typename Scalar>
235  static constexpr std::decay_t<Scalar>
236  wrap_impl(Scalar&& a)
237 #endif
238  {
239  auto ap = values::real(a);
240  if (ap >= min and ap < max)
241  {
242  return std::forward<decltype(a)>(a);
243  }
244  else
245  {
246  using R = std::decay_t<decltype(ap)>;
247  constexpr R period {max - min};
248  using std::fmod;
249  R ar {fmod(ap - R{min}, period)};
250  if (ar < 0) return values::internal::update_real_part(std::forward<decltype(a)>(a), R{min} + ar + period);
251  else return values::internal::update_real_part(std::forward<decltype(a)>(a), R{min} + ar);
252  }
253  }
254 
255  public:
256 
257  /*
258  * \brief Return a collection_view that can get and update an angle wrapped into the primary range.
259  */
260  static constexpr auto
261  wrap =
262 #ifdef __cpp_concepts
263  [](const T&, collections::collection_view auto&& data_view) noexcept
264 #else
265  [](const T&, auto&& data_view) noexcept
266 #endif
267  {
268  using D = decltype(data_view);
270 #ifdef __cpp_lib_ranges
271  if constexpr (std::ranges::output_range<D, Scalar>)
272 #else
273  if constexpr (ranges::output_range<D, Scalar>)
274 #endif
275  {
276  Scalar& a = collections::get(data_view, std::integral_constant<std::size_t, 0>{});
277  }
278 
279 
280 
281  using V = decltype(data_view);
282  auto wrap_get = [](V& v, auto i) { return wrap_impl(collections::get(v, std::move(i))); };
283  auto wrap_set = [](V& v, auto i, auto x) -> auto&
284  {
285  auto& ret = collections::get(v, std::move(i));
286  ret = wrap_impl(std::move(x));
287  return ret;
288  };
289  return std::forward<decltype(data_view)>(data_view) | collections::views::update(wrap_get, wrap_set);
290  };
291 
292  };
293 
294 
295 } // namespace OpenKalman::interface
296 
297 
298 #endif //OPENKALMAN_ANGLE_HPP
Definition: basics.hpp:41
decltype(auto) constexpr get(Arg &&arg, I i)
A generalization of std::get.
Definition: get.hpp:62
A fixed version of 2*pi.
Definition: fixed-constants.hpp:73
typename common_collection_type< T >::type common_collection_type_t
Helper template for common_collection_type.
Definition: common_collection_type.hpp:54
Definition: tuple_reverse.hpp:103
Definition for collections::generate_view and collections::views::generate.
constexpr auto sin(const Arg &arg)
Constexpr alternative to the std::sin function.
Definition: sin.hpp:43
constexpr auto cos(const Arg &arg, std::enable_if_t< values::value< Arg >, int >=0)
Constexpr alternative to the std::cos function.
Definition: cos.hpp:43
Definition for collections::collection_view.
Definition: compares_with.hpp:28
constexpr auto fixed_number_of_v
Helper template for fixed_number_of.
Definition: fixed_number_of.hpp:84
Definitions relating to the availability of c++ language features.
constexpr auto atan2(const Y &y_arg, const X &x_arg)
Constexpr alternative to the std::atan2 function.
Definition: atan2.hpp:46
Definition for collections::update_view and collections::views::update.
constexpr auto real(Arg arg)
A constexpr function to obtain the real part of a (complex) number.
Definition: real.hpp:40
constexpr bool collection_view
A view to a collection which is also a std::ranges:view.
Definition: collection_view.hpp:36
Traits for coordinates::pattern objects.
Definition: coordinate_descriptor_traits.hpp:41
constexpr detail::generate_adaptor generate
a collection_view generator associated with generate_view.
Definition: generate.hpp:416
Definition: Fixed.hpp:36
Definitions implementing features of the c++ ranges library for compatibility.
std::decay_t< decltype(values::to_number(std::declval< T >()))> number_type_of_t
Obtain the values::number type associated with avalues::value.
Definition: number_type_of_t.hpp:34
Definition for ::fixed.
An angle or any other simple modular value.
Definition: Angle.hpp:62
Definition for collections::common_collection_type.
std::decay_t< decltype(values::real(std::declval< T >()))> real_type_of_t
Obtain the real type associated with a number (typically a values::complex number.
Definition: real_type_of_t.hpp:35
Definition for ::value.
constexpr detail::update_adaptor update
a std::ranges::range_adaptor_closure associated with update_view.
Definition: update.hpp:403