diff options
author | Chris Robinson <[email protected]> | 2023-11-18 22:11:58 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2023-11-18 22:11:58 -0800 |
commit | d6d572df66eb2b5beeb3da7f3b11a328500c33c3 (patch) | |
tree | 8ea2703148fecab9c06ca96d80eddfc47c673c56 /common | |
parent | 3ff5cb1f969f798450acb81359c7ff4f76c04e1e (diff) |
Handle systems that don't support std::cyl_bessel_i
Diffstat (limited to 'common')
-rw-r--r-- | common/polyphase_resampler.cpp | 47 |
1 files changed, 45 insertions, 2 deletions
diff --git a/common/polyphase_resampler.cpp b/common/polyphase_resampler.cpp index f1e9eaf3..9c569b3a 100644 --- a/common/polyphase_resampler.cpp +++ b/common/polyphase_resampler.cpp @@ -4,6 +4,7 @@ #include <algorithm> #include <cmath> #include <numeric> +#include <stdexcept> #include "alnumbers.h" #include "opthelpers.h" @@ -15,6 +16,48 @@ constexpr double Epsilon{1e-9}; using uint = unsigned int; +#if __cpp_lib_math_special_functions >= 201603L +using std::cyl_bessel_i; + +#else + +/* The zero-order modified Bessel function of the first kind, used for the + * Kaiser window. + * + * I_0(x) = sum_{k=0}^inf (1 / k!)^2 (x / 2)^(2 k) + * = sum_{k=0}^inf ((x / 2)^k / k!)^2 + * + * This implementation only handles nu = 0, and isn't the most precise (it + * starts with the largest value and accumulates successively smaller values, + * compounding the rounding and precision error), but it's good enough. + */ +template<typename T, typename U> +U cyl_bessel_i(T nu, U x) +{ + if(nu != T{0}) + throw std::runtime_error{"cyl_bessel_i: nu != 0"}; + + /* Start at k=1 since k=0 is trivial. */ + const double x2{x/2.0}; + double term{1.0}; + double sum{1.0}; + int k{1}; + + /* Let the integration converge until the term of the sum is no longer + * significant. + */ + double last_sum{}; + do { + const double y{x2 / k}; + ++k; + last_sum = sum; + term *= y * y; + sum += term; + } while(sum != last_sum); + return static_cast<U>(sum); +} +#endif + /* This is the normalized cardinal sine (sinc) function. * * sinc(x) = { 1, x = 0 @@ -45,7 +88,7 @@ double Kaiser(const double beta, const double k, const double besseli_0_beta) { if(!(k >= -1.0 && k <= 1.0)) return 0.0; - return std::cyl_bessel_i(0, beta * std::sqrt(1.0 - k*k)) / besseli_0_beta; + return cyl_bessel_i(0, beta * std::sqrt(1.0 - k*k)) / besseli_0_beta; } /* Calculates the size (order) of the Kaiser window. Rejection is in dB and @@ -122,7 +165,7 @@ void PPhaseResampler::init(const uint srcRate, const uint dstRate) // calculating the left offset to avoid increasing the transition width. const uint l{(CalcKaiserOrder(180.0, width)+1) / 2}; const double beta{CalcKaiserBeta(180.0)}; - const double besseli_0_beta{std::cyl_bessel_i(0, beta)}; + const double besseli_0_beta{cyl_bessel_i(0, beta)}; mM = l*2 + 1; mL = l; mF.resize(mM); |