Monado OpenXR Runtime
t_lowpass.hpp
Go to the documentation of this file.
1 // Copyright 2019, Collabora, Ltd.
2 // SPDX-License-Identifier: BSL-1.0
3 /*!
4  * @file
5  * @brief Low-pass IIR filter
6  * @author Ryan Pavlik <ryan.pavlik@collabora.com>
7  * @ingroup aux_tracking
8  */
9 
10 #pragma once
11 
12 #ifndef __cplusplus
13 #error "This header is C++-only."
14 #endif
15 
16 #include "util/u_time.h"
17 
18 #include <cmath>
19 #include <type_traits>
20 
21 
22 namespace xrt_fusion {
23 namespace implementation {
24  /*!
25  * The shared implementation (between vector and scalar versions) of an
26  * IIR low-pass filter.
27  */
28  template <typename Value, typename Scalar> struct LowPassIIR
29  {
30  // For fixed point, you'd need more bits of data storage. See
31  // https://www.embeddedrelated.com/showarticle/779.php
32  static_assert(
33  std::is_floating_point<Scalar>::value,
34  "Filter is designed only for floating-point values. If "
35  "you want fixed-point, you must reimplement it.");
36  // EIGEN_MAKE_ALIGNED_OPERATOR_NEW
37 
38  /*!
39  * Constructor
40  *
41  * @param cutoff_hz A cutoff frequency in Hertz: signal changes
42  * much lower in frequency will be passed through the filter,
43  * while signal changes much higher in frequency will be
44  * blocked.
45  *
46  * @param val The value to initialize the filter with. Does not
47  * affect the filter itself: only seen if you access state
48  * before initializing the filter with the first sample.
49  */
50  explicit LowPassIIR(Scalar cutoff_hz, Value const &val) noexcept
51  : state(val), time_constant(1.f / (2.f * M_PI * cutoff_hz))
52  {}
53 
54  /*!
55  * Reset the filter to just-created state.
56  */
57  void
58  reset(Value const &val) noexcept
59  {
60  state = val;
61  initialized = false;
62  filter_timestamp_ns = 0;
63  }
64 
65  /*!
66  * Filter a sample, with an optional weight.
67  *
68  * @param sample The value to filter
69  * @param timestamp_ns The time that this sample was measured.
70  * @param weight An optional value between 0 and 1. The smaller
71  * this value, the less the current sample influences the filter
72  * state. For the first call, this is always assumed to be 1.
73  */
74  void
75  addSample(Value const &sample,
76  timepoint_ns timestamp_ns,
77  Scalar weight = 1)
78  {
79  if (!initialized) {
80  initialized = true;
81  state = sample;
82  filter_timestamp_ns = timestamp_ns;
83  return;
84  }
85  // get dt in seconds
86  Scalar dt =
87  time_ns_to_s(timestamp_ns - filter_timestamp_ns);
88  //! @todo limit max dt?
89  Scalar weighted = dt * weight;
90  Scalar alpha = weighted / (time_constant + weighted);
91 
92  // The update step below is equivalent to
93  // state = state * (1 - alpha) + alpha * sample;
94  // -- it blends the current sample and the filter state
95  // using alpha as the blending parameter.
96  state += alpha * (sample - state);
97  filter_timestamp_ns = timestamp_ns;
98  }
99 
100  Value state;
101  Scalar time_constant;
102  bool initialized{false};
103  timepoint_ns filter_timestamp_ns{0};
104  };
105 } // namespace implementation
106 
107 /*!
108  * A very simple low-pass filter, using a "one-pole infinite impulse response"
109  * design (one-pole IIR).
110  *
111  * Configurable in scalar type.
112  */
113 template <typename Scalar> class LowPassIIRFilter
114 {
115 public:
116  /*!
117  * Constructor
118  *
119  * @param cutoff_hz A cutoff frequency in Hertz: signal changes much
120  * lower in frequency will be passed through the filter, while signal
121  * changes much higher in frequency will be blocked.
122  */
123  explicit LowPassIIRFilter(Scalar cutoff_hz) noexcept
124  : impl_(cutoff_hz, 0)
125  {}
126 
127 
128  /*!
129  * Reset the filter to just-created state.
130  */
131  void
132  reset() noexcept
133  {
134  impl_.reset(0);
135  }
136 
137  /*!
138  * Filter a sample, with an optional weight.
139  *
140  * @param sample The value to filter
141  * @param timestamp_ns The time that this sample was measured.
142  * @param weight An optional value between 0 and 1. The smaller this
143  * value, the less the current sample influences the filter state. For
144  * the first call, this is always assumed to be 1.
145  */
146  void
147  addSample(Scalar sample, timepoint_ns timestamp_ns, Scalar weight = 1)
148  {
149  impl_.addSample(sample, timestamp_ns, weight);
150  }
151 
152  /*!
153  * Access the filtered value.
154  */
155  Scalar
156  getState() const noexcept
157  {
158  return impl_.state;
159  }
160 
161  /*!
162  * Access the time of last update.
163  */
165  getTimestampNs() const noexcept
166  {
167  return impl_.filter_timestamp_ns;
168  }
169 
170  /*!
171  * Access whether we have initialized state.
172  */
173  bool
174  isInitialized() const noexcept
175  {
176  return impl_.initialized;
177  }
178 
179 private:
181 };
182 
183 } // namespace xrt_fusion
LowPassIIR(Scalar cutoff_hz, Value const &val) noexcept
Constructor.
Definition: t_lowpass.hpp:50
LowPassIIRFilter(Scalar cutoff_hz) noexcept
Constructor.
Definition: t_lowpass.hpp:123
A very simple low-pass filter, using a "one-pole infinite impulse response" design (one-pole IIR)...
Definition: t_lowpass.hpp:113
void addSample(Value const &sample, timepoint_ns timestamp_ns, Scalar weight=1)
Filter a sample, with an optional weight.
Definition: t_lowpass.hpp:75
void addSample(Scalar sample, timepoint_ns timestamp_ns, Scalar weight=1)
Filter a sample, with an optional weight.
Definition: t_lowpass.hpp:147
Time-keeping: a clock that is steady, convertible to system time, and ideally high-resolution.
void reset(Value const &val) noexcept
Reset the filter to just-created state.
Definition: t_lowpass.hpp:58
timepoint_ns getTimestampNs() const noexcept
Access the time of last update.
Definition: t_lowpass.hpp:165
int64_t timepoint_ns
Integer timestamp type.
Definition: u_time.h:34
bool isInitialized() const noexcept
Access whether we have initialized state.
Definition: t_lowpass.hpp:174
Scalar getState() const noexcept
Access the filtered value.
Definition: t_lowpass.hpp:156
Definition: t_fusion.hpp:25
void reset() noexcept
Reset the filter to just-created state.
Definition: t_lowpass.hpp:132
The shared implementation (between vector and scalar versions) of an IIR low-pass filter...
Definition: t_lowpass.hpp:28