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 "values/functions/internal/update_real_part.hpp"
21 #include "coordinates/interfaces/coordinate_descriptor_traits.hpp"
22 #include "Any.hpp"
23 
25 {
36 #ifdef __cpp_concepts
37  template<values::fixed Min = values::fixed_minus_pi<long double>, values::fixed Max = values::fixed_pi<long double>>
38  requires (values::fixed_value_of_v<Min> <= 0) and (values::fixed_value_of_v<Max> > 0) and
39  (not values::complex<Min>) and (not values::complex<Max>) and
40  std::convertible_to<values::value_type_of_t<Min>, float> and
41  std::convertible_to<values::value_type_of_t<Max>, float> and
42  std::common_with<values::value_type_of_t<Min>, values::value_type_of_t<Max>>
43 #else
44 template<typename Min = values::fixed_minus_pi<long double>, typename Max = values::fixed_pi<long double>>
45 #endif
46  struct Angle
47  {
48 #ifndef __cpp_concepts
49  static_assert(values::fixed<Min>);
50  static_assert(values::fixed<Max>);
51  static_assert(values::fixed_value_of_v<Min> <= 0);
52  static_assert(values::fixed_value_of_v<Max> > 0);
53  static_assert(not values::complex<Min>);
54  static_assert(not values::complex<Max>);
55  static_assert(stdex::convertible_to<values::value_type_of_t<Min>, float>);
56  static_assert(stdex::convertible_to<values::value_type_of_t<Max>, float>);
57  static_assert(stdex::common_with<values::value_type_of_t<Min>, values::value_type_of_t<Max>>);
58 #endif
59  };
60 
61 
63  namespace angle
64  {
66  using Radians = Angle<>;
67 
68 
71 
72 
75 
76 
79 
80 
83 
84 
85  namespace detail
86  {
87  template<typename T>
88  struct is_angle : std::false_type {};
89 
90  template<typename Min, typename Max>
91  struct is_angle<Angle<Min, Max>> : std::true_type {};
92  }
93 
94 
98  template<typename T>
99 #ifdef __cpp_concepts
100  concept angle =
101 #else
102  static constexpr bool angle =
103 #endif
105 
106  }
107 
108 }
109 
110 
111 namespace OpenKalman::interface
112 {
117  template<typename Min, typename Max>
118  struct coordinate_descriptor_traits<coordinates::Angle<Min, Max>>
119  {
120  private:
121 
123  static constexpr auto min = values::fixed_value_of_v<Min>;
124  static constexpr auto max = values::fixed_value_of_v<Max>;
125 
126 
127  template<typename...Args>
128  static constexpr auto make_range(Args&&...args)
129  {
130  if constexpr ((... or values::fixed<Args>))
131  return std::tuple {std::forward<Args>(args)...};
132  else
133  return std::array<std::common_type_t<Args...>, sizeof...(Args)> {std::forward<Args>(args)...};
134  }
135 
136  public:
137 
138  static constexpr bool is_specialized = true;
139 
140 
141  static constexpr auto dimension = [](const T&) { return std::integral_constant<std::size_t, 1>{}; };
142 
143 
144  static constexpr auto stat_dimension = [](const T&) { return std::integral_constant<std::size_t, 2>{}; };
145 
146 
147  static constexpr auto is_euclidean = [](const T&) { return std::false_type{}; };
148 
149 
150  static constexpr auto hash_code = [](const T&)
151  {
152  constexpr auto min_float = static_cast<float>(min);
153  constexpr auto max_float = static_cast<float>(max);
154  constexpr float a = (max_float * 3.f + min_float * 2.f + 1.f) / (max_float - min_float + 1.f);
155  constexpr auto bits = std::numeric_limits<std::size_t>::digits;
156  if constexpr (bits < 32)
157  return std::integral_constant<std::size_t, 0x62BB_uz + static_cast<std::size_t>(a * a * 0x1.p2f)>{};
158  else if constexpr (bits < 64)
159  return std::integral_constant<std::size_t, 0x62BB0D37_uz + static_cast<std::size_t>(a * a * 0x1.p4f)>{};
160  else
161  return std::integral_constant<std::size_t, 0x62BB0D37A58D6F96_uz + static_cast<std::size_t>(a * a * 0x1.p8f)>{};
162  };
163 
164 
165  /*
166  * \brief Maps the angle to corresponding x and y coordinates on a unit circle.
167  */
168  static constexpr auto
169  to_stat_space = [](const T&, auto&& data_view)
170  {
171  decltype(auto) a = collections::get<0>(std::forward<decltype(data_view)>(data_view));
173  if constexpr (min == -stdex::numbers::pi_v<R> and max == stdex::numbers::pi_v<R>) //< Avoid scaling, if possible.
174  {
175  return make_range(values::cos(a), values::sin(a));
176  }
177  else
178  {
179  constexpr auto period = values::cast_to<R>(values::operation(std::minus{}, Max{}, Min{}));
180  constexpr auto scale = values::operation(std::divides{}, values::fixed_2pi<R>{}, period);
181  auto phi = values::operation(std::multiplies{}, std::forward<decltype(a)>(a), scale);
182  return make_range(values::cos(phi), values::sin(phi));
183  }
184  };
185 
186  private:
187 
188  struct wrap_phi
189  {
190  template<typename R>
191  constexpr R operator()(const R& phi_real) const
192  {
193  constexpr R period = max - min;
194  if (phi_real < R{min}) return phi_real + period;
195  if (phi_real >= R{max}) return phi_real - period;
196  return phi_real;
197  }
198  };
199 
200  public:
201 
202  /*
203  * \brief Maps x and y coordinates on Euclidean space back to an angle.
204  * \details This performs bounds checking to ensure that the angle is within the primary range.
205  */
206  static constexpr auto
207  from_stat_space = [](const T&, auto&& data_view)
208  {
209  decltype(auto) x = collections::get<0>(std::forward<decltype(data_view)>(data_view));
210  decltype(auto) y = collections::get<1>(std::forward<decltype(data_view)>(data_view));
212  if constexpr (min == -stdex::numbers::pi_v<R> and max == stdex::numbers::pi_v<R>) //< Avoid scaling and wrapping, if possible.
213  {
214  return std::array {values::atan2(std::forward<decltype(y)>(y), std::forward<decltype(x)>(x))};
215  }
216  else
217  {
218  constexpr auto period = values::cast_to<R>(values::operation(std::minus{}, Max{}, Min{}));
219  constexpr auto scale = values::operation(std::divides{}, period, values::fixed_2pi<R>{});
220  auto phi = values::operation(std::multiplies{}, values::atan2(std::forward<decltype(y)>(y), std::forward<decltype(x)>(x)), scale);
221  return std::array {values::internal::update_real_part(std::move(phi),
222  values::operation(wrap_phi{}, values::real(std::move(phi))))};
223  }
224  };
225 
226  private:
227 
228  struct wrap_phi_mod
229  {
230  template<typename R>
231  constexpr R operator()(const R& phi_real) const
232  {
233  constexpr R period = max - min;
234  if (phi_real >= R{min} and phi_real < max) return phi_real;
235  R phi_real_mod {values::fmod(phi_real - R{min}, period)};
236  if (phi_real_mod < 0) return R{min} + phi_real_mod + period;
237  return R{min} + phi_real_mod;
238  }
239  };
240 
241  public:
242 
243  /*
244  * \brief Wrap the angle to its primary range.
245  */
246  static constexpr auto
247  wrap = [](const T&, auto&& data_view)
248  {
249  decltype(auto) phi = collections::get<0>(std::forward<decltype(data_view)>(data_view));
250  return std::array {values::internal::update_real_part(std::forward<decltype(phi)>(phi),
251  values::operation(wrap_phi_mod{}, values::real(values::real(phi))))};
252  };
253 
254  };
255 
256 }
257 
258 
259 namespace std
260 {
261  template<typename Min1, typename Max1, typename Min2, typename Max2>
262  struct common_type<OpenKalman::coordinates::Angle<Min1, Max1>, OpenKalman::coordinates::Angle<Min2, Max2>>
263  : std::conditional<
264  OpenKalman::values::fixed_value_of_v<Min1> == OpenKalman::values::fixed_value_of_v<Min2> and
265  OpenKalman::values::fixed_value_of_v<Max1> == OpenKalman::values::fixed_value_of_v<Max2>,
266  OpenKalman::coordinates::Angle<Min1, Max1>,
267  OpenKalman::coordinates::Any<>> {};
268 
269 
270  template<typename Min1, typename Max1, typename Scalar>
271  struct common_type<OpenKalman::coordinates::Angle<Min1, Max1>, OpenKalman::coordinates::Any<Scalar>>
272  : common_type<OpenKalman::coordinates::Any<Scalar>, OpenKalman::coordinates::Angle<Min1, Max1>> {};
273 
274 
275  template<typename Min1, typename Max1, typename T>
276  struct common_type<OpenKalman::coordinates::Angle<Min1, Max1>, T>
277  : std::conditional_t<
278  OpenKalman::coordinates::descriptor<T>,
279  OpenKalman::stdex::type_identity<OpenKalman::coordinates::Any<>>,
280  std::monostate> {};
281 }
282 
283 #endif
constexpr auto fmod(const X &x, const Y &y)
A constexpr function for fmod.
Definition: fmod.hpp:44
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
Definition: fixed_value.hpp:41
typename value_type_of< T >::type value_type_of_t
Helper template for value_type_of.
Definition: value_type_of.hpp:52
constexpr auto fixed_value_of_v
Helper template for fixed_value_of.
Definition: fixed_value_of.hpp:84
constexpr bool complex
T is a value that reduces to std::complex or a custom complex type.
Definition: complex.hpp:47
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
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
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
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
constexpr auto operation(Operation &&op, Args &&...args)
A potentially constant-evaluated operation involving some number of values.
Definition: operation.hpp:98