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-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_POLAR_HPP
17 #define OPENKALMAN_POLAR_HPP
18 
19 #include <type_traits>
20 #include <cmath>
21 #include <array>
23 #include "coordinates/interfaces/coordinate_descriptor_traits.hpp"
25 #include "Distance.hpp"
26 #include "Angle.hpp"
27 #include "Any.hpp"
28 
29 
31 {
41  template<typename C1 = Distance, typename C2 = angle::Radians>
42 #ifdef __cpp_concepts
43  requires (std::same_as<C1, Distance> and angle::angle<C2>) or (std::same_as<C2, Distance> and angle::angle<C1>)
44 #endif
45  struct Polar
46  {
47 #ifndef __cpp_concepts
48  static_assert((std::is_same_v<C1, Distance> and angle::angle<C2>) or (std::is_same_v<C2, Distance> and angle::angle<C1>));
49 #endif
50  };
51 
52 }
53 
54 
55 namespace OpenKalman::interface
56 {
57  namespace detail
58  {
59  // Implementation of polar coordinates.
60  template<typename T, typename Min, typename Max,
61  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>
62  struct PolarBase
63  {
64  private:
65 
66  static constexpr auto min = values::fixed_value_of_v<Min>;
67  static constexpr auto max = values::fixed_value_of_v<Max>;
68 
69  public:
70 
71  static constexpr bool is_specialized = true;
72 
73 
74  static constexpr auto dimension = [](const T&) { return std::integral_constant<std::size_t, 2>{}; };
75 
76 
77  static constexpr auto stat_dimension = [](const T&) { return std::integral_constant<std::size_t, 3>{}; };
78 
79 
80  static constexpr auto is_euclidean = [](const T&) { return std::false_type{}; };
81 
82 
83  static constexpr auto hash_code = [](const T&)
84  {
85  constexpr auto a = coordinates::internal::get_descriptor_hash_code(coordinates::Angle<Min, Max>{});
86  constexpr auto bits = std::numeric_limits<std::size_t>::digits;
87  if constexpr (bits < 32)
88  return std::integral_constant<std::size_t, a - 0x97C1_uz + d_i * 0x1000_uz>{};
89  else if constexpr (bits < 64)
90  return std::integral_constant<std::size_t, a - 0x97C195FE_uz + d_i * 0x10000000_uz>{};
91  else
92  return std::integral_constant<std::size_t, a - 0x97C195FEC488C0BC_uz + d_i * 0x1000000000000000_uz>{};
93  };
94 
95  private:
96 
97  struct flip_axis
98  {
99  template<typename R>
100  constexpr auto operator()(bool flip_, R a_) const { return flip_ ? -std::move(a_) : std::move(a_); }
101  };
102 
103 
104  template<typename...Args>
105  static constexpr auto make_range(Args&&...args)
106  {
107  if constexpr ((... or values::fixed<Args>))
108  {
109  return std::tuple {std::forward<Args>(args)...};
110  }
111  else
112  {
113  using C = std::common_type_t<Args...>;
114  return std::array {static_cast<C>(std::forward<Args>(args))...};
115  }
116  }
117 
118  public:
119 
125  static constexpr auto
126  to_stat_space = [](const T&, auto&& data_view)
127  {
128  decltype(auto) d = collections::get<d_i>(std::forward<decltype(data_view)>(data_view));
129  decltype(auto) a = collections::get<a_i>(std::forward<decltype(data_view)>(data_view));
131  auto phi = [](auto&& a)
132  {
133  if constexpr (min == -stdex::numbers::pi_v<R> and max == stdex::numbers::pi_v<R>) //< Avoid scaling, if possible.
134  {
135  return std::forward<decltype(a)>(a);
136  }
137  else
138  {
139  constexpr auto period = values::cast_to<R>(values::operation(std::minus{}, Max{}, Min{}));
140  constexpr auto scale = values::operation(std::divides{}, values::fixed_2pi<R>{}, period);
141  return values::operation(std::multiplies{}, std::forward<decltype(a)>(a), scale);
142  }
143  }(std::forward<decltype(a)>(a));
144 
145  auto x = values::cos(phi);
146  auto y = values::sin(phi);
147  auto d_real = values::real(d);
148  auto flip = values::signbit(d_real);
149  auto d_flip = values::internal::update_real_part(std::forward<decltype(d)>(d), values::abs(d_real));
150  auto x_flip = values::internal::update_real_part(std::move(x), values::operation(flip_axis{}, flip, values::real(x)));
151  auto y_flip = values::internal::update_real_part(std::move(y), values::operation(flip_axis{}, flip, values::real(y)));
152  if constexpr (d2_i == 0)
153  return make_range(std::move(d_flip), std::move(x_flip), std::move(y_flip));
154  else
155  return make_range(std::move(x_flip), std::move(y_flip), std::move(d_flip));
156  };
157 
158  private:
159 
160  struct wrap_phi
161  {
162  template<typename R>
163  constexpr R operator()(const R& phi_real) const
164  {
165  constexpr R period = max - min;
166  if (phi_real < R{min}) return phi_real + period;
167  if (phi_real >= R{max}) return phi_real - period;
168  return phi_real;
169  }
170  };
171 
172 
173  template<typename D, typename Phi>
174  static constexpr auto make_ordered_range(D d, Phi phi)
175  {
176  if constexpr (d_i == 0)
177  return make_range(std::move(d), std::move(phi));
178  else
179  return make_range(std::move(phi), std::move(d));
180  }
181 
182  public:
183 
190  static constexpr auto
191  from_stat_space = [](const T&, auto&& data_view)
192  {
193  decltype(auto) d = collections::get<d2_i>(std::forward<decltype(data_view)>(data_view));
194  decltype(auto) x = collections::get<x_i>(std::forward<decltype(data_view)>(data_view));
195  decltype(auto) y = collections::get<y_i>(std::forward<decltype(data_view)>(data_view));
197  if constexpr (min == -stdex::numbers::pi_v<R> and max == stdex::numbers::pi_v<R>) //< Avoid scaling, if possible.
198  {
199  return make_ordered_range(std::forward<decltype(d)>(d), values::atan2(std::forward<decltype(y)>(y), std::forward<decltype(x)>(x)));
200  }
201  else
202  {
203  constexpr auto period = values::cast_to<R>(values::operation(std::minus{}, Max{}, Min{}));
204  constexpr auto scale = values::operation(std::divides{}, period, values::fixed_2pi<R>{});
205  auto phi = values::operation(std::multiplies{}, values::atan2(std::forward<decltype(y)>(y), std::forward<decltype(x)>(x)), scale);
206  return make_ordered_range(std::forward<decltype(d)>(d),
207  values::internal::update_real_part(std::move(phi), values::operation(wrap_phi{}, values::real(phi))));
208  }
209  };
210 
211  private:
212 
213  struct wrap_phi_mod
214  {
215  template<typename R>
216  constexpr R operator()(const R& d_r, const R& phi_r) const
217  {
218  constexpr R period = max - min;
219  R phi_flip_real = values::signbit(d_r) ? phi_r + period * R{0.5} : phi_r;
220  if (phi_flip_real >= R{min} and phi_flip_real < max) return phi_flip_real;
221  R phi_real_mod = values::fmod(phi_flip_real - R{min}, period);
222  if (phi_real_mod < 0) return R{min} + phi_real_mod + period;
223  return R{min} + phi_real_mod;
224  }
225  };
226 
227  public:
228 
233  static constexpr auto
234  wrap = [](const T&, auto&& data_view)
235  {
236  decltype(auto) d = collections::get<d_i>(std::forward<decltype(data_view)>(data_view));
237  decltype(auto) phi = collections::get<a_i>(std::forward<decltype(data_view)>(data_view));
238 
239  auto d_real = values::real(values::real(d));
240  auto phi_real = values::real(values::real(phi));
241 
242  return make_ordered_range(
243  values::internal::update_real_part(std::forward<decltype(d)>(d), values::abs(d_real)),
244  values::internal::update_real_part(std::forward<decltype(phi)>(phi), values::operation(wrap_phi_mod{}, std::move(d_real), std::move(phi_real))));
245  };
246 
247  };
248 
249  }
250 
251 
256  template<typename Min, typename Max>
257  struct coordinate_descriptor_traits<coordinates::Polar<coordinates::Distance, coordinates::Angle<Min, Max>>>
258  : detail::PolarBase<coordinates::Polar<coordinates::Distance, coordinates::Angle<Min, Max>>, Min, Max, 0, 1, 0, 1, 2>
259  {};
260 
261 
266  template<typename Min, typename Max>
267  struct coordinate_descriptor_traits<coordinates::Polar<coordinates::Angle<Min, Max>, coordinates::Distance>>
268  : detail::PolarBase<coordinates::Polar<coordinates::Angle<Min, Max>, coordinates::Distance>, Min, Max, 1, 0, 2, 0, 1>
269  {};
270 
271 }
272 
273 
274 namespace std
275 {
276  template<typename Min1, typename Max1, typename Min2, typename Max2>
277  struct common_type<
278  OpenKalman::coordinates::Polar<OpenKalman::coordinates::Distance, OpenKalman::coordinates::Angle<Min1, Max1>>,
279  OpenKalman::coordinates::Polar<OpenKalman::coordinates::Distance, OpenKalman::coordinates::Angle<Min2, Max2>>>
280  : std::conditional<
281  OpenKalman::values::fixed_value_of_v<Min1> == OpenKalman::values::fixed_value_of_v<Min2> and
282  OpenKalman::values::fixed_value_of_v<Max1> == OpenKalman::values::fixed_value_of_v<Max2>,
283  OpenKalman::coordinates::Polar<OpenKalman::coordinates::Distance, OpenKalman::coordinates::Angle<Min1, Max1>>,
284  OpenKalman::coordinates::Any<>> {};
285 
286 
287  template<typename Min1, typename Max1, typename Min2, typename Max2>
288  struct common_type<
289  OpenKalman::coordinates::Polar<OpenKalman::coordinates::Angle<Min1, Max1>, OpenKalman::coordinates::Distance>,
290  OpenKalman::coordinates::Polar<OpenKalman::coordinates::Angle<Min2, Max2>, OpenKalman::coordinates::Distance>>
291  : std::conditional<
292  OpenKalman::values::fixed_value_of_v<Min1> == OpenKalman::values::fixed_value_of_v<Min2> and
293  OpenKalman::values::fixed_value_of_v<Max1> == OpenKalman::values::fixed_value_of_v<Max2>,
294  OpenKalman::coordinates::Polar<OpenKalman::coordinates::Angle<Min1, Max1>, OpenKalman::coordinates::Distance>,
295  OpenKalman::coordinates::Any<>> {};
296 
297 
298  template<typename C1, typename C2, typename Scalar>
299  struct common_type<OpenKalman::coordinates::Polar<C1, C2>, OpenKalman::coordinates::Any<Scalar>>
300  : common_type<OpenKalman::coordinates::Any<Scalar>, OpenKalman::coordinates::Polar<C1, C2>> {};
301 
302 
303  template<typename C1, typename C2, typename T>
304  struct common_type<OpenKalman::coordinates::Polar<C1, C2>, T>
305  : std::conditional_t<
306  OpenKalman::coordinates::descriptor<T>,
307  OpenKalman::stdex::type_identity<OpenKalman::coordinates::Any<>>,
308  std::monostate> {};
309 }
310 
311 
312 #endif
constexpr auto fmod(const X &x, const Y &y)
A constexpr function for fmod.
Definition: fmod.hpp:44
Definition of the Distance class.
Definition: basics.hpp:41
decltype(auto) constexpr to_stat_space(const T &t, R &&data_view)
Maps a range reflecting vector-space data to a corresponding range in a vector space for directional ...
Definition: to_stat_space.hpp:44
A fixed version of 2*pi.
Definition: fixed-constants.hpp:73
typename real_type_of< T >::type real_type_of_t
Helper template for real_type_of.
Definition: real_type_of.hpp:55
decltype(auto) constexpr wrap(const T &t, R &&data_view)
wraps a range reflecting vector-space data to its primary range.
Definition: wrap.hpp:59
constexpr auto cos(const Arg &arg)
Constexpr alternative to the std::cos function.
Definition: cos.hpp:42
constexpr auto sin(const Arg &arg)
Constexpr alternative to the std::sin function.
Definition: sin.hpp:43
The root namespace for OpenKalman.
Definition: basics.hpp:34
The namespace for features relating to coordinates::pattern object.
Definition: compares_with.hpp:25
Definition for get_descriptor_hash_code.
Inclusion file for collections.
constexpr auto atan2(const Y &y_arg, const X &x_arg)
Constexpr alternative to the std::atan2 function.
Definition: atan2.hpp:46
An atomic coordinates::descriptor reflecting polar coordinates.
Definition: Polar.hpp:45
constexpr auto real(const Arg &arg)
A constexpr function to obtain the real part of a (complex) number.
Definition: real.hpp:40
Traits for coordinates::pattern objects.
Definition: coordinate_descriptor_traits.hpp:36
constexpr auto signbit(const Arg &arg)
A constexpr function analogous to std::signbit.
Definition: signbit.hpp:41
constexpr auto abs(const Arg &arg)
A constexpr alternative to std::abs.
Definition: abs.hpp:38
decltype(auto) constexpr from_stat_space(const T &t, R &&stat_data_view)
Maps a range in a vector space for directional-statistics back to a range reflecting vector-space dat...
Definition: from_stat_space.hpp:44
auto scale(M &&m, const S s)
Scale a covariance by a factor.
Definition: covariance-arithmetic.hpp:518
An angle or any other simple modular value.
Definition: Angle.hpp:46
Definition: Any.hpp:42
Definition of the Angle class and related limits.
constexpr auto operation(Operation &&op, Args &&...args)
A potentially constant-evaluated operation involving some number of values.
Definition: operation.hpp:98