aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2023-11-18 22:11:58 -0800
committerChris Robinson <[email protected]>2023-11-18 22:11:58 -0800
commitd6d572df66eb2b5beeb3da7f3b11a328500c33c3 (patch)
tree8ea2703148fecab9c06ca96d80eddfc47c673c56 /common
parent3ff5cb1f969f798450acb81359c7ff4f76c04e1e (diff)
Handle systems that don't support std::cyl_bessel_i
Diffstat (limited to 'common')
-rw-r--r--common/polyphase_resampler.cpp47
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);