DSD demod: highpass filtering for audio completed

This commit is contained in:
f4exb 2017-12-26 20:29:23 +01:00
parent e1d7ada5dd
commit 2145eeb7b6
15 changed files with 174 additions and 78 deletions

View File

@ -70,6 +70,7 @@ public:
void setAudioGain(float gain) { m_decoder.setAudioGain(gain); }
void setBaudRate(int baudRate);
void setSymbolPLLLock(bool pllLock) { m_decoder.setSymbolPLLLock(pllLock); }
void useHPMbelib(bool useHP) { m_decoder.useHPMbelib(useHP); }
private:
DSDcc::DSDDecoder m_decoder;

View File

@ -215,6 +215,7 @@ void DSDDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto
m_dsdDecoder.getMbeRateIndex(),
m_settings.m_volume * 10.0,
m_settings.m_tdmaStereo ? 1 : 3, // left or both channels
m_settings.m_highPassFilter,
&m_audioFifo1);
}
@ -230,6 +231,7 @@ void DSDDemod::feed(const SampleVector::const_iterator& begin, const SampleVecto
m_dsdDecoder.getMbeRateIndex(),
m_settings.m_volume * 10.0,
m_settings.m_tdmaStereo ? 2 : 3, // right or both channels
m_settings.m_highPassFilter,
&m_audioFifo2);
}
@ -375,6 +377,7 @@ bool DSDDemod::handleMessage(const Message& cmd)
<< " m_udpCopyAudio: " << m_settings.m_udpCopyAudio
<< " m_udpAddress: " << m_settings.m_udpAddress
<< " m_udpPort: " << m_settings.m_udpPort
<< " m_highPassFilter: "<< m_settings.m_highPassFilter
<< " force: " << cfg.getForce();
return true;
@ -467,6 +470,11 @@ void DSDDemod::applySettings(DSDDemodSettings& settings, bool force)
m_audioFifo2.setCopyToUDP(settings.m_slot2On && !settings.m_slot1On && settings.m_udpCopyAudio);
}
if ((settings.m_highPassFilter != m_settings.m_highPassFilter) || force)
{
m_dsdDecoder.useHPMbelib(settings.m_highPassFilter);
}
m_settings = settings;
}

View File

@ -196,6 +196,12 @@ void DSDDemodGUI::on_audioMute_toggled(bool checked)
applySettings();
}
void DSDDemodGUI::on_highPassFilter_toggled(bool checked)
{
m_settings.m_highPassFilter = checked;
applySettings();
}
void DSDDemodGUI::on_symbolPLLLock_toggled(bool checked)
{
if (checked) {

View File

@ -124,6 +124,7 @@ private slots:
void on_fmDeviation_valueChanged(int value);
void on_squelchGate_valueChanged(int value);
void on_squelch_valueChanged(int value);
void on_highPassFilter_toggled(bool checked);
void on_audioMute_toggled(bool checked);
void on_symbolPLLLock_toggled(bool checked);
void on_udpOutput_toggled(bool checked);

View File

@ -584,7 +584,7 @@
<widget class="QLabel" name="volumeText">
<property name="minimumSize">
<size>
<width>0</width>
<width>30</width>
<height>0</height>
</size>
</property>
@ -598,7 +598,7 @@
<string>Sound volume</string>
</property>
<property name="text">
<string>2.0</string>
<string>10.0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@ -828,7 +828,7 @@
<item>
<widget class="ButtonSwitch" name="highPassFilter">
<property name="toolTip">
<string>Toggle audio high pass filter for mbelib (&gt;300 Hz)</string>
<string>Toggle audio high pass filter</string>
</property>
<property name="text">
<string/>

View File

@ -134,8 +134,6 @@ void FileSourceGui::handleInputMessages()
while ((message = m_inputMessageQueue.pop()) != 0)
{
qDebug("FileSourceGui::handleInputMessages: message: %s", message->getIdentifier());
if (DSPSignalNotification::match(*message))
{
DSPSignalNotification* notif = (DSPSignalNotification*) message;

View File

@ -231,9 +231,15 @@ void DSPEngine::getDVSerialNames(std::vector<std::string>& deviceNames __attribu
#endif
#ifdef DSD_USE_SERIALDV
void DSPEngine::pushMbeFrame(const unsigned char *mbeFrame, int mbeRateIndex, int mbeVolumeIndex, unsigned char channels, AudioFifo *audioFifo)
void DSPEngine::pushMbeFrame(
const unsigned char *mbeFrame,
int mbeRateIndex,
int mbeVolumeIndex,
unsigned char channels,
bool useHP,
AudioFifo *audioFifo)
{
m_dvSerialEngine.pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, audioFifo);
m_dvSerialEngine.pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useHP, audioFifo);
}
#else
void DSPEngine::pushMbeFrame(
@ -241,6 +247,7 @@ void DSPEngine::pushMbeFrame(
int mbeRateIndex __attribute((unused)),
int mbeVolumeIndex __attribute((unused)),
unsigned char channels __attribute((unused)),
bool useHP __attribute((unused)),
AudioFifo *audioFifo __attribute((unused)))
{}
#endif

View File

@ -78,7 +78,13 @@ public:
bool hasDVSerialSupport();
void setDVSerialSupport(bool support);
void getDVSerialNames(std::vector<std::string>& deviceNames);
void pushMbeFrame(const unsigned char *mbeFrame, int mbeRateIndex, int mbeVolumeIndex, unsigned char channels, AudioFifo *audioFifo);
void pushMbeFrame(
const unsigned char *mbeFrame,
int mbeRateIndex,
int mbeVolumeIndex,
unsigned char channels,
bool useHP,
AudioFifo *audioFifo);
const QTimer& getMasterTimer() const { return m_masterTimer; }

View File

@ -247,7 +247,13 @@ void DVSerialEngine::getDevicesNames(std::vector<std::string>& deviceNames)
}
}
void DVSerialEngine::pushMbeFrame(const unsigned char *mbeFrame, int mbeRateIndex, int mbeVolumeIndex, unsigned char channels, AudioFifo *audioFifo)
void DVSerialEngine::pushMbeFrame(
const unsigned char *mbeFrame,
int mbeRateIndex,
int mbeVolumeIndex,
unsigned char channels,
bool useLP,
AudioFifo *audioFifo)
{
std::vector<DVSerialController>::iterator it = m_controllers.begin();
std::vector<DVSerialController>::iterator itAvail = m_controllers.end();
@ -258,7 +264,7 @@ void DVSerialEngine::pushMbeFrame(const unsigned char *mbeFrame, int mbeRateInde
{
if (it->worker->hasFifo(audioFifo))
{
it->worker->pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, audioFifo);
it->worker->pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useLP, audioFifo);
done = true;
}
else if (it->worker->isAvailable())
@ -276,7 +282,7 @@ void DVSerialEngine::pushMbeFrame(const unsigned char *mbeFrame, int mbeRateInde
int wNum = itAvail - m_controllers.begin();
qDebug("DVSerialEngine::pushMbeFrame: push %p on empty queue %d", audioFifo, wNum);
itAvail->worker->pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, audioFifo);
itAvail->worker->pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useLP, audioFifo);
}
else
{

View File

@ -41,7 +41,13 @@ public:
int getNbDevices() const { return m_controllers.size(); }
void getDevicesNames(std::vector<std::string>& devicesNames);
void pushMbeFrame(const unsigned char *mbeFrame, int mbeRateIndex, int mbeVolumeIndex, unsigned char channels, AudioFifo *audioFifo);
void pushMbeFrame(
const unsigned char *mbeFrame,
int mbeRateIndex,
int mbeVolumeIndex,
unsigned char channels,
bool useHP,
AudioFifo *audioFifo);
private:
struct DVSerialController

View File

@ -81,6 +81,8 @@ void DVSerialWorker::handleInputMessages()
MsgMbeDecode *decodeMsg = (MsgMbeDecode *) message;
int dBVolume = (decodeMsg->getVolumeIndex() - 30) / 2;
m_upsampleFilter.useHP(decodeMsg->getUseHP());
if (m_dvController.decode(m_dvAudioSamples, decodeMsg->getMbeFrame(), decodeMsg->getMbeRate(), dBVolume))
{
upsample6(m_dvAudioSamples, SerialDV::MBE_AUDIO_BLOCK_SIZE, decodeMsg->getChannels());
@ -111,10 +113,12 @@ void DVSerialWorker::handleInputMessages()
void DVSerialWorker::pushMbeFrame(const unsigned char *mbeFrame,
int mbeRateIndex,
int mbeVolumeIndex,
unsigned char channels, AudioFifo *audioFifo)
unsigned char channels,
bool useHP,
AudioFifo *audioFifo)
{
m_audioFifo = audioFifo;
m_inputMessageQueue.push(MsgMbeDecode::create(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, audioFifo));
m_inputMessageQueue.push(MsgMbeDecode::create(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useHP, audioFifo));
}
bool DVSerialWorker::isAvailable()

View File

@ -54,11 +54,18 @@ public:
SerialDV::DVRate getMbeRate() const { return m_mbeRate; }
int getVolumeIndex() const { return m_volumeIndex; }
unsigned char getChannels() const { return m_channels % 4; }
bool getUseHP() const { return m_useHP; }
AudioFifo *getAudioFifo() { return m_audioFifo; }
static MsgMbeDecode* create(const unsigned char *mbeFrame, int mbeRateIndex, int volumeIndex, unsigned char channels, AudioFifo *audioFifo)
static MsgMbeDecode* create(
const unsigned char *mbeFrame,
int mbeRateIndex,
int volumeIndex,
unsigned char channels,
bool useHP,
AudioFifo *audioFifo)
{
return new MsgMbeDecode(mbeFrame, (SerialDV::DVRate) mbeRateIndex, volumeIndex, channels, audioFifo);
return new MsgMbeDecode(mbeFrame, (SerialDV::DVRate) mbeRateIndex, volumeIndex, channels, useHP, audioFifo);
}
private:
@ -66,17 +73,20 @@ public:
SerialDV::DVRate m_mbeRate;
int m_volumeIndex;
unsigned char m_channels;
bool m_useHP;
AudioFifo *m_audioFifo;
MsgMbeDecode(const unsigned char *mbeFrame,
SerialDV::DVRate mbeRate,
int volumeIndex,
unsigned char channels,
bool useHP,
AudioFifo *audioFifo) :
Message(),
m_mbeRate(mbeRate),
m_volumeIndex(volumeIndex),
m_channels(channels),
m_useHP(useHP),
m_audioFifo(audioFifo)
{
memcpy((void *) m_mbeFrame, (const void *) mbeFrame, SerialDV::DVController::getNbMbeBytes(m_mbeRate));
@ -90,6 +100,7 @@ public:
int mbeRateIndex,
int mbeVolumeIndex,
unsigned char channels,
bool useHP,
AudioFifo *audioFifo);
bool open(const std::string& serialDevice);

View File

@ -17,40 +17,23 @@
#include "filtermbe.h"
const float MBEAudioInterpolatorFilter::m_a0 = 3.869430E-02;
const float MBEAudioInterpolatorFilter::m_a1 = 7.738860E-02;
const float MBEAudioInterpolatorFilter::m_a2 = 3.869430E-02;
const float MBEAudioInterpolatorFilter::m_b1 = 1.392667E+00;
const float MBEAudioInterpolatorFilter::m_b2 = -5.474446E-01;
const float MBEAudioInterpolatorFilter::m_lpa[3] = {1.0, 1.392667E+00, -5.474446E-01};
const float MBEAudioInterpolatorFilter::m_lpb[3] = {3.869430E-02, 7.738860E-02, 3.869430E-02};
MBEAudioInterpolatorFilter::MBEAudioInterpolatorFilter()
const float MBEAudioInterpolatorFilter::m_hpa[3] = {1.000000e+00, 1.911437E+00, -9.155749E-01};
const float MBEAudioInterpolatorFilter::m_hpb[3] = {9.567529E-01, -1.913506E+00, 9.567529E-01};
MBEAudioInterpolatorFilter::MBEAudioInterpolatorFilter() :
m_filterLP(m_lpa, m_lpb),
m_filterHP(m_hpa, m_hpb),
m_useHP(false)
{
init();
}
MBEAudioInterpolatorFilter::~MBEAudioInterpolatorFilter()
{}
void MBEAudioInterpolatorFilter::init()
float MBEAudioInterpolatorFilter::run(const float& sample)
{
m_x[0] = 0.0f;
m_x[1] = 0.0f;
m_y[0] = 0.0f;
m_y[1] = 0.0f;
return m_useHP ? m_filterLP.run(m_filterHP.run(sample)) : m_filterLP.run(sample);
}
float MBEAudioInterpolatorFilter::run(float sample)
{
float y = m_a0*sample + m_a1*m_x[0] + m_a2*m_x[1] + m_b1*m_y[0] + m_b2*m_y[1]; // this is y[n]
m_x[1] = m_x[0];
m_x[0] = sample;
m_y[1] = m_y[0];
m_y[0] = y;
return y;
}

View File

@ -19,35 +19,61 @@
#define SDRBASE_DSP_FILTERMBE_H_
/**
* This is a 2 pole lowpass Chebyshev (recursive) filter at fc=0.075 using coefficients found in table 20-1 of
* http://www.analog.com/media/en/technical-documentation/dsp-book/dsp_book_Ch20.pdf
* Uses the generic IIR filter internally
*
* Low pass / High pass:
*
* This is a 2 pole Chebyshev (recursive) filter using coefficients found in table 20-1 (low pass)
* or table 20-2 (high pass) of http://www.analog.com/media/en/technical-documentation/dsp-book/dsp_book_Ch20.pdf
*
* For low pass fc = 0.075
* For high oass fc = 0.01
*
* Convention taken here exchanges A and B coefficients as shown in this image:
* https://cdn.mikroe.com/ebooks/img/8/2016/02/digital-filter-design-chapter-03-image-2-9.gif
* So A applies to Y and B to X
*
* At the interpolated sampling frequency of 48 kHz the -3 dB corner is at 48 * .075 = 3.6 kHz which is perfect for voice
* The high pass has a 3 dB corner of 48 * 0.01 = 0.48 kHz
*
* a0= 3.869430E-02
* a1= 7.738860E-02 b1= 1.392667E+00
* a2= 3.869430E-02 b2= -5.474446E-01
* Low pass:
*
* b0 = 3.869430E-02 (a0 = 1.0)
* b1 = 7.738860E-02 a1 = 1.392667E+00
* b2 = 3.869430E-02 a2 = -5.474446E-01
*
* High pass:
*
* b0 = 9.567529E-01 (a0 = 1.0)
* b1 = -1.913506E+00 a1 = 1.911437E+00
* b2 = 9.567529E-01 a2 = -9.155749E-01
*
* given x[n] is the new input sample and y[n] the returned output sample:
*
* y[n] = a0*x[n] + a1*x[n] + a2*x[n] + b1*y[n-1] + b2*y[n-2]
* y[n] = b0*x[n] + b1*x[n] + b2*x[n] + a1*y[n-1] + a2*y[n-2]
*
* This one works directly with floats
*
*
*/
#include "iirfilter.h"
class MBEAudioInterpolatorFilter
{
public:
MBEAudioInterpolatorFilter();
~MBEAudioInterpolatorFilter();
void init();
float run(float sample);
void useHP(bool useHP) { m_useHP = useHP; }
float run(const float& sample);
private:
float m_x[2];
float m_y[2];
static const float m_a0, m_a1, m_a2, m_b1, m_b2;
IIRFilter<float, 2> m_filterLP;
IIRFilter<float, 2> m_filterHP;
bool m_useHP;
static const float m_lpa[3], m_lpb[3]; // low pass coefficients
static const float m_hpa[3], m_hpb[3]; // band pass coefficients
};

View File

@ -15,10 +15,16 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
/**
* IIR filter
* See: https://cdn.mikroe.com/ebooks/img/8/2016/02/digital-filter-design-chapter-03-image-2-9.gif
*/
#ifndef SDRBASE_DSP_IIRFILTER_H_
#define SDRBASE_DSP_IIRFILTER_H_
#include <stdint.h>
#include <string.h>
#include <assert.h>
template <typename Type, uint32_t Order> class IIRFilter
@ -26,7 +32,8 @@ template <typename Type, uint32_t Order> class IIRFilter
public:
IIRFilter(const Type *a, const Type *b);
~IIRFilter();
Type run(Type sample);
void setCoeffs(const Type *a, const Type *b);
Type run(const Type& sample);
private:
Type *m_a;
@ -40,7 +47,8 @@ template <typename Type> class IIRFilter<Type, 2>
public:
IIRFilter(const Type *a, const Type *b);
~IIRFilter();
Type run(Type sample);
void setCoeffs(const Type *a, const Type *b);
Type run(const Type& sample);
private:
Type m_a[3];
@ -59,14 +67,7 @@ IIRFilter<Type, Order>::IIRFilter(const Type *a, const Type *b)
m_x = new Type[Order];
m_y = new Type[Order];
memcpy(m_a, a, (Order+1)*sizeof(Type));
memcpy(m_b, b, (Order+1)*sizeof(Type));
for (int i = 0; i < Order; i++)
{
m_x[i] = 0;
m_y[i] = 0;
}
setCoeffs(a, b);
}
template <typename Type, uint32_t Order>
@ -79,30 +80,47 @@ IIRFilter<Type, Order>::~IIRFilter()
}
template <typename Type, uint32_t Order>
Type IIRFilter<Type, Order>::run(Type sample)
void IIRFilter<Type, Order>::setCoeffs(const Type *a, const Type *b)
{
Type y = m_a[0] * sample;
memcpy(m_a, b, (Order+1)*sizeof(Type));
memcpy(m_b, a, (Order+1)*sizeof(Type));
for (int i = 0; i < Order; i++)
for (uint32_t i = 0; i < Order; i++)
{
y += m_a[i+1] * m_x[i] + m_b[i+1] * m_y[i];
m_x[i] = 0;
m_y[i] = 0;
}
}
template <typename Type, uint32_t Order>
Type IIRFilter<Type, Order>::run(const Type& sample)
{
Type y = m_b[0] * sample;
for (uint32_t i = Order; i > 0; i--)
{
y += m_b[i] * m_x[i-1] + m_a[i] * m_y[i-1];
if (i > 1) // shift
{
m_x[i-1] = m_x[i-2];
m_y[i-1] = m_y[i-2];
}
}
// last shift
m_x[0] = sample;
m_y[0] = y;
memcpy(&m_x[1], &m_x[0], (Order-1)*sizeof(Type));
return y;
}
template <typename Type>
IIRFilter<Type, 2>::IIRFilter(const Type *a, const Type *b)
{
m_a[0] = a[0];
m_a[1] = a[1];
m_a[2] = a[2];
m_b[0] = b[0];
m_b[1] = b[1];
m_b[2] = b[2];
setCoeffs(a, b);
}
template <typename Type>
@ -111,9 +129,24 @@ IIRFilter<Type, 2>::~IIRFilter()
}
template <typename Type>
Type IIRFilter<Type, 2>::run(Type sample)
void IIRFilter<Type, 2>::setCoeffs(const Type *a, const Type *b)
{
Type y = m_a[0]*sample + m_a[1]*m_x[0] + m_a[2]*m_x[1] + m_b[1]*m_y[0] + m_b[2]*m_y[1]; // this is y[n]
m_a[0] = a[0];
m_a[1] = a[1];
m_a[2] = a[2];
m_b[0] = b[0];
m_b[1] = b[1];
m_b[2] = b[2];
m_x[0] = 0;
m_x[1] = 0;
m_y[0] = 0;
m_y[1] = 0;
}
template <typename Type>
Type IIRFilter<Type, 2>::run(const Type& sample)
{
Type y = m_b[0]*sample + m_b[1]*m_x[0] + m_b[2]*m_x[1] + m_a[1]*m_y[0] + m_a[2]*m_y[1]; // this is y[n]
m_x[1] = m_x[0];
m_x[0] = sample;