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