/* Copyright (C) 2015-2017 Sergey V. Mikayev
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
#include
#ifdef SRCTOOLS_SINC_RESAMPLER_DEBUG_LOG
#include
#endif
#include "../include/SincResampler.h"
#ifndef M_PI
static const double M_PI = 3.1415926535897932;
#endif
using namespace SRCTools;
using namespace SincResampler;
using namespace Utils;
void Utils::computeResampleFactors(unsigned int &upsampleFactor, double &downsampleFactor, const double inputFrequency, const double outputFrequency, const unsigned int maxUpsampleFactor) {
static const double RATIONAL_RATIO_ACCURACY_FACTOR = 1E15;
upsampleFactor = static_cast(outputFrequency);
unsigned int downsampleFactorInt = static_cast(inputFrequency);
if ((upsampleFactor == outputFrequency) && (downsampleFactorInt == inputFrequency)) {
// Input and output frequencies are integers, try to reduce them
const unsigned int gcd = greatestCommonDivisor(upsampleFactor, downsampleFactorInt);
if (gcd > 1) {
upsampleFactor /= gcd;
downsampleFactor = downsampleFactorInt / gcd;
} else {
downsampleFactor = downsampleFactorInt;
}
if (upsampleFactor <= maxUpsampleFactor) return;
} else {
// Try to recover rational resample ratio by brute force
double inputToOutputRatio = inputFrequency / outputFrequency;
for (unsigned int i = 1; i <= maxUpsampleFactor; ++i) {
double testFactor = inputToOutputRatio * i;
if (floor(RATIONAL_RATIO_ACCURACY_FACTOR * testFactor + 0.5) == RATIONAL_RATIO_ACCURACY_FACTOR * floor(testFactor + 0.5)) {
// inputToOutputRatio found to be rational within the accuracy
upsampleFactor = i;
downsampleFactor = floor(testFactor + 0.5);
return;
}
}
}
// Use interpolation of FIR taps as the last resort
upsampleFactor = maxUpsampleFactor;
downsampleFactor = maxUpsampleFactor * inputFrequency / outputFrequency;
}
unsigned int Utils::greatestCommonDivisor(unsigned int a, unsigned int b) {
while (0 < b) {
unsigned int r = a % b;
a = b;
b = r;
}
return a;
}
double KaizerWindow::estimateBeta(double dbRipple) {
return 0.1102 * (dbRipple - 8.7);
}
unsigned int KaizerWindow::estimateOrder(double dbRipple, double fp, double fs) {
const double transBW = (fs - fp);
return static_cast(ceil((dbRipple - 8) / (2.285 * 2 * M_PI * transBW)));
}
double KaizerWindow::bessel(const double x) {
static const double EPS = 1.11E-16;
double sum = 0.0;
double f = 1.0;
for (unsigned int i = 1;; ++i) {
f *= (0.5 * x / i);
double f2 = f * f;
if (f2 <= sum * EPS) break;
sum += f2;
}
return 1.0 + sum;
}
void KaizerWindow::windowedSinc(FIRCoefficient kernel[], const unsigned int order, const double fc, const double beta, const double amp) {
const double fc_pi = M_PI * fc;
const double recipOrder = 1.0 / order;
const double mult = 2.0 * fc * amp / bessel(beta);
for (int i = order, j = 0; 0 <= i; i -= 2, ++j) {
double xw = i * recipOrder;
double win = bessel(beta * sqrt(fabs(1.0 - xw * xw)));
double xs = i * fc_pi;
double sinc = (i == 0) ? 1.0 : sin(xs) / xs;
FIRCoefficient imp = FIRCoefficient(mult * sinc * win);
kernel[j] = imp;
kernel[order - j] = imp;
}
}
ResamplerStage *SincResampler::createSincResampler(const double inputFrequency, const double outputFrequency, const double passbandFrequency, const double stopbandFrequency, const double dbSNR, const unsigned int maxUpsampleFactor) {
unsigned int upsampleFactor;
double downsampleFactor;
computeResampleFactors(upsampleFactor, downsampleFactor, inputFrequency, outputFrequency, maxUpsampleFactor);
double baseSamplePeriod = 1.0 / (inputFrequency * upsampleFactor);
double fp = passbandFrequency * baseSamplePeriod;
double fs = stopbandFrequency * baseSamplePeriod;
double fc = 0.5 * (fp + fs);
double beta = KaizerWindow::estimateBeta(dbSNR);
unsigned int order = KaizerWindow::estimateOrder(dbSNR, fp, fs);
const unsigned int kernelLength = order + 1;
#ifdef SRCTOOLS_SINC_RESAMPLER_DEBUG_LOG
std::clog << "FIR: " << upsampleFactor << "/" << downsampleFactor << ", N=" << kernelLength << ", NPh=" << kernelLength / double(upsampleFactor) << ", C=" << 0.5 / fc << ", fp=" << fp << ", fs=" << fs << ", M=" << maxUpsampleFactor << std::endl;
#endif
FIRCoefficient *windowedSincKernel = new FIRCoefficient[kernelLength];
KaizerWindow::windowedSinc(windowedSincKernel, order, fc, beta, upsampleFactor);
ResamplerStage *windowedSincStage = new FIRResampler(upsampleFactor, downsampleFactor, windowedSincKernel, kernelLength);
delete[] windowedSincKernel;
return windowedSincStage;
}