diff --git a/CMakeLists.txt b/CMakeLists.txt index e72e16a..2bfff0b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,18 @@ set(CMAKE_CXX_STANDARD 14) setup_project() option(DISABLE_TESTS "Disable unit tests." OFF) +option(THROW_ON_CONTRACT_VIOLATION "Throw an error when program fails." ON) +option(TERMINATE_ON_CONTRACT_VIOLATION "Terminate program when an error occurs. (Default)" OFF) +option(UNENFORCED_ON_CONTRACT_VIOLATION "Do not perform any check." OFF) + +# Handle contracts specifications +if(${THROW_ON_CONTRACT_VIOLATION}) + add_compile_options(-DGSL_THROW_ON_CONTRACT_VIOLATION) +elseif(${TERMINATE_ON_CONTRACT_VIOLATION}) + add_compile_options(-DGSL_TERMINATE_ON_CONTRACT_VIOLATION) +elseif() + add_compile_options(-DGSL_UNENFORCED_ON_CONTRACT_VIOLATION) +endif() # for MSVC if(MSVC) diff --git a/LICENSE b/LICENSE index f488bc4..6110ab6 100644 --- a/LICENSE +++ b/LICENSE @@ -22,3 +22,7 @@ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +Note: GSL has a different license \ No newline at end of file diff --git a/include/BilinearTransform.h b/include/BilinearTransform.h index 3fc9c8f..0c0aa0e 100644 --- a/include/BilinearTransform.h +++ b/include/BilinearTransform.h @@ -25,8 +25,10 @@ #pragma once +#include "gsl/gsl_assert.h" #include "type_checks.h" #include "typedefs.h" +#include namespace difi { @@ -69,6 +71,7 @@ struct BilinearTransform { template void BilinearTransform::SToZ(SubType fs, const T& sPlanePole, T& zPlanePole) { + Expects(std::abs(2 * fs - sPlanePole) > std::numeric_limits::epsilon()); // Divide-by-zero otherwise T scalePole = sPlanePole / (2 * fs); zPlanePole = (T(1) + scalePole) / (T(1) - scalePole); } @@ -76,7 +79,7 @@ void BilinearTransform::SToZ(SubType fs, const T& sPlanePole, T& zPlanePole) template void BilinearTransform::SToZ(SubType fs, const vectX_t& sPlanePoles, Eigen::Ref>& zPlanePoles) { - assert(sPlanePoles.size() == zPlanePoles.size()); + Expects(sPlanePoles.size() == zPlanePoles.size()); for (Eigen::Index k = 0; k < sPlanePoles.size(); ++k) SToZ(fs, sPlanePoles(k), zPlanePoles(k)); } @@ -84,6 +87,7 @@ void BilinearTransform::SToZ(SubType fs, const vectX_t& sPlanePoles, Eigen template void BilinearTransform::ZToS(SubType fs, const T& zPlanePole, T& sPlanePole) { + Expects(std::abs(T(1) + zPlanePole) > std::numeric_limits::epsilon()); // Divide-by-zero otherwise T invPole = T(1) / zPlanePole; sPlanePole = 2 * fs * (T(1) - invPole) / (T(1) + invPole); } @@ -91,7 +95,7 @@ void BilinearTransform::ZToS(SubType fs, const T& zPlanePole, T& sPlanePole) template void BilinearTransform::ZToS(SubType fs, const vectX_t& zPlanePoles, Eigen::Ref>& sPlanePoles) { - assert(zPlanePoles.size() == sPlanePoles.size()); + Expects(sPlanePoles.size() == zPlanePoles.size()); for (Eigen::Index k = 0; k < sPlanePoles.size(); ++k) ZToS(fs, zPlanePoles(k), sPlanePoles(k)); } diff --git a/include/Butterworth.tpp b/include/Butterworth.tpp index 9fdeb73..820c563 100644 --- a/include/Butterworth.tpp +++ b/include/Butterworth.tpp @@ -34,6 +34,8 @@ T Butterworth::PI = static_cast(M_PI); template std::pair Butterworth::findMinimumButter(T wPass, T wStop, T APass, T AStop) { + Expects(wPass > T(0) && wPass < T(1)); + Expects(wStop > T(0) && wPass < T(1)); T num = std::log10((std::pow(T(10), T(0.1) * std::abs(AStop)) - 1) / (std::pow(T(10), T(0.1) * std::abs(APass)) - 1)); // pre-warp T fwPass = std::tan(T(0.5) * PI * wPass); @@ -65,25 +67,27 @@ template Butterworth::Butterworth(int order, T fc, T fs, Type type) : m_type(type) { - initialize(order, fc, 0, fs); + setFilterParameters(order, fc, fs); } template Butterworth::Butterworth(int order, T fLower, T fUpper, T fs, Type type) : m_type(type) { - initialize(order, fLower, fUpper, fs); + setFilterParameters(order, fLower, fUpper, fs); } template void Butterworth::setFilterParameters(int order, T fc, T fs) { + Expects(fc < fs / T(2)); initialize(order, fc, 0, fs); } template void Butterworth::setFilterParameters(int order, T fLower, T fUpper, T fs) { + Expects(fLower < fUpper); initialize(order, fLower, fUpper, fs); } @@ -92,25 +96,8 @@ void Butterworth::initialize(int order, T f1, T f2, T fs) { // f1 = fc for LowPass/HighPass filter // f1 = fLower, f2 = fUpper for BandPass/BandReject filter - if (order <= 0) { - m_status = FilterStatus::BAD_ORDER_SIZE; - return; - } - - if (f1 <= 0 || fs <= 0) { - m_status = FilterStatus::BAD_FREQUENCY_VALUE; - return; - } - - if ((m_type == Type::BandPass || m_type == Type::BandReject) && f1 >= f2) { - m_status = FilterStatus::BAD_BAND_FREQUENCY; - return; - } - - if ((m_type == Type::LowPass || m_type == Type::HighPass) && f1 > fs / 2.) { - m_status = FilterStatus::BAD_CUTOFF_FREQUENCY; - return; - } + Expects(order > 0); + Expects(f1 > 0 && fs > 0); // f2 must be > f1 check in setFilterParameters m_order = order; m_fs = fs; @@ -118,8 +105,6 @@ void Butterworth::initialize(int order, T f1, T f2, T fs) computeDigitalRep(f1); else computeBandDigitalRep(f1, f2); // For band-like filters - - resetFilter(); } template @@ -195,8 +180,9 @@ std::complex Butterworth::generateAnalogPole(int k, T fpw1) case Type::HighPass: return T(2) * PI * fpw1 / analogPole; case Type::LowPass: - default: return T(2) * PI * fpw1 * analogPole; + default: + GSL_ASSUME(0); } } @@ -218,11 +204,12 @@ std::pair, std::complex> Butterworth::generateBandAnalogPo poles.second = s0 * (s - std::complex(T(0), T(1)) * std::sqrt(T(1) - s * s)); return poles; case Type::BandPass: - default: s *= analogPole; poles.first = s0 * (s + std::complex(T(0), T(1)) * std::sqrt(T(1) - s * s)); poles.second = s0 * (s - std::complex(T(0), T(1)) * std::sqrt(T(1) - s * s)); return poles; + default: + GSL_ASSUME(0); } } diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index d4b69b1..86629ba 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -24,6 +24,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. set(HEADERS + gsl/gsl_assert.h BilinearTransform.h Butterworth.h Butterworth.tpp diff --git a/include/GenericFilter.h b/include/GenericFilter.h index 75051ff..84455bf 100644 --- a/include/GenericFilter.h +++ b/include/GenericFilter.h @@ -46,13 +46,6 @@ template class GenericFilter { static_assert(std::is_floating_point::value && !std::is_const::value, "Only accept non-complex floating point types."); -public: - /*! \brief Get the meaning of the filter status. - * \param status Filter status to get the meaning from. - * \return The meaning. - */ - static std::string filterStatus(FilterStatus status); - public: /*! \brief Filter a new data. * @@ -75,22 +68,22 @@ public: * \param data Signal. * \return False if vector's lengths do not match. */ - bool getFilterResults(Eigen::Ref> results, const vectX_t& data); + void getFilterResults(Eigen::Ref> results, const vectX_t& data); /*! \brief Reset the data and filtered data. */ - void resetFilter(); + void resetFilter() noexcept; /*! \brief Get digital filter coefficients. * * It will automatically resize the given vectors. * \param[out] aCoeff Denominator coefficients of the filter in decreasing order. * \param[out] bCoeff Numerator coefficients of the filter in decreasing order. */ - void getCoeffs(vectX_t& aCoeff, vectX_t& bCoeff) const; - /*! \brief Return the current filter status. */ - FilterStatus status() const noexcept { return m_status; } + void getCoeffs(vectX_t& aCoeff, vectX_t& bCoeff) const noexcept; /*! \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. */ Eigen::Index bOrder() const noexcept { return m_bCoeff.size(); } + /*! \brief Return the initialization state of the filter0 */ + bool isInitialized() const noexcept { return m_isInitialized; } protected: /*! \brief Default uninitialized constructor. */ @@ -120,12 +113,10 @@ protected: * \param bCoeff Numerator coefficients of the filter. * \return True if the filter status is set on READY. */ - bool checkCoeffs(const vectX_t& aCoeff, const vectX_t& bCoeff); - -protected: - FilterStatus m_status; /*!< Filter status */ + void checkCoeffs(const vectX_t& aCoeff, const vectX_t& bCoeff); private: + 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 */ vectX_t m_filteredData; /*!< Last set of filtered data */ diff --git a/include/GenericFilter.tpp b/include/GenericFilter.tpp index 9b374d4..54c8c84 100644 --- a/include/GenericFilter.tpp +++ b/include/GenericFilter.tpp @@ -27,40 +27,12 @@ namespace difi { -// Public static functions -template -std::string GenericFilter::filterStatus(FilterStatus status) -{ - switch (status) { - case FilterStatus::NONE: - return "Filter is uninitialized"; - case FilterStatus::READY: - return "Filter is ready to be used"; - case FilterStatus::BAD_ORDER_SIZE: - return "You try to initialize the filter with an order inferior or equal to 0 (window size for the moving average)"; - case FilterStatus::ALL_COEFF_MISSING: - return "Filter has none of its coefficient initialized"; - case FilterStatus::A_COEFF_MISSING: - return "Filter has its 'a' coefficients uninitialized"; - case FilterStatus::B_COEFF_MISSING: - return "Filter has its 'b' coefficients uninitialized"; - case FilterStatus::BAD_FREQUENCY_VALUE: - return "Filter has a received a frequency that is negative or equal to zero"; - case FilterStatus::BAD_CUTOFF_FREQUENCY: - return "Filter has a received a bad cut-off frequency. It must be inferior to the sampling frequency"; - case FilterStatus::BAD_BAND_FREQUENCY: - return "You try to initialize the filter with a bad combination of the frequency and bandwith, you must have fCenter > bw/2"; - default: - return "I forgot to implement this error documentation"; - } -} - // Public functions template T GenericFilter::stepFilter(const T& data) { - assert(m_status == FilterStatus::READY); + Expects(m_isInitialized); // Slide data (can't use SIMD, but should be small) for (Eigen::Index i = m_rawData.size() - 1; i > 0; --i) @@ -78,27 +50,21 @@ template vectX_t GenericFilter::filter(const vectX_t& data) { vectX_t results(data.size()); - if (!getFilterResults(results, data)) - return vectX_t(); - + getFilterResults(results, data); return results; } template -bool GenericFilter::getFilterResults(Eigen::Ref> results, const vectX_t& data) +void GenericFilter::getFilterResults(Eigen::Ref> results, const vectX_t& data) { - assert(m_status == FilterStatus::READY); - if (results.size() != data.size()) - return false; - + Expects(m_isInitialized); + Expects(results.size() == data.size()); for (Eigen::Index i = 0; i < data.size(); ++i) results(i) = stepFilter(data(i)); - - return true; } template -void GenericFilter::resetFilter() +void GenericFilter::resetFilter() noexcept { m_filteredData.setZero(m_aCoeff.size()); m_rawData.setZero(m_bCoeff.size()); @@ -110,17 +76,16 @@ void GenericFilter::setCoeffs(T2&& aCoeff, T2&& bCoeff) { static_assert(std::is_convertible_v>, "The coefficients types should be convertible to vectX_t"); - if (!checkCoeffs(aCoeff, bCoeff)) - return; - + checkCoeffs(aCoeff, bCoeff); m_aCoeff = aCoeff; m_bCoeff = bCoeff; - resetFilter(); normalizeCoeffs(); + resetFilter(); + m_isInitialized = true; } template -void GenericFilter::getCoeffs(vectX_t& aCoeff, vectX_t& bCoeff) const +void GenericFilter::getCoeffs(vectX_t& aCoeff, vectX_t& bCoeff) const noexcept { aCoeff = m_aCoeff; bCoeff = m_bCoeff; @@ -135,18 +100,15 @@ GenericFilter::GenericFilter(const vectX_t& aCoeff, const vectX_t& bCoe , m_filteredData(aCoeff.size()) , m_rawData(bCoeff.size()) { - if (!checkCoeffs(aCoeff, bCoeff)) - return; - - resetFilter(); + checkCoeffs(aCoeff, bCoeff); normalizeCoeffs(); + resetFilter(); + m_isInitialized = true; } template void GenericFilter::normalizeCoeffs() { - assert(m_status == FilterStatus::READY); - T a0 = m_aCoeff(0); if (std::abs(a0 - T(1)) < std::numeric_limits::epsilon()) return; @@ -156,21 +118,11 @@ void GenericFilter::normalizeCoeffs() } template -bool GenericFilter::checkCoeffs(const vectX_t& aCoeff, const vectX_t& bCoeff) +void GenericFilter::checkCoeffs(const vectX_t& aCoeff, const vectX_t& bCoeff) { - m_status = FilterStatus::NONE; - if (aCoeff.size() == 0) - m_status = FilterStatus::A_COEFF_MISSING; - else if (std::abs(aCoeff[0]) < std::numeric_limits::epsilon()) - m_status = FilterStatus::BAD_A_COEFF; - - if (bCoeff.size() == 0) - m_status = (m_status == FilterStatus::A_COEFF_MISSING ? FilterStatus::ALL_COEFF_MISSING : FilterStatus::B_COEFF_MISSING); - - if (m_status == FilterStatus::NONE) - m_status = FilterStatus::READY; - - return m_status == FilterStatus::READY; + Expects(aCoeff.size() > 0); + Expects(std::abs(aCoeff[0]) > std::numeric_limits::epsilon()); + Expects(bCoeff.size() > 0); } } // namespace difi \ No newline at end of file diff --git a/include/MovingAverage.h b/include/MovingAverage.h index 5a3a05e..d1f29b1 100644 --- a/include/MovingAverage.h +++ b/include/MovingAverage.h @@ -50,11 +50,7 @@ public: /*! \brief Set the size of the moving average window. */ void setWindowSize(int windowSize) { - if (windowSize <= 0) { - m_status = FilterStatus::BAD_ORDER_SIZE; - return; - } - + Expects(windowSize > 0); setCoeffs(vectX_t::Constant(1, T(1)), vectX_t::Constant(windowSize, T(1) / windowSize)); } /*! \brief Get the size of the moving average window. */ diff --git a/include/gsl/gsl_assert.h b/include/gsl/gsl_assert.h new file mode 100644 index 0000000..80dbb93 --- /dev/null +++ b/include/gsl/gsl_assert.h @@ -0,0 +1,162 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include // for logic_error + +// +// make suppress attributes parse for some compilers +// Hopefully temporary until suppresion standardization occurs +// +#if defined(_MSC_VER) +#define GSL_SUPPRESS(x) [[gsl::suppress(x)]] +#else +#if defined(__clang__) +#define GSL_SUPPRESS(x) [[gsl::suppress("x")]] +#else +#define GSL_SUPPRESS(x) +#endif // __clang__ +#endif // _MSC_VER + +// +// Temporary until MSVC STL supports no-exceptions mode. +// Currently terminate is a no-op in this mode, so we add termination behavior back +// +#if defined(_MSC_VER) && defined(_HAS_EXCEPTIONS) && !_HAS_EXCEPTIONS +#define GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND +#include +#define RANGE_CHECKS_FAILURE 0 +#endif + +// +// There are three configuration options for this GSL implementation's behavior +// when pre/post conditions on the GSL types are violated: +// +// 1. GSL_TERMINATE_ON_CONTRACT_VIOLATION: std::terminate will be called (default) +// 2. GSL_THROW_ON_CONTRACT_VIOLATION: a gsl::fail_fast exception will be thrown +// 3. GSL_UNENFORCED_ON_CONTRACT_VIOLATION: nothing happens +// +#if !(defined(GSL_THROW_ON_CONTRACT_VIOLATION) || defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) || defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION)) +#define GSL_TERMINATE_ON_CONTRACT_VIOLATION +#endif + +#define GSL_STRINGIFY_DETAIL(x) #x +#define GSL_STRINGIFY(x) GSL_STRINGIFY_DETAIL(x) + +#if defined(__clang__) || defined(__GNUC__) +#define GSL_LIKELY(x) __builtin_expect(!!(x), 1) +#define GSL_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define GSL_LIKELY(x) (!!(x)) +#define GSL_UNLIKELY(x) (!!(x)) +#endif + +// +// GSL_ASSUME(cond) +// +// Tell the optimizer that the predicate cond must hold. It is unspecified +// whether or not cond is actually evaluated. +// +#ifdef _MSC_VER +#define GSL_ASSUME(cond) __assume(cond) +#elif defined(__GNUC__) +#define GSL_ASSUME(cond) ((cond) ? static_cast(0) : __builtin_unreachable()) +#else +#define GSL_ASSUME(cond) static_cast((cond) ? 0 : 0) +#endif + +// +// GSL.assert: assertions +// + +namespace gsl { +struct fail_fast : public std::logic_error { + explicit fail_fast(char const* const message) + : std::logic_error(message) + { + } +}; + +namespace details { +#if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) + + typedef void(__cdecl* terminate_handler)(); + + GSL_SUPPRESS(f .6) // NO-FORMAT: attribute + [[noreturn]] inline void __cdecl default_terminate_handler() + { + __fastfail(RANGE_CHECKS_FAILURE); + } + + inline gsl::details::terminate_handler& get_terminate_handler() noexcept + { + static terminate_handler handler = &default_terminate_handler; + return handler; + } + +#endif + + [[noreturn]] inline void terminate() noexcept + { +#if defined(GSL_MSVC_USE_STL_NOEXCEPTION_WORKAROUND) + (*gsl::details::get_terminate_handler())(); +#else + std::terminate(); +#endif + } + +#if defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) + + template + [[noreturn]] void throw_exception(Exception&&) noexcept + { + gsl::details::terminate(); + } + +#else + + template + [[noreturn]] void throw_exception(Exception&& exception) { + throw std::forward(exception); + } + +#endif + +} // namespace details +} // namespace gsl + +#if defined(GSL_THROW_ON_CONTRACT_VIOLATION) + +#define GSL_CONTRACT_CHECK(type, cond) \ + (GSL_LIKELY(cond) ? static_cast(0) \ + : gsl::details::throw_exception(gsl::fail_fast( \ + "GSL: " type " failure in " __FILE__ " at line " GSL_STRINGIFY(__LINE__) ")"))) + +#elif defined(GSL_TERMINATE_ON_CONTRACT_VIOLATION) + +#define GSL_CONTRACT_CHECK(type, cond) \ + (GSL_LIKELY(cond) ? static_cast(0) : gsl::details::terminate()) + +#elif defined(GSL_UNENFORCED_ON_CONTRACT_VIOLATION) + +#define GSL_CONTRACT_CHECK(type, cond) GSL_ASSUME(cond) + +#endif + +#define Expects(cond) GSL_CONTRACT_CHECK("Precondition", cond) +#define Ensures(cond) GSL_CONTRACT_CHECK("Postcondition", cond) \ No newline at end of file diff --git a/include/typedefs.h b/include/typedefs.h index 591cb64..e855754 100644 --- a/include/typedefs.h +++ b/include/typedefs.h @@ -35,21 +35,4 @@ using vectX_t = Eigen::Matrix; /*!< Eigen column-vector */ template using vectXc_t = vectX_t>; /*!< Eigen complex column-vector */ -/*! \brief Filter status */ -enum class FilterStatus { - // Generic filter - NONE, /*!< Filter has not yet been initialized */ - READY, /*!< Filter is ready to process data */ - BAD_ORDER_SIZE, /*!< Order of the filter is bad */ - BAD_A_COEFF, /*!< Denominator coefficients of the filter is bad */ - A_COEFF_MISSING, /*!< Denominator coefficients have not been set */ - B_COEFF_MISSING, /*!< Numerator coefficients have not been set */ - ALL_COEFF_MISSING = A_COEFF_MISSING | B_COEFF_MISSING, /*!< Coefficients have not been set */ - - // Butterworth filter - BAD_FREQUENCY_VALUE, /*!< Given frequency is bad */ - BAD_CUTOFF_FREQUENCY, /*!< Given ctu-off frequency is bad */ - BAD_BAND_FREQUENCY /*!< Given band frequency is bad */ -}; - } // namespace difi \ No newline at end of file diff --git a/tests/GenericFilterTests.cpp b/tests/GenericFilterTests.cpp index 63a4f70..29285e3 100644 --- a/tests/GenericFilterTests.cpp +++ b/tests/GenericFilterTests.cpp @@ -27,26 +27,31 @@ #include "difi" #include +#include #include BOOST_AUTO_TEST_CASE(FILTER_FAILURES) { - auto dfd = difi::DigitalFilterd(Eigen::VectorXd(), Eigen::VectorXd::Constant(2, 0)); - BOOST_REQUIRE(dfd.status() == difi::FilterStatus::A_COEFF_MISSING); - dfd = difi::DigitalFilterd(Eigen::VectorXd::Constant(2, 1), Eigen::VectorXd()); - BOOST_REQUIRE(dfd.status() == difi::FilterStatus::B_COEFF_MISSING); - dfd = difi::DigitalFilterd(Eigen::VectorXd(), Eigen::VectorXd()); - BOOST_REQUIRE(dfd.status() == difi::FilterStatus::ALL_COEFF_MISSING); - dfd = difi::DigitalFilterd(Eigen::VectorXd::Constant(2, 0), Eigen::VectorXd::Constant(2, 0)); - BOOST_REQUIRE(dfd.status() == difi::FilterStatus::BAD_A_COEFF); - dfd = difi::DigitalFilterd(); - BOOST_REQUIRE(dfd.status() == difi::FilterStatus::NONE); - dfd = difi::DigitalFilterd(Eigen::VectorXd::Constant(2, 1), Eigen::VectorXd::Constant(2, 0)); - BOOST_REQUIRE(dfd.status() == difi::FilterStatus::READY); - auto mad = difi::MovingAveraged(0); - BOOST_REQUIRE(mad.status() == difi::FilterStatus::BAD_ORDER_SIZE); - auto bfd = difi::Butterworthd(0, 10, 100); - BOOST_REQUIRE(bfd.status() == difi::FilterStatus::BAD_ORDER_SIZE); - bfd = difi::Butterworthd(5, 6, 5, 100); - BOOST_REQUIRE(bfd.status() == difi::FilterStatus::BAD_BAND_FREQUENCY); + // A coeff are missing + BOOST_REQUIRE_THROW(difi::DigitalFilterd(Eigen::VectorXd(), Eigen::VectorXd::Constant(2, 0)), std::logic_error); + // B coeff are missing + BOOST_REQUIRE_THROW(difi::DigitalFilterd(Eigen::VectorXd::Constant(2, 1), Eigen::VectorXd()), std::logic_error); + // aCoeff(0) = 0 + BOOST_REQUIRE_THROW(difi::DigitalFilterd(Eigen::VectorXd::Constant(2, 0), Eigen::VectorXd::Constant(2, 0)), std::logic_error); + // Filter left uninitialized + BOOST_REQUIRE_NO_THROW(difi::DigitalFilterd()); + auto df = difi::DigitalFilterd(); + // Filter data with uninitialized filter + BOOST_REQUIRE_THROW(df.stepFilter(10.), std::logic_error); + // window <= 0 + BOOST_REQUIRE_THROW(difi::MovingAveraged(0), std::logic_error); + // order <= 0 + BOOST_REQUIRE_THROW(difi::Butterworthd(0, 10, 100), std::logic_error); + // fc > 2*fs + BOOST_REQUIRE_THROW(difi::Butterworthd(2, 60, 100), std::logic_error); + // Upper frequency < lower frequency + BOOST_REQUIRE_THROW(difi::Butterworthd(2, 6, 5, 100), std::logic_error); + + // Ok + BOOST_REQUIRE_NO_THROW(difi::DigitalFilterd(Eigen::VectorXd::Constant(2, 1), Eigen::VectorXd::Constant(2, 0))); }