diff --git a/plugins/channelrx/demoddsd/dsddecoder.h b/plugins/channelrx/demoddsd/dsddecoder.h index 566a6e623..525281a8d 100644 --- a/plugins/channelrx/demoddsd/dsddecoder.h +++ b/plugins/channelrx/demoddsd/dsddecoder.h @@ -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; diff --git a/plugins/channelrx/demoddsd/dsddemod.cpp b/plugins/channelrx/demoddsd/dsddemod.cpp index 9f03195a4..3e9ee2a59 100644 --- a/plugins/channelrx/demoddsd/dsddemod.cpp +++ b/plugins/channelrx/demoddsd/dsddemod.cpp @@ -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; } diff --git a/plugins/channelrx/demoddsd/dsddemodgui.cpp b/plugins/channelrx/demoddsd/dsddemodgui.cpp index 00e8a5663..e74e2e6d1 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.cpp +++ b/plugins/channelrx/demoddsd/dsddemodgui.cpp @@ -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) { diff --git a/plugins/channelrx/demoddsd/dsddemodgui.h b/plugins/channelrx/demoddsd/dsddemodgui.h index cede0f549..40fa8e18d 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.h +++ b/plugins/channelrx/demoddsd/dsddemodgui.h @@ -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); diff --git a/plugins/channelrx/demoddsd/dsddemodgui.ui b/plugins/channelrx/demoddsd/dsddemodgui.ui index 1e8777b28..6ac50170c 100644 --- a/plugins/channelrx/demoddsd/dsddemodgui.ui +++ b/plugins/channelrx/demoddsd/dsddemodgui.ui @@ -584,7 +584,7 @@ - 0 + 30 0 @@ -598,7 +598,7 @@ Sound volume - 2.0 + 10.0 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -828,7 +828,7 @@ - Toggle audio high pass filter for mbelib (>300 Hz) + Toggle audio high pass filter diff --git a/plugins/samplesource/filesource/filesourcegui.cpp b/plugins/samplesource/filesource/filesourcegui.cpp index af431da91..4d0c392c1 100644 --- a/plugins/samplesource/filesource/filesourcegui.cpp +++ b/plugins/samplesource/filesource/filesourcegui.cpp @@ -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; diff --git a/sdrbase/dsp/dspengine.cpp b/sdrbase/dsp/dspengine.cpp index 67c93c9d6..524b57c4e 100644 --- a/sdrbase/dsp/dspengine.cpp +++ b/sdrbase/dsp/dspengine.cpp @@ -231,9 +231,15 @@ void DSPEngine::getDVSerialNames(std::vector& 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 diff --git a/sdrbase/dsp/dspengine.h b/sdrbase/dsp/dspengine.h index 144b3dbb0..7f8bac5b1 100644 --- a/sdrbase/dsp/dspengine.h +++ b/sdrbase/dsp/dspengine.h @@ -78,7 +78,13 @@ public: bool hasDVSerialSupport(); void setDVSerialSupport(bool support); void getDVSerialNames(std::vector& 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; } diff --git a/sdrbase/dsp/dvserialengine.cpp b/sdrbase/dsp/dvserialengine.cpp index 73ef225b8..d042a2019 100644 --- a/sdrbase/dsp/dvserialengine.cpp +++ b/sdrbase/dsp/dvserialengine.cpp @@ -247,7 +247,13 @@ void DVSerialEngine::getDevicesNames(std::vector& 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::iterator it = m_controllers.begin(); std::vector::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 { diff --git a/sdrbase/dsp/dvserialengine.h b/sdrbase/dsp/dvserialengine.h index 5b7c621ba..f2248f90e 100644 --- a/sdrbase/dsp/dvserialengine.h +++ b/sdrbase/dsp/dvserialengine.h @@ -41,7 +41,13 @@ public: int getNbDevices() const { return m_controllers.size(); } void getDevicesNames(std::vector& 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 diff --git a/sdrbase/dsp/dvserialworker.cpp b/sdrbase/dsp/dvserialworker.cpp index 6d2b40990..8db6935a5 100644 --- a/sdrbase/dsp/dvserialworker.cpp +++ b/sdrbase/dsp/dvserialworker.cpp @@ -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() diff --git a/sdrbase/dsp/dvserialworker.h b/sdrbase/dsp/dvserialworker.h index a79037770..da7347037 100644 --- a/sdrbase/dsp/dvserialworker.h +++ b/sdrbase/dsp/dvserialworker.h @@ -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); diff --git a/sdrbase/dsp/filtermbe.cpp b/sdrbase/dsp/filtermbe.cpp index a4b5b0f85..2ce391587 100644 --- a/sdrbase/dsp/filtermbe.cpp +++ b/sdrbase/dsp/filtermbe.cpp @@ -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; -} - - - diff --git a/sdrbase/dsp/filtermbe.h b/sdrbase/dsp/filtermbe.h index 5f3f3239b..f3d6788fb 100644 --- a/sdrbase/dsp/filtermbe.h +++ b/sdrbase/dsp/filtermbe.h @@ -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 m_filterLP; + IIRFilter 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 }; diff --git a/sdrbase/dsp/iirfilter.h b/sdrbase/dsp/iirfilter.h index c29571171..18480c531 100644 --- a/sdrbase/dsp/iirfilter.h +++ b/sdrbase/dsp/iirfilter.h @@ -15,10 +15,16 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// +/** + * 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 +#include #include template class IIRFilter @@ -26,7 +32,8 @@ template 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 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[3]; @@ -59,14 +67,7 @@ IIRFilter::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 @@ -79,30 +80,47 @@ IIRFilter::~IIRFilter() } template -Type IIRFilter::run(Type sample) +void IIRFilter::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 +Type IIRFilter::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 IIRFilter::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 @@ -111,9 +129,24 @@ IIRFilter::~IIRFilter() } template -Type IIRFilter::run(Type sample) +void IIRFilter::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 +Type IIRFilter::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;