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) if (m_settings.m_bandpassEnable)
{ {
demod = m_bandpass.filter(demod); demod = m_bandpass.filter(demod);
demod /= 301.0f;
} }
else else
{ {

Wyświetl plik

@ -121,7 +121,7 @@ void NFMDemodSink::processOneSample(Complex &ci)
if (m_settings.m_deltaSquelch) 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; m_afSquelchOpen = m_afSquelch.evaluate(); // ? m_squelchGate + m_squelchDecay : 0;
@ -132,7 +132,7 @@ void NFMDemodSink::processOneSample(Complex &ci)
if (m_afSquelchOpen) if (m_afSquelchOpen)
{ {
m_squelchDelayLine.write(demod * m_discriCompensation); m_squelchDelayLine.write(demod);
if (m_squelchCount < 2*m_squelchGate) { if (m_squelchCount < 2*m_squelchGate) {
m_squelchCount++; m_squelchCount++;
@ -159,7 +159,7 @@ void NFMDemodSink::processOneSample(Complex &ci)
} }
else else
{ {
m_squelchDelayLine.write(demod * m_discriCompensation); m_squelchDelayLine.write(demod);
if (m_squelchCount < 2*m_squelchGate) { if (m_squelchCount < 2*m_squelchGate) {
m_squelchCount++; m_squelchCount++;
@ -179,7 +179,7 @@ void NFMDemodSink::processOneSample(Complex &ci)
{ {
if (m_settings.m_ctcssOn) 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 if ((m_sampleCount & 7) == 7) // decimate 48k -> 6k
{ {
@ -224,7 +224,7 @@ void NFMDemodSink::processOneSample(Complex &ci)
else else
{ {
if (m_settings.m_highPass) { 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 { } else {
sample = m_lowpass.filter(m_squelchDelayLine.readBack(m_squelchGate)) * m_settings.m_volume * 301.0f; 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) { 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 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 { } 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_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_phaseDiscri.setFMScaling((8.0f*sampleRate) / static_cast<float>(m_settings.m_fmDeviation)); // integrate 4x factor
m_audioFifo.setSize(sampleRate); m_audioFifo.setSize(sampleRate);
m_squelchDelayLine.resize(sampleRate/2); m_squelchDelayLine.resize(sampleRate/2);
m_audioSampleRate = sampleRate; m_audioSampleRate = sampleRate;
} }

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -134,14 +134,11 @@ void NFMModSource::modulateSample()
calculateLevel(t); calculateLevel(t);
m_audioBufferFill++; m_audioBufferFill++;
if (m_settings.m_ctcssOn) // 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 * 189.0f * m_ctcssNco.next()) * (M_PI / 189.0f); 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 {
else m_modPhasor += (m_settings.m_fmDeviation / (float) m_audioSampleRate) * m_bandpass.filter(t) * (M_PI / 0.625f);
{
// 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);
} }
// limit phasor range to ]-pi,pi] // limit phasor range to ]-pi,pi]
@ -368,4 +365,4 @@ void NFMModSource::applyChannelSettings(int channelSampleRate, int channelFreque
m_channelSampleRate = channelSampleRate; m_channelSampleRate = channelSampleRate;
m_channelFrequencyOffset = channelFrequencyOffset; m_channelFrequencyOffset = channelFrequencyOffset;
} }

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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