OpenKalman
Polar.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) 2019-2024 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_POLAR_HPP
17 #define OPENKALMAN_POLAR_HPP
18 
19 #include <type_traits>
20 #include <typeindex>
21 #include <cmath>
22 #include <array>
23 #include "../../../basics/compatibility/language-features.hpp"
26 #include "values/functions/internal/update_real_part.hpp"
27 #include "values/math/signbit.hpp"
28 #include "values/math/abs.hpp"
29 #include "values/math/cos.hpp"
30 #include "values/math/sin.hpp"
31 #include "values/math/atan2.hpp"
32 #include "linear-algebra/coordinates/interfaces/coordinate_descriptor_traits.hpp"
34 #include "Distance.hpp"
35 #include "Angle.hpp"
36 
37 
39 {
50  template<typename C1 = Distance, typename C2 = angle::Radians>
51 #ifdef __cpp_concepts
52  requires (std::same_as<C1, Distance> and angle::angle<C2>) or (std::same_as<C2, Distance> and angle::angle<C1>)
53 #endif
54  struct Polar
55  {
56 #ifndef __cpp_concepts
57  static_assert((std::is_same_v<C1, Distance> and angle::angle<C2>) or (std::is_same_v<C2, Distance> and angle::angle<C1>));
58 #endif
59  };
60 
61 } // namespace OpenKalman::coordinates
62 
63 
64 namespace OpenKalman::interface
65 {
66  namespace detail
67  {
68  // Implementation of polar coordinates.
69  template<typename T, typename Min, typename Max,
70  std::size_t d_i, std::size_t a_i, std::size_t d2_i, std::size_t x_i, std::size_t y_i>
71  struct PolarBase
72  {
73  private:
74 
75  static constexpr auto min = values::fixed_number_of_v<Min>;
76  static constexpr auto max = values::fixed_number_of_v<Max>;
77 
78  public:
79 
80  static constexpr bool is_specialized = true;
81 
82 
83  static constexpr auto
84  dimension(const T&) { return std::integral_constant<std::size_t, 2>{}; };
85 
86 
87  static constexpr auto
88  stat_dimension(const T&) { return std::integral_constant<std::size_t, 3>{}; };
89 
90 
91  static constexpr auto
92  is_euclidean(const T&) { return std::false_type{}; }
93 
94 
95  static constexpr std::size_t
96  hash_code(const T&)
97  {
98  constexpr auto a = coordinates::internal::get_hash_code(coordinates::Angle<Min, Max>{});
99  constexpr auto bits = std::numeric_limits<std::size_t>::digits;
100  if constexpr (bits < 32) return a - 0x97C1_uz + d_i * 0x1000_uz;
101  else if constexpr (bits < 64) return a - 0x97C195FE_uz + d_i * 0x10000000_uz;
102  else return a - 0x97C195FEC488C0BC_uz + d_i * 0x1000000000000000_uz;
103  }
104 
105 
114 #ifdef __cpp_concepts
115  static constexpr values::value auto
116  to_euclidean_component(const T& t, const auto& g, const values::index auto& euclidean_local_index)
117  requires requires(std::size_t i){ {g(i)} -> values::value; }
118 #else
119  template<typename Getter, typename L, std::enable_if_t<values::index<L> and
120  values::value<typename std::invoke_result<const Getter&, std::size_t>::type>, int> = 0>
121  static constexpr auto
122  to_euclidean_component(const T& t, const Getter& g, const L& euclidean_local_index)
123 #endif
124  {
125  using Scalar = std::decay_t<decltype(g(std::declval<std::size_t>()))>;
126  Scalar d = g(d_i);
127  if (euclidean_local_index == d2_i)
128  {
129  return d;
130  }
131  else
132  {
133  using R = std::decay_t<decltype(values::real(std::declval<Scalar>()))>;
134  const Scalar cf {2 * numbers::pi_v<R> / (max - min)};
135  const Scalar mid {R{max + min} * R{0.5}};
136 
137  Scalar theta = cf * g(a_i) - mid;
138  switch(euclidean_local_index)
139  {
140  case x_i: return values::cos(theta);
141  default: return values::sin(theta); // case y_i
142  }
143  }
144  }
145 
146 
155 #ifdef __cpp_concepts
156  static constexpr values::value auto
157  from_euclidean_component(const T& t, const auto& g, const values::index auto& local_index)
158  requires requires(std::size_t i){ {g(i)} -> values::value; }
159 #else
160  template<typename Getter, typename L, std::enable_if_t<values::index<L> and
161  values::value<typename std::invoke_result<const Getter&, std::size_t>::type>, int> = 0>
162  static constexpr auto
163  from_euclidean_component(const T& t, const Getter& g, const L& local_index)
164  #endif
165  {
166  using Scalar = std::decay_t<decltype(g(std::declval<std::size_t>()))>;
167  Scalar d = g(d2_i);
168  auto dr = values::real(d);
169  if (local_index == d_i)
170  {
171  // A negative distance is reflected to the positive axis.
172  return values::internal::update_real_part(d, values::abs(dr));
173  }
174  else
175  {
176  using R = std::decay_t<decltype(values::real(std::declval<Scalar>()))>;
177  const Scalar cf {2 * numbers::pi_v<R> / (max - min)};
178  const Scalar mid {R{max + min} * R{0.5}};
179 
180  // If distance is negative, flip 180 degrees:
181  Scalar x = values::signbit(dr) ? -g(x_i) : g(x_i);
182  Scalar y = values::signbit(dr) ? -g(y_i) : g(y_i);
183 
184  if constexpr (values::complex<Scalar>) return values::atan2(y, x) / cf + mid;
185  else { return values::atan2(y, x) / cf + mid; }
186  }
187  }
188 
189  private:
190 
191 #ifdef __cpp_concepts
192  static constexpr auto
193  polar_angle_wrap_impl(bool distance_is_negative, auto&& a) -> std::decay_t<decltype(a)>
194 #else
195  template<typename Scalar>
196  static constexpr std::decay_t<Scalar>
197  polar_angle_wrap_impl(bool distance_is_negative, Scalar&& a)
198 #endif
199  {
200  using R = std::decay_t<decltype(values::real(std::declval<decltype(a)>()))>;
201  constexpr R period {max - min};
202  R ap {distance_is_negative ? values::real(a) + period * R{0.5} : values::real(a)};
203 
204  if (ap >= min and ap < max) // Check if the angle doesn't need wrapping.
205  {
206  return values::internal::update_real_part(std::forward<decltype(a)>(a), ap);;
207  }
208  else // Wrap the angle.
209  {
210  using std::fmod;
211  auto ar = fmod(ap - R{min}, period);
212  if (ar < 0) return values::internal::update_real_part(std::forward<decltype(a)>(a), R{min} + ar + period);
213  else return values::internal::update_real_part(std::forward<decltype(a)>(a), R{min} + ar);
214  }
215  }
216 
217  public:
218 
226 #ifdef __cpp_concepts
227  static constexpr values::value auto
228  get_wrapped_component(const T& t, const auto& g, const values::index auto& local_index)
229  requires requires(std::size_t i){ {g(i)} -> values::value; }
230 #else
231  template<typename Getter, typename L, std::enable_if_t<values::index<L> and
232  values::value<typename std::invoke_result<const Getter&, std::size_t>::type>, int> = 0>
233  static constexpr auto
234  get_wrapped_component(const T& t, const Getter& g, const L& local_index)
235 #endif
236  {
237  auto d = g(d_i);
238  switch(local_index)
239  {
240  case d_i: return values::internal::update_real_part(d, values::abs(values::real(d)));
241  default: return polar_angle_wrap_impl(values::signbit(values::real(d)), g(a_i)); // case a_i
242  }
243  }
244 
245 
255 #ifdef __cpp_concepts
256  static constexpr void
257  set_wrapped_component(const T& t, const auto& s, const auto& g, const values::value auto& x, const values::index auto& local_index)
258  requires requires(std::size_t i){ s(x, i); s(g(i), i); }
259 #else
260  template<typename Setter, typename Getter, typename X, typename L, std::enable_if_t<values::value<X> and values::index<L> and
261  std::is_invocable<const Setter&, const X&, std::size_t>::value and
262  std::is_invocable<const Setter&, typename std::invoke_result<const Getter&, std::size_t>::type, std::size_t>::value, int> = 0>
263  static constexpr void
264  set_wrapped_component(const T& t, const Setter& s, const Getter& g, const X& x, const L& local_index)
265 #endif
266  {
267  switch(local_index)
268  {
269  case d_i:
270  {
271  auto xp = values::real(x);
272  s(values::internal::update_real_part(x, values::abs(xp)), d_i);
273  s(polar_angle_wrap_impl(values::signbit(xp), g(a_i)), a_i); //< Possibly reflect angle
274  break;
275  }
276  default: // case a_i
277  {
278  s(polar_angle_wrap_impl(false, x), a_i);
279  break;
280  }
281  }
282  }
283 
284  };
285 
286  } // namespace detail
287 
288 
293  template<typename Min, typename Max>
294  struct coordinate_descriptor_traits<coordinates::Polar<coordinates::Distance, coordinates::Angle<Min, Max>>>
295  : detail::PolarBase<coordinates::Polar<coordinates::Distance, coordinates::Angle<Min, Max>>, Min, Max, 0, 1, 0, 1, 2>
296  {};
297 
298 
303  template<typename Min, typename Max>
304  struct coordinate_descriptor_traits<coordinates::Polar<coordinates::Angle<Min, Max>, coordinates::Distance>>
305  : detail::PolarBase<coordinates::Polar<coordinates::Angle<Min, Max>, coordinates::Distance>, Min, Max, 1, 0, 2, 0, 1>
306  {};
307 
308 
309 }// namespace OpenKalman::interface
310 
311 #endif //OPENKALMAN_POLAR_HPP
Definition for get_hash_code.
Definition of the Distance class.
Definition: basics.hpp:41
static constexpr void set_wrapped_component(const T &t, const Setter &s, const Getter &g, const X &x, const L &local_index)
Set an element and then perform any necessary modular wrapping.
Definition: Polar.hpp:264
static constexpr auto from_euclidean_component(const T &t, const Getter &g, const L &local_index)
Maps a coordinate in Euclidean space to an element.
Definition: Polar.hpp:163
constexpr bool value
T is numerical value or is reducible to a numerical value.
Definition: value.hpp:31
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: compares_with.hpp:28
constexpr auto atan2(const Y &y_arg, const X &x_arg)
Constexpr alternative to the std::atan2 function.
Definition: atan2.hpp:46
constexpr bool signbit(const Arg &arg)
A constexpr function analogous to std::signbit.
Definition: signbit.hpp:39
constexpr auto real(Arg arg)
A constexpr function to obtain the real part of a (complex) number.
Definition: real.hpp:40
An atomic coordinates::descriptor reflecting polar coordinates.
Definition: Polar.hpp:54
Traits for coordinates::pattern objects.
Definition: coordinate_descriptor_traits.hpp:41
static constexpr auto get_wrapped_component(const T &t, const Getter &g, const L &local_index)
Perform modular wrapping of polar coordinates.
Definition: Polar.hpp:234
constexpr auto abs(const Arg &arg)
A constexpr alternative to std::abs.
Definition: abs.hpp:38
static constexpr auto to_euclidean_component(const T &t, const Getter &g, const L &euclidean_local_index)
Maps a polar coordinate to coordinates in Euclidean space.
Definition: Polar.hpp:122
constexpr bool index
T is an index value.
Definition: index.hpp:56
Definition for values::number.
An angle or any other simple modular value.
Definition: Angle.hpp:62
Definition of the Angle class and related limits.
Definition for ::complex.