mirror of
https://gitlab.freedesktop.org/monado/monado.git
synced 2024-12-28 02:26:16 +00:00
a/math: Add a C wrapper for the integer low-pass filter.
This commit is contained in:
parent
a0e2df4cf0
commit
cf07791e93
|
@ -46,6 +46,8 @@ add_library(
|
|||
math/m_imu_pre.h
|
||||
math/m_lowpass_float.hpp
|
||||
math/m_lowpass_float_vector.hpp
|
||||
math/m_lowpass_integer.cpp
|
||||
math/m_lowpass_integer.h
|
||||
math/m_lowpass_integer.hpp
|
||||
math/m_optics.c
|
||||
math/m_permutation.c
|
||||
|
|
100
src/xrt/auxiliary/math/m_lowpass_integer.cpp
Normal file
100
src/xrt/auxiliary/math/m_lowpass_integer.cpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
// Copyright 2022, Collabora, Ltd.
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief Wrap integer filters for C
|
||||
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
|
||||
* @ingroup aux_math
|
||||
*/
|
||||
|
||||
#include "m_lowpass_integer.h"
|
||||
#include "m_lowpass_integer.hpp"
|
||||
|
||||
#include "util/u_logging.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
using xrt::auxiliary::math::IntegerLowPassIIRFilter;
|
||||
// using xrt::auxiliary::math::Rational;
|
||||
using Rational64 = xrt::auxiliary::math::Rational<int64_t>;
|
||||
|
||||
struct m_lowpass_integer
|
||||
{
|
||||
m_lowpass_integer(Rational64 alpha) : filter(alpha) {}
|
||||
|
||||
IntegerLowPassIIRFilter<int64_t> filter;
|
||||
};
|
||||
|
||||
#define DEFAULT_CATCH(RETURNVAL) \
|
||||
catch (std::exception const &e) \
|
||||
{ \
|
||||
U_LOG_E("Caught exception: %s", e.what()); \
|
||||
return RETURNVAL; \
|
||||
} \
|
||||
catch (...) \
|
||||
{ \
|
||||
U_LOG_E("Caught exception"); \
|
||||
return RETURNVAL; \
|
||||
}
|
||||
|
||||
struct m_lowpass_integer *
|
||||
m_lowpass_integer_create(int64_t alpha_numerator, int64_t alpha_denominator)
|
||||
{
|
||||
try {
|
||||
if (alpha_denominator <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (alpha_numerator <= 0 || alpha_numerator >= alpha_denominator) {
|
||||
return nullptr;
|
||||
}
|
||||
auto ret = std::make_unique<m_lowpass_integer>(Rational64{alpha_numerator, alpha_denominator});
|
||||
return ret.release();
|
||||
}
|
||||
DEFAULT_CATCH(nullptr)
|
||||
}
|
||||
|
||||
void
|
||||
m_lowpass_integer_add_sample(struct m_lowpass_integer *mli, int64_t sample)
|
||||
{
|
||||
try {
|
||||
mli->filter.addSample(sample);
|
||||
}
|
||||
DEFAULT_CATCH()
|
||||
}
|
||||
|
||||
int64_t
|
||||
m_lowpass_integer_get_state(const struct m_lowpass_integer *mli)
|
||||
{
|
||||
|
||||
try {
|
||||
return mli->filter.getState();
|
||||
}
|
||||
DEFAULT_CATCH(0)
|
||||
}
|
||||
|
||||
bool
|
||||
m_lowpass_integer_is_initialized(const struct m_lowpass_integer *mli)
|
||||
{
|
||||
|
||||
try {
|
||||
return mli->filter.isInitialized();
|
||||
}
|
||||
DEFAULT_CATCH(false)
|
||||
}
|
||||
|
||||
void
|
||||
m_lowpass_integer_destroy(struct m_lowpass_integer **ptr_to_mli)
|
||||
{
|
||||
try {
|
||||
if (ptr_to_mli == nullptr) {
|
||||
return;
|
||||
}
|
||||
struct m_lowpass_integer *mli = *ptr_to_mli;
|
||||
if (mli == nullptr) {
|
||||
return;
|
||||
}
|
||||
delete mli;
|
||||
*ptr_to_mli = nullptr;
|
||||
}
|
||||
DEFAULT_CATCH()
|
||||
}
|
91
src/xrt/auxiliary/math/m_lowpass_integer.h
Normal file
91
src/xrt/auxiliary/math/m_lowpass_integer.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
// Copyright 2019, 2022, Collabora, Ltd.
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief Low-pass IIR filter for integers - C wrapper
|
||||
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
|
||||
* @ingroup aux_math
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*!
|
||||
* An IIR (low pass) filter for integer values.
|
||||
*
|
||||
* Wraps xrt::auxiliary::math::IntegerLowPassIIRFilter - see that if you need a different scalar type.
|
||||
*
|
||||
*/
|
||||
struct m_lowpass_integer;
|
||||
|
||||
/*!
|
||||
* Constructor
|
||||
*
|
||||
* @note Taking alpha, not a cutoff frequency, here, because it's easier with the rational math.
|
||||
*
|
||||
* Together, the two parameters specify the alpha value used to blend between new input and existing state. Larger
|
||||
* values mean more influence from new input.
|
||||
*
|
||||
* @param alpha_numerator The numerator of the alpha value. Must be greater than 0 and less than @p alpha_denominator
|
||||
* @param alpha_denominator The denominator of the alpha value. Must be greater than 0.
|
||||
*
|
||||
* @return null if a parameter is out of range
|
||||
*
|
||||
* @public @memberof m_lowpass_integer
|
||||
*/
|
||||
struct m_lowpass_integer *
|
||||
m_lowpass_integer_create(int64_t alpha_numerator, int64_t alpha_denominator);
|
||||
|
||||
|
||||
/*!
|
||||
* Filter a sample
|
||||
*
|
||||
* @param mli self-pointer
|
||||
* @param sample The value to filter
|
||||
*
|
||||
* @public @memberof m_lowpass_integer
|
||||
*/
|
||||
void
|
||||
m_lowpass_integer_add_sample(struct m_lowpass_integer *mli, int64_t sample);
|
||||
|
||||
/*!
|
||||
* Access the filtered value.
|
||||
*
|
||||
* Probably 0 or other meaningless value if it's not initialized: see @ref m_lowpass_integer_is_initialized
|
||||
*
|
||||
* @param mli self-pointer
|
||||
*
|
||||
* @public @memberof m_lowpass_integer
|
||||
*/
|
||||
int64_t
|
||||
m_lowpass_integer_get_state(const struct m_lowpass_integer *mli);
|
||||
|
||||
/*!
|
||||
* Access whether we have initialized state.
|
||||
*
|
||||
* @param mli self-pointer
|
||||
*
|
||||
* @public @memberof m_lowpass_integer
|
||||
*/
|
||||
bool
|
||||
m_lowpass_integer_is_initialized(const struct m_lowpass_integer *mli);
|
||||
|
||||
/*!
|
||||
* Destroy a lowpass integer filter.
|
||||
*
|
||||
* Does null checks.
|
||||
*
|
||||
* @param ptr_to_mli Address of your lowpass integer filter. Will be set to zero.
|
||||
*
|
||||
* @public @memberof m_lowpass_integer
|
||||
*/
|
||||
void
|
||||
m_lowpass_integer_destroy(struct m_lowpass_integer **ptr_to_mli);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -182,6 +182,8 @@ lib_aux_math = static_library(
|
|||
'math/m_imu_pre.h',
|
||||
'math/m_lowpass_float.hpp',
|
||||
'math/m_lowpass_float_vector.hpp',
|
||||
'math/m_lowpass_integer.cpp',
|
||||
'math/m_lowpass_integer.h',
|
||||
'math/m_lowpass_integer.hpp',
|
||||
'math/m_optics.c',
|
||||
'math/m_permutation.c',
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
*/
|
||||
|
||||
#include <math/m_lowpass_integer.hpp>
|
||||
#include <math/m_lowpass_integer.h>
|
||||
|
||||
#include "catch/catch.hpp"
|
||||
|
||||
|
||||
using xrt::auxiliary::math::Rational;
|
||||
using xrt::auxiliary::math::IntegerLowPassIIRFilter;
|
||||
using xrt::auxiliary::math::Rational;
|
||||
static constexpr uint16_t InitialState = 300;
|
||||
|
||||
TEMPLATE_TEST_CASE("IntegerLowPassIIRFilter", "", int32_t, uint32_t)
|
||||
|
@ -67,3 +67,59 @@ TEMPLATE_TEST_CASE("IntegerLowPassIIRFilter", "", int32_t, uint32_t)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("m_lowpass_integer")
|
||||
{
|
||||
|
||||
struct m_lowpass_integer *filter = m_lowpass_integer_create(1, 2);
|
||||
CHECK(filter);
|
||||
|
||||
CHECK_FALSE(m_lowpass_integer_is_initialized(filter));
|
||||
|
||||
m_lowpass_integer_add_sample(filter, InitialState);
|
||||
REQUIRE(m_lowpass_integer_is_initialized(filter));
|
||||
CHECK(m_lowpass_integer_get_state(filter) == InitialState);
|
||||
|
||||
|
||||
auto prev = m_lowpass_integer_get_state(filter);
|
||||
SECTION("Increase")
|
||||
{
|
||||
constexpr auto newTarget = InitialState * 2;
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
m_lowpass_integer_add_sample(filter, newTarget);
|
||||
REQUIRE(m_lowpass_integer_is_initialized(filter));
|
||||
// not going to exceed this
|
||||
if (prev == newTarget || prev == newTarget - 1) {
|
||||
REQUIRE(m_lowpass_integer_get_state(filter) == prev);
|
||||
} else {
|
||||
REQUIRE(m_lowpass_integer_get_state(filter) > prev);
|
||||
prev = m_lowpass_integer_get_state(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Decrease")
|
||||
{
|
||||
constexpr auto newTarget = InitialState / 2;
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
|
||||
m_lowpass_integer_add_sample(filter, newTarget);
|
||||
REQUIRE(m_lowpass_integer_is_initialized(filter));
|
||||
if (prev == newTarget) {
|
||||
REQUIRE(m_lowpass_integer_get_state(filter) == newTarget);
|
||||
} else {
|
||||
REQUIRE(m_lowpass_integer_get_state(filter) < prev);
|
||||
prev = m_lowpass_integer_get_state(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
SECTION("Stay Same")
|
||||
{
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
|
||||
m_lowpass_integer_add_sample(filter, InitialState);
|
||||
REQUIRE(m_lowpass_integer_is_initialized(filter));
|
||||
REQUIRE(m_lowpass_integer_get_state(filter) == InitialState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue