Corrected Highpass and Bandpass filters normalization. Implements #642

pull/647/head
f4exb 2020-09-21 23:51:45 +02:00
rodzic 71f96aded6
commit ae60808de8
8 zmienionych plików z 82 dodań i 73 usunięć

Wyświetl plik

@ -189,7 +189,6 @@ void AMDemodSink::processOneSample(Complex &ci)
if (m_settings.m_bandpassEnable)
{
demod = m_bandpass.filter(demod);
demod /= 301.0f;
}
else
{

Wyświetl plik

@ -121,7 +121,7 @@ void NFMDemodSink::processOneSample(Complex &ci)
if (m_settings.m_deltaSquelch)
{
if (m_afSquelch.analyze(demod * m_discriCompensation))
if (m_afSquelch.analyze(demod))
{
m_afSquelchOpen = m_afSquelch.evaluate(); // ? m_squelchGate + m_squelchDecay : 0;
@ -132,7 +132,7 @@ void NFMDemodSink::processOneSample(Complex &ci)
if (m_afSquelchOpen)
{
m_squelchDelayLine.write(demod * m_discriCompensation);
m_squelchDelayLine.write(demod);
if (m_squelchCount < 2*m_squelchGate) {
m_squelchCount++;
@ -159,7 +159,7 @@ void NFMDemodSink::processOneSample(Complex &ci)
}
else
{
m_squelchDelayLine.write(demod * m_discriCompensation);
m_squelchDelayLine.write(demod);
if (m_squelchCount < 2*m_squelchGate) {
m_squelchCount++;
@ -179,7 +179,7 @@ void NFMDemodSink::processOneSample(Complex &ci)
{
if (m_settings.m_ctcssOn)
{
Real ctcss_sample = m_ctcssLowpass.filter(demod * m_discriCompensation);
Real ctcss_sample = m_ctcssLowpass.filter(demod);
if ((m_sampleCount & 7) == 7) // decimate 48k -> 6k
{
@ -224,7 +224,7 @@ void NFMDemodSink::processOneSample(Complex &ci)
else
{
if (m_settings.m_highPass) {
sample = m_bandpass.filter(m_squelchDelayLine.readBack(m_squelchGate)) * m_settings.m_volume;
sample = m_bandpass.filter(m_squelchDelayLine.readBack(m_squelchGate)) * m_settings.m_volume * 301.0f;
} else {
sample = m_lowpass.filter(m_squelchDelayLine.readBack(m_squelchGate)) * m_settings.m_volume * 301.0f;
}
@ -375,17 +375,13 @@ void NFMDemodSink::applyAudioSampleRate(unsigned int sampleRate)
if (sampleRate < 16000) {
m_afSquelch.setCoefficients(sampleRate/2000, 600, sampleRate, 200, 0, afSqTones_lowrate); // 0.5ms test period, 300ms average span, audio SR, 100ms attack, no decay
} else {
m_afSquelch.setCoefficients(sampleRate/2000, 600, sampleRate, 200, 0, afSqTones); // 0.5ms test period, 300ms average span, audio SR, 100ms attack, no decay
}
m_discriCompensation = (sampleRate/48000.0f);
m_discriCompensation *= sqrt(m_discriCompensation);
m_phaseDiscri.setFMScaling((8.0f*sampleRate) / static_cast<float>(m_settings.m_fmDeviation)); // integrate 4x factor
m_audioFifo.setSize(sampleRate);
m_squelchDelayLine.resize(sampleRate/2);
m_audioSampleRate = sampleRate;
}
}

Wyświetl plik

@ -104,8 +104,6 @@ private:
uint m_audioBufferFill;
AudioFifo m_audioFifo;
float m_discriCompensation; //!< compensation factor that depends on audio rate (1 for 48 kS/s)
NCO m_nco;
Interpolator m_interpolator;
Real m_interpolatorDistance;

Wyświetl plik

@ -256,7 +256,6 @@ void UDPSinkSink::feed(const SampleVector::const_iterator& begin, const SampleVe
{
double demodf = sqrt(inMagSq);
demodf = m_bandpass.filter(demodf);
demodf /= 301.0;
Real amplitude = demodf * agcFactor * m_settings.m_gain;
FixReal demod = (FixReal) amplitude;
udpWriteMono(demod);

Wyświetl plik

@ -134,14 +134,11 @@ void NFMModSource::modulateSample()
calculateLevel(t);
m_audioBufferFill++;
if (m_settings.m_ctcssOn)
{
m_modPhasor += (m_settings.m_fmDeviation / (float) m_audioSampleRate) * (0.85f * m_bandpass.filter(t) + 0.15f * 189.0f * m_ctcssNco.next()) * (M_PI / 189.0f);
}
else
{
// 378 = 302 * 1.25; 302 = number of filter taps (established experimentally) and 189 = 378/2 for 2*PI
m_modPhasor += (m_settings.m_fmDeviation / (float) m_audioSampleRate) * m_bandpass.filter(t) * (M_PI / 189.0f);
// 0.625 = 1/1.25 (heuristic)
if (m_settings.m_ctcssOn) {
m_modPhasor += (m_settings.m_fmDeviation / (float) m_audioSampleRate) * (0.85f * m_bandpass.filter(t) + 0.15f * 0.625f * m_ctcssNco.next()) * (M_PI / 0.625f);
} else {
m_modPhasor += (m_settings.m_fmDeviation / (float) m_audioSampleRate) * m_bandpass.filter(t) * (M_PI / 0.625f);
}
// limit phasor range to ]-pi,pi]
@ -368,4 +365,4 @@ void NFMModSource::applyChannelSettings(int channelSampleRate, int channelFreque
m_channelSampleRate = channelSampleRate;
m_channelFrequencyOffset = channelFrequencyOffset;
}
}

Wyświetl plik

@ -23,27 +23,34 @@ public:
int i;
// check constraints
if(!(nTaps & 1)) {
if (!(nTaps & 1))
{
qDebug("Bandpass filter has to have an odd number of taps");
nTaps++;
}
// make room
m_samples.resize(nTaps);
for(int i = 0; i < nTaps; i++)
for (int i = 0; i < nTaps; i++) {
m_samples[i] = 0;
}
m_ptr = 0;
m_taps.resize(nTaps / 2 + 1);
taps_lp.resize(nTaps / 2 + 1);
taps_hp.resize(nTaps / 2 + 1);
// generate Sinc filter core
for(i = 0; i < nTaps / 2 + 1; i++) {
if(i == (nTaps - 1) / 2) {
for (i = 0; i < nTaps / 2 + 1; i++)
{
if (i == (nTaps - 1) / 2)
{
taps_lp[i] = Wch / M_PI;
taps_hp[i] = -(Wcl / M_PI);
}
else {
else
{
taps_lp[i] = sin(((double)i - ((double)nTaps - 1.0) / 2.0) * Wch) / (((double)i - ((double)nTaps - 1.0) / 2.0) * M_PI);
taps_hp[i] = -sin(((double)i - ((double)nTaps - 1.0) / 2.0) * Wcl) / (((double)i - ((double)nTaps - 1.0) / 2.0) * M_PI);
}
@ -52,7 +59,8 @@ public:
taps_hp[(nTaps - 1) / 2] += 1;
// apply Hamming window and combine lowpass and highpass
for(i = 0; i < nTaps / 2 + 1; i++) {
for (i = 0; i < nTaps / 2 + 1; i++)
{
taps_lp[i] *= 0.54 + 0.46 * cos((2.0 * M_PI * ((double)i - ((double)nTaps - 1.0) / 2.0)) / (double)nTaps);
taps_hp[i] *= 0.54 + 0.46 * cos((2.0 * M_PI * ((double)i - ((double)nTaps - 1.0) / 2.0)) / (double)nTaps);
m_taps[i] = -(taps_lp[i]+taps_hp[i]);
@ -63,13 +71,13 @@ public:
// normalize
Real sum = 0;
for(i = 0; i < (int)m_taps.size() - 1; i++) {
for (i = 0; i < (int)m_taps.size() - 1; i++) {
sum += m_taps[i] * 2;
}
sum += m_taps[i];
sum += m_taps[i] - 1;
for(i = 0; i < (int)m_taps.size(); i++) {
for (i = 0; i < (int)m_taps.size(); i++) {
m_taps[i] /= sum;
}
}
@ -84,8 +92,7 @@ public:
m_samples[m_ptr] = sample;
size = m_samples.size(); // Valgrind optim (2)
while(b < 0)
{
while (b < 0) {
b += size;
}
@ -96,15 +103,13 @@ public:
acc += (m_samples[a] + m_samples[b]) * m_taps[i];
a++;
while (a >= size)
{
while (a >= size) {
a -= size;
}
b--;
while(b < 0)
{
while(b < 0) {
b += size;
}
}
@ -113,8 +118,7 @@ public:
m_ptr++;
while (m_ptr >= size)
{
while (m_ptr >= size) {
m_ptr -= size;
}

Wyświetl plik

@ -16,39 +16,51 @@ public:
int i;
// check constraints
if(!(nTaps & 1)) {
if (!(nTaps & 1))
{
qDebug("Highpass filter has to have an odd number of taps");
nTaps++;
}
// make room
m_samples.resize(nTaps);
for(int i = 0; i < nTaps; i++)
for (int i = 0; i < nTaps; i++) {
m_samples[i] = 0;
}
m_ptr = 0;
m_taps.resize(nTaps / 2 + 1);
// generate Sinc filter core for lowpass but inverting every other tap for highpass keeping center tap
for(i = 0; i < nTaps / 2 + 1; i++) {
if(i == (nTaps - 1) / 2)
for (i = 0; i < nTaps / 2 + 1; i++)
{
if (i == (nTaps - 1) / 2) {
m_taps[i] = -(Wc / M_PI);
else
} else {
m_taps[i] = -sin(((double)i - ((double)nTaps - 1.0) / 2.0) * Wc) / (((double)i - ((double)nTaps - 1.0) / 2.0) * M_PI);
}
}
m_taps[(nTaps - 1) / 2] += 1;
// apply Hamming window
for(i = 0; i < nTaps / 2 + 1; i++)
for (i = 0; i < nTaps / 2 + 1; i++) {
m_taps[i] *= 0.54 + 0.46 * cos((2.0 * M_PI * ((double)i - ((double)nTaps - 1.0) / 2.0)) / (double)nTaps);
}
// normalize
Real sum = 0;
for(i = 0; i < (int)m_taps.size() - 1; i++)
for (i = 0; i < (int)m_taps.size() - 1; i++) {
sum += m_taps[i] * 2;
sum += m_taps[i];
for(i = 0; i < (int)m_taps.size(); i++)
}
sum += m_taps[i] - 1;
for (i = 0; i < (int)m_taps.size(); i++) {
m_taps[i] /= sum;
}
}
Type filter(Type sample)
@ -61,8 +73,7 @@ public:
m_samples[m_ptr] = sample;
size = m_samples.size(); // Valgrind optim (2)
while(b < 0)
{
while (b < 0) {
b += size;
}
@ -73,15 +84,13 @@ public:
acc += (m_samples[a] + m_samples[b]) * m_taps[i];
a++;
while (a >= size)
{
while (a >= size) {
a -= size;
}
b--;
while (b < 0)
{
while (b < 0) {
b += size;
}
}
@ -89,8 +98,7 @@ public:
acc += m_samples[a] * m_taps[i];
m_ptr++;
while (m_ptr >= size)
{
while (m_ptr >= size) {
m_ptr -= size;
}

Wyświetl plik

@ -16,37 +16,49 @@ public:
int i;
// check constraints
if(!(nTaps & 1)) {
if (!(nTaps & 1))
{
qDebug("Lowpass filter has to have an odd number of taps");
nTaps++;
}
// make room
m_samples.resize(nTaps);
for(int i = 0; i < nTaps; i++)
for (int i = 0; i < nTaps; i++) {
m_samples[i] = 0;
}
m_ptr = 0;
m_taps.resize(nTaps / 2 + 1);
// generate Sinc filter core
for(i = 0; i < nTaps / 2 + 1; i++) {
if(i == (nTaps - 1) / 2)
for (i = 0; i < nTaps / 2 + 1; i++)
{
if(i == (nTaps - 1) / 2) {
m_taps[i] = Wc / M_PI;
else
} else {
m_taps[i] = sin(((double)i - ((double)nTaps - 1.0) / 2.0) * Wc) / (((double)i - ((double)nTaps - 1.0) / 2.0) * M_PI);
}
}
// apply Hamming window
for(i = 0; i < nTaps / 2 + 1; i++)
for (i = 0; i < nTaps / 2 + 1; i++) {
m_taps[i] *= 0.54 + 0.46 * cos((2.0 * M_PI * ((double)i - ((double)nTaps - 1.0) / 2.0)) / (double)nTaps);
}
// normalize
Real sum = 0;
for(i = 0; i < (int)m_taps.size() - 1; i++)
for (i = 0; i < (int)m_taps.size() - 1; i++) {
sum += m_taps[i] * 2;
}
sum += m_taps[i];
for(i = 0; i < (int)m_taps.size(); i++)
for (i = 0; i < (int)m_taps.size(); i++) {
m_taps[i] /= sum;
}
}
Type filter(Type sample)
@ -59,8 +71,7 @@ public:
m_samples[m_ptr] = sample;
size = m_samples.size(); // Valgrind optim (2)
while (b < 0)
{
while (b < 0) {
b += size;
}
@ -71,15 +82,13 @@ public:
acc += (m_samples[a] + m_samples[b]) * m_taps[i];
a++;
while (a >= size)
{
while (a >= size) {
a -= size;
}
b--;
while(b < 0)
{
while(b < 0) {
b += size;
}
}
@ -87,8 +96,7 @@ public:
acc += m_samples[a] * m_taps[i];
m_ptr++;
while(m_ptr >= size)
{
while(m_ptr >= size) {
m_ptr -= size;
}