diff --git a/include/DigitalFilter.h b/include/DigitalFilter.h index 4157d8a..f5dae97 100644 --- a/include/DigitalFilter.h +++ b/include/DigitalFilter.h @@ -50,6 +50,35 @@ public: : GenericFilter(aCoeff, bCoeff) { } + void setCoefficients(vectX_t&& aCoeff, vectX_t&& bCoeff) + { + setCoeffs(std::forward(aCoeff), std::forward(bCoeff)); + } +}; + +/*! \brief Basic centered digital filter. + * + * This filter allows you to set any centered digital filter based on its coefficients. + * \tparam T Floating type. + */ +template +class CenteredDigitalFilter : public GenericFilter { +public: + /*! \brief Default uninitialized constructor. */ + CenteredDigitalFilter() = default; + /*! \brief Constructor. + * \param aCoeff Denominator coefficients of the filter in decreasing order. + * \param bCoeff Numerator coefficients of the filter in decreasing order. + */ + CenteredDigitalFilter(const vectX_t& aCoeff, const vectX_t& bCoeff) + : GenericFilter(aCoeff, bCoeff, Type::Centered) + { + } + void setCoefficients(vectX_t&& aCoeff, vectX_t&& bCoeff) + { + setCoeffs(std::forward(aCoeff), std::forward(bCoeff)); + setType(Type::Centered); + } }; } // namespace difi \ No newline at end of file diff --git a/include/GenericFilter.h b/include/GenericFilter.h index 278dda0..f8f85ce 100644 --- a/include/GenericFilter.h +++ b/include/GenericFilter.h @@ -35,6 +35,9 @@ namespace difi { +// TODO: noexcept(Function of gsl variable) +// TODO: constructor with universal refs + /*! \brief Low-level filter. * * It creates the basic and common functions of all linear filter that can written as a digital filter. @@ -49,6 +52,12 @@ template class GenericFilter { static_assert(std::is_floating_point::value && !std::is_const::value, "Only accept non-complex floating point types."); +public: + enum class Type { + OneSided, + Centered + }; + public: /*! \brief Filter a new data. * @@ -64,16 +73,11 @@ public: * \return Filtered signal. */ vectX_t filter(const vectX_t& data); - /*! \brief Filter a signal and store in a user-defined Eigen vector. - * - * Useful if the length of the signal is known in advance. - * \param[out] results Filtered signal. - * \param data Signal. - * \return False if vector's lengths do not match. - */ - void getFilterResults(Eigen::Ref> results, const vectX_t& data); /*! \brief Reset the data and filtered data. */ void resetFilter() noexcept; + + /*!< \brief Return the filter type */ + Type type() const noexcept { return (m_center == 0 ? Type::OneSided : Type::Centered); } /*! \brief Get digital filter coefficients. * * It will automatically resize the given vectors. @@ -81,6 +85,10 @@ public: * \param[out] bCoeff Numerator coefficients of the filter in decreasing order. */ void getCoeffs(vectX_t& aCoeff, vectX_t& bCoeff) const noexcept; + /*! \brief Return coefficients of the denominator polynome. */ + const vectX_t& aCoeff() const noexcept { return m_aCoeff; } + /*! \brief Return coefficients of the numerator polynome. */ + const vectX_t& bCoeff() const noexcept { return m_bCoeff; } /*! \brief Return the order the denominator polynome order of the filter. */ Eigen::Index aOrder() const noexcept { return m_aCoeff.size(); } /*! \brief Return the order the numerator polynome order of the filter. */ @@ -94,11 +102,18 @@ protected: /*! \brief Constructor. * \param aCoeff Denominator coefficients of the filter in decreasing order. * \param bCoeff Numerator coefficients of the filter in decreasing order. + * \param center */ - GenericFilter(const vectX_t& aCoeff, const vectX_t& bCoeff); + GenericFilter(const vectX_t& aCoeff, const vectX_t& bCoeff, Type type = Type::OneSided); /*! \brief Default destructor. */ virtual ~GenericFilter() = default; + /*! \brief Set type of filter (one-sided or centered) + * + * \param type The filter type. + * \warning bCoeff must be set before. + */ + void setType(Type type); /*! \brief Set the new coefficients of the filters. * * It awaits a universal reference. @@ -116,9 +131,10 @@ protected: * \param bCoeff Numerator coefficients of the filter. * \return True if the filter status is set on READY. */ - void checkCoeffs(const vectX_t& aCoeff, const vectX_t& bCoeff); + bool checkCoeffs(const vectX_t& aCoeff, const vectX_t& bCoeff, Type type); private: + Eigen::Index m_center = 0; /*!< Center of the filter. 0 is a one-sided filter. Default is 0. */ bool m_isInitialized = false; /*!< Initialization state of the filter. Default is false */ vectX_t m_aCoeff; /*!< Denominator coefficients of the filter */ vectX_t m_bCoeff; /*!< Numerator coefficients of the filter */ diff --git a/include/GenericFilter.tpp b/include/GenericFilter.tpp index f2334ac..611a849 100644 --- a/include/GenericFilter.tpp +++ b/include/GenericFilter.tpp @@ -2,13 +2,13 @@ // All rights reserved. // Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// modification, are permitted provided that the following conditions are met: // 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. +// this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. +// and/or other materials provided with the distribution. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -22,7 +22,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // The views and conclusions contained in the software and documentation are those -// of the authors and should not be interpreted as representing official policies, +// of the authors and should not be interpreted as representing official policies, // either expressed or implied, of the FreeBSD Project. #include @@ -44,25 +44,18 @@ T GenericFilter::stepFilter(const T& data) m_rawData[0] = data; m_filteredData[0] = 0; - m_filteredData[0] = m_bCoeff.dot(m_rawData) - m_aCoeff.dot(m_filteredData); - return m_filteredData[0]; + m_filteredData[m_center] = m_bCoeff.dot(m_rawData) - m_aCoeff.dot(m_filteredData); + return m_filteredData[m_center]; } template vectX_t GenericFilter::filter(const vectX_t& data) -{ - vectX_t results(data.size()); - getFilterResults(results, data); - return results; -} - -template -void GenericFilter::getFilterResults(Eigen::Ref> results, const vectX_t& data) { Expects(m_isInitialized); - Expects(results.size() == data.size()); + vectX_t results(data.size()); for (Eigen::Index i = 0; i < data.size(); ++i) results(i) = stepFilter(data(i)); + return results; } template @@ -72,13 +65,20 @@ void GenericFilter::resetFilter() noexcept m_rawData.setZero(m_bCoeff.size()); } +template +void GenericFilter::setType(Type type) +{ + Expects(type == Type::Centered ? m_bCoeff.size() > 2 && m_bCoeff.size() % 2 == 1 : true); + m_center = (type == Type::OneSided ? 0 : (m_bCoeff.size() - 1) / 2); +} + template template void GenericFilter::setCoeffs(T2&& aCoeff, T2&& bCoeff) { static_assert(std::is_convertible_v>, "The coefficients types should be convertible to vectX_t"); - checkCoeffs(aCoeff, bCoeff); + Expects(checkCoeffs(aCoeff, bCoeff, (m_center == 0 ? Type::OneSided : Type::Centered))); m_aCoeff = aCoeff; m_bCoeff = bCoeff; normalizeCoeffs(); @@ -96,13 +96,14 @@ void GenericFilter::getCoeffs(vectX_t& aCoeff, vectX_t& bCoeff) const n // Protected functions template -GenericFilter::GenericFilter(const vectX_t& aCoeff, const vectX_t& bCoeff) +GenericFilter::GenericFilter(const vectX_t& aCoeff, const vectX_t& bCoeff, Type type) : m_aCoeff(aCoeff) , m_bCoeff(bCoeff) , m_filteredData(aCoeff.size()) , m_rawData(bCoeff.size()) { - checkCoeffs(aCoeff, bCoeff); + Expects(checkCoeffs(aCoeff, bCoeff, type)); + m_center = (type == Type::OneSided ? 0 : (bCoeff.size() - 1) / 2); normalizeCoeffs(); resetFilter(); m_isInitialized = true; @@ -120,11 +121,10 @@ void GenericFilter::normalizeCoeffs() } template -void GenericFilter::checkCoeffs(const vectX_t& aCoeff, const vectX_t& bCoeff) +bool GenericFilter::checkCoeffs(const vectX_t& aCoeff, const vectX_t& bCoeff, Type type) { - Expects(aCoeff.size() > 0); - Expects(std::abs(aCoeff[0]) > std::numeric_limits::epsilon()); - Expects(bCoeff.size() > 0); + bool centering = (type == Type::Centered ? (bCoeff.size() % 2 == 1) : true); + return aCoeff.size() > 0 && std::abs(aCoeff[0]) > std::numeric_limits::epsilon() && bCoeff.size() > 0 && centering; } } // namespace difi \ No newline at end of file diff --git a/include/MovingAverage.h b/include/MovingAverage.h index 3912de9..e2cc0d2 100644 --- a/include/MovingAverage.h +++ b/include/MovingAverage.h @@ -60,4 +60,32 @@ public: int windowSize() const noexcept { return bOrder(); } }; +/*! \brief Centered moving average digital filter. + * + * This is a specialization of a digital filter in order to use a centered moving average. + * \tparam T Floating type. + */ +template +class CenteredMovingAverage : public DigitalFilter { +public: + /*! \brief Default uninitialized constructor. */ + CenteredMovingAverage() = default; + /*! \brief Constructor. + * \param windowSize Size of the moving average window. + */ + CenteredMovingAverage(int windowSize) + { + setWindowSize(windowSize); + } + /*! \brief Set the size of the moving average window. */ + void setWindowSize(int windowSize) + { + Expects(windowSize > 2 && windowSize % 2 == 1); + setCoeffs(vectX_t::Constant(1, T(1)), vectX_t::Constant(windowSize, T(1) / windowSize)); + setType(Type::Centered); + } + /*! \brief Get the size of the moving average window. */ + int windowSize() const noexcept { return bOrder(); } +}; + } // namespace difi