improve interpolator
This commit is contained in:
parent
37e6f6ff29
commit
fb090cdfa9
|
@ -8,7 +8,7 @@ class SDRANGELOVE_API Interpolator {
|
||||||
public:
|
public:
|
||||||
Interpolator();
|
Interpolator();
|
||||||
|
|
||||||
void create(int nTaps, int phaseSteps, double sampleRate, double cutoff);
|
void create(int phaseSteps, double sampleRate, double cutoff);
|
||||||
|
|
||||||
bool interpolate(Real* distance, const Complex& next, bool* consumed, Complex* result)
|
bool interpolate(Real* distance, const Complex& next, bool* consumed, Complex* result)
|
||||||
{
|
{
|
||||||
|
@ -39,7 +39,7 @@ private:
|
||||||
{
|
{
|
||||||
m_ptr--;
|
m_ptr--;
|
||||||
if(m_ptr < 0)
|
if(m_ptr < 0)
|
||||||
m_ptr = m_nTaps;
|
m_ptr = m_nTaps - 1;
|
||||||
m_samples[m_ptr] = next;
|
m_samples[m_ptr] = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,9 +53,7 @@ private:
|
||||||
for(int i = 0; i < m_nTaps; i++) {
|
for(int i = 0; i < m_nTaps; i++) {
|
||||||
rAcc += *coeff * m_samples[sample].real();
|
rAcc += *coeff * m_samples[sample].real();
|
||||||
iAcc += *coeff * m_samples[sample].imag();
|
iAcc += *coeff * m_samples[sample].imag();
|
||||||
sample++;
|
sample = (sample + 1) % m_nTaps;
|
||||||
if(sample >= m_nTaps)
|
|
||||||
sample = 0;
|
|
||||||
coeff++;
|
coeff++;
|
||||||
}
|
}
|
||||||
*result = Complex(rAcc, iAcc);
|
*result = Complex(rAcc, iAcc);
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
/*
|
/*
|
||||||
* supported filter orders: 64, 48, 32
|
* supported filter orders: 64, 48, 32
|
||||||
*/
|
*/
|
||||||
#define HB_FILTERORDER 48
|
#define HB_FILTERORDER 32
|
||||||
#define HB_SHIFT 14
|
#define HB_SHIFT 14
|
||||||
|
|
||||||
class SDRANGELOVE_API IntHalfbandFilter {
|
class SDRANGELOVE_API IntHalfbandFilter {
|
||||||
|
|
|
@ -35,7 +35,7 @@ NFMDemod::NFMDemod(AudioFifo* audioFifo, SampleSink* sampleSink) :
|
||||||
m_squelchLevel *= m_squelchLevel;
|
m_squelchLevel *= m_squelchLevel;
|
||||||
|
|
||||||
m_nco.setFreq(m_frequency, m_sampleRate);
|
m_nco.setFreq(m_frequency, m_sampleRate);
|
||||||
m_interpolator.create(1, 32, 32 * m_sampleRate, 12500);
|
m_interpolator.create(16, m_sampleRate, 12500);
|
||||||
m_sampleDistanceRemain = (Real)m_sampleRate / 44100.0;
|
m_sampleDistanceRemain = (Real)m_sampleRate / 44100.0;
|
||||||
|
|
||||||
m_lowpass.create(21, 44100, 3000);
|
m_lowpass.create(21, 44100, 3000);
|
||||||
|
@ -123,7 +123,7 @@ bool NFMDemod::handleMessage(Message* cmd)
|
||||||
qDebug("%d samples/sec, %lld Hz offset", signal->getSampleRate(), signal->getFrequencyOffset());
|
qDebug("%d samples/sec, %lld Hz offset", signal->getSampleRate(), signal->getFrequencyOffset());
|
||||||
m_sampleRate = signal->getSampleRate();
|
m_sampleRate = signal->getSampleRate();
|
||||||
m_nco.setFreq(-signal->getFrequencyOffset(), m_sampleRate);
|
m_nco.setFreq(-signal->getFrequencyOffset(), m_sampleRate);
|
||||||
m_interpolator.create(25, 32, 32 * m_sampleRate, m_rfBandwidth / 2.0);
|
m_interpolator.create(16, m_sampleRate, m_rfBandwidth / 2.1);
|
||||||
m_sampleDistanceRemain = m_sampleRate / 44100.0;
|
m_sampleDistanceRemain = m_sampleRate / 44100.0;
|
||||||
m_squelchState = 0;
|
m_squelchState = 0;
|
||||||
cmd->completed();
|
cmd->completed();
|
||||||
|
@ -131,7 +131,7 @@ bool NFMDemod::handleMessage(Message* cmd)
|
||||||
} else if(cmd->id() == MsgConfigureNFMDemod::ID()) {
|
} else if(cmd->id() == MsgConfigureNFMDemod::ID()) {
|
||||||
MsgConfigureNFMDemod* cfg = (MsgConfigureNFMDemod*)cmd;
|
MsgConfigureNFMDemod* cfg = (MsgConfigureNFMDemod*)cmd;
|
||||||
m_rfBandwidth = cfg->getRFBandwidth();
|
m_rfBandwidth = cfg->getRFBandwidth();
|
||||||
m_interpolator.create(25, 32, 32 * m_sampleRate, m_rfBandwidth / 2.0);
|
m_interpolator.create(16, m_sampleRate, m_rfBandwidth / 2.1);
|
||||||
m_lowpass.create(21, 44100, cfg->getAFBandwidth());
|
m_lowpass.create(21, 44100, cfg->getAFBandwidth());
|
||||||
m_squelchLevel = pow(10.0, cfg->getSquelch() / 20.0);
|
m_squelchLevel = pow(10.0, cfg->getSquelch() / 20.0);
|
||||||
m_squelchLevel *= m_squelchLevel;
|
m_squelchLevel *= m_squelchLevel;
|
||||||
|
|
|
@ -30,7 +30,7 @@ TetraDemod::TetraDemod(SampleSink* sampleSink) :
|
||||||
m_frequency = 0;
|
m_frequency = 0;
|
||||||
|
|
||||||
m_nco.setFreq(m_frequency, m_sampleRate);
|
m_nco.setFreq(m_frequency, m_sampleRate);
|
||||||
m_interpolator.create(1, 32, 32 * m_sampleRate, 36000);
|
m_interpolator.create(32, 32 * m_sampleRate, 36000);
|
||||||
m_sampleDistanceRemain = (Real)m_sampleRate / 36000.0;
|
m_sampleDistanceRemain = (Real)m_sampleRate / 36000.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ bool TetraDemod::handleMessage(Message* cmd)
|
||||||
qDebug("%d samples/sec, %lld Hz offset", signal->getSampleRate(), signal->getFrequencyOffset());
|
qDebug("%d samples/sec, %lld Hz offset", signal->getSampleRate(), signal->getFrequencyOffset());
|
||||||
m_sampleRate = signal->getSampleRate();
|
m_sampleRate = signal->getSampleRate();
|
||||||
m_nco.setFreq(-signal->getFrequencyOffset(), m_sampleRate);
|
m_nco.setFreq(-signal->getFrequencyOffset(), m_sampleRate);
|
||||||
m_interpolator.create(51, 32, 32 * m_sampleRate, 25000 / 2);
|
m_interpolator.create(32, m_sampleRate, 25000 / 2);
|
||||||
m_sampleDistanceRemain = m_sampleRate / 36000.0;
|
m_sampleDistanceRemain = m_sampleRate / 36000.0;
|
||||||
cmd->completed();
|
cmd->completed();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,56 +1,82 @@
|
||||||
#define _USE_MATH_DEFINES
|
#define _USE_MATH_DEFINES
|
||||||
|
#include <stdio.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "dsp/interpolator.h"
|
#include "dsp/interpolator.h"
|
||||||
|
|
||||||
|
static std::vector<Real> createPolyphaseLowPass(
|
||||||
|
int phaseSteps,
|
||||||
|
double gain,
|
||||||
|
double sampleRateHz,
|
||||||
|
double cutoffFreqHz,
|
||||||
|
double transitionWidthHz,
|
||||||
|
double oobAttenuationdB)
|
||||||
|
{
|
||||||
|
int ntaps = (int)(oobAttenuationdB * sampleRateHz / (22.0 * transitionWidthHz));
|
||||||
|
if((ntaps % 2) == 0)
|
||||||
|
ntaps++;
|
||||||
|
ntaps *= phaseSteps;
|
||||||
|
|
||||||
|
std::vector<float> taps(ntaps);
|
||||||
|
std::vector<float> window(ntaps);
|
||||||
|
|
||||||
|
for(int n = 0; n < ntaps; n++)
|
||||||
|
window[n] = 0.54 - 0.46 * cos ((2 * M_PI * n) / (ntaps - 1));
|
||||||
|
|
||||||
|
int M = (ntaps - 1) / 2;
|
||||||
|
double fwT0 = 2 * M_PI * cutoffFreqHz / sampleRateHz;
|
||||||
|
for(int n = -M; n <= M; n++) {
|
||||||
|
if(n == 0) taps[n + M] = fwT0 / M_PI * window[n + M];
|
||||||
|
else taps[n + M] = sin (n * fwT0) / (n * M_PI) * window[n + M];
|
||||||
|
}
|
||||||
|
|
||||||
|
double max = taps[0 + M];
|
||||||
|
for(int n = 1; n <= M; n++)
|
||||||
|
max += 2.0 * taps[n + M];
|
||||||
|
|
||||||
|
gain /= max;
|
||||||
|
|
||||||
|
for(int i = 0; i < ntaps; i++)
|
||||||
|
taps[i] *= gain;
|
||||||
|
|
||||||
|
return taps;
|
||||||
|
}
|
||||||
|
|
||||||
Interpolator::Interpolator()
|
Interpolator::Interpolator()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpolator::create(int nTaps, int phaseSteps, double sampleRate, double cutoff)
|
void Interpolator::create(int phaseSteps, double sampleRate, double cutoff)
|
||||||
{
|
{
|
||||||
double wc = 2.0 * M_PI * cutoff;
|
std::vector<Real> taps = createPolyphaseLowPass(
|
||||||
double Wc = wc / sampleRate;
|
phaseSteps, // number of polyphases
|
||||||
int numTaps = nTaps * phaseSteps;
|
1.0, // gain
|
||||||
int i;
|
phaseSteps * sampleRate, // sampling frequency
|
||||||
Real sum;
|
cutoff, // hz beginning of transition band
|
||||||
|
sampleRate / 5.0, // hz width of transition band
|
||||||
|
20.0); // out of band attenuation
|
||||||
|
|
||||||
// make room
|
// init state
|
||||||
m_samples.resize(nTaps * 2);
|
|
||||||
for(int i = 0; i < nTaps; i++)
|
|
||||||
m_samples[i] = 0;
|
|
||||||
m_ptr = 0;
|
m_ptr = 0;
|
||||||
m_nTaps = nTaps;
|
m_nTaps = taps.size() / phaseSteps;
|
||||||
m_phaseSteps = phaseSteps;
|
m_phaseSteps = phaseSteps;
|
||||||
m_taps.resize(numTaps);
|
m_taps.resize(taps.size());
|
||||||
|
m_samples.resize(m_nTaps);
|
||||||
std::vector<Real> taps;
|
for(int i = 0; i < m_nTaps; i++)
|
||||||
taps.resize(numTaps);
|
m_samples[i] = 0;
|
||||||
|
|
||||||
// generate Sinc filter core
|
|
||||||
for(i = 0; i < numTaps; i++) {
|
|
||||||
if(i == (numTaps - 1) / 2)
|
|
||||||
taps[i] = Wc / M_PI;
|
|
||||||
else
|
|
||||||
taps[i] = sin(((double)i - ((double)numTaps - 1.0) / 2.0) * Wc) / (((double)i - ((double)numTaps - 1.0) / 2.0) * M_PI);
|
|
||||||
}
|
|
||||||
|
|
||||||
// apply Hamming window
|
|
||||||
for(i = 0; i < numTaps; i++)
|
|
||||||
taps[i] *= 0.54 + 0.46 * cos((2.0 * M_PI * ((double)i - ((double)numTaps - 1.0) / 2.0)) / (double)numTaps);
|
|
||||||
|
|
||||||
// copy to filter taps
|
// copy to filter taps
|
||||||
for(int phase = 0; phase < phaseSteps; phase++) {
|
for(int phase = 0; phase < phaseSteps; phase++) {
|
||||||
for(int i = 0; i < nTaps; i++)
|
for(int i = 0; i < m_nTaps; i++)
|
||||||
m_taps[phase * nTaps + i] = taps[i* phaseSteps + phase];
|
m_taps[phase * m_nTaps + i] = taps[i * phaseSteps + phase];
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalize phase filters
|
// normalize phase filters
|
||||||
for(int phase = 0; phase < phaseSteps; phase++) {
|
for(int phase = 0; phase < phaseSteps; phase++) {
|
||||||
sum = 0;
|
Real sum = 0;
|
||||||
for(i = phase * nTaps; i < phase * nTaps + nTaps; i++)
|
for(int i = phase * m_nTaps; i < phase * m_nTaps + m_nTaps; i++)
|
||||||
sum += m_taps[i];
|
sum += m_taps[i];
|
||||||
for(i = phase * nTaps; i < phase * nTaps + nTaps; i++)
|
for(int i = phase * m_nTaps; i < phase * m_nTaps + m_nTaps; i++)
|
||||||
m_taps[i] /= sum;
|
m_taps[i] /= sum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue