From 735f1cdbb473539a7518119f36340307bd55c418 Mon Sep 17 00:00:00 2001 From: f4exb Date: Tue, 3 Dec 2019 18:49:52 +0100 Subject: [PATCH] Rx plugins: refactoring of classes (2) --- plugins/channelrx/demodbfm/CMakeLists.txt | 21 +- plugins/channelrx/demodbfm/bfmdemod.cpp | 463 +----- plugins/channelrx/demodbfm/bfmdemod.h | 188 +-- .../channelrx/demodbfm/bfmdemodbaseband.cpp | 190 +++ plugins/channelrx/demodbfm/bfmdemodbaseband.h | 101 ++ plugins/channelrx/demodbfm/bfmdemodgui.cpp | 14 +- plugins/channelrx/demodbfm/bfmdemodreport.cpp | 26 + plugins/channelrx/demodbfm/bfmdemodreport.h | 50 + plugins/channelrx/demodbfm/bfmdemodsettings.h | 9 + plugins/channelrx/demodbfm/bfmdemodsink.cpp | 357 ++++ plugins/channelrx/demodbfm/bfmdemodsink.h | 162 ++ plugins/channelrx/demodbfm/bfmplugin.cpp | 2 +- plugins/channelrx/demoddatv/CMakeLists.txt | 6 + plugins/channelrx/demoddatv/datvdemod.cpp | 42 +- plugins/channelrx/demoddatv/datvdemod.h | 330 +--- .../channelrx/demoddatv/datvdemodbaseband.cpp | 169 ++ .../channelrx/demoddatv/datvdemodbaseband.h | 118 ++ .../channelrx/demoddatv/datvdemodreport.cpp | 26 + plugins/channelrx/demoddatv/datvdemodreport.h | 58 + plugins/channelrx/demoddatv/datvdemodsink.cpp | 1480 +++++++++++++++++ plugins/channelrx/demoddatv/datvdemodsink.h | 316 ++++ plugins/channelrx/demodlora/CMakeLists.txt | 9 +- plugins/channelrx/demodlora/lorabits.h | 81 - plugins/channelrx/demodlora/lorademod.cpp | 339 +--- plugins/channelrx/demodlora/lorademod.h | 85 +- .../channelrx/demodlora/lorademodbaseband.cpp | 169 ++ .../channelrx/demodlora/lorademodbaseband.h | 84 + plugins/channelrx/demodlora/lorademodgui.cpp | 6 - plugins/channelrx/demodlora/lorademodsink.cpp | 286 ++++ plugins/channelrx/demodlora/lorademodsink.h | 163 ++ plugins/channelrx/demodlora/loraplugin.cpp | 2 +- plugins/channelrx/demodnfm/nfmdemod.cpp | 1 - plugins/channelrx/demodnfm/nfmdemod.h | 9 +- plugins/channelrx/demodssb/CMakeLists.txt | 12 +- plugins/channelrx/demodssb/ssbdemod.cpp | 516 +----- plugins/channelrx/demodssb/ssbdemod.h | 236 +-- .../channelrx/demodssb/ssbdemodbaseband.cpp | 182 ++ plugins/channelrx/demodssb/ssbdemodbaseband.h | 92 + plugins/channelrx/demodssb/ssbdemodgui.cpp | 33 +- plugins/channelrx/demodssb/ssbdemodgui.h | 4 +- plugins/channelrx/demodssb/ssbdemodsink.cpp | 399 +++++ plugins/channelrx/demodssb/ssbdemodsink.h | 130 ++ plugins/channelrx/demodssb/ssbplugin.cpp | 2 +- plugins/channelrx/demodwfm/CMakeLists.txt | 11 +- plugins/channelrx/demodwfm/wfmdemod.cpp | 305 +--- plugins/channelrx/demodwfm/wfmdemod.h | 120 +- .../channelrx/demodwfm/wfmdemodbaseband.cpp | 171 ++ plugins/channelrx/demodwfm/wfmdemodbaseband.h | 89 + plugins/channelrx/demodwfm/wfmdemodgui.cpp | 5 - plugins/channelrx/demodwfm/wfmdemodsettings.h | 9 + plugins/channelrx/demodwfm/wfmdemodsink.cpp | 232 +++ plugins/channelrx/demodwfm/wfmdemodsink.h | 121 ++ plugins/channelrx/demodwfm/wfmplugin.cpp | 2 +- 53 files changed, 5515 insertions(+), 2518 deletions(-) create mode 100644 plugins/channelrx/demodbfm/bfmdemodbaseband.cpp create mode 100644 plugins/channelrx/demodbfm/bfmdemodbaseband.h create mode 100644 plugins/channelrx/demodbfm/bfmdemodreport.cpp create mode 100644 plugins/channelrx/demodbfm/bfmdemodreport.h create mode 100644 plugins/channelrx/demodbfm/bfmdemodsink.cpp create mode 100644 plugins/channelrx/demodbfm/bfmdemodsink.h create mode 100644 plugins/channelrx/demoddatv/datvdemodbaseband.cpp create mode 100644 plugins/channelrx/demoddatv/datvdemodbaseband.h create mode 100644 plugins/channelrx/demoddatv/datvdemodreport.cpp create mode 100644 plugins/channelrx/demoddatv/datvdemodreport.h create mode 100644 plugins/channelrx/demoddatv/datvdemodsink.cpp create mode 100644 plugins/channelrx/demoddatv/datvdemodsink.h delete mode 100644 plugins/channelrx/demodlora/lorabits.h create mode 100644 plugins/channelrx/demodlora/lorademodbaseband.cpp create mode 100644 plugins/channelrx/demodlora/lorademodbaseband.h create mode 100644 plugins/channelrx/demodlora/lorademodsink.cpp create mode 100644 plugins/channelrx/demodlora/lorademodsink.h create mode 100644 plugins/channelrx/demodssb/ssbdemodbaseband.cpp create mode 100644 plugins/channelrx/demodssb/ssbdemodbaseband.h create mode 100644 plugins/channelrx/demodssb/ssbdemodsink.cpp create mode 100644 plugins/channelrx/demodssb/ssbdemodsink.h create mode 100644 plugins/channelrx/demodwfm/wfmdemodbaseband.cpp create mode 100644 plugins/channelrx/demodwfm/wfmdemodbaseband.h create mode 100644 plugins/channelrx/demodwfm/wfmdemodsink.cpp create mode 100644 plugins/channelrx/demodwfm/wfmdemodsink.h diff --git a/plugins/channelrx/demodbfm/CMakeLists.txt b/plugins/channelrx/demodbfm/CMakeLists.txt index 6b0bd9e93..7e6f22d25 100644 --- a/plugins/channelrx/demodbfm/CMakeLists.txt +++ b/plugins/channelrx/demodbfm/CMakeLists.txt @@ -3,6 +3,9 @@ project(bfm) set(bfm_SOURCES bfmdemod.cpp bfmdemodsettings.cpp + bfmdemodsink.cpp + bfmdemodbaseband.cpp + bfmdemodreport.cpp bfmdemodwebapiadapter.cpp bfmplugin.cpp rdsdemod.cpp @@ -14,6 +17,9 @@ set(bfm_SOURCES set(bfm_HEADERS bfmdemod.h bfmdemodsettings.h + bfmdemodsink.h + bfmdemodbaseband.h + bfmdemodreport.h bfmdemodwebapiadapter.h bfmplugin.h rdsdemod.h @@ -23,16 +29,15 @@ set(bfm_HEADERS ) include_directories( - ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client - ${Boost_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client + ${Boost_INCLUDE_DIRS} ) if(NOT SERVER_MODE) set(bfm_SOURCES ${bfm_SOURCES} - bfmdemodgui.cpp - - bfmdemodgui.ui + bfmdemodgui.cpp + bfmdemodgui.ui ) set(bfm_HEADERS ${bfm_HEADERS} @@ -55,10 +60,10 @@ add_library(${TARGET_NAME} SHARED ) target_link_libraries(${TARGET_NAME} - Qt5::Core - ${TARGET_LIB} + Qt5::Core + ${TARGET_LIB} sdrbase - ${TARGET_LIB_GUI} + ${TARGET_LIB_GUI} ) install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER}) diff --git a/plugins/channelrx/demodbfm/bfmdemod.cpp b/plugins/channelrx/demodbfm/bfmdemod.cpp index 4d62d371d..12e709fd4 100644 --- a/plugins/channelrx/demodbfm/bfmdemod.cpp +++ b/plugins/channelrx/demodbfm/bfmdemod.cpp @@ -17,14 +17,13 @@ /////////////////////////////////////////////////////////////////////////////////// #include "boost/format.hpp" -#include -#include #include #include #include #include #include +#include #include "SWGChannelSettings.h" #include "SWGBFMDemodSettings.h" @@ -32,79 +31,34 @@ #include "SWGBFMDemodReport.h" #include "SWGRDSReport.h" -#include "audio/audiooutput.h" #include "dsp/dspengine.h" -#include "dsp/downchannelizer.h" -#include "dsp/threadedbasebandsamplesink.h" #include "dsp/dspcommands.h" #include "dsp/devicesamplemimo.h" #include "device/deviceapi.h" #include "util/db.h" -#include "rdsparser.h" #include "bfmdemod.h" -MESSAGE_CLASS_DEFINITION(BFMDemod::MsgConfigureChannelizer, Message) -MESSAGE_CLASS_DEFINITION(BFMDemod::MsgReportChannelSampleRateChanged, Message) MESSAGE_CLASS_DEFINITION(BFMDemod::MsgConfigureBFMDemod, Message) const QString BFMDemod::m_channelIdURI = "sdrangel.channel.bfm"; const QString BFMDemod::m_channelId = "BFMDemod"; -const Real BFMDemod::default_deemphasis = 50.0; // 50 us const int BFMDemod::m_udpBlockSize = 512; BFMDemod::BFMDemod(DeviceAPI *deviceAPI) : - ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink), - m_deviceAPI(deviceAPI), - m_inputSampleRate(384000), - m_inputFrequencyOffset(0), - m_audioFifo(250000), - m_settingsMutex(QMutex::Recursive), - m_pilotPLL(19000/384000, 50/384000, 0.01), - m_deemphasisFilterX(default_deemphasis * 48000 * 1.0e-6), - m_deemphasisFilterY(default_deemphasis * 48000 * 1.0e-6), - m_fmExcursion(default_excursion) + ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink), + m_deviceAPI(deviceAPI), + m_basebandSampleRate(0) { setObjectName(m_channelId); - DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(&m_audioFifo, getInputMessageQueue()); - m_audioSampleRate = DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate(); + m_thread = new QThread(this); + m_basebandSink = new BFMDemodBaseband(); + m_basebandSink->moveToThread(m_thread); - m_magsq = 0.0f; - m_magsqSum = 0.0f; - m_magsqPeak = 0.0f; - m_magsqCount = 0; + applySettings(m_settings, true); - m_squelchLevel = 0; - m_squelchState = 0; - - m_interpolatorDistance = 0.0f; - m_interpolatorDistanceRemain = 0.0f; - - m_interpolatorRDSDistance = 0.0f; - m_interpolatorRDSDistanceRemain = 0.0f; - - m_interpolatorStereoDistance = 0.0f; - m_interpolatorStereoDistanceRemain = 0.0f; - - m_sampleSink = 0; - m_m1Arg = 0; - - m_rfFilter = new fftfilt(-50000.0 / 384000.0, 50000.0 / 384000.0, filtFftLen); - - m_deemphasisFilterX.configure(default_deemphasis * m_audioSampleRate * 1.0e-6); - m_deemphasisFilterY.configure(default_deemphasis * m_audioSampleRate * 1.0e-6); - m_phaseDiscri.setFMScaling(384000/m_fmExcursion); - - m_audioBuffer.resize(16384); - m_audioBufferFill = 0; - - applyChannelSettings(m_inputSampleRate, m_inputFrequencyOffset, true); - applySettings(m_settings, true); - - m_channelizer = new DownChannelizer(this); - m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); - m_deviceAPI->addChannelSink(m_threadedChannelizer); + m_deviceAPI->addChannelSink(this); m_deviceAPI->addChannelSinkAPI(this); m_networkManager = new QNetworkAccessManager(); @@ -116,13 +70,10 @@ BFMDemod::~BFMDemod() disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); delete m_networkManager; - DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(&m_audioFifo); - - m_deviceAPI->removeChannelSinkAPI(this); - m_deviceAPI->removeChannelSink(m_threadedChannelizer); - delete m_threadedChannelizer; - delete m_channelizer; - delete m_rfFilter; + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + delete m_basebandSink; + delete m_thread; } uint32_t BFMDemod::getNumberOfDeviceStreams() const @@ -133,217 +84,31 @@ uint32_t BFMDemod::getNumberOfDeviceStreams() const void BFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst) { (void) firstOfBurst; - Complex ci, cs, cr; - fftfilt::cmplx *rf; - int rf_out; - double msq; - Real demod; - - m_sampleBuffer.clear(); - - m_settingsMutex.lock(); - - for (SampleVector::const_iterator it = begin; it != end; ++it) - { - Complex c(it->real() / SDR_RX_SCALEF, it->imag() / SDR_RX_SCALEF); - c *= m_nco.nextIQ(); - - rf_out = m_rfFilter->runFilt(c, &rf); // filter RF before demod - - for (int i =0 ; i m_magsqPeak) { - m_magsqPeak = msq; - } - - m_magsqCount++; - - if (msq >= m_squelchLevel) - { - if (m_squelchState < m_settings.m_rfBandwidth / 10) { // twice attack and decay rate - m_squelchState++; - } - } - else - { - if (m_squelchState > 0) { - m_squelchState--; - } - } - - if (m_squelchState > m_settings.m_rfBandwidth / 20) { // squelch open - demod = m_phaseDiscri.phaseDiscriminator(rf[i]); - } else { - demod = 0; - } - - if (!m_settings.m_showPilot) { - m_sampleBuffer.push_back(Sample(demod * SDR_RX_SCALEF, 0.0)); - } - - if (m_settings.m_rdsActive) - { - //Complex r(demod * 2.0 * std::cos(3.0 * m_pilotPLLSamples[3]), 0.0); - Complex r(demod * 2.0 * std::cos(3.0 * m_pilotPLLSamples[3]), 0.0); - - if (m_interpolatorRDS.decimate(&m_interpolatorRDSDistanceRemain, r, &cr)) - { - bool bit; - - if (m_rdsDemod.process(cr.real(), bit)) - { - if (m_rdsDecoder.frameSync(bit)) { - m_rdsParser.parseGroup(m_rdsDecoder.getGroup()); - } - } - - m_interpolatorRDSDistanceRemain += m_interpolatorRDSDistance; - } - } - - Real sampleStereo = 0.0f; - - // Process stereo if stereo mode is selected - - if (m_settings.m_audioStereo) - { - m_pilotPLL.process(demod, m_pilotPLLSamples); - - if (m_settings.m_showPilot) { - m_sampleBuffer.push_back(Sample(m_pilotPLLSamples[1] * SDR_RX_SCALEF, 0.0)); // debug 38 kHz pilot - } - - if (m_settings.m_lsbStereo) - { - // 1.17 * 0.7 = 0.819 - Complex s(demod * m_pilotPLLSamples[1], demod * m_pilotPLLSamples[2]); - - if (m_interpolatorStereo.decimate(&m_interpolatorStereoDistanceRemain, s, &cs)) - { - sampleStereo = cs.real() + cs.imag(); - m_interpolatorStereoDistanceRemain += m_interpolatorStereoDistance; - } - } - else - { - Complex s(demod * 1.17 * m_pilotPLLSamples[1], 0); - - if (m_interpolatorStereo.decimate(&m_interpolatorStereoDistanceRemain, s, &cs)) - { - sampleStereo = cs.real(); - m_interpolatorStereoDistanceRemain += m_interpolatorStereoDistance; - } - } - } - - Complex e(demod, 0); - - if (m_interpolator.decimate(&m_interpolatorDistanceRemain, e, &ci)) - { - if (m_settings.m_audioStereo) - { - Real deemph_l, deemph_r; // Pre-emphasis is applied on each channel before multiplexing - m_deemphasisFilterX.process(ci.real() + sampleStereo, deemph_l); - m_deemphasisFilterY.process(ci.real() - sampleStereo, deemph_r); - m_audioBuffer[m_audioBufferFill].l = (qint16)(deemph_l * (1<<12) * m_settings.m_volume); - m_audioBuffer[m_audioBufferFill].r = (qint16)(deemph_r * (1<<12) * m_settings.m_volume); - } - else - { - Real deemph; - m_deemphasisFilterX.process(ci.real(), deemph); - quint16 sample = (qint16)(deemph * (1<<12) * m_settings.m_volume); - m_audioBuffer[m_audioBufferFill].l = sample; - m_audioBuffer[m_audioBufferFill].r = sample; - } - - ++m_audioBufferFill; - - if (m_audioBufferFill >= m_audioBuffer.size()) - { - uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); - - if(res != m_audioBufferFill) { - qDebug("BFMDemod::feed: %u/%u audio samples written", res, m_audioBufferFill); - } - - m_audioBufferFill = 0; - } - - m_interpolatorDistanceRemain += m_interpolatorDistance; - } - } - } - - if (m_audioBufferFill > 0) - { - uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); - - if (res != m_audioBufferFill) { - qDebug("BFMDemod::feed: %u/%u tail samples written", res, m_audioBufferFill); - } - - m_audioBufferFill = 0; - } - - if (m_sampleSink != 0) { - m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), true); - } - - m_sampleBuffer.clear(); - - m_settingsMutex.unlock(); + m_basebandSink->feed(begin, end); } void BFMDemod::start() { - m_squelchState = 0; - m_audioFifo.clear(); - m_phaseDiscri.reset(); - applyChannelSettings(m_inputSampleRate, m_inputFrequencyOffset, true); + qDebug() << "BFMDemod::start"; + + if (m_basebandSampleRate != 0) { + m_basebandSink->setBasebandSampleRate(m_basebandSampleRate); + } + + m_basebandSink->reset(); + m_thread->start(); } void BFMDemod::stop() { + qDebug() << "BFMDemod::stop"; + m_thread->exit(); + m_thread->wait(); } bool BFMDemod::handleMessage(const Message& cmd) { - if (DownChannelizer::MsgChannelizerNotification::match(cmd)) - { - DownChannelizer::MsgChannelizerNotification& notif = (DownChannelizer::MsgChannelizerNotification&) cmd; - - qDebug() << "BFMDemod::handleMessage: MsgChannelizerNotification:" - << " inputSampleRate: " << notif.getSampleRate() - << " inputFrequencyOffset: " << notif.getFrequencyOffset(); - - applyChannelSettings(notif.getSampleRate(), notif.getFrequencyOffset()); - - if (getMessageQueueToGUI()) - { - MsgReportChannelSampleRateChanged *msg = MsgReportChannelSampleRateChanged::create(getSampleRate()); - getMessageQueueToGUI()->push(msg); - } - - return true; - } - else if (MsgConfigureChannelizer::match(cmd)) - { - MsgConfigureChannelizer& cfg = (MsgConfigureChannelizer&) cmd; - - qDebug() << "BFMDemod::handleMessage: MsgConfigureChannelizer: sampleRate: " << cfg.getSampleRate() - << " centerFrequency: " << cfg.getCenterFrequency(); - - m_channelizer->configure(m_channelizer->getInputMessageQueue(), - cfg.getSampleRate(), - cfg.getCenterFrequency()); - - return true; - } - else if (MsgConfigureBFMDemod::match(cmd)) + if (MsgConfigureBFMDemod::match(cmd)) { MsgConfigureBFMDemod& cfg = (MsgConfigureBFMDemod&) cmd; qDebug() << "BFMDemod::handleMessage: MsgConfigureBFMDemod"; @@ -352,107 +117,23 @@ bool BFMDemod::handleMessage(const Message& cmd) return true; } - else if (DSPConfigureAudio::match(cmd)) - { - DSPConfigureAudio& cfg = (DSPConfigureAudio&) cmd; - uint32_t sampleRate = cfg.getSampleRate(); - - qDebug() << "BFMDemod::handleMessage: DSPConfigureAudio:" - << " sampleRate: " << sampleRate; - - if (sampleRate != m_audioSampleRate) { - applyAudioSampleRate(sampleRate); - } - - return true; - } - else if (BasebandSampleSink::MsgThreadedSink::match(cmd)) - { - return true; - } else if (DSPSignalNotification::match(cmd)) { + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + m_basebandSampleRate = notif.getSampleRate(); + // Forward to the sink + DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy + qDebug() << "BFMDemod::handleMessage: DSPSignalNotification"; + m_basebandSink->getInputMessageQueue()->push(rep); + return true; } else { - qDebug() << "BFMDemod::handleMessage: passed: " << cmd.getIdentifier(); - - if (m_sampleSink != 0) - { - return m_sampleSink->handleMessage(cmd); - } - else - { - return false; - } + return false; } } -void BFMDemod::applyAudioSampleRate(int sampleRate) -{ - qDebug("BFMDemod::applyAudioSampleRate: %d", sampleRate); - - m_settingsMutex.lock(); - - m_interpolator.create(16, m_inputSampleRate, m_settings.m_afBandwidth); - m_interpolatorDistanceRemain = (Real) m_inputSampleRate / sampleRate; - m_interpolatorDistance = (Real) m_inputSampleRate / (Real) sampleRate; - - m_interpolatorStereo.create(16, m_inputSampleRate, m_settings.m_afBandwidth); - m_interpolatorStereoDistanceRemain = (Real) m_inputSampleRate / sampleRate; - m_interpolatorStereoDistance = (Real) m_inputSampleRate / (Real) sampleRate; - - m_deemphasisFilterX.configure(default_deemphasis * sampleRate * 1.0e-6); - m_deemphasisFilterY.configure(default_deemphasis * sampleRate * 1.0e-6); - - m_settingsMutex.unlock(); - - m_audioSampleRate = sampleRate; -} - -void BFMDemod::applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force) -{ - qDebug() << "BFMDemod::applyChannelSettings:" - << " inputSampleRate: " << inputSampleRate - << " inputFrequencyOffset: " << inputFrequencyOffset; - - if((inputFrequencyOffset != m_inputFrequencyOffset) || - (inputSampleRate != m_inputSampleRate) || force) - { - m_nco.setFreq(-inputFrequencyOffset, inputSampleRate); - } - - if ((inputSampleRate != m_inputSampleRate) || force) - { - m_pilotPLL.configure(19000.0/inputSampleRate, 50.0/inputSampleRate, 0.01); - - m_settingsMutex.lock(); - - m_interpolator.create(16, inputSampleRate, m_settings.m_afBandwidth); - m_interpolatorDistanceRemain = (Real) inputSampleRate / m_audioSampleRate; - m_interpolatorDistance = (Real) inputSampleRate / (Real) m_audioSampleRate; - - m_interpolatorStereo.create(16, inputSampleRate, m_settings.m_afBandwidth); - m_interpolatorStereoDistanceRemain = (Real) inputSampleRate / m_audioSampleRate; - m_interpolatorStereoDistance = (Real) inputSampleRate / (Real) m_audioSampleRate; - - m_interpolatorRDS.create(4, inputSampleRate, 600.0); - m_interpolatorRDSDistanceRemain = (Real) inputSampleRate / 250000.0; - m_interpolatorRDSDistance = (Real) inputSampleRate / 250000.0; - - Real lowCut = -(m_settings.m_rfBandwidth / 2.0) / inputSampleRate; - Real hiCut = (m_settings.m_rfBandwidth / 2.0) / inputSampleRate; - m_rfFilter->create_filter(lowCut, hiCut); - m_phaseDiscri.setFMScaling(inputSampleRate / m_fmExcursion); - - m_settingsMutex.unlock(); - } - - m_inputSampleRate = inputSampleRate; - m_inputFrequencyOffset = inputFrequencyOffset; -} - void BFMDemod::applySettings(const BFMDemodSettings& settings, bool force) { qDebug() << "BFMDemod::applySettings: MsgConfigureBFMDemod:" @@ -490,63 +171,17 @@ void BFMDemod::applySettings(const BFMDemodSettings& settings, bool force) if ((settings.m_rdsActive != m_settings.m_rdsActive) || force) { reverseAPIKeys.append("rdsActive"); } - - if ((settings.m_audioStereo && (settings.m_audioStereo != m_settings.m_audioStereo)) || force) - { - m_pilotPLL.configure(19000.0/m_inputSampleRate, 50.0/m_inputSampleRate, 0.01); - } - - if ((settings.m_afBandwidth != m_settings.m_afBandwidth) || force) - { + if ((settings.m_afBandwidth != m_settings.m_afBandwidth) || force) { reverseAPIKeys.append("afBandwidth"); - m_settingsMutex.lock(); - - m_interpolator.create(16, m_inputSampleRate, settings.m_afBandwidth); - m_interpolatorDistanceRemain = (Real) m_inputSampleRate / m_audioSampleRate; - m_interpolatorDistance = (Real) m_inputSampleRate / (Real) m_audioSampleRate; - - m_interpolatorStereo.create(16, m_inputSampleRate, settings.m_afBandwidth); - m_interpolatorStereoDistanceRemain = (Real) m_inputSampleRate / m_audioSampleRate; - m_interpolatorStereoDistance = (Real) m_inputSampleRate / (Real) m_audioSampleRate; - - m_interpolatorRDS.create(4, m_inputSampleRate, 600.0); - m_interpolatorRDSDistanceRemain = (Real) m_inputSampleRate / 250000.0; - m_interpolatorRDSDistance = (Real) m_inputSampleRate / 250000.0; - - m_lowpass.create(21, m_audioSampleRate, settings.m_afBandwidth); - - m_settingsMutex.unlock(); } - - if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) - { + if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) { reverseAPIKeys.append("rfBandwidth"); - m_settingsMutex.lock(); - Real lowCut = -(settings.m_rfBandwidth / 2.0) / m_inputSampleRate; - Real hiCut = (settings.m_rfBandwidth / 2.0) / m_inputSampleRate; - m_rfFilter->create_filter(lowCut, hiCut); - m_phaseDiscri.setFMScaling(m_inputSampleRate / m_fmExcursion); - m_settingsMutex.unlock(); } - - if ((settings.m_squelch != m_settings.m_squelch) || force) - { + if ((settings.m_squelch != m_settings.m_squelch) || force) { reverseAPIKeys.append("squelch"); - m_squelchLevel = std::pow(10.0, settings.m_squelch / 10.0); } - - if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) - { + if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) { reverseAPIKeys.append("audioDeviceName"); - AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); - int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_audioDeviceName); - //qDebug("AMDemod::applySettings: audioDeviceName: %s audioDeviceIndex: %d", qPrintable(settings.m_audioDeviceName), audioDeviceIndex); - audioDeviceManager->addAudioSink(&m_audioFifo, getInputMessageQueue(), audioDeviceIndex); - uint32_t audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex); - - if (m_audioSampleRate != audioSampleRate) { - applyAudioSampleRate(audioSampleRate); - } } if (m_settings.m_streamIndex != settings.m_streamIndex) @@ -554,16 +189,17 @@ void BFMDemod::applySettings(const BFMDemodSettings& settings, bool force) if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only { m_deviceAPI->removeChannelSinkAPI(this, m_settings.m_streamIndex); - m_deviceAPI->removeChannelSink(m_threadedChannelizer, m_settings.m_streamIndex); - m_deviceAPI->addChannelSink(m_threadedChannelizer, settings.m_streamIndex); + m_deviceAPI->removeChannelSink(this, m_settings.m_streamIndex); + m_deviceAPI->addChannelSink(this, settings.m_streamIndex); m_deviceAPI->addChannelSinkAPI(this, settings.m_streamIndex); - // apply stream sample rate to itself - applyChannelSettings(m_deviceAPI->getSampleMIMO()->getSourceSampleRate(settings.m_streamIndex), m_inputFrequencyOffset); } reverseAPIKeys.append("streamIndex"); } + BFMDemodBaseband::MsgConfigureBFMDemodBaseband *msg = BFMDemodBaseband::MsgConfigureBFMDemodBaseband::create(settings, force); + m_basebandSink->getInputMessageQueue()->push(msg); + if (settings.m_useReverseAPI) { bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || @@ -620,13 +256,6 @@ int BFMDemod::webapiSettingsPutPatch( BFMDemodSettings settings = m_settings; webapiUpdateChannelSettings(settings, channelSettingsKeys, response); - if (settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) - { - MsgConfigureChannelizer* channelConfigMsg = MsgConfigureChannelizer::create( - requiredBW(settings.m_rfBandwidth), settings.m_inputFrequencyOffset); - m_inputMessageQueue.push(channelConfigMsg); - } - MsgConfigureBFMDemod *msg = MsgConfigureBFMDemod::create(settings, force); m_inputMessageQueue.push(msg); @@ -760,9 +389,9 @@ void BFMDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples); response.getBfmDemodReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg)); - response.getBfmDemodReport()->setSquelch(m_squelchState > 0 ? 1 : 0); - response.getBfmDemodReport()->setAudioSampleRate(m_audioSampleRate); - response.getBfmDemodReport()->setChannelSampleRate(m_inputSampleRate); + response.getBfmDemodReport()->setSquelch(m_basebandSink->getSquelchState() > 0 ? 1 : 0); + response.getBfmDemodReport()->setAudioSampleRate(m_basebandSink->getAudioSampleRate()); + response.getBfmDemodReport()->setChannelSampleRate(m_basebandSink->getChannelSampleRate()); response.getBfmDemodReport()->setPilotLocked(getPilotLock() ? 1 : 0); response.getBfmDemodReport()->setPilotPowerDb(CalcDb::dbPower(getPilotLevel())); diff --git a/plugins/channelrx/demodbfm/bfmdemod.h b/plugins/channelrx/demodbfm/bfmdemod.h index 018209fca..f6638d389 100644 --- a/plugins/channelrx/demodbfm/bfmdemod.h +++ b/plugins/channelrx/demodbfm/bfmdemod.h @@ -26,27 +26,15 @@ #include "dsp/basebandsamplesink.h" #include "channel/channelapi.h" -#include "dsp/nco.h" -#include "dsp/interpolator.h" -#include "dsp/lowpass.h" -#include "dsp/movingaverage.h" -#include "dsp/fftfilt.h" -#include "dsp/phaselock.h" -#include "dsp/filterrc.h" -#include "dsp/phasediscri.h" -#include "audio/audiofifo.h" #include "util/message.h" -#include "rdsparser.h" -#include "rdsdecoder.h" -#include "rdsdemod.h" +#include "bfmdemodbaseband.h" #include "bfmdemodsettings.h" class QNetworkAccessManager; class QNetworkReply; +class QThread; class DeviceAPI; -class ThreadedBasebandSampleSink; -class DownChannelizer; namespace SWGSDRangel { class SWGRDSReport; @@ -78,55 +66,12 @@ public: { } }; - class MsgConfigureChannelizer : public Message { - MESSAGE_CLASS_DECLARATION - - public: - int getSampleRate() const { return m_sampleRate; } - int getCenterFrequency() const { return m_centerFrequency; } - - static MsgConfigureChannelizer* create(int sampleRate, int centerFrequency) - { - return new MsgConfigureChannelizer(sampleRate, centerFrequency); - } - - private: - int m_sampleRate; - int m_centerFrequency; - - MsgConfigureChannelizer(int sampleRate, int centerFrequency) : - Message(), - m_sampleRate(sampleRate), - m_centerFrequency(centerFrequency) - { } - }; - - class MsgReportChannelSampleRateChanged : public Message { - MESSAGE_CLASS_DECLARATION - - public: - int getSampleRate() const { return m_sampleRate; } - - static MsgReportChannelSampleRateChanged* create(int sampleRate) - { - return new MsgReportChannelSampleRateChanged(sampleRate); - } - - private: - int m_sampleRate; - - MsgReportChannelSampleRateChanged(int sampleRate) : - Message(), - m_sampleRate(sampleRate) - { } - }; - BFMDemod(DeviceAPI *deviceAPI); virtual ~BFMDemod(); virtual void destroy() { delete this; } - void setSampleSink(BasebandSampleSink* sampleSink) { m_sampleSink = sampleSink; } + void setSpectrumSink(BasebandSampleSink* spectrumSink) { m_basebandSink->setSpectrumSink(spectrumSink); } + void setMessageQueueToGUI(MessageQueue *messageQueue) { m_basebandSink->setMessageQueueToGUI(messageQueue); } - int getSampleRate() const { return m_inputSampleRate; } virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); virtual void start(); virtual void stop(); @@ -149,36 +94,20 @@ public: return m_settings.m_inputFrequencyOffset; } - double getMagSq() const { return m_magsq; } + double getMagSq() const { return m_basebandSink->getMagSq(); } - bool getPilotLock() const { return m_pilotPLL.locked(); } - Real getPilotLevel() const { return m_pilotPLL.get_pilot_level(); } + bool getPilotLock() const { return m_basebandSink->getPilotLock(); } + Real getPilotLevel() const { return m_basebandSink->getPilotLevel(); } - Real getDecoderQua() const { return m_rdsDecoder.m_qua; } - bool getDecoderSynced() const { return m_rdsDecoder.synced(); } - Real getDemodAcc() const { return m_rdsDemod.m_report.acc; } - Real getDemodQua() const { return m_rdsDemod.m_report.qua; } - Real getDemodFclk() const { return m_rdsDemod.m_report.fclk; } + Real getDecoderQua() const { return m_basebandSink->getDecoderQua(); } + bool getDecoderSynced() const { return m_basebandSink->getDecoderSynced(); } + Real getDemodAcc() const { return m_basebandSink->getDemodAcc(); } + Real getDemodQua() const { return m_basebandSink->getDemodQua(); } + Real getDemodFclk() const { return m_basebandSink->getDemodFclk(); } - void getMagSqLevels(double& avg, double& peak, int& nbSamples) - { - if (m_magsqCount > 0) - { - m_magsq = m_magsqSum / m_magsqCount; - m_magSqLevelStore.m_magsq = m_magsq; - m_magSqLevelStore.m_magsqPeak = m_magsqPeak; - } + void getMagSqLevels(double& avg, double& peak, int& nbSamples) { m_basebandSink->getMagSqLevels(avg, peak, nbSamples); } - avg = m_magSqLevelStore.m_magsq; - peak = m_magSqLevelStore.m_magsqPeak; - nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount; - - m_magsqSum = 0.0f; - m_magsqPeak = 0.0f; - m_magsqCount = 0; - } - - RDSParser& getRDSParser() { return m_rdsParser; } + RDSParser& getRDSParser() { return m_basebandSink->getRDSParser(); } virtual int webapiSettingsGet( SWGSDRangel::SWGChannelSettings& response, @@ -203,104 +132,23 @@ public: const QStringList& channelSettingsKeys, SWGSDRangel::SWGChannelSettings& response); - static int requiredBW(int rfBW) - { - if (rfBW <= 48000) { - return 48000; - } else { - return (3*rfBW)/2; - } - } - uint32_t getNumberOfDeviceStreams() const; static const QString m_channelIdURI; static const QString m_channelId; private: - struct MagSqLevelsStore - { - MagSqLevelsStore() : - m_magsq(1e-12), - m_magsqPeak(1e-12) - {} - double m_magsq; - double m_magsqPeak; - }; - - enum RateState { - RSInitialFill, - RSRunning - }; - DeviceAPI *m_deviceAPI; - ThreadedBasebandSampleSink* m_threadedChannelizer; - DownChannelizer* m_channelizer; - - int m_inputSampleRate; - int m_inputFrequencyOffset; - BFMDemodSettings m_settings; - quint32 m_audioSampleRate; - - NCO m_nco; - Interpolator m_interpolator; //!< Interpolator between fixed demod bandwidth and audio bandwidth (rational) - Real m_interpolatorDistance; - Real m_interpolatorDistanceRemain; - - Interpolator m_interpolatorStereo; //!< Twin Interpolator for stereo subcarrier - Real m_interpolatorStereoDistance; - Real m_interpolatorStereoDistanceRemain; - - Interpolator m_interpolatorRDS; //!< Twin Interpolator for stereo subcarrier - Real m_interpolatorRDSDistance; - Real m_interpolatorRDSDistanceRemain; - - Lowpass m_lowpass; - fftfilt* m_rfFilter; - static const int filtFftLen = 1024; - - Real m_squelchLevel; - int m_squelchState; - - Real m_m1Arg; //!> x^-1 real sample - - double m_magsq; - double m_magsqSum; - double m_magsqPeak; - int m_magsqCount; - MagSqLevelsStore m_magSqLevelStore; - - AudioVector m_audioBuffer; - uint m_audioBufferFill; - - BasebandSampleSink* m_sampleSink; - AudioFifo m_audioFifo; - SampleVector m_sampleBuffer; - QMutex m_settingsMutex; - - RDSPhaseLock m_pilotPLL; - Real m_pilotPLLSamples[4]; - - RDSDemod m_rdsDemod; - RDSDecoder m_rdsDecoder; - RDSParser m_rdsParser; - - LowPassFilterRC m_deemphasisFilterX; - LowPassFilterRC m_deemphasisFilterY; - static const Real default_deemphasis; - - Real m_fmExcursion; - static const int default_excursion = 750000; // +/- 75 kHz - - PhaseDiscriminators m_phaseDiscri; + QThread *m_thread; + BFMDemodBaseband* m_basebandSink; + BFMDemodSettings m_settings; + int m_basebandSampleRate; //!< stored from device message used when starting baseband sink static const int m_udpBlockSize; QNetworkAccessManager *m_networkManager; QNetworkRequest m_networkRequest; - void applyAudioSampleRate(int sampleRate); - void applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force = false); void applySettings(const BFMDemodSettings& settings, bool force = false); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); diff --git a/plugins/channelrx/demodbfm/bfmdemodbaseband.cpp b/plugins/channelrx/demodbfm/bfmdemodbaseband.cpp new file mode 100644 index 000000000..863a6ca86 --- /dev/null +++ b/plugins/channelrx/demodbfm/bfmdemodbaseband.cpp @@ -0,0 +1,190 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "dsp/downsamplechannelizer.h" + +#include "bfmdemodreport.h" +#include "bfmdemodbaseband.h" + +MESSAGE_CLASS_DEFINITION(BFMDemodBaseband::MsgConfigureBFMDemodBaseband, Message) + +BFMDemodBaseband::BFMDemodBaseband() : + m_mutex(QMutex::Recursive) +{ + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000)); + m_channelizer = new DownSampleChannelizer(&m_sink); + + qDebug("BFMDemodBaseband::BFMDemodBaseband"); + QObject::connect( + &m_sampleFifo, + &SampleSinkFifo::dataReady, + this, + &BFMDemodBaseband::handleData, + Qt::QueuedConnection + ); + + DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(m_sink.getAudioFifo(), getInputMessageQueue()); + m_sink.applyAudioSampleRate(DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate()); + + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); +} + +BFMDemodBaseband::~BFMDemodBaseband() +{ + DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(m_sink.getAudioFifo()); + delete m_channelizer; +} + +void BFMDemodBaseband::reset() +{ + QMutexLocker mutexLocker(&m_mutex); + m_sampleFifo.reset(); +} + +void BFMDemodBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +{ + m_sampleFifo.write(begin, end); +} + +void BFMDemodBaseband::handleData() +{ + QMutexLocker mutexLocker(&m_mutex); + + while ((m_sampleFifo.fill() > 0) && (m_inputMessageQueue.size() == 0)) + { + SampleVector::iterator part1begin; + SampleVector::iterator part1end; + SampleVector::iterator part2begin; + SampleVector::iterator part2end; + + std::size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end); + + // first part of FIFO data + if (part1begin != part1end) { + m_channelizer->feed(part1begin, part1end); + } + + // second part of FIFO data (used when block wraps around) + if(part2begin != part2end) { + m_channelizer->feed(part2begin, part2end); + } + + m_sampleFifo.readCommit((unsigned int) count); + } +} + +void BFMDemodBaseband::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != nullptr) + { + if (handleMessage(*message)) { + delete message; + } + } +} + +bool BFMDemodBaseband::handleMessage(const Message& cmd) +{ + if (MsgConfigureBFMDemodBaseband::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + MsgConfigureBFMDemodBaseband& cfg = (MsgConfigureBFMDemodBaseband&) cmd; + qDebug() << "BFMDemodBaseband::handleMessage: MsgConfigureBFMDemodBaseband"; + + applySettings(cfg.getSettings(), cfg.getForce()); + + return true; + } + else if (DSPSignalNotification::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + qDebug() << "BFMDemodBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate(); + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate())); + m_channelizer->setBasebandSampleRate(notif.getSampleRate()); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + + if (getMessageQueueToGUI()) + { + BFMDemodReport::MsgReportChannelSampleRateChanged *msg = BFMDemodReport::MsgReportChannelSampleRateChanged::create(m_channelizer->getChannelSampleRate()); + getMessageQueueToGUI()->push(msg); + } + + return true; + } + else + { + return false; + } +} + +void BFMDemodBaseband::applySettings(const BFMDemodSettings& settings, bool force) +{ + if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) + || (settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) + { + m_channelizer->setChannelization(BFMDemodSettings::requiredBW(settings.m_rfBandwidth), settings.m_inputFrequencyOffset); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + + if (getMessageQueueToGUI()) + { + BFMDemodReport::MsgReportChannelSampleRateChanged *msg = BFMDemodReport::MsgReportChannelSampleRateChanged::create(m_channelizer->getChannelSampleRate()); + getMessageQueueToGUI()->push(msg); + } + } + + if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) + { + AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); + int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_audioDeviceName); + //qDebug("AMDemod::applySettings: audioDeviceName: %s audioDeviceIndex: %d", qPrintable(settings.m_audioDeviceName), audioDeviceIndex); + audioDeviceManager->addAudioSink(m_sink.getAudioFifo(), getInputMessageQueue(), audioDeviceIndex); + uint32_t audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex); + + if (m_sink.getAudioSampleRate() != audioSampleRate) { + m_sink.applyAudioSampleRate(audioSampleRate); + } + } + + m_sink.applySettings(settings, force); + + m_settings = settings; +} + +int BFMDemodBaseband::getChannelSampleRate() const +{ + return m_channelizer->getChannelSampleRate(); +} + + +void BFMDemodBaseband::setBasebandSampleRate(int sampleRate) +{ + m_channelizer->setBasebandSampleRate(sampleRate); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + + if (getMessageQueueToGUI()) + { + BFMDemodReport::MsgReportChannelSampleRateChanged *msg = BFMDemodReport::MsgReportChannelSampleRateChanged::create(m_channelizer->getChannelSampleRate()); + getMessageQueueToGUI()->push(msg); + } +} diff --git a/plugins/channelrx/demodbfm/bfmdemodbaseband.h b/plugins/channelrx/demodbfm/bfmdemodbaseband.h new file mode 100644 index 000000000..d1a4360ae --- /dev/null +++ b/plugins/channelrx/demodbfm/bfmdemodbaseband.h @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_BFMDEMODBASEBAND_H +#define INCLUDE_BFMDEMODBASEBAND_H + +#include +#include + +#include "dsp/samplesinkfifo.h" +#include "util/message.h" +#include "util/messagequeue.h" + +#include "bfmdemodsink.h" + +class DownSampleChannelizer; + +class BFMDemodBaseband : public QObject +{ + Q_OBJECT +public: + class MsgConfigureBFMDemodBaseband : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const BFMDemodSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureBFMDemodBaseband* create(const BFMDemodSettings& settings, bool force) + { + return new MsgConfigureBFMDemodBaseband(settings, force); + } + + private: + BFMDemodSettings m_settings; + bool m_force; + + MsgConfigureBFMDemodBaseband(const BFMDemodSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + BFMDemodBaseband(); + ~BFMDemodBaseband(); + void reset(); + void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication + int getChannelSampleRate() const; + void setBasebandSampleRate(int sampleRate); + void setSpectrumSink(BasebandSampleSink* spectrumSink) { m_sink.setSpectrumSink(spectrumSink); } + void setMessageQueueToGUI(MessageQueue *messageQueue) { m_messageQueueToGUI = messageQueue; } + + unsigned int getAudioSampleRate() const { return m_sink.getAudioSampleRate(); } + int getSquelchState() const { return m_sink.getSquelchState(); } + double getMagSq() const { return m_sink.getMagSq(); } + bool getPilotLock() const { return m_sink.getPilotLock(); } + Real getPilotLevel() const { return m_sink.getPilotLevel(); } + Real getDecoderQua() const { return m_sink.getDecoderQua(); } + bool getDecoderSynced() const { return m_sink.getDecoderSynced(); } + Real getDemodAcc() const { return m_sink.getDemodAcc(); } + Real getDemodQua() const { return m_sink.getDemodQua(); } + Real getDemodFclk() const { return m_sink.getDemodFclk(); } + void getMagSqLevels(double& avg, double& peak, int& nbSamples) { m_sink.getMagSqLevels(avg, peak, nbSamples); } + RDSParser& getRDSParser() { return m_sink.getRDSParser(); } + +private: + SampleSinkFifo m_sampleFifo; + DownSampleChannelizer *m_channelizer; + BFMDemodSink m_sink; + MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication + BFMDemodSettings m_settings; + QMutex m_mutex; + MessageQueue *m_messageQueueToGUI; + + MessageQueue *getMessageQueueToGUI() { return m_messageQueueToGUI; } + + bool handleMessage(const Message& cmd); + void applySettings(const BFMDemodSettings& settings, bool force = false); + +private slots: + void handleInputMessages(); + void handleData(); //!< Handle data when samples have to be processed +}; + +#endif // INCLUDE_BFMDEMODBASEBAND_H diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.cpp b/plugins/channelrx/demodbfm/bfmdemodgui.cpp index d1251d5fc..6bffc1d04 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodgui.cpp @@ -41,6 +41,7 @@ #include "gui/audioselectdialog.h" #include "mainwindow.h" +#include "bfmdemodreport.h" #include "bfmdemodsettings.h" #include "bfmdemod.h" #include "rdstmc.h" @@ -111,11 +112,11 @@ bool BFMDemodGUI::deserialize(const QByteArray& data) bool BFMDemodGUI::handleMessage(const Message& message) { - if (BFMDemod::MsgReportChannelSampleRateChanged::match(message)) + if (BFMDemodReport::MsgReportChannelSampleRateChanged::match(message)) { - BFMDemod::MsgReportChannelSampleRateChanged& report = (BFMDemod::MsgReportChannelSampleRateChanged&) message; + BFMDemodReport::MsgReportChannelSampleRateChanged& report = (BFMDemodReport::MsgReportChannelSampleRateChanged&) message; m_rate = report.getSampleRate(); - qDebug("BFMDemodGUI::handleMessage: MsgReportChannelSampleRateChanged: %d S/s", m_rate); + qDebug("BFMDemodGUI::handleMessage: BFMDemodReport::MsgReportChannelSampleRateChanged: %d S/s", m_rate); ui->glSpectrum->setCenterFrequency(m_rate / 4); ui->glSpectrum->setSampleRate(m_rate / 2); return true; @@ -389,7 +390,7 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_spectrumVis = new SpectrumVis(SDR_RX_SCALEF, ui->glSpectrum); m_bfmDemod = (BFMDemod*) rxChannel; //new BFMDemod(m_deviceUISet->m_deviceSourceAPI); m_bfmDemod->setMessageQueueToGUI(getInputMessageQueue()); - m_bfmDemod->setSampleSink(m_spectrumVis); + m_bfmDemod->setSpectrumSink(m_spectrumVis); ui->glSpectrum->setCenterFrequency(m_rate / 4); ui->glSpectrum->setSampleRate(m_rate / 2); @@ -455,11 +456,6 @@ void BFMDemodGUI::applySettings(bool force) { if (m_doApplySettings) { - BFMDemod::MsgConfigureChannelizer *msgChan = BFMDemod::MsgConfigureChannelizer::create( - BFMDemod::requiredBW(m_settings.m_rfBandwidth), - m_settings.m_inputFrequencyOffset); - m_bfmDemod->getInputMessageQueue()->push(msgChan); - BFMDemod::MsgConfigureBFMDemod* msgConfig = BFMDemod::MsgConfigureBFMDemod::create( m_settings, force); m_bfmDemod->getInputMessageQueue()->push(msgConfig); } diff --git a/plugins/channelrx/demodbfm/bfmdemodreport.cpp b/plugins/channelrx/demodbfm/bfmdemodreport.cpp new file mode 100644 index 000000000..6fa9593ac --- /dev/null +++ b/plugins/channelrx/demodbfm/bfmdemodreport.cpp @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "bfmdemodreport.h" + +MESSAGE_CLASS_DEFINITION(BFMDemodReport::MsgReportChannelSampleRateChanged, Message) + +BFMDemodReport::BFMDemodReport() +{} + +BFMDemodReport::~BFMDemodReport() +{} diff --git a/plugins/channelrx/demodbfm/bfmdemodreport.h b/plugins/channelrx/demodbfm/bfmdemodreport.h new file mode 100644 index 000000000..c392a29ff --- /dev/null +++ b/plugins/channelrx/demodbfm/bfmdemodreport.h @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_BFMDEMODREPORT_H +#define INCLUDE_BFMDEMODREPORT_H + +#include "util/message.h" + +class BFMDemodReport +{ +public: + class MsgReportChannelSampleRateChanged : public Message { + MESSAGE_CLASS_DECLARATION + + public: + int getSampleRate() const { return m_sampleRate; } + + static MsgReportChannelSampleRateChanged* create(int sampleRate) + { + return new MsgReportChannelSampleRateChanged(sampleRate); + } + + private: + int m_sampleRate; + + MsgReportChannelSampleRateChanged(int sampleRate) : + Message(), + m_sampleRate(sampleRate) + { } + }; + + BFMDemodReport(); + ~BFMDemodReport(); +}; + +#endif // INCLUDE_BFMDEMODREPORT_H diff --git a/plugins/channelrx/demodbfm/bfmdemodsettings.h b/plugins/channelrx/demodbfm/bfmdemodsettings.h index 9e1d0a78a..986474400 100644 --- a/plugins/channelrx/demodbfm/bfmdemodsettings.h +++ b/plugins/channelrx/demodbfm/bfmdemodsettings.h @@ -58,6 +58,15 @@ struct BFMDemodSettings static int getRFBW(int index); static int getRFBWIndex(int rfbw); + + static int requiredBW(int rfBW) + { + if (rfBW <= 48000) { + return 48000; + } else { + return (3*rfBW)/2; + } + } }; diff --git a/plugins/channelrx/demodbfm/bfmdemodsink.cpp b/plugins/channelrx/demodbfm/bfmdemodsink.cpp new file mode 100644 index 000000000..73e380b9f --- /dev/null +++ b/plugins/channelrx/demodbfm/bfmdemodsink.cpp @@ -0,0 +1,357 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "boost/format.hpp" +#include +#include + +#include +#include + +#include "audio/audiooutput.h" +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "dsp/devicesamplemimo.h" +#include "dsp/basebandsamplesink.h" +#include "util/db.h" + +#include "rdsparser.h" +#include "bfmdemodsink.h" + +const Real BFMDemodSink::default_deemphasis = 50.0; // 50 us +const int BFMDemodSink::default_excursion = 750000; // +/- 75 kHz + +BFMDemodSink::BFMDemodSink() : + m_channelSampleRate(48000), + m_channelFrequencyOffset(0), + m_audioSampleRate(48000), + m_audioBufferFill(0), + m_audioFifo(48000), + m_pilotPLL(19000/384000, 50/384000, 0.01), + m_deemphasisFilterX(default_deemphasis * 48000 * 1.0e-6), + m_deemphasisFilterY(default_deemphasis * 48000 * 1.0e-6), + m_fmExcursion(default_excursion) +{ + m_magsq = 0.0f; + m_magsqSum = 0.0f; + m_magsqPeak = 0.0f; + m_magsqCount = 0; + + m_squelchLevel = 0; + m_squelchState = 0; + + m_interpolatorDistance = 0.0f; + m_interpolatorDistanceRemain = 0.0f; + + m_interpolatorRDSDistance = 0.0f; + m_interpolatorRDSDistanceRemain = 0.0f; + + m_interpolatorStereoDistance = 0.0f; + m_interpolatorStereoDistanceRemain = 0.0f; + + m_spectrumSink = nullptr; + m_m1Arg = 0; + + m_rfFilter = new fftfilt(-50000.0 / 384000.0, 50000.0 / 384000.0, filtFftLen); + + m_deemphasisFilterX.configure(default_deemphasis * m_audioSampleRate * 1.0e-6); + m_deemphasisFilterY.configure(default_deemphasis * m_audioSampleRate * 1.0e-6); + m_phaseDiscri.setFMScaling(384000/m_fmExcursion); + + m_audioBuffer.resize(16384); + m_audioBufferFill = 0; + + applySettings(m_settings, true); + applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true); +} + +BFMDemodSink::~BFMDemodSink() +{ + delete m_rfFilter; +} + +void BFMDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +{ + Complex ci, cs, cr; + fftfilt::cmplx *rf; + int rf_out; + double msq; + Real demod; + + m_sampleBuffer.clear(); + + for (SampleVector::const_iterator it = begin; it != end; ++it) + { + Complex c(it->real() / SDR_RX_SCALEF, it->imag() / SDR_RX_SCALEF); + c *= m_nco.nextIQ(); + + rf_out = m_rfFilter->runFilt(c, &rf); // filter RF before demod + + for (int i =0 ; i m_magsqPeak) { + m_magsqPeak = msq; + } + + m_magsqCount++; + + if (msq >= m_squelchLevel) + { + if (m_squelchState < m_settings.m_rfBandwidth / 10) { // twice attack and decay rate + m_squelchState++; + } + } + else + { + if (m_squelchState > 0) { + m_squelchState--; + } + } + + if (m_squelchState > m_settings.m_rfBandwidth / 20) { // squelch open + demod = m_phaseDiscri.phaseDiscriminator(rf[i]); + } else { + demod = 0; + } + + if (!m_settings.m_showPilot) { + m_sampleBuffer.push_back(Sample(demod * SDR_RX_SCALEF, 0.0)); + } + + if (m_settings.m_rdsActive) + { + //Complex r(demod * 2.0 * std::cos(3.0 * m_pilotPLLSamples[3]), 0.0); + Complex r(demod * 2.0 * std::cos(3.0 * m_pilotPLLSamples[3]), 0.0); + + if (m_interpolatorRDS.decimate(&m_interpolatorRDSDistanceRemain, r, &cr)) + { + bool bit; + + if (m_rdsDemod.process(cr.real(), bit)) + { + if (m_rdsDecoder.frameSync(bit)) { + m_rdsParser.parseGroup(m_rdsDecoder.getGroup()); + } + } + + m_interpolatorRDSDistanceRemain += m_interpolatorRDSDistance; + } + } + + Real sampleStereo = 0.0f; + + // Process stereo if stereo mode is selected + + if (m_settings.m_audioStereo) + { + m_pilotPLL.process(demod, m_pilotPLLSamples); + + if (m_settings.m_showPilot) { + m_sampleBuffer.push_back(Sample(m_pilotPLLSamples[1] * SDR_RX_SCALEF, 0.0)); // debug 38 kHz pilot + } + + if (m_settings.m_lsbStereo) + { + // 1.17 * 0.7 = 0.819 + Complex s(demod * m_pilotPLLSamples[1], demod * m_pilotPLLSamples[2]); + + if (m_interpolatorStereo.decimate(&m_interpolatorStereoDistanceRemain, s, &cs)) + { + sampleStereo = cs.real() + cs.imag(); + m_interpolatorStereoDistanceRemain += m_interpolatorStereoDistance; + } + } + else + { + Complex s(demod * 1.17 * m_pilotPLLSamples[1], 0); + + if (m_interpolatorStereo.decimate(&m_interpolatorStereoDistanceRemain, s, &cs)) + { + sampleStereo = cs.real(); + m_interpolatorStereoDistanceRemain += m_interpolatorStereoDistance; + } + } + } + + Complex e(demod, 0); + + if (m_interpolator.decimate(&m_interpolatorDistanceRemain, e, &ci)) + { + if (m_settings.m_audioStereo) + { + Real deemph_l, deemph_r; // Pre-emphasis is applied on each channel before multiplexing + m_deemphasisFilterX.process(ci.real() + sampleStereo, deemph_l); + m_deemphasisFilterY.process(ci.real() - sampleStereo, deemph_r); + m_audioBuffer[m_audioBufferFill].l = (qint16)(deemph_l * (1<<12) * m_settings.m_volume); + m_audioBuffer[m_audioBufferFill].r = (qint16)(deemph_r * (1<<12) * m_settings.m_volume); + } + else + { + Real deemph; + m_deemphasisFilterX.process(ci.real(), deemph); + quint16 sample = (qint16)(deemph * (1<<12) * m_settings.m_volume); + m_audioBuffer[m_audioBufferFill].l = sample; + m_audioBuffer[m_audioBufferFill].r = sample; + } + + ++m_audioBufferFill; + + if (m_audioBufferFill >= m_audioBuffer.size()) + { + uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); + + if(res != m_audioBufferFill) { + qDebug("BFMDemodSink::feed: %u/%u audio samples written", res, m_audioBufferFill); + } + + m_audioBufferFill = 0; + } + + m_interpolatorDistanceRemain += m_interpolatorDistance; + } + } + } + + if (m_audioBufferFill > 0) + { + uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); + + if (res != m_audioBufferFill) { + qDebug("BFMDemodSink::feed: %u/%u tail samples written", res, m_audioBufferFill); + } + + m_audioBufferFill = 0; + } + + if (m_spectrumSink) { + m_spectrumSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), true); + } + + m_sampleBuffer.clear(); +} + +void BFMDemodSink::applyAudioSampleRate(unsigned int sampleRate) +{ + qDebug("BFMDemodSink::applyAudioSampleRate: %u", sampleRate); + + m_interpolator.create(16, m_channelSampleRate, m_settings.m_afBandwidth); + m_interpolatorDistanceRemain = (Real) m_channelSampleRate / sampleRate; + m_interpolatorDistance = (Real) m_channelSampleRate / (Real) sampleRate; + + m_interpolatorStereo.create(16, m_channelSampleRate, m_settings.m_afBandwidth); + m_interpolatorStereoDistanceRemain = (Real) m_channelSampleRate / sampleRate; + m_interpolatorStereoDistance = (Real) m_channelSampleRate / (Real) sampleRate; + + m_deemphasisFilterX.configure(default_deemphasis * sampleRate * 1.0e-6); + m_deemphasisFilterY.configure(default_deemphasis * sampleRate * 1.0e-6); + + m_audioSampleRate = sampleRate; +} + +void BFMDemodSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force) +{ + qDebug() << "BFMDemodSink::applyChannelSettings:" + << " channelSampleRate: " << channelSampleRate + << " channelFrequencyOffset: " << channelFrequencyOffset; + + if((channelFrequencyOffset != m_channelFrequencyOffset) || + (channelSampleRate != m_channelSampleRate) || force) + { + m_nco.setFreq(-channelFrequencyOffset, channelSampleRate); + } + + if ((channelSampleRate != m_channelSampleRate) || force) + { + m_pilotPLL.configure(19000.0/channelSampleRate, 50.0/channelSampleRate, 0.01); + + m_interpolator.create(16, channelSampleRate, m_settings.m_afBandwidth); + m_interpolatorDistanceRemain = (Real) channelSampleRate / m_audioSampleRate; + m_interpolatorDistance = (Real) channelSampleRate / (Real) m_audioSampleRate; + + m_interpolatorStereo.create(16, channelSampleRate, m_settings.m_afBandwidth); + m_interpolatorStereoDistanceRemain = (Real) channelSampleRate / m_audioSampleRate; + m_interpolatorStereoDistance = (Real) channelSampleRate / (Real) m_audioSampleRate; + + m_interpolatorRDS.create(4, channelSampleRate, 600.0); + m_interpolatorRDSDistanceRemain = (Real) channelSampleRate / 250000.0; + m_interpolatorRDSDistance = (Real) channelSampleRate / 250000.0; + + Real lowCut = -(m_settings.m_rfBandwidth / 2.0) / channelSampleRate; + Real hiCut = (m_settings.m_rfBandwidth / 2.0) / channelSampleRate; + m_rfFilter->create_filter(lowCut, hiCut); + m_phaseDiscri.setFMScaling(channelSampleRate / m_fmExcursion); + } + + m_channelSampleRate = channelSampleRate; + m_channelFrequencyOffset = channelFrequencyOffset; +} + +void BFMDemodSink::applySettings(const BFMDemodSettings& settings, bool force) +{ + qDebug() << "BFMDemodSink::applySettings: MsgConfigureBFMDemod:" + << " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset + << " m_rfBandwidth: " << settings.m_rfBandwidth + << " m_afBandwidth: " << settings.m_afBandwidth + << " m_volume: " << settings.m_volume + << " m_squelch: " << settings.m_squelch + << " m_audioStereo: " << settings.m_audioStereo + << " m_lsbStereo: " << settings.m_lsbStereo + << " m_showPilot: " << settings.m_showPilot + << " m_rdsActive: " << settings.m_rdsActive + << " m_audioDeviceName: " << settings.m_audioDeviceName + << " m_streamIndex: " << settings.m_streamIndex + << " m_useReverseAPI: " << settings.m_useReverseAPI + << " force: " << force; + + if ((settings.m_audioStereo && (settings.m_audioStereo != m_settings.m_audioStereo)) || force) { + m_pilotPLL.configure(19000.0/m_channelSampleRate, 50.0/m_channelSampleRate, 0.01); + } + + if ((settings.m_afBandwidth != m_settings.m_afBandwidth) || force) + { + m_interpolator.create(16, m_channelSampleRate, settings.m_afBandwidth); + m_interpolatorDistanceRemain = (Real) m_channelSampleRate / m_audioSampleRate; + m_interpolatorDistance = (Real) m_channelSampleRate / (Real) m_audioSampleRate; + + m_interpolatorStereo.create(16, m_channelSampleRate, settings.m_afBandwidth); + m_interpolatorStereoDistanceRemain = (Real) m_channelSampleRate / m_audioSampleRate; + m_interpolatorStereoDistance = (Real) m_channelSampleRate / (Real) m_audioSampleRate; + + m_interpolatorRDS.create(4, m_channelSampleRate, 600.0); + m_interpolatorRDSDistanceRemain = (Real) m_channelSampleRate / 250000.0; + m_interpolatorRDSDistance = (Real) m_channelSampleRate / 250000.0; + + m_lowpass.create(21, m_audioSampleRate, settings.m_afBandwidth); + } + + if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) + { + Real lowCut = -(settings.m_rfBandwidth / 2.0) / m_channelSampleRate; + Real hiCut = (settings.m_rfBandwidth / 2.0) / m_channelSampleRate; + m_rfFilter->create_filter(lowCut, hiCut); + m_phaseDiscri.setFMScaling(m_channelSampleRate / m_fmExcursion); + } + + if ((settings.m_squelch != m_settings.m_squelch) || force) { + m_squelchLevel = std::pow(10.0, settings.m_squelch / 10.0); + } + + m_settings = settings; +} diff --git a/plugins/channelrx/demodbfm/bfmdemodsink.h b/plugins/channelrx/demodbfm/bfmdemodsink.h new file mode 100644 index 000000000..c46a19d17 --- /dev/null +++ b/plugins/channelrx/demodbfm/bfmdemodsink.h @@ -0,0 +1,162 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_BFMDEMODSINK_H +#define INCLUDE_BFMDEMODSINK_H + +#include + +#include "dsp/channelsamplesink.h" +#include "dsp/nco.h" +#include "dsp/interpolator.h" +#include "dsp/lowpass.h" +#include "dsp/movingaverage.h" +#include "dsp/fftfilt.h" +#include "dsp/phaselock.h" +#include "dsp/filterrc.h" +#include "dsp/phasediscri.h" +#include "audio/audiofifo.h" + +#include "rdsparser.h" +#include "rdsdecoder.h" +#include "rdsdemod.h" +#include "bfmdemodsettings.h" + +class BasebandSampleSink; + +class BFMDemodSink : public ChannelSampleSink { +public: + BFMDemodSink(); + ~BFMDemodSink(); + + virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + + void setSpectrumSink(BasebandSampleSink* spectrumSink) { m_spectrumSink = spectrumSink; } + + double getMagSq() const { return m_magsq; } + + bool getPilotLock() const { return m_pilotPLL.locked(); } + Real getPilotLevel() const { return m_pilotPLL.get_pilot_level(); } + + Real getDecoderQua() const { return m_rdsDecoder.m_qua; } + bool getDecoderSynced() const { return m_rdsDecoder.synced(); } + Real getDemodAcc() const { return m_rdsDemod.m_report.acc; } + Real getDemodQua() const { return m_rdsDemod.m_report.qua; } + Real getDemodFclk() const { return m_rdsDemod.m_report.fclk; } + int getSquelchState() const { return m_squelchState; } + + void getMagSqLevels(double& avg, double& peak, int& nbSamples) + { + if (m_magsqCount > 0) + { + m_magsq = m_magsqSum / m_magsqCount; + m_magSqLevelStore.m_magsq = m_magsq; + m_magSqLevelStore.m_magsqPeak = m_magsqPeak; + } + + avg = m_magSqLevelStore.m_magsq; + peak = m_magSqLevelStore.m_magsqPeak; + nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount; + + m_magsqSum = 0.0f; + m_magsqPeak = 0.0f; + m_magsqCount = 0; + } + + RDSParser& getRDSParser() { return m_rdsParser; } + + void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false); + void applySettings(const BFMDemodSettings& settings, bool force = false); + + AudioFifo *getAudioFifo() { return &m_audioFifo; } + void applyAudioSampleRate(unsigned int sampleRate); + unsigned int getAudioSampleRate() const { return m_audioSampleRate; } + +private: + struct MagSqLevelsStore + { + MagSqLevelsStore() : + m_magsq(1e-12), + m_magsqPeak(1e-12) + {} + double m_magsq; + double m_magsqPeak; + }; + + enum RateState { + RSInitialFill, + RSRunning + }; + + int m_channelSampleRate; + int m_channelFrequencyOffset; + BFMDemodSettings m_settings; + + quint32 m_audioSampleRate; + AudioVector m_audioBuffer; + uint m_audioBufferFill; + AudioFifo m_audioFifo; + SampleVector m_sampleBuffer; + + NCO m_nco; + Interpolator m_interpolator; //!< Interpolator between fixed demod bandwidth and audio bandwidth (rational) + Real m_interpolatorDistance; + Real m_interpolatorDistanceRemain; + + Interpolator m_interpolatorStereo; //!< Twin Interpolator for stereo subcarrier + Real m_interpolatorStereoDistance; + Real m_interpolatorStereoDistanceRemain; + + Interpolator m_interpolatorRDS; //!< Twin Interpolator for stereo subcarrier + Real m_interpolatorRDSDistance; + Real m_interpolatorRDSDistanceRemain; + + Lowpass m_lowpass; + fftfilt* m_rfFilter; + static const int filtFftLen = 1024; + + Real m_squelchLevel; + int m_squelchState; + + Real m_m1Arg; //!> x^-1 real sample + + double m_magsq; + double m_magsqSum; + double m_magsqPeak; + int m_magsqCount; + MagSqLevelsStore m_magSqLevelStore; + + RDSPhaseLock m_pilotPLL; + Real m_pilotPLLSamples[4]; + + RDSDemod m_rdsDemod; + RDSDecoder m_rdsDecoder; + RDSParser m_rdsParser; + + LowPassFilterRC m_deemphasisFilterX; + LowPassFilterRC m_deemphasisFilterY; + static const Real default_deemphasis; + + Real m_fmExcursion; + static const int default_excursion; + + PhaseDiscriminators m_phaseDiscri; + + BasebandSampleSink *m_spectrumSink; +}; + +#endif // INCLUDE_BFMDEMODSINK_H diff --git a/plugins/channelrx/demodbfm/bfmplugin.cpp b/plugins/channelrx/demodbfm/bfmplugin.cpp index 97b14b9b0..2c65be199 100644 --- a/plugins/channelrx/demodbfm/bfmplugin.cpp +++ b/plugins/channelrx/demodbfm/bfmplugin.cpp @@ -30,7 +30,7 @@ const PluginDescriptor BFMPlugin::m_pluginDescriptor = { QString("Broadcast FM Demodulator"), - QString("4.11.6"), + QString("4.12.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/demoddatv/CMakeLists.txt b/plugins/channelrx/demoddatv/CMakeLists.txt index da2f475cc..9c82887aa 100644 --- a/plugins/channelrx/demoddatv/CMakeLists.txt +++ b/plugins/channelrx/demoddatv/CMakeLists.txt @@ -10,6 +10,9 @@ set(datv_SOURCES datvideostream.cpp datvudpstream.cpp datvideorender.cpp + datvdemodreport.cpp + datvdemodsink.cpp + datvdemodbaseband.cpp leansdr/dvb.cpp leansdr/filtergen.cpp leansdr/framework.cpp @@ -29,6 +32,9 @@ set(datv_HEADERS datvideorender.h datvconstellation.h datvdvbs2constellation.h + datvdemodreport.h + datvdemodsink.h + datvdemodbaseband.h leansdr/dvb.h leansdr/dvbs2.h leansdr/filtergen.h diff --git a/plugins/channelrx/demoddatv/datvdemod.cpp b/plugins/channelrx/demoddatv/datvdemod.cpp index a98487fe2..da1598110 100644 --- a/plugins/channelrx/demoddatv/datvdemod.cpp +++ b/plugins/channelrx/demoddatv/datvdemod.cpp @@ -55,7 +55,7 @@ DATVDemod::DATVDemod(DeviceAPI *deviceAPI) : m_modcodModulation(-1), m_modcodCodeRate(-1), m_enmModulation(DATVDemodSettings::BPSK /*DATV_FM1*/), - m_sampleRate(1024000), + m_channelSampleRate(1024000), m_objSettingsMutex(QMutex::NonRecursive) { setObjectName("DATVDemod"); @@ -556,12 +556,12 @@ void DATVDemod::InitDATVFramework() << " RollOff: " << m_settings.m_rollOff << " Viterbi: " << m_settings.m_viterbi << " Excursion: " << m_settings.m_excursion - << " Sample rate: " << m_sampleRate; + << " Sample rate: " << m_channelSampleRate; m_objCfg.standard = m_settings.m_standard; m_objCfg.fec = (leansdr::code_rate) getLeanDVBCodeRateFromDATV(m_settings.m_fec); - m_objCfg.Fs = (float) m_sampleRate; + m_objCfg.Fs = (float) m_channelSampleRate; m_objCfg.Fm = (float) m_settings.m_symbolRate; m_objCfg.fastlock = m_settings.m_fastLock; @@ -886,14 +886,14 @@ void DATVDemod::InitDATVS2Framework() << " RollOff: " << m_settings.m_rollOff << " Viterbi: " << m_settings.m_viterbi << " Excursion: " << m_settings.m_excursion - << " Sample rate: " << m_sampleRate ; + << " Sample rate: " << m_channelSampleRate ; m_objCfg.standard = m_settings.m_standard; m_objCfg.fec = (leansdr::code_rate) getLeanDVBCodeRateFromDATV(m_settings.m_fec); - m_objCfg.Fs = (float) m_sampleRate; + m_objCfg.Fs = (float) m_channelSampleRate; m_objCfg.Fm = (float) m_settings.m_symbolRate; m_objCfg.fastlock = m_settings.m_fastLock; @@ -1310,8 +1310,8 @@ bool DATVDemod::handleMessage(const Message& cmd) << " m_intSampleRate: " << objNotif.getSampleRate() << " m_intFrequencyOffset: " << objNotif.getFrequencyOffset(); - m_inputFrequencyOffset = objNotif.getFrequencyOffset(); - applyChannelSettings(m_sampleRate /*objNotif.getSampleRate()*/, m_inputFrequencyOffset); + m_channelFrequencyOffset = objNotif.getFrequencyOffset(); + applyChannelSettings(m_channelSampleRate /*objNotif.getSampleRate()*/, m_channelFrequencyOffset); return true; } @@ -1326,8 +1326,8 @@ bool DATVDemod::handleMessage(const Message& cmd) qDebug() << "DATVDemod::handleMessage: MsgConfigureChannelizer: sampleRate: " << m_channelizer->getInputSampleRate() << " centerFrequency: " << cfg.getCenterFrequency(); - m_sampleRate = m_channelizer->getInputSampleRate(); - applyChannelSettings(m_sampleRate /*objNotif.getSampleRate()*/, m_inputFrequencyOffset); + m_channelSampleRate = m_channelizer->getInputSampleRate(); + applyChannelSettings(m_channelSampleRate /*objNotif.getSampleRate()*/, m_channelFrequencyOffset); return true; } @@ -1341,9 +1341,9 @@ bool DATVDemod::handleMessage(const Message& cmd) } else if(DSPSignalNotification::match(cmd)) { - m_sampleRate = m_channelizer->getInputSampleRate(); - qDebug("DATVDemod::handleMessage: DSPSignalNotification: sent sample rate: %d", m_sampleRate); - applyChannelSettings(m_sampleRate /*objNotif.getSampleRate()*/, m_inputFrequencyOffset); + m_channelSampleRate = m_channelizer->getInputSampleRate(); + qDebug("DATVDemod::handleMessage: DSPSignalNotification: sent sample rate: %d", m_channelSampleRate); + applyChannelSettings(m_channelSampleRate /*objNotif.getSampleRate()*/, m_channelFrequencyOffset); return true; } @@ -1362,7 +1362,7 @@ void DATVDemod::applyChannelSettings(int inputSampleRate, int inputFrequencyOffs bool callApplySettings = false; if ((m_settings.m_centerFrequency != inputFrequencyOffset) || - (m_sampleRate != inputSampleRate) || force) + (m_channelSampleRate != inputSampleRate) || force) { m_objNCO.setFreq(-(float) inputFrequencyOffset, (float) inputSampleRate); qDebug("DATVDemod::applyChannelSettings: NCO: IF: %d <> TF: %d ISR: %d", @@ -1370,7 +1370,7 @@ void DATVDemod::applyChannelSettings(int inputSampleRate, int inputFrequencyOffs callApplySettings = true; } - if ((m_sampleRate != inputSampleRate) || force) + if ((m_channelSampleRate != inputSampleRate) || force) { //Bandpass filter shaping Real fltLowCut = -((float) m_settings.m_rfBandwidth / 2.0) / (float) inputSampleRate; @@ -1378,7 +1378,7 @@ void DATVDemod::applyChannelSettings(int inputSampleRate, int inputFrequencyOffs m_objRFFilter->create_filter(fltLowCut, fltHiCut); } - m_sampleRate = inputSampleRate; + m_channelSampleRate = inputSampleRate; m_settings.m_centerFrequency = inputFrequencyOffset; if (callApplySettings) { @@ -1391,9 +1391,9 @@ void DATVDemod::applySettings(const DATVDemodSettings& settings, bool force) QString msg = tr("DATVDemod::applySettings: force: %1").arg(force); settings.debug(msg); - qDebug("DATVDemod::applySettings: m_sampleRate: %d", m_sampleRate); + qDebug("DATVDemod::applySettings: m_channelSampleRate: %d", m_channelSampleRate); - if (m_sampleRate == 0) { + if (m_channelSampleRate == 0) { return; } @@ -1435,15 +1435,15 @@ void DATVDemod::applySettings(const DATVDemodSettings& settings, bool force) { //Bandpass filter shaping - Real fltLowCut = -((float) settings.m_rfBandwidth / 2.0) / (float) m_sampleRate; - Real fltHiCut = ((float) settings.m_rfBandwidth / 2.0) / (float) m_sampleRate; + Real fltLowCut = -((float) settings.m_rfBandwidth / 2.0) / (float) m_channelSampleRate; + Real fltHiCut = ((float) settings.m_rfBandwidth / 2.0) / (float) m_channelSampleRate; m_objRFFilter->create_filter(fltLowCut, fltHiCut); } if ((m_settings.m_centerFrequency != settings.m_centerFrequency) || force) { - m_objNCO.setFreq(-(float) settings.m_centerFrequency, (float) m_sampleRate); + m_objNCO.setFreq(-(float) settings.m_centerFrequency, (float) m_channelSampleRate); } if ((m_settings.m_udpTS != settings.m_udpTS) || force) { @@ -1468,7 +1468,7 @@ void DATVDemod::applySettings(const DATVDemodSettings& settings, bool force) int DATVDemod::GetSampleRate() { - return m_sampleRate; + return m_channelSampleRate; } DATVDemodSettings::DATVCodeRate DATVDemod::getCodeRateFromLeanDVBCode(int leanDVBCodeRate) diff --git a/plugins/channelrx/demoddatv/datvdemod.h b/plugins/channelrx/demoddatv/datvdemod.h index cec8c5b6a..4d2d586a3 100644 --- a/plugins/channelrx/demoddatv/datvdemod.h +++ b/plugins/channelrx/demoddatv/datvdemod.h @@ -24,17 +24,6 @@ class DeviceAPI; class ThreadedBasebandSampleSink; class DownChannelizer; -#define rfFilterFftLength 1024 - -//LeanSDR -#include "leansdr/framework.h" -#include "leansdr/generic.h" -#include "leansdr/dvb.h" -#include "leansdr/filtergen.h" - -#include "leansdr/hdlc.h" -#include "leansdr/iess.h" - #include "datvconstellation.h" #include "datvdvbs2constellation.h" #include "datvvideoplayer.h" @@ -59,68 +48,12 @@ class DownChannelizer; #include +#include "datvdemodbaseband.h" + // enum DATVModulation { BPSK, QPSK, PSK8, APSK16, APSK32, APSK64E, QAM16, QAM64, QAM256 }; // enum dvb_version { DVB_S, DVB_S2 }; // enum dvb_sampler { SAMP_NEAREST, SAMP_LINEAR, SAMP_RRC }; -inline int decimation(float Fin, float Fout) { int d = Fin / Fout; return std::max(d, 1); } - -struct config -{ - DATVDemodSettings::dvb_version standard; - DATVDemodSettings::dvb_sampler sampler; - - int buf_factor; // Buffer sizing - float Fs; // Sampling frequency (Hz) - float Fderot; // Shift the signal (Hz). Note: Ftune is faster - int anf; // Number of auto notch filters - bool cnr; // Measure CNR - unsigned int decim; // Decimation, 0=auto - float Fm; // QPSK symbol rate (Hz) - leansdr::cstln_lut::predef constellation; - leansdr::code_rate fec; - float Ftune; // Bias frequency for the QPSK demodulator (Hz) - bool allow_drift; - bool fastlock; - bool viterbi; - bool hard_metric; - bool resample; - float resample_rej; // Approx. filter rejection in dB - int rrc_steps; // Discrete steps between symbols, 0=auto - float rrc_rej; // Approx. RRC filter rejection in dB - float rolloff; // Roll-off 0..1 - bool hdlc; // Expect HDLC frames instead of MPEG packets - bool packetized; // Output frames with 16-bit BE length - float Finfo; // Desired refresh rate on fd_info (Hz) - - config() : - standard(DATVDemodSettings::DVB_S), - sampler(DATVDemodSettings::SAMP_LINEAR), - buf_factor(4), - Fs(2.4e6), - Fderot(0), - anf(0), - cnr(false), - decim(0), - Fm(2e6), - constellation(leansdr::cstln_lut::QPSK), - fec(leansdr::FEC12), - Ftune(0), - allow_drift(false), - fastlock(true), - viterbi(false), - hard_metric(false), - resample(false), - resample_rej(10), - rrc_steps(0), - rrc_rej(10), - rolloff(0.35), - hdlc(false), - packetized(false), - Finfo(5) - { - } -}; class DATVDemod : public BasebandSampleSink, public ChannelAPI { @@ -154,69 +87,23 @@ public: return m_settings.m_centerFrequency; } - bool SetTVScreen(TVScreen *objScreen); - DATVideostream * SetVideoRender(DATVideoRender *objScreen); - bool audioActive(); - bool audioDecodeOK(); - bool videoActive(); - bool videoDecodeOK(); + bool SetTVScreen(TVScreen *objScreen) { m_basebandSink->setTVScreen(objScreen); } + DATVideostream *SetVideoRender(DATVideoRender *objScreen) { return m_basebandSink->SetVideoRender(objScreen); } + bool audioActive() { return m_basebandSink->audioActive(); } + bool audioDecodeOK() { return m_basebandSink->audioDecodeOK(); } + bool videoActive() { return m_basebandSink->videoActive(); } + bool videoDecodeOK() { return m_basebandSink->videoDecodeOK(); } - bool PlayVideo(bool blnStartStop); + bool PlayVideo(bool blnStartStop) { m_basebandSink->PlayVideo(blnStartStop); } - void InitDATVParameters( - int intMsps, - int intRFBandwidth, - int intCenterFrequency, - DATVDemodSettings::dvb_version enmStandard, - DATVDemodSettings::DATVModulation enmModulation, - leansdr::code_rate enmFEC, - int intSampleRate, - int intSymbolRate, - int intNotchFilters, - bool blnAllowDrift, - bool blnFastLock, - DATVDemodSettings::dvb_sampler enmFilter, - bool blnHardMetric, - float fltRollOff, - bool blnViterbi, - int intEExcursion); - - void CleanUpDATVFramework(bool blnRelease); - int GetSampleRate(); - void InitDATVFramework(); - void InitDATVS2Framework(); - double getMagSq() const { return m_objMagSqAverage; } //!< Beware this is scaled to 2^30 - int getModcodModulation() const { return m_modcodModulation; } - int getModcodCodeRate() const { return m_modcodCodeRate; } - bool isCstlnSetByModcod() const { return m_cstlnSetByModcod; } - static DATVDemodSettings::DATVCodeRate getCodeRateFromLeanDVBCode(int leanDVBCodeRate); - static DATVDemodSettings::DATVModulation getModulationFromLeanDVBCode(int leanDVBModulation); - static int getLeanDVBCodeRateFromDATV(DATVDemodSettings::DATVCodeRate datvCodeRate); - static int getLeanDVBModulationFromDATV(DATVDemodSettings::DATVModulation datvModulation); + double getMagSq() const { return m_basebandSink->getMagSq(); } //!< Beware this is scaled to 2^30 + int getModcodModulation() const { return m_basebandSink->getModcodModulation(); } + int getModcodCodeRate() const { return m_basebandSink->getModcodCodeRate(); } + bool isCstlnSetByModcod() const { return m_basebandSink->isCstlnSetByModcod(); } static const QString m_channelIdURI; static const QString m_channelId; - class MsgConfigureChannelizer : public Message - { - MESSAGE_CLASS_DECLARATION - - public: - int getCenterFrequency() const { return m_centerFrequency; } - - static MsgConfigureChannelizer* create(int centerFrequency) { - return new MsgConfigureChannelizer(centerFrequency); - } - - private: - int m_centerFrequency; - - MsgConfigureChannelizer(int centerFrequency) : - Message(), - m_centerFrequency(centerFrequency) - {} - }; - class MsgConfigureDATVDemod : public Message { MESSAGE_CLASS_DECLARATION @@ -240,198 +127,13 @@ public: { } }; - class MsgReportModcodCstlnChange : public Message { - MESSAGE_CLASS_DECLARATION - - public: - DATVDemodSettings::DATVModulation getModulation() const { return m_modulation; } - DATVDemodSettings::DATVCodeRate getCodeRate() const { return m_codeRate; } - - static MsgReportModcodCstlnChange* create(const DATVDemodSettings::DATVModulation& modulation, - const DATVDemodSettings::DATVCodeRate& codeRate) - { - return new MsgReportModcodCstlnChange(modulation, codeRate); - } - - private: - DATVDemodSettings::DATVModulation m_modulation; - DATVDemodSettings::DATVCodeRate m_codeRate; - - MsgReportModcodCstlnChange( - const DATVDemodSettings::DATVModulation& modulation, - const DATVDemodSettings::DATVCodeRate& codeRate - ) : - Message(), - m_modulation(modulation), - m_codeRate(codeRate) - { } - }; - private: - unsigned long m_lngExpectedReadIQ; - long m_lngReadIQ; - - //************** LEANDBV Parameters ************** - - unsigned long BUF_BASEBAND; - unsigned long BUF_SYMBOLS; - unsigned long BUF_BYTES; - unsigned long BUF_MPEGBYTES; - unsigned long BUF_PACKETS; - unsigned long BUF_SLOW; - - - //dvbs2 - unsigned long BUF_SLOTS; - unsigned long BUF_FRAMES; - unsigned long BUF_S2PACKETS; - unsigned long S2_MAX_SYMBOLS; - - //************** LEANDBV Scheduler *************** - - leansdr::scheduler * m_objScheduler; - struct config m_objCfg; - - bool m_blnDVBInitialized; - bool m_blnNeedConfigUpdate; - - //LeanSDR Pipe Buffer - // INPUT - - leansdr::pipebuf *p_rawiq; - leansdr::pipewriter *p_rawiq_writer; - leansdr::pipebuf *p_preprocessed; - - // NOTCH FILTER - leansdr::auto_notch *r_auto_notch; - leansdr::pipebuf *p_autonotched; - - // FREQUENCY CORRECTION : DEROTATOR - leansdr::pipebuf *p_derot; - leansdr::rotator *r_derot; - - // CNR ESTIMATION - leansdr::pipebuf *p_cnr; - leansdr::cnr_fft *r_cnr; - - //FILTERING - leansdr::fir_filter *r_resample; - leansdr::pipebuf *p_resampled; - float *coeffs; - int ncoeffs; - - // OUTPUT PREPROCESSED DATA - leansdr::sampler_interface *sampler; - float *coeffs_sampler; - int ncoeffs_sampler; - - leansdr::pipebuf *p_symbols; - leansdr::pipebuf *p_freq; - leansdr::pipebuf *p_ss; - leansdr::pipebuf *p_mer; - leansdr::pipebuf *p_sampled; - - //dvb-s2 - void *p_slots_dvbs2; - leansdr::pipebuf *p_cstln; - leansdr::pipebuf *p_cstln_pls; - leansdr::pipebuf *p_framelock; - void *m_objDemodulatorDVBS2; - void *p_fecframes; - void *p_bbframes; - void *p_s2_deinterleaver; - void *r_fecdec; - void *p_deframer; - - //DECIMATION - leansdr::pipebuf *p_decimated; - leansdr::decimator *p_decim; - - //PROCESSED DATA MONITORING - leansdr::file_writer *r_ppout; - - //GENERIC CONSTELLATION RECEIVER - leansdr::cstln_receiver *m_objDemodulator; - - // DECONVOLUTION AND SYNCHRONIZATION - leansdr::pipebuf *p_bytes; - leansdr::deconvol_sync_simple *r_deconv; - leansdr::viterbi_sync *r; - leansdr::pipebuf *p_descrambled; - leansdr::pipebuf *p_frames; - - leansdr::etr192_descrambler * r_etr192_descrambler; - leansdr::hdlc_sync *r_sync; - - leansdr::pipebuf *p_mpegbytes; - leansdr::pipebuf *p_lock; - leansdr::pipebuf *p_locktime; - leansdr::mpeg_sync *r_sync_mpeg; - - - // DEINTERLEAVING - leansdr::pipebuf > *p_rspackets; - leansdr::deinterleaver *r_deinter; - - // REED-SOLOMON - leansdr::pipebuf *p_vbitcount; - leansdr::pipebuf *p_verrcount; - leansdr::pipebuf *p_rtspackets; - leansdr::rs_decoder *r_rsdec; - - // BER ESTIMATION - leansdr::pipebuf *p_vber; - leansdr::rate_estimator *r_vber; - - // DERANDOMIZATION - leansdr::pipebuf *p_tspackets; - leansdr::derandomizer *r_derand; - - - //OUTPUT - leansdr::file_writer *r_stdout; - leansdr::datvvideoplayer *r_videoplayer; - - //CONSTELLATION - leansdr::datvconstellation *r_scope_symbols; - leansdr::datvdvbs2constellation *r_scope_symbols_dvbs2; - DeviceAPI* m_deviceAPI; - - ThreadedBasebandSampleSink* m_threadedChannelizer; - DownChannelizer* m_channelizer; - - //*************** DATV PARAMETERS *************** - TVScreen *m_objRegisteredTVScreen; - DATVideoRender *m_objRegisteredVideoRender; - DATVideostream *m_objVideoStream; - DATVUDPStream m_udpStream; - DATVideoRenderThread *m_objRenderThread; - - // Audio - AudioFifo m_audioFifo; - - fftfilt * m_objRFFilter; - NCO m_objNCO; - - bool m_blnInitialized; - bool m_blnRenderingVideo; - bool m_blnStartStopVideo; - bool m_cstlnSetByModcod; - int m_modcodModulation; - int m_modcodCodeRate; - - DATVDemodSettings::DATVModulation m_enmModulation; - - //DATVConfig m_objRunning; + QThread *m_thread; + DATVDemodBaseband* m_basebandSink; DATVDemodSettings m_settings; - int m_sampleRate; - int m_inputFrequencyOffset; - MovingAverageUtil m_objMagSqAverage; + int m_basebandSampleRate; //!< stored from device message used when starting baseband sink - QMutex m_objSettingsMutex; - - //void ApplySettings(); void applySettings(const DATVDemodSettings& settings, bool force = false); void applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force = false); }; diff --git a/plugins/channelrx/demoddatv/datvdemodbaseband.cpp b/plugins/channelrx/demoddatv/datvdemodbaseband.cpp new file mode 100644 index 000000000..2d2b8f205 --- /dev/null +++ b/plugins/channelrx/demoddatv/datvdemodbaseband.cpp @@ -0,0 +1,169 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "dsp/downsamplechannelizer.h" + +#include "datvdemodbaseband.h" + +MESSAGE_CLASS_DEFINITION(DATVDemodBaseband::MsgConfigureDATVDemodBaseband, Message) +MESSAGE_CLASS_DEFINITION(DATVDemodBaseband::MsgConfigureChannelizer, Message) + +DATVDemodBaseband::DATVDemodBaseband() : + m_mutex(QMutex::Recursive) +{ + qDebug("DATVDemodBaseband::DATVDemodBaseband"); + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000)); + m_channelizer = new DownSampleChannelizer(&m_sink); + + QObject::connect( + &m_sampleFifo, + &SampleSinkFifo::dataReady, + this, + &DATVDemodBaseband::handleData, + Qt::QueuedConnection + ); + + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); +} + +DATVDemodBaseband::~DATVDemodBaseband() +{ + delete m_channelizer; +} + +void DATVDemodBaseband::reset() +{ + QMutexLocker mutexLocker(&m_mutex); + m_sampleFifo.reset(); +} + +void DATVDemodBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +{ + m_sampleFifo.write(begin, end); +} + +void DATVDemodBaseband::handleData() +{ + QMutexLocker mutexLocker(&m_mutex); + + while ((m_sampleFifo.fill() > 0) && (m_inputMessageQueue.size() == 0)) + { + SampleVector::iterator part1begin; + SampleVector::iterator part1end; + SampleVector::iterator part2begin; + SampleVector::iterator part2end; + + std::size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end); + + // first part of FIFO data + if (part1begin != part1end) { + m_channelizer->feed(part1begin, part1end); + } + + // second part of FIFO data (used when block wraps around) + if(part2begin != part2end) { + m_channelizer->feed(part2begin, part2end); + } + + m_sampleFifo.readCommit((unsigned int) count); + } +} + +void DATVDemodBaseband::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != nullptr) + { + if (handleMessage(*message)) { + delete message; + } + } +} + +bool DATVDemodBaseband::handleMessage(const Message& cmd) +{ + if (MsgConfigureDATVDemodBaseband::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + MsgConfigureDATVDemodBaseband& cfg = (MsgConfigureDATVDemodBaseband&) cmd; + qDebug() << "DATVDemodBaseband::handleMessage: MsgConfigureDATVDemodBaseband"; + + applySettings(cfg.getSettings(), cfg.getForce()); + + return true; + } + else if (MsgConfigureChannelizer::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + MsgConfigureChannelizer& cfg = (MsgConfigureChannelizer&) cmd; + qDebug() << "DATVDemodBaseband::handleMessage: MsgConfigureChannelizer" + << "(requested) sinkSampleRate: " << cfg.getSinkSampleRate() + << "(requested) sinkCenterFrequency: " << cfg.getSinkCenterFrequency(); + m_channelizer->setChannelization(cfg.getSinkSampleRate(), cfg.getSinkCenterFrequency()); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + + return true; + } + else if (DSPSignalNotification::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + qDebug() << "DATVDemodBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate(); + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate())); + m_channelizer->setBasebandSampleRate(notif.getSampleRate()); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + + return true; + } + else + { + return false; + } +} + +void DATVDemodBaseband::applySettings(const DATVDemodSettings& settings, bool force) +{ + qDebug("DATVDemodBaseband::applySettings"); + + if ((settings.m_centerFrequency != m_settings.m_centerFrequency)|| force) + { + unsigned int desiredSampleRate = m_channelizer->getBasebandSampleRate(); + m_channelizer->setChannelization(desiredSampleRate, settings.m_centerFrequency); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + } + + m_sink.applySettings(settings, force); + m_settings = settings; +} + +int DATVDemodBaseband::getChannelSampleRate() const +{ + return m_channelizer->getChannelSampleRate(); +} + + +void DATVDemodBaseband::setBasebandSampleRate(int sampleRate) +{ + qDebug("DATVDemodBaseband::setBasebandSampleRate: %d", sampleRate); + m_channelizer->setBasebandSampleRate(sampleRate); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); +} diff --git a/plugins/channelrx/demoddatv/datvdemodbaseband.h b/plugins/channelrx/demoddatv/datvdemodbaseband.h new file mode 100644 index 000000000..410807908 --- /dev/null +++ b/plugins/channelrx/demoddatv/datvdemodbaseband.h @@ -0,0 +1,118 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_DATVDEMODBASEBAND_H +#define INCLUDE_DATVDEMODBASEBAND_H + +#include +#include + +#include "dsp/samplesinkfifo.h" +#include "util/message.h" +#include "util/messagequeue.h" + +#include "datvdemodsink.h" + +class DownSampleChannelizer; + +class DATVDemodBaseband : public QObject +{ + Q_OBJECT +public: + class MsgConfigureDATVDemodBaseband : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const DATVDemodSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureDATVDemodBaseband* create(const DATVDemodSettings& settings, bool force) + { + return new MsgConfigureDATVDemodBaseband(settings, force); + } + + private: + DATVDemodSettings m_settings; + bool m_force; + + MsgConfigureDATVDemodBaseband(const DATVDemodSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + class MsgConfigureChannelizer : public Message { + MESSAGE_CLASS_DECLARATION + + public: + int getSinkSampleRate() const { return m_sinkSampleRate; } + int getSinkCenterFrequency() const { return m_sinkCenterFrequency; } + + static MsgConfigureChannelizer* create(int sinkSampleRate, int sinkCenterFrequency) { + return new MsgConfigureChannelizer(sinkSampleRate, sinkCenterFrequency); + } + + private: + int m_sinkSampleRate; + int m_sinkCenterFrequency; + + MsgConfigureChannelizer(int sinkSampleRate, int sinkCenterFrequency) : + Message(), + m_sinkSampleRate(sinkSampleRate), + m_sinkCenterFrequency(sinkCenterFrequency) + { } + }; + + DATVDemodBaseband(); + ~DATVDemodBaseband(); + void reset(); + void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication + int getChannelSampleRate() const; + double getMagSq() const { return m_sink.getMagSq(); } + void setTVScreen(TVScreen *tvScreen) { m_sink.setTVScreen(tvScreen); } + void setMessageQueueToGUI(MessageQueue *messageQueue) { m_sink.setMessageQueueToGUI(messageQueue); } + void setBasebandSampleRate(int sampleRate); //!< To be used when supporting thread is stopped + DATVideostream *SetVideoRender(DATVideoRender *objScreen) { return m_sink.SetVideoRender(objScreen); } + bool audioActive() { return m_sink.audioActive(); } + bool audioDecodeOK() { return m_sink.audioDecodeOK(); } + bool videoActive() { return m_sink.videoActive(); } + bool videoDecodeOK() { return m_sink.videoDecodeOK(); } + bool PlayVideo(bool blnStartStop) { return m_sink.PlayVideo(blnStartStop); } + + int getModcodModulation() const { return m_sink.getModcodModulation(); } + int getModcodCodeRate() const { return m_sink.getModcodCodeRate(); } + bool isCstlnSetByModcod() const { return m_sink.isCstlnSetByModcod(); } + +private: + SampleSinkFifo m_sampleFifo; + DownSampleChannelizer *m_channelizer; + DATVDemodSink m_sink; + MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication + DATVDemodSettings m_settings; + QMutex m_mutex; + + bool handleMessage(const Message& cmd); + void applySettings(const DATVDemodSettings& settings, bool force = false); + +private slots: + void handleInputMessages(); + void handleData(); //!< Handle data when samples have to be processed +}; + +#endif // INCLUDE_CHANNELANALYZERBASEBAND_H \ No newline at end of file diff --git a/plugins/channelrx/demoddatv/datvdemodreport.cpp b/plugins/channelrx/demoddatv/datvdemodreport.cpp new file mode 100644 index 000000000..1a959694c --- /dev/null +++ b/plugins/channelrx/demoddatv/datvdemodreport.cpp @@ -0,0 +1,26 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "datvdemodreport.h" + +MESSAGE_CLASS_DEFINITION(DATVDemodReport::MsgReportModcodCstlnChange, Message) + +DATVDemodReport::DATVDemodReport() +{} + +DATVDemodReport::~DATVDemodReport() +{} diff --git a/plugins/channelrx/demoddatv/datvdemodreport.h b/plugins/channelrx/demoddatv/datvdemodreport.h new file mode 100644 index 000000000..c14c41c3e --- /dev/null +++ b/plugins/channelrx/demoddatv/datvdemodreport.h @@ -0,0 +1,58 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_DATVDEMODREPORT_H +#define INCLUDE_DATVDEMODREPORT_H + +#include "util/message.h" + +#include "datvdemodsettings.h" + +class DATVDemodReport +{ +public: + DATVDemodReport(); + ~DATVDemodReport(); + class MsgReportModcodCstlnChange : public Message { + MESSAGE_CLASS_DECLARATION + + public: + DATVDemodSettings::DATVModulation getModulation() const { return m_modulation; } + DATVDemodSettings::DATVCodeRate getCodeRate() const { return m_codeRate; } + + static MsgReportModcodCstlnChange* create(const DATVDemodSettings::DATVModulation& modulation, + const DATVDemodSettings::DATVCodeRate& codeRate) + { + return new MsgReportModcodCstlnChange(modulation, codeRate); + } + + private: + DATVDemodSettings::DATVModulation m_modulation; + DATVDemodSettings::DATVCodeRate m_codeRate; + + MsgReportModcodCstlnChange( + const DATVDemodSettings::DATVModulation& modulation, + const DATVDemodSettings::DATVCodeRate& codeRate + ) : + Message(), + m_modulation(modulation), + m_codeRate(codeRate) + { } + }; +}; + +#endif // INCLUDE_DATVDEMODREPORT_H diff --git a/plugins/channelrx/demoddatv/datvdemodsink.cpp b/plugins/channelrx/demoddatv/datvdemodsink.cpp new file mode 100644 index 000000000..d7cd6b902 --- /dev/null +++ b/plugins/channelrx/demoddatv/datvdemodsink.cpp @@ -0,0 +1,1480 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "datvdemodreport.h" +#include "datvdemodsink.h" + +#include "leansdr/dvbs2.h" + +#include +#include +#include +#include +#include +#include "audio/audiooutput.h" +#include "dsp/dspengine.h" + +#include "dsp/downchannelizer.h" +#include "dsp/threadedbasebandsamplesink.h" +#include "device/deviceapi.h" + +const unsigned int DATVDemodSink::m_rfFilterFftLength = 1024; + +DATVDemodSink::DATVDemodSink() : + m_blnNeedConfigUpdate(false), + m_objRegisteredTVScreen(0), + m_objRegisteredVideoRender(0), + m_objVideoStream(nullptr), + m_udpStream(leansdr::tspacket::SIZE), + m_objRenderThread(nullptr), + m_audioFifo(48000), + m_blnRenderingVideo(false), + m_blnStartStopVideo(false), + m_cstlnSetByModcod(false), + m_modcodModulation(-1), + m_modcodCodeRate(-1), + m_enmModulation(DATVDemodSettings::BPSK /*DATV_FM1*/), + m_channelSampleRate(1024000) +{ + //*************** DATV PARAMETERS *************** + m_blnInitialized=false; + CleanUpDATVFramework(false); + m_objVideoStream = new DATVideostream(); + m_objRFFilter = new fftfilt(-256000.0 / 1024000.0, 256000.0 / 1024000.0, m_rfFilterFftLength); +} + +DATVDemodSink::~DATVDemodSink() +{ + m_blnInitialized=false; + + if (m_objVideoStream) + { + //Immediately exit from DATVideoStream if waiting for data before killing thread + m_objVideoStream->ThreadTimeOut = 0; + m_objVideoStream->deleteLater(); + } + + if (m_objRenderThread) + { + if (m_objRenderThread->isRunning()) + { + m_objRenderThread->stopRendering(); + m_objRenderThread->quit(); + } + + m_objRenderThread->wait(2000); + } + + CleanUpDATVFramework(true); + + delete m_objRFFilter; +} + +bool DATVDemodSink::setTVScreen(TVScreen *objScreen) +{ + m_objRegisteredTVScreen = objScreen; + return true; +} + +DATVideostream *DATVDemodSink::SetVideoRender(DATVideoRender *objScreen) +{ + m_objRegisteredVideoRender = objScreen; + m_objRegisteredVideoRender->setAudioFIFO(&m_audioFifo); + m_objRenderThread = new DATVideoRenderThread(m_objRegisteredVideoRender, m_objVideoStream); + return m_objVideoStream; +} + +bool DATVDemodSink::audioActive() +{ + if (m_objRegisteredVideoRender) { + return m_objRegisteredVideoRender->getAudioStreamIndex() >= 0; + } else { + return false; + } +} + +bool DATVDemodSink::videoActive() +{ + if (m_objRegisteredVideoRender) { + return m_objRegisteredVideoRender->getVideoStreamIndex() >= 0; + } else { + return false; + } +} + +bool DATVDemodSink::audioDecodeOK() +{ + if (m_objRegisteredVideoRender) { + return m_objRegisteredVideoRender->getAudioDecodeOK(); + } else { + return false; + } +} + +bool DATVDemodSink::videoDecodeOK() +{ + if (m_objRegisteredVideoRender) { + return m_objRegisteredVideoRender->getVideoDecodeOK(); + } else { + return false; + } +} + +bool DATVDemodSink::PlayVideo(bool blnStartStop) +{ + + if (m_objVideoStream == nullptr) { + return false; + } + + if (m_objRegisteredVideoRender == nullptr) { + return false; + } + + if (m_objRenderThread == nullptr) { + return false; + } + + if (m_blnStartStopVideo && !blnStartStop) { + return true; + } + + if (blnStartStop == true) { + m_blnStartStopVideo = true; + } + + if (m_objRenderThread->isRunning()) + { + if (blnStartStop == true) { + m_objRenderThread->stopRendering(); + } + + return true; + } + + if (m_objVideoStream->bytesAvailable() > 0) + { + m_objRenderThread->setStreamAndRenderer(m_objRegisteredVideoRender, m_objVideoStream); + m_objVideoStream->MultiThreaded = true; + m_objVideoStream->ThreadTimeOut = 5000; //5000 ms + m_objRenderThread->start(); + } + + return true; +} + +void DATVDemodSink::CleanUpDATVFramework(bool blnRelease) +{ + if (blnRelease == true) + { + if (m_objScheduler != nullptr) + { + m_objScheduler->shutdown(); + delete m_objScheduler; + } + + // NOTCH FILTER + + if (r_auto_notch != nullptr) { + delete r_auto_notch; + } + if (p_autonotched != nullptr) { + delete p_autonotched; + } + + // FREQUENCY CORRECTION : DEROTATOR + if (p_derot != nullptr) { + delete p_derot; + } + if (r_derot != nullptr) { + delete r_derot; + } + + // CNR ESTIMATION + if (p_cnr != nullptr) { + delete p_cnr; + } + if (r_cnr != nullptr) { + delete r_cnr; + } + + //FILTERING + if (r_resample != nullptr) { + delete r_resample; + } + if (p_resampled != nullptr) { + delete p_resampled; + } + if (coeffs != nullptr) { + delete coeffs; + } + + // OUTPUT PREPROCESSED DATA + if (sampler != nullptr) { + delete sampler; + } + if (coeffs_sampler != nullptr) { + delete coeffs_sampler; + } + if (p_symbols != nullptr) { + delete p_symbols; + } + if (p_freq != nullptr) { + delete p_freq; + } + if (p_ss != nullptr) { + delete p_ss; + } + if (p_mer != nullptr) { + delete p_mer; + } + if (p_sampled != nullptr) { + delete p_sampled; + } + + //DECIMATION + if (p_decimated != nullptr) { + delete p_decimated; + } + if (p_decim != nullptr) { + delete p_decim; + } + if (r_ppout != nullptr) { + delete r_ppout; + } + + //GENERIC CONSTELLATION RECEIVER + if (m_objDemodulator != nullptr) { + delete m_objDemodulator; + } + + //DECONVOLUTION AND SYNCHRONIZATION + if (p_bytes != nullptr) { + delete p_bytes; + } + if (r_deconv != nullptr) { + delete r_deconv; + } + if (r != nullptr) { + delete r; + } + if (p_descrambled != nullptr) { + delete p_descrambled; + } + if (p_frames != nullptr) { + delete p_frames; + } + if (r_etr192_descrambler != nullptr) { + delete r_etr192_descrambler; + } + if (r_sync != nullptr) { + delete r_sync; + } + if (p_mpegbytes != nullptr) { + delete p_mpegbytes; + } + if (p_lock != nullptr) { + delete p_lock; + } + if (p_locktime != nullptr) { + delete p_locktime; + } + if (r_sync_mpeg != nullptr) { + delete r_sync_mpeg; + } + + // DEINTERLEAVING + if (p_rspackets != nullptr) { + delete p_rspackets; + } + if (r_deinter != nullptr) { + delete r_deinter; + } + if (p_vbitcount != nullptr) { + delete p_vbitcount; + } + if (p_verrcount != nullptr) { + delete p_verrcount; + } + if (p_rtspackets != nullptr) { + delete p_rtspackets; + } + if (r_rsdec != nullptr) { + delete r_rsdec; + } + + //BER ESTIMATION + if (p_vber != nullptr) { + delete p_vber; + } + if (r_vber != nullptr) { + delete r_vber; + } + + // DERANDOMIZATION + if (p_tspackets != nullptr) { + delete p_tspackets; + } + if (r_derand != nullptr) { + delete r_derand; + } + + //OUTPUT : To remove + if (r_stdout != nullptr) { + delete r_stdout; + } + if (r_videoplayer != nullptr) { + delete r_videoplayer; + } + + //CONSTELLATION + if (r_scope_symbols != nullptr) { + delete r_scope_symbols; + } + + // INPUT + //if(p_rawiq!=nullptr) delete p_rawiq; + //if(p_rawiq_writer!=nullptr) delete p_rawiq_writer; + //if(p_preprocessed!=nullptr) delete p_preprocessed; + + //DVB-S2 + + if(p_slots_dvbs2 != nullptr) + { + delete (leansdr::pipebuf< leansdr::plslot >*) p_slots_dvbs2; + } + + if(p_cstln != nullptr) + { + delete p_cstln; + } + + if(p_cstln_pls != nullptr) + { + delete p_cstln_pls; + } + + if(p_framelock != nullptr) + { + delete p_framelock; + } + + if(m_objDemodulatorDVBS2 != nullptr) + { + delete (leansdr::s2_frame_receiver*) m_objDemodulatorDVBS2; + } + + if(p_fecframes != nullptr) + { + delete (leansdr::pipebuf< leansdr::fecframe >*) p_fecframes; + } + + if(p_bbframes != nullptr) + { + delete (leansdr::pipebuf*) p_bbframes; + } + + if(p_s2_deinterleaver != nullptr) + { + delete (leansdr::s2_deinterleaver*) p_s2_deinterleaver; + } + + if(r_fecdec != nullptr) + { + delete (leansdr::s2_fecdec*) r_fecdec; + } + + if(p_deframer != nullptr) + { + delete (leansdr::s2_deframer*) p_deframer; + } + + if(r_scope_symbols_dvbs2 != nullptr) + { + delete r_scope_symbols_dvbs2; + } + } + + m_objScheduler=nullptr; + + // INPUT + + p_rawiq = nullptr; + p_rawiq_writer = nullptr; + + p_preprocessed = nullptr; + + // NOTCH FILTER + r_auto_notch = nullptr; + p_autonotched = nullptr; + + // FREQUENCY CORRECTION : DEROTATOR + p_derot = nullptr; + r_derot=nullptr; + + // CNR ESTIMATION + p_cnr = nullptr; + r_cnr = nullptr; + + //FILTERING + r_resample = nullptr; + p_resampled = nullptr; + coeffs = nullptr; + ncoeffs=0; + + // OUTPUT PREPROCESSED DATA + sampler = nullptr; + coeffs_sampler=nullptr; + ncoeffs_sampler=0; + + p_symbols = nullptr; + p_freq = nullptr; + p_ss = nullptr; + p_mer = nullptr; + p_sampled = nullptr; + + //DECIMATION + p_decimated = nullptr; + p_decim = nullptr; + r_ppout = nullptr; + + //GENERIC CONSTELLATION RECEIVER + m_objDemodulator = nullptr; + + //DECONVOLUTION AND SYNCHRONIZATION + p_bytes=nullptr; + r_deconv=nullptr; + r = nullptr; + + p_descrambled = nullptr; + p_frames = nullptr; + r_etr192_descrambler = nullptr; + r_sync = nullptr; + + p_mpegbytes = nullptr; + p_lock = nullptr; + p_locktime = nullptr; + r_sync_mpeg = nullptr; + + + // DEINTERLEAVING + p_rspackets = nullptr; + r_deinter = nullptr; + + p_vbitcount = nullptr; + p_verrcount = nullptr; + p_rtspackets = nullptr; + r_rsdec = nullptr; + + + //BER ESTIMATION + p_vber = nullptr; + r_vber = nullptr; + + + // DERANDOMIZATION + p_tspackets = nullptr; + r_derand = nullptr; + + + //OUTPUT : To remove void * + r_stdout = nullptr; + r_videoplayer = nullptr; + + + //CONSTELLATION + r_scope_symbols = nullptr; + + //DVB-S2 + p_slots_dvbs2 = nullptr; + p_cstln = nullptr; + p_cstln_pls = nullptr; + p_framelock = nullptr; + m_objDemodulatorDVBS2 = nullptr; + p_fecframes = nullptr; + p_bbframes = nullptr; + p_s2_deinterleaver = nullptr; + r_fecdec = nullptr; + p_deframer = nullptr; + r_scope_symbols_dvbs2 = nullptr; +} + +void DATVDemodSink::InitDATVFramework() +{ + m_blnDVBInitialized = false; + m_lngReadIQ = 0; + CleanUpDATVFramework(false); + + qDebug() << "DATVDemodSink::InitDATVFramework:" + << " Standard: " << m_settings.m_standard + << " Symbol Rate: " << m_settings.m_symbolRate + << " Modulation: " << m_settings.m_modulation + << " Notch Filters: " << m_settings.m_notchFilters + << " Allow Drift: " << m_settings.m_allowDrift + << " Fast Lock: " << m_settings.m_fastLock + << " Filter: " << m_settings.m_filter + << " HARD METRIC: " << m_settings.m_hardMetric + << " RollOff: " << m_settings.m_rollOff + << " Viterbi: " << m_settings.m_viterbi + << " Excursion: " << m_settings.m_excursion + << " Sample rate: " << m_channelSampleRate; + + m_objCfg.standard = m_settings.m_standard; + + m_objCfg.fec = (leansdr::code_rate) getLeanDVBCodeRateFromDATV(m_settings.m_fec); + m_objCfg.Fs = (float) m_channelSampleRate; + m_objCfg.Fm = (float) m_settings.m_symbolRate; + m_objCfg.fastlock = m_settings.m_fastLock; + + m_objCfg.sampler = m_settings.m_filter; + m_objCfg.rolloff = m_settings.m_rollOff; //0...1 + m_objCfg.rrc_rej = (float) m_settings.m_excursion; //dB + m_objCfg.rrc_steps = 0; //auto + + switch(m_settings.m_modulation) + { + case DATVDemodSettings::BPSK: + m_objCfg.constellation = leansdr::cstln_lut::BPSK; + break; + case DATVDemodSettings::QPSK: + m_objCfg.constellation = leansdr::cstln_lut::QPSK; + break; + case DATVDemodSettings::PSK8: + m_objCfg.constellation = leansdr::cstln_lut::PSK8; + break; + case DATVDemodSettings::APSK16: + m_objCfg.constellation = leansdr::cstln_lut::APSK16; + break; + case DATVDemodSettings::APSK32: + m_objCfg.constellation = leansdr::cstln_lut::APSK32; + break; + case DATVDemodSettings::APSK64E: + m_objCfg.constellation = leansdr::cstln_lut::APSK64E; + break; + case DATVDemodSettings::QAM16: + m_objCfg.constellation = leansdr::cstln_lut::QAM16; + break; + case DATVDemodSettings::QAM64: + m_objCfg.constellation = leansdr::cstln_lut::QAM64; + break; + case DATVDemodSettings::QAM256: + m_objCfg.constellation = leansdr::cstln_lut::QAM256; + break; + default: + m_objCfg.constellation = leansdr::cstln_lut::BPSK; + break; + } + + m_objCfg.allow_drift = m_settings.m_allowDrift; + m_objCfg.anf = m_settings.m_notchFilters; + m_objCfg.hard_metric = m_settings.m_hardMetric; + m_objCfg.sampler = m_settings.m_filter; + m_objCfg.viterbi = m_settings.m_viterbi; + + // Min buffer size for baseband data + // scopes: 1024 + // ss_estimator: 1024 + // anf: 4096 + // cstln_receiver: reads in chunks of 128+1 + BUF_BASEBAND = 4096 * m_objCfg.buf_factor; + + // Min buffer size for IQ symbols + // cstln_receiver: writes in chunks of 128/omega symbols (margin 128) + // deconv_sync: reads at least 64+32 + // A larger buffer improves performance significantly. + BUF_SYMBOLS = 1024 * m_objCfg.buf_factor; + + // Min buffer size for unsynchronized bytes + // deconv_sync: writes 32 bytes + // mpeg_sync: reads up to 204*scan_syncs = 1632 bytes + BUF_BYTES = 2048 * m_objCfg.buf_factor; + + // Min buffer size for synchronized (but interleaved) bytes + // mpeg_sync: writes 1 rspacket + // deinterleaver: reads 17*11*12+204 = 2448 bytes + BUF_MPEGBYTES = 2448 * m_objCfg.buf_factor; + + // Min buffer size for packets: 1 + BUF_PACKETS = m_objCfg.buf_factor; + + // Min buffer size for misc measurements: 1 + BUF_SLOW = m_objCfg.buf_factor; + + m_lngExpectedReadIQ = BUF_BASEBAND; + + m_objScheduler = new leansdr::scheduler(); + + //*************** + p_rawiq = new leansdr::pipebuf(m_objScheduler, "rawiq", BUF_BASEBAND); + p_rawiq_writer = new leansdr::pipewriter(*p_rawiq); + p_preprocessed = p_rawiq; + + // NOTCH FILTER + + if (m_objCfg.anf>0) + { + p_autonotched = new leansdr::pipebuf(m_objScheduler, "autonotched", BUF_BASEBAND); + r_auto_notch = new leansdr::auto_notch(m_objScheduler, *p_preprocessed, *p_autonotched, m_objCfg.anf, 0); + p_preprocessed = p_autonotched; + } + + + // FREQUENCY CORRECTION + + //******** -> if ( m_objCfg.Fderot>0 ) + + // CNR ESTIMATION + + p_cnr = new leansdr::pipebuf(m_objScheduler, "cnr", BUF_SLOW); + + if (m_objCfg.cnr == true) + { + r_cnr = new leansdr::cnr_fft(m_objScheduler, *p_preprocessed, *p_cnr, m_objCfg.Fm/m_objCfg.Fs); + r_cnr->decimation = decimation(m_objCfg.Fs, 1); // 1 Hz + } + + // FILTERING + + int decim = 1; + + //******** -> if ( m_objCfg.resample ) + + + // DECIMATION + // (Unless already done in resampler) + + //******** -> if ( !m_objCfg.resample && m_objCfg.decim>1 ) + + //Resampling FS + + + // Generic constellation receiver + + p_symbols = new leansdr::pipebuf(m_objScheduler, "PSK soft-symbols", BUF_SYMBOLS); + p_freq = new leansdr::pipebuf (m_objScheduler, "freq", BUF_SLOW); + p_ss = new leansdr::pipebuf (m_objScheduler, "SS", BUF_SLOW); + p_mer = new leansdr::pipebuf (m_objScheduler, "MER", BUF_SLOW); + p_sampled = new leansdr::pipebuf (m_objScheduler, "PSK symbols", BUF_BASEBAND); + + switch (m_objCfg.sampler) + { + case DATVDemodSettings::SAMP_NEAREST: + sampler = new leansdr::nearest_sampler(); + break; + case DATVDemodSettings::SAMP_LINEAR: + sampler = new leansdr::linear_sampler(); + break; + case DATVDemodSettings::SAMP_RRC: + { + if (m_objCfg.rrc_steps == 0) + { + // At least 64 discrete sampling points between symbols + m_objCfg.rrc_steps = std::max(1, (int)(64*m_objCfg.Fm / m_objCfg.Fs)); + } + + float Frrc = m_objCfg.Fs * m_objCfg.rrc_steps; // Sample freq of the RRC filter + float transition = (m_objCfg.Fm/2) * m_objCfg.rolloff; + int order = m_objCfg.rrc_rej * Frrc / (22*transition); + ncoeffs_sampler = leansdr::filtergen::root_raised_cosine(order, m_objCfg.Fm/Frrc, m_objCfg.rolloff, &coeffs_sampler); + sampler = new leansdr::fir_sampler(ncoeffs_sampler, coeffs_sampler, m_objCfg.rrc_steps); + break; + } + default: + qCritical("DATVDemodSink::InitDATVFramework: Interpolator not implemented"); + return; + } + + m_objDemodulator = new leansdr::cstln_receiver( + m_objScheduler, + sampler, + *p_preprocessed, + *p_symbols, + p_freq, + p_ss, + p_mer, + p_sampled); + + if (m_objCfg.standard == DATVDemodSettings::DVB_S) + { + if ( m_objCfg.constellation != leansdr::cstln_lut::QPSK + && m_objCfg.constellation != leansdr::cstln_lut::BPSK ) + { + qWarning("DATVDemodSink::InitDATVFramework: non-standard constellation for DVB-S"); + } + } + + if (m_objCfg.standard == DATVDemodSettings::DVB_S2) + { + // For DVB-S2 testing only. + // Constellation should be determined from PL signalling. + qDebug("DATVDemodSink::InitDATVFramework: DVB-S2: Testing symbol sampler only."); + } + + m_objDemodulator->cstln = make_dvbs_constellation(m_objCfg.constellation, m_objCfg.fec); + + if (m_objCfg.hard_metric) { + m_objDemodulator->cstln->harden(); + } + + m_objDemodulator->set_omega(m_objCfg.Fs/m_objCfg.Fm); + + //******** if ( m_objCfg.Ftune ) + //{ + // m_objDemodulator->set_freq(m_objCfg.Ftune/m_objCfg.Fs); + //} + + if (m_objCfg.allow_drift) { + m_objDemodulator->set_allow_drift(true); + } + + //******** -> if ( m_objCfg.viterbi ) + if (m_objCfg.viterbi) { + m_objDemodulator->pll_adjustment /= 6; + } + + m_objDemodulator->meas_decimation = decimation(m_objCfg.Fs, m_objCfg.Finfo); + + // TRACKING FILTERS + + if (r_cnr) + { + r_cnr->freq_tap = &m_objDemodulator->freq_tap; + r_cnr->tap_multiplier = 1.0 / decim; + } + + //constellation + + if (m_objRegisteredTVScreen) + { + qDebug("DATVDemodSink::InitDATVFramework: Register DVBSTVSCREEN"); + + m_objRegisteredTVScreen->resizeTVScreen(256,256); + r_scope_symbols = new leansdr::datvconstellation(m_objScheduler, *p_sampled, -128,128, nullptr, m_objRegisteredTVScreen); + r_scope_symbols->decimation = 1; + r_scope_symbols->cstln = &m_objDemodulator->cstln; + r_scope_symbols->calculate_cstln_points(); + } + + // DECONVOLUTION AND SYNCHRONIZATION + + p_bytes = new leansdr::pipebuf(m_objScheduler, "bytes", BUF_BYTES); + + r_deconv = nullptr; + + //******** -> if ( m_objCfg.viterbi ) + + if (m_objCfg.viterbi) + { + if (m_objCfg.fec == leansdr::FEC23 && (m_objDemodulator->cstln->nsymbols == 4 || m_objDemodulator->cstln->nsymbols == 64)) { + m_objCfg.fec = leansdr::FEC46; + } + + //To uncomment -> Linking Problem : undefined symbol: _ZN7leansdr21viterbi_dec_interfaceIhhiiE6updateEPiS2_ + r = new leansdr::viterbi_sync(m_objScheduler, (*p_symbols), (*p_bytes), m_objDemodulator->cstln, m_objCfg.fec); + + if (m_objCfg.fastlock) { + r->resync_period = 1; + } + } + else + { + r_deconv = make_deconvol_sync_simple(m_objScheduler, (*p_symbols), (*p_bytes), m_objCfg.fec); + r_deconv->fastlock = m_objCfg.fastlock; + } + + //******* -> if ( m_objCfg.hdlc ) + + p_mpegbytes = new leansdr::pipebuf (m_objScheduler, "mpegbytes", BUF_MPEGBYTES); + p_lock = new leansdr::pipebuf (m_objScheduler, "lock", BUF_SLOW); + p_locktime = new leansdr::pipebuf (m_objScheduler, "locktime", BUF_PACKETS); + + r_sync_mpeg = new leansdr::mpeg_sync(m_objScheduler, *p_bytes, *p_mpegbytes, r_deconv, p_lock, p_locktime); + r_sync_mpeg->fastlock = m_objCfg.fastlock; + + // DEINTERLEAVING + + p_rspackets = new leansdr::pipebuf >(m_objScheduler, "RS-enc packets", BUF_PACKETS); + r_deinter = new leansdr::deinterleaver(m_objScheduler, *p_mpegbytes, *p_rspackets); + + // REED-SOLOMON + + p_vbitcount = new leansdr::pipebuf(m_objScheduler, "Bits processed", BUF_PACKETS); + p_verrcount = new leansdr::pipebuf(m_objScheduler, "Bits corrected", BUF_PACKETS); + p_rtspackets = new leansdr::pipebuf(m_objScheduler, "rand TS packets", BUF_PACKETS); + r_rsdec = new leansdr::rs_decoder(m_objScheduler, *p_rspackets, *p_rtspackets, p_vbitcount, p_verrcount); + + // BER ESTIMATION + + /* + p_vber = new pipebuf (m_objScheduler, "VBER", BUF_SLOW); + r_vber = new rate_estimator (m_objScheduler, *p_verrcount, *p_vbitcount, *p_vber); + r_vber->sample_size = m_objCfg.Fm/2; // About twice per second, depending on CR + // Require resolution better than 2E-5 + if ( r_vber->sample_size < 50000 ) + { + r_vber->sample_size = 50000; + } + */ + + // DERANDOMIZATION + p_tspackets = new leansdr::pipebuf(m_objScheduler, "TS packets", BUF_PACKETS); + r_derand = new leansdr::derandomizer(m_objScheduler, *p_rtspackets, *p_tspackets); + + // OUTPUT + r_videoplayer = new leansdr::datvvideoplayer(m_objScheduler, *p_tspackets, m_objVideoStream, &m_udpStream); + + m_blnDVBInitialized = true; +} + +//************ DVB-S2 Decoder ************ +void DATVDemodSink::InitDATVS2Framework() +{ + leansdr::s2_frame_receiver * objDemodulatorDVBS2; + + m_blnDVBInitialized = false; + m_lngReadIQ = 0; + CleanUpDATVFramework(false); + + qDebug() << "DATVDemodSink::InitDATVS2Framework:" + << " Standard: " << m_settings.m_standard + << " Symbol Rate: " << m_settings.m_symbolRate + << " Modulation: " << m_settings.m_modulation + << " Notch Filters: " << m_settings.m_notchFilters + << " Allow Drift: " << m_settings.m_allowDrift + << " Fast Lock: " << m_settings.m_fastLock + << " Filter: " << m_settings.m_filter + << " HARD METRIC: " << m_settings.m_hardMetric + << " RollOff: " << m_settings.m_rollOff + << " Viterbi: " << m_settings.m_viterbi + << " Excursion: " << m_settings.m_excursion + << " Sample rate: " << m_channelSampleRate ; + + + + m_objCfg.standard = m_settings.m_standard; + + m_objCfg.fec = (leansdr::code_rate) getLeanDVBCodeRateFromDATV(m_settings.m_fec); + m_objCfg.Fs = (float) m_channelSampleRate; + m_objCfg.Fm = (float) m_settings.m_symbolRate; + m_objCfg.fastlock = m_settings.m_fastLock; + + m_objCfg.sampler = m_settings.m_filter; + m_objCfg.rolloff = m_settings.m_rollOff; //0...1 + m_objCfg.rrc_rej = (float) m_settings.m_excursion; //dB + m_objCfg.rrc_steps = 0; //auto + + switch(m_settings.m_modulation) + { + case DATVDemodSettings::BPSK: + m_objCfg.constellation = leansdr::cstln_lut::BPSK; + break; + case DATVDemodSettings::QPSK: + m_objCfg.constellation = leansdr::cstln_lut::QPSK; + break; + case DATVDemodSettings::PSK8: + m_objCfg.constellation = leansdr::cstln_lut::PSK8; + break; + case DATVDemodSettings::APSK16: + m_objCfg.constellation = leansdr::cstln_lut::APSK16; + break; + case DATVDemodSettings::APSK32: + m_objCfg.constellation = leansdr::cstln_lut::APSK32; + break; + case DATVDemodSettings::APSK64E: + m_objCfg.constellation = leansdr::cstln_lut::APSK64E; + break; + case DATVDemodSettings::QAM16: + m_objCfg.constellation = leansdr::cstln_lut::QAM16; + break; + case DATVDemodSettings::QAM64: + m_objCfg.constellation = leansdr::cstln_lut::QAM64; + break; + case DATVDemodSettings::QAM256: + m_objCfg.constellation = leansdr::cstln_lut::QAM256; + break; + default: + m_objCfg.constellation = leansdr::cstln_lut::BPSK; + break; + } + + m_objCfg.allow_drift = m_settings.m_allowDrift; + m_objCfg.anf = m_settings.m_notchFilters; + m_objCfg.hard_metric = m_settings.m_hardMetric; + m_objCfg.sampler = m_settings.m_filter; + m_objCfg.viterbi = m_settings.m_viterbi; + + // Min buffer size for baseband data + S2_MAX_SYMBOLS = (90*(1+360)+36*((360-1)/16)); + + BUF_BASEBAND = S2_MAX_SYMBOLS * 2 * (m_objCfg.Fs/m_objCfg.Fm) * m_objCfg.buf_factor; + // Min buffer size for IQ symbols + // cstln_receiver: writes in chunks of 128/omega symbols (margin 128) + // deconv_sync: reads at least 64+32 + // A larger buffer improves performance significantly. + BUF_SYMBOLS = 1024 * m_objCfg.buf_factor; + + + // Min buffer size for misc measurements: 1 + BUF_SLOW = m_objCfg.buf_factor; + + // dvbs2 : Min buffer size for slots: 4 for deinterleaver + BUF_SLOTS = leansdr::modcod_info::MAX_SLOTS_PER_FRAME * m_objCfg.buf_factor; + + BUF_FRAMES = m_objCfg.buf_factor; + + // Min buffer size for TS packets: Up to 39 per BBFRAME + BUF_S2PACKETS = (leansdr::fec_info::KBCH_MAX/188/8+1) * m_objCfg.buf_factor; + + m_lngExpectedReadIQ = BUF_BASEBAND; + + m_objScheduler = new leansdr::scheduler(); + + //*************** + p_rawiq = new leansdr::pipebuf(m_objScheduler, "rawiq", BUF_BASEBAND); + p_rawiq_writer = new leansdr::pipewriter(*p_rawiq); + p_preprocessed = p_rawiq; + + // NOTCH FILTER + + if (m_objCfg.anf>0) + { + p_autonotched = new leansdr::pipebuf(m_objScheduler, "autonotched", BUF_BASEBAND); + r_auto_notch = new leansdr::auto_notch(m_objScheduler, *p_preprocessed, *p_autonotched, m_objCfg.anf, 0); + p_preprocessed = p_autonotched; + } + + // FREQUENCY CORRECTION + + //******** -> if ( m_objCfg.Fderot>0 ) + + // CNR ESTIMATION + /** + p_cnr = new leansdr::pipebuf(m_objScheduler, "cnr", BUF_SLOW); + + if (m_objCfg.cnr == true) + { + r_cnr = new leansdr::cnr_fft(m_objScheduler, *p_preprocessed, *p_cnr, m_objCfg.Fm/m_objCfg.Fs); + r_cnr->decimation = decimation(m_objCfg.Fs, 1); // 1 Hz + } + **/ + // FILTERING + + int decim = 1; + + //******** -> if ( m_objCfg.resample ) + + + // DECIMATION + // (Unless already done in resampler) + + //******** -> if ( !m_objCfg.resample && m_objCfg.decim>1 ) + + //Resampling FS + + + // Generic constellation receiver + + p_freq = new leansdr::pipebuf (m_objScheduler, "freq", BUF_SLOW); + p_ss = new leansdr::pipebuf (m_objScheduler, "SS", BUF_SLOW); + p_mer = new leansdr::pipebuf (m_objScheduler, "MER", BUF_SLOW); + + switch (m_objCfg.sampler) + { + case DATVDemodSettings::SAMP_NEAREST: + sampler = new leansdr::nearest_sampler(); + break; + case DATVDemodSettings::SAMP_LINEAR: + sampler = new leansdr::linear_sampler(); + break; + case DATVDemodSettings::SAMP_RRC: + { + if (m_objCfg.rrc_steps == 0) + { + // At least 64 discrete sampling points between symbols + m_objCfg.rrc_steps = std::max(1, (int)(64*m_objCfg.Fm / m_objCfg.Fs)); + } + + float Frrc = m_objCfg.Fs * m_objCfg.rrc_steps; // Sample freq of the RRC filter + float transition = (m_objCfg.Fm/2) * m_objCfg.rolloff; + int order = m_objCfg.rrc_rej * Frrc / (22*transition); + ncoeffs_sampler = leansdr::filtergen::root_raised_cosine(order, m_objCfg.Fm/Frrc, m_objCfg.rolloff, &coeffs_sampler); + sampler = new leansdr::fir_sampler(ncoeffs_sampler, coeffs_sampler, m_objCfg.rrc_steps); + break; + } + default: + qCritical("DATVDemodSink::InitDATVS2Framework: Interpolator not implemented"); + return; + } + + p_slots_dvbs2 = new leansdr::pipebuf< leansdr::plslot > (m_objScheduler, "PL slots", BUF_SLOTS); + + p_cstln = new leansdr::pipebuf(m_objScheduler, "cstln", BUF_BASEBAND); + p_cstln_pls = new leansdr::pipebuf(m_objScheduler, "PLS cstln", BUF_BASEBAND); + p_framelock = new leansdr::pipebuf(m_objScheduler, "frame lock", BUF_SLOW); + + m_objDemodulatorDVBS2 = new leansdr::s2_frame_receiver( + m_objScheduler, + sampler, + *p_preprocessed, + *(leansdr::pipebuf< leansdr::plslot > *) p_slots_dvbs2, + /* p_freq */ nullptr, + /* p_ss */ nullptr, + /* p_mer */ nullptr, + p_cstln, + /* p_cstln_pls */ nullptr, + /*p_iqsymbols*/ nullptr, + /* p_framelock */nullptr); + + objDemodulatorDVBS2 = (leansdr::s2_frame_receiver *) m_objDemodulatorDVBS2; + + + objDemodulatorDVBS2->omega = m_objCfg.Fs/m_objCfg.Fm; +//objDemodulatorDVBS2->mu=1; + + + m_objCfg.Ftune=0.0f; + objDemodulatorDVBS2->Ftune = m_objCfg.Ftune / m_objCfg.Fm; + +/* + demod.strongpls = cfg.strongpls; +*/ + + objDemodulatorDVBS2->Fm = m_objCfg.Fm; + objDemodulatorDVBS2->meas_decimation = decimation(m_objCfg.Fs, m_objCfg.Finfo); + + objDemodulatorDVBS2->strongpls = false; + + + objDemodulatorDVBS2->cstln = make_dvbs2_constellation(m_objCfg.constellation, m_objCfg.fec); + m_cstlnSetByModcod = false; + + //constellation + + if (m_objRegisteredTVScreen) + { + qDebug("DATVDemodSink::InitDATVS2Framework: Register DVBS 2 TVSCREEN"); + + m_objRegisteredTVScreen->resizeTVScreen(256,256); + r_scope_symbols_dvbs2 = new leansdr::datvdvbs2constellation(m_objScheduler, *p_cstln /* *p_sampled */ /* *p_cstln */, -128,128, nullptr, m_objRegisteredTVScreen); + r_scope_symbols_dvbs2->decimation = 1; + r_scope_symbols_dvbs2->cstln = (leansdr::cstln_base**) &objDemodulatorDVBS2->cstln; + r_scope_symbols_dvbs2->calculate_cstln_points(); + } + + // Bit-flipping mode. + // Deinterleave into hard bits. + + p_bbframes = new leansdr::pipebuf(m_objScheduler, "BB frames", BUF_FRAMES); + + p_fecframes = new leansdr::pipebuf< leansdr::fecframe >(m_objScheduler, "FEC frames", BUF_FRAMES); + + p_s2_deinterleaver = new leansdr::s2_deinterleaver( + m_objScheduler, + *(leansdr::pipebuf< leansdr::plslot > *) p_slots_dvbs2, + *(leansdr::pipebuf< leansdr::fecframe > * ) p_fecframes + ); + + p_vbitcount= new leansdr::pipebuf(m_objScheduler, "Bits processed", BUF_S2PACKETS); + p_verrcount = new leansdr::pipebuf(m_objScheduler, "Bits corrected", BUF_S2PACKETS); + + r_fecdec = new leansdr::s2_fecdec( + m_objScheduler, *(leansdr::pipebuf< leansdr::fecframe > * ) p_fecframes, + *(leansdr::pipebuf *) p_bbframes, + p_vbitcount, + p_verrcount + ); + leansdr::s2_fecdec *fecdec = (leansdr::s2_fecdec * ) r_fecdec; + + fecdec->bitflips=0; + + /* + fecdec->bitflips = cfg.ldpc_bf; //int TODO + if ( ! cfg.ldpc_bf ) + fprintf(stderr, "Warning: No LDPC error correction selected.\n") + */ + + // Deframe BB frames to TS packets + p_lock = new leansdr::pipebuf (m_objScheduler, "lock", BUF_SLOW); + p_locktime = new leansdr::pipebuf (m_objScheduler, "locktime", BUF_S2PACKETS); + p_tspackets = new leansdr::pipebuf(m_objScheduler, "TS packets", BUF_S2PACKETS); + + p_deframer = new leansdr::s2_deframer(m_objScheduler,*(leansdr::pipebuf *) p_bbframes, *p_tspackets, p_lock, p_locktime); + +/* + if ( cfg.fd_gse >= 0 ) deframer.fd_gse = cfg.fd_gse; +*/ + //********************************************** + + // OUTPUT + r_videoplayer = new leansdr::datvvideoplayer(m_objScheduler, *p_tspackets, m_objVideoStream, &m_udpStream); + + m_blnDVBInitialized = true; +} + +void DATVDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +{ + float fltI; + float fltQ; + leansdr::cf32 objIQ; + //Complex objC; + fftfilt::cmplx *objRF; + int intRFOut; + double magSq; + + int lngWritable=0; + + //********** Bis repetita : Let's rock and roll buddy ! ********** + +#ifdef EXTENDED_DIRECT_SAMPLE + + qint16 * ptrBuffer; + qint32 intLen; + + //********** Reading direct samples ********** + + SampleVector::const_iterator it = begin; + intLen = it->intLen; + ptrBuffer = it->ptrBuffer; + ptrBufferToRelease = ptrBuffer; + ++it; + + for(qint32 intInd=0; intIndreal(); + fltQ = it->imag(); +#endif + + + //********** demodulation ********** + + + if (m_blnNeedConfigUpdate) + { + qDebug("DATVDemod::feed: Settings applied. Standard : %d...", m_settings.m_standard); + m_blnNeedConfigUpdate=false; + + if(m_settings.m_standard==DATVDemodSettings::DVB_S2) + { + printf("SWITCHING TO DVBS-2\r\n"); + InitDATVS2Framework(); + } + else + { + printf("SWITCHING TO DVBS\r\n"); + InitDATVFramework(); + } + } + + + //********** iq stream **************** + + Complex objC(fltI,fltQ); + + objC *= m_objNCO.nextIQ(); + + intRFOut = m_objRFFilter->runFilt(objC, &objRF); // filter RF before demod + + for (int intI = 0 ; intI < intRFOut; intI++) + { + objIQ.re = objRF->real(); + objIQ.im = objRF->imag(); + magSq = objIQ.re*objIQ.re + objIQ.im*objIQ.im; + m_objMagSqAverage(magSq); + + objRF ++; + + if (m_blnDVBInitialized + && (p_rawiq_writer!=nullptr) + && (m_objScheduler!=nullptr)) + { + p_rawiq_writer->write(objIQ); + m_lngReadIQ++; + + lngWritable = p_rawiq_writer->writable(); + + //Leave +1 by safety + //if(((m_lngReadIQ+1)>=lngWritable) || (m_lngReadIQ>=768)) + if((m_lngReadIQ+1)>=lngWritable) + { + m_objScheduler->step(); + + m_lngReadIQ=0; + //delete p_rawiq_writer; + //p_rawiq_writer = new leansdr::pipewriter(*p_rawiq); + } + } + + } + } // Samples for loop + + // DVBS2: Track change of constellation via MODCOD + if (m_settings.m_standard==DATVDemodSettings::DVB_S2) + { + leansdr::s2_frame_receiver * objDemodulatorDVBS2 = (leansdr::s2_frame_receiver *) m_objDemodulatorDVBS2; + + if (objDemodulatorDVBS2->cstln->m_setByModcod && !m_cstlnSetByModcod) + { + qDebug("DATVDemod::feed: change by MODCOD detected"); + + if (r_scope_symbols_dvbs2) { + r_scope_symbols_dvbs2->calculate_cstln_points(); + } + + if (getMessageQueueToGUI()) + { + DATVDemodReport::MsgReportModcodCstlnChange *msg = DATVDemodReport::MsgReportModcodCstlnChange::create( + getModulationFromLeanDVBCode(objDemodulatorDVBS2->cstln->m_typeCode), + getCodeRateFromLeanDVBCode(objDemodulatorDVBS2->cstln->m_rateCode) + ); + + getMessageQueueToGUI()->push(msg); + } + } + + m_cstlnSetByModcod = objDemodulatorDVBS2->cstln->m_setByModcod; + m_modcodModulation = objDemodulatorDVBS2->m_modcodType; + m_modcodCodeRate = objDemodulatorDVBS2->m_modcodRate; + } +} + +void DATVDemodSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force) +{ + qDebug() << "DATVDemodSink::applyChannelSettings:" + << " channelSampleRate: " << channelSampleRate + << " channelFrequencyOffset: " << channelFrequencyOffset; + + bool callApplySettings = false; + + if ((m_settings.m_centerFrequency != channelFrequencyOffset) || + (m_channelSampleRate != channelSampleRate) || force) + { + m_objNCO.setFreq(-(float) channelFrequencyOffset, (float) channelSampleRate); + qDebug("DATVDemod::applyChannelSettings: NCO: IF: %d <> TF: %d ISR: %d", + channelFrequencyOffset, m_settings.m_centerFrequency, channelSampleRate); + callApplySettings = true; + } + + if ((m_channelSampleRate != channelSampleRate) || force) + { + //Bandpass filter shaping + Real fltLowCut = -((float) m_settings.m_rfBandwidth / 2.0) / (float) channelSampleRate; + Real fltHiCut = ((float) m_settings.m_rfBandwidth / 2.0) / (float) channelSampleRate; + m_objRFFilter->create_filter(fltLowCut, fltHiCut); + } + + m_channelSampleRate = channelSampleRate; + m_settings.m_centerFrequency = channelFrequencyOffset; + + if (callApplySettings) { + applySettings(m_settings, true); + } +} + +void DATVDemodSink::applySettings(const DATVDemodSettings& settings, bool force) +{ + QString msg = QObject::tr("DATVDemodSink::applySettings: force: %1").arg(force); + settings.debug(msg); + + qDebug("DATVDemodSink::applySettings: m_channelSampleRate: %d", m_channelSampleRate); + + if (m_channelSampleRate == 0) { + return; + } + + if ((settings.m_audioVolume) != (m_settings.m_audioVolume) || force) + { + if (m_objRegisteredVideoRender) { + m_objRegisteredVideoRender->setAudioVolume(settings.m_audioVolume); + } + } + + if ((settings.m_audioMute) != (m_settings.m_audioMute) || force) + { + if (m_objRegisteredVideoRender) { + m_objRegisteredVideoRender->setAudioMute(settings.m_audioMute); + } + } + + if ((settings.m_videoMute) != (m_settings.m_videoMute) || force) + { + if (m_objRegisteredVideoRender) { + m_objRegisteredVideoRender->setVideoMute(settings.m_videoMute); + } + } + + if ((m_settings.m_rfBandwidth != settings.m_rfBandwidth) + || force) + { + + //Bandpass filter shaping + Real fltLowCut = -((float) settings.m_rfBandwidth / 2.0) / (float) m_channelSampleRate; + Real fltHiCut = ((float) settings.m_rfBandwidth / 2.0) / (float) m_channelSampleRate; + m_objRFFilter->create_filter(fltLowCut, fltHiCut); + } + + if ((m_settings.m_centerFrequency != settings.m_centerFrequency) + || force) + { + m_objNCO.setFreq(-(float) settings.m_centerFrequency, (float) m_channelSampleRate); + } + + if ((m_settings.m_udpTS != settings.m_udpTS) || force) { + m_udpStream.setActive(settings.m_udpTS); + } + + if ((m_settings.m_udpTSAddress != settings.m_udpTSAddress) || force) { + m_udpStream.setAddress(settings.m_udpTSAddress); + } + + if ((m_settings.m_udpTSPort != settings.m_udpTSPort) || force) { + m_udpStream.setPort(settings.m_udpTSPort); + } + + if (m_settings.isDifferent(settings) || force) + { + m_blnNeedConfigUpdate = true; + } + + m_settings = settings; +} + +DATVDemodSettings::DATVCodeRate DATVDemodSink::getCodeRateFromLeanDVBCode(int leanDVBCodeRate) +{ + if (leanDVBCodeRate == leansdr::code_rate::FEC12) { + return DATVDemodSettings::DATVCodeRate::FEC12; + } else if (leanDVBCodeRate == leansdr::code_rate::FEC13) { + return DATVDemodSettings::DATVCodeRate::FEC13; + } else if (leanDVBCodeRate == leansdr::code_rate::FEC14) { + return DATVDemodSettings::DATVCodeRate::FEC14; + } else if (leanDVBCodeRate == leansdr::code_rate::FEC23) { + return DATVDemodSettings::DATVCodeRate::FEC23; + } else if (leanDVBCodeRate == leansdr::code_rate::FEC25) { + return DATVDemodSettings::DATVCodeRate::FEC25; + } else if (leanDVBCodeRate == leansdr::code_rate::FEC34) { + return DATVDemodSettings::DATVCodeRate::FEC34; + } else if (leanDVBCodeRate == leansdr::code_rate::FEC35) { + return DATVDemodSettings::DATVCodeRate::FEC35; + } else if (leanDVBCodeRate == leansdr::code_rate::FEC45) { + return DATVDemodSettings::DATVCodeRate::FEC45; + } else if (leanDVBCodeRate == leansdr::code_rate::FEC46) { + return DATVDemodSettings::DATVCodeRate::FEC46; + } else if (leanDVBCodeRate == leansdr::code_rate::FEC56) { + return DATVDemodSettings::DATVCodeRate::FEC56; + } else if (leanDVBCodeRate == leansdr::code_rate::FEC78) { + return DATVDemodSettings::DATVCodeRate::FEC78; + } else if (leanDVBCodeRate == leansdr::code_rate::FEC89) { + return DATVDemodSettings::DATVCodeRate::FEC89; + } else if (leanDVBCodeRate == leansdr::code_rate::FEC910) { + return DATVDemodSettings::DATVCodeRate::FEC910; + } else { + return DATVDemodSettings::DATVCodeRate::RATE_UNSET; + } +} + +DATVDemodSettings::DATVModulation DATVDemodSink::getModulationFromLeanDVBCode(int leanDVBModulation) +{ + if (leanDVBModulation == leansdr::cstln_base::predef::APSK16) { + return DATVDemodSettings::DATVModulation::APSK16; + } else if (leanDVBModulation == leansdr::cstln_base::predef::APSK32) { + return DATVDemodSettings::DATVModulation::APSK32; + } else if (leanDVBModulation == leansdr::cstln_base::predef::APSK64E) { + return DATVDemodSettings::DATVModulation::APSK64E; + } else if (leanDVBModulation == leansdr::cstln_base::predef::BPSK) { + return DATVDemodSettings::DATVModulation::BPSK; + } else if (leanDVBModulation == leansdr::cstln_base::predef::PSK8) { + return DATVDemodSettings::DATVModulation::PSK8; + } else if (leanDVBModulation == leansdr::cstln_base::predef::QAM16) { + return DATVDemodSettings::DATVModulation::QAM16; + } else if (leanDVBModulation == leansdr::cstln_base::predef::QAM64) { + return DATVDemodSettings::DATVModulation::QAM64; + } else if (leanDVBModulation == leansdr::cstln_base::predef::QAM256) { + return DATVDemodSettings::DATVModulation::QAM256; + } else if (leanDVBModulation == leansdr::cstln_base::predef::QPSK) { + return DATVDemodSettings::DATVModulation::QPSK; + } else { + return DATVDemodSettings::DATVModulation::MOD_UNSET; + } +} + +int DATVDemodSink::getLeanDVBCodeRateFromDATV(DATVDemodSettings::DATVCodeRate datvCodeRate) +{ + if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC12) { + return (int) leansdr::code_rate::FEC12; + } else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC13) { + return (int) leansdr::code_rate::FEC13; + } else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC14) { + return (int) leansdr::code_rate::FEC14; + } else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC23) { + return (int) leansdr::code_rate::FEC23; + } else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC25) { + return (int) leansdr::code_rate::FEC25; + } else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC34) { + return (int) leansdr::code_rate::FEC34; + } else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC35) { + return (int) leansdr::code_rate::FEC35; + } else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC45) { + return (int) leansdr::code_rate::FEC45; + } else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC46) { + return (int) leansdr::code_rate::FEC46; + } else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC56) { + return (int) leansdr::code_rate::FEC56; + } else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC78) { + return (int) leansdr::code_rate::FEC78; + } else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC89) { + return (int) leansdr::code_rate::FEC89; + } else if (datvCodeRate == DATVDemodSettings::DATVCodeRate::FEC910) { + return (int) leansdr::code_rate::FEC910; + } else { + return -1; + } +} + +int DATVDemodSink::getLeanDVBModulationFromDATV(DATVDemodSettings::DATVModulation datvModulation) +{ + if (datvModulation == DATVDemodSettings::DATVModulation::APSK16) { + return (int) leansdr::cstln_base::predef::APSK16; + } else if (datvModulation == DATVDemodSettings::DATVModulation::APSK32) { + return (int) leansdr::cstln_base::predef::APSK32; + } else if (datvModulation == DATVDemodSettings::DATVModulation::APSK64E) { + return (int) leansdr::cstln_base::predef::APSK64E; + } else if (datvModulation == DATVDemodSettings::DATVModulation::BPSK) { + return (int) leansdr::cstln_base::predef::BPSK; + } else if (datvModulation == DATVDemodSettings::DATVModulation::PSK8) { + return (int) leansdr::cstln_base::predef::PSK8; + } else if (datvModulation == DATVDemodSettings::DATVModulation::QAM16) { + return (int) leansdr::cstln_base::predef::QAM16; + } else if (datvModulation == DATVDemodSettings::DATVModulation::QAM64) { + return (int) leansdr::cstln_base::predef::QAM64; + } else if (datvModulation == DATVDemodSettings::DATVModulation::QAM256) { + return (int) leansdr::cstln_base::predef::QAM256; + } else if (datvModulation == DATVDemodSettings::DATVModulation::QPSK) { + return (int) leansdr::cstln_base::predef::QPSK; + } else { + return -1; + } +} + diff --git a/plugins/channelrx/demoddatv/datvdemodsink.h b/plugins/channelrx/demoddatv/datvdemodsink.h new file mode 100644 index 000000000..19fbd1b68 --- /dev/null +++ b/plugins/channelrx/demoddatv/datvdemodsink.h @@ -0,0 +1,316 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_DATVDEMODSINK_H +#define INCLUDE_DATVDEMODSINK_H + +//LeanSDR +#include "leansdr/framework.h" +#include "leansdr/generic.h" +#include "leansdr/dvb.h" +#include "leansdr/filtergen.h" + +#include "leansdr/hdlc.h" +#include "leansdr/iess.h" + +#include "datvconstellation.h" +#include "datvdvbs2constellation.h" +#include "datvvideoplayer.h" +#include "datvideostream.h" +#include "datvudpstream.h" +#include "datvideorender.h" +#include "datvdemodsettings.h" + +#include "channel/channelapi.h" +#include "dsp/channelsamplesink.h" +#include "dsp/basebandsamplesink.h" +#include "dsp/devicesamplesource.h" +#include "dsp/dspcommands.h" +#include "dsp/downchannelizer.h" +#include "dsp/fftfilt.h" +#include "dsp/nco.h" +#include "dsp/interpolator.h" +#include "dsp/movingaverage.h" +#include "dsp/agc.h" +#include "audio/audiofifo.h" +#include "util/message.h" +#include "util/movingaverage.h" + +#include + +class TVScreen; +class DATVideoRender; + +class DATVDemodSink : public ChannelSampleSink { +public: + struct config + { + DATVDemodSettings::dvb_version standard; + DATVDemodSettings::dvb_sampler sampler; + + int buf_factor; // Buffer sizing + float Fs; // Sampling frequency (Hz) + float Fderot; // Shift the signal (Hz). Note: Ftune is faster + int anf; // Number of auto notch filters + bool cnr; // Measure CNR + unsigned int decim; // Decimation, 0=auto + float Fm; // QPSK symbol rate (Hz) + leansdr::cstln_lut::predef constellation; + leansdr::code_rate fec; + float Ftune; // Bias frequency for the QPSK demodulator (Hz) + bool allow_drift; + bool fastlock; + bool viterbi; + bool hard_metric; + bool resample; + float resample_rej; // Approx. filter rejection in dB + int rrc_steps; // Discrete steps between symbols, 0=auto + float rrc_rej; // Approx. RRC filter rejection in dB + float rolloff; // Roll-off 0..1 + bool hdlc; // Expect HDLC frames instead of MPEG packets + bool packetized; // Output frames with 16-bit BE length + float Finfo; // Desired refresh rate on fd_info (Hz) + + config() : + standard(DATVDemodSettings::DVB_S), + sampler(DATVDemodSettings::SAMP_LINEAR), + buf_factor(4), + Fs(2.4e6), + Fderot(0), + anf(0), + cnr(false), + decim(0), + Fm(2e6), + constellation(leansdr::cstln_lut::QPSK), + fec(leansdr::FEC12), + Ftune(0), + allow_drift(false), + fastlock(true), + viterbi(false), + hard_metric(false), + resample(false), + resample_rej(10), + rrc_steps(0), + rrc_rej(10), + rolloff(0.35), + hdlc(false), + packetized(false), + Finfo(5) + { + } + }; + + DATVDemodSink(); + ~DATVDemodSink(); + + virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + + bool setTVScreen(TVScreen *objScreen); + DATVideostream * SetVideoRender(DATVideoRender *objScreen); + bool audioActive(); + bool audioDecodeOK(); + bool videoActive(); + bool videoDecodeOK(); + + bool PlayVideo(bool blnStartStop); + + int GetSampleRate(); + double getMagSq() const { return m_objMagSqAverage; } //!< Beware this is scaled to 2^30 + int getModcodModulation() const { return m_modcodModulation; } + int getModcodCodeRate() const { return m_modcodCodeRate; } + bool isCstlnSetByModcod() const { return m_cstlnSetByModcod; } + static DATVDemodSettings::DATVCodeRate getCodeRateFromLeanDVBCode(int leanDVBCodeRate); + static DATVDemodSettings::DATVModulation getModulationFromLeanDVBCode(int leanDVBModulation); + static int getLeanDVBCodeRateFromDATV(DATVDemodSettings::DATVCodeRate datvCodeRate); + static int getLeanDVBModulationFromDATV(DATVDemodSettings::DATVModulation datvModulation); + void setMessageQueueToGUI(MessageQueue *messageQueue) { m_messageQueueToGUI = messageQueue; } + + void applySettings(const DATVDemodSettings& settings, bool force = false); + void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false); + +private: + inline int decimation(float Fin, float Fout) { int d = Fin / Fout; return std::max(d, 1); } + + void CleanUpDATVFramework(bool blnRelease); + void InitDATVFramework(); + void InitDATVS2Framework(); + + unsigned long m_lngExpectedReadIQ; + long m_lngReadIQ; + + //************** LEANDBV Parameters ************** + + unsigned long BUF_BASEBAND; + unsigned long BUF_SYMBOLS; + unsigned long BUF_BYTES; + unsigned long BUF_MPEGBYTES; + unsigned long BUF_PACKETS; + unsigned long BUF_SLOW; + + + //dvbs2 + unsigned long BUF_SLOTS; + unsigned long BUF_FRAMES; + unsigned long BUF_S2PACKETS; + unsigned long S2_MAX_SYMBOLS; + + //************** LEANDBV Scheduler *************** + + leansdr::scheduler * m_objScheduler; + struct config m_objCfg; + + bool m_blnDVBInitialized; + bool m_blnNeedConfigUpdate; + + //LeanSDR Pipe Buffer + // INPUT + + leansdr::pipebuf *p_rawiq; + leansdr::pipewriter *p_rawiq_writer; + leansdr::pipebuf *p_preprocessed; + + // NOTCH FILTER + leansdr::auto_notch *r_auto_notch; + leansdr::pipebuf *p_autonotched; + + // FREQUENCY CORRECTION : DEROTATOR + leansdr::pipebuf *p_derot; + leansdr::rotator *r_derot; + + // CNR ESTIMATION + leansdr::pipebuf *p_cnr; + leansdr::cnr_fft *r_cnr; + + //FILTERING + leansdr::fir_filter *r_resample; + leansdr::pipebuf *p_resampled; + float *coeffs; + int ncoeffs; + + // OUTPUT PREPROCESSED DATA + leansdr::sampler_interface *sampler; + float *coeffs_sampler; + int ncoeffs_sampler; + + leansdr::pipebuf *p_symbols; + leansdr::pipebuf *p_freq; + leansdr::pipebuf *p_ss; + leansdr::pipebuf *p_mer; + leansdr::pipebuf *p_sampled; + + //dvb-s2 + void *p_slots_dvbs2; + leansdr::pipebuf *p_cstln; + leansdr::pipebuf *p_cstln_pls; + leansdr::pipebuf *p_framelock; + void *m_objDemodulatorDVBS2; + void *p_fecframes; + void *p_bbframes; + void *p_s2_deinterleaver; + void *r_fecdec; + void *p_deframer; + + //DECIMATION + leansdr::pipebuf *p_decimated; + leansdr::decimator *p_decim; + + //PROCESSED DATA MONITORING + leansdr::file_writer *r_ppout; + + //GENERIC CONSTELLATION RECEIVER + leansdr::cstln_receiver *m_objDemodulator; + + // DECONVOLUTION AND SYNCHRONIZATION + leansdr::pipebuf *p_bytes; + leansdr::deconvol_sync_simple *r_deconv; + leansdr::viterbi_sync *r; + leansdr::pipebuf *p_descrambled; + leansdr::pipebuf *p_frames; + + leansdr::etr192_descrambler * r_etr192_descrambler; + leansdr::hdlc_sync *r_sync; + + leansdr::pipebuf *p_mpegbytes; + leansdr::pipebuf *p_lock; + leansdr::pipebuf *p_locktime; + leansdr::mpeg_sync *r_sync_mpeg; + + + // DEINTERLEAVING + leansdr::pipebuf > *p_rspackets; + leansdr::deinterleaver *r_deinter; + + // REED-SOLOMON + leansdr::pipebuf *p_vbitcount; + leansdr::pipebuf *p_verrcount; + leansdr::pipebuf *p_rtspackets; + leansdr::rs_decoder *r_rsdec; + + // BER ESTIMATION + leansdr::pipebuf *p_vber; + leansdr::rate_estimator *r_vber; + + // DERANDOMIZATION + leansdr::pipebuf *p_tspackets; + leansdr::derandomizer *r_derand; + + + //OUTPUT + leansdr::file_writer *r_stdout; + leansdr::datvvideoplayer *r_videoplayer; + + //CONSTELLATION + leansdr::datvconstellation *r_scope_symbols; + leansdr::datvdvbs2constellation *r_scope_symbols_dvbs2; + + //*************** DATV PARAMETERS *************** + TVScreen *m_objRegisteredTVScreen; + DATVideoRender *m_objRegisteredVideoRender; + DATVideostream *m_objVideoStream; + DATVUDPStream m_udpStream; + DATVideoRenderThread *m_objRenderThread; + + // Audio + AudioFifo m_audioFifo; + + fftfilt * m_objRFFilter; + NCO m_objNCO; + + bool m_blnInitialized; + bool m_blnRenderingVideo; + bool m_blnStartStopVideo; + bool m_cstlnSetByModcod; + int m_modcodModulation; + int m_modcodCodeRate; + + DATVDemodSettings::DATVModulation m_enmModulation; + + //DATVConfig m_objRunning; + DATVDemodSettings m_settings; + int m_channelSampleRate; + int m_channelFrequencyOffset; + MovingAverageUtil m_objMagSqAverage; + + MessageQueue *m_messageQueueToGUI; + + static const unsigned int m_rfFilterFftLength; + + MessageQueue *getMessageQueueToGUI() { return m_messageQueueToGUI; } + +}; + +#endif // INCLUDE_DATVDEMODSINK_H \ No newline at end of file diff --git a/plugins/channelrx/demodlora/CMakeLists.txt b/plugins/channelrx/demodlora/CMakeLists.txt index c0dc591fd..66e487f71 100644 --- a/plugins/channelrx/demodlora/CMakeLists.txt +++ b/plugins/channelrx/demodlora/CMakeLists.txt @@ -4,8 +4,9 @@ set(lora_SOURCES lorademod.cpp lorademodgui.cpp lorademodsettings.cpp + lorademodsink.cpp + lorademodbaseband.cpp loraplugin.cpp - lorademodgui.ui ) @@ -13,6 +14,8 @@ set(lora_HEADERS lorademod.h lorademodgui.h lorademodsettings.h + lorademodsink.h + lorademodbaseband.h loraplugin.h ) @@ -24,8 +27,8 @@ add_library(demodlora SHARED ) target_link_libraries(demodlora - Qt5::Core - Qt5::Widgets + Qt5::Core + Qt5::Widgets sdrbase sdrgui ) diff --git a/plugins/channelrx/demodlora/lorabits.h b/plugins/channelrx/demodlora/lorabits.h deleted file mode 100644 index 3da5c6a4e..000000000 --- a/plugins/channelrx/demodlora/lorabits.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - Interleaving is "easiest" if the same number of bits is used per symbol as for FEC - Chosen mode "spreading 8, low rate" has 6 bits per symbol, so use 4:6 FEC - - More spreading needs higher frequency resolution and longer time on air, increasing drift errors. - Want higher bandwidth when using more spreading, which needs more CPU and a better FFT. - - Six bit Hamming can only correct long runs of drift errors when not using interleaving. Interleaving defeats the point of using Gray code and puts multiple bit errors into single FEC blocks. Hardware decoding uses RSSI to detect the symbols most likely to be damaged, so that individual bits can be repaired after de-interleaving. - - Using Implicit Mode: explicit starts with a 4:8 block and seems to have a different whitening sequence. -*/ - -// Six bits per symbol, six chars per block -void LoRaDemod::interleave6(char* inout, int size) -{ - int i, j; - char in[6 * 2]; - short s; - - for (j = 0; j < size; j+=6) { - for (i = 0; i < 6; i++) - in[i] = in[i + 6] = inout[i + j]; - // top bits are swapped - for (i = 0; i < 6; i++) { - s = (32 & in[2 + i]) | (16 & in[1 + i]) | (8 & in[3 + i]) - | (4 & in[4 + i]) | (2 & in[5 + i]) | (1 & in[6 + i]); - // bits are also rotated - s = (s << 3) | (s >> 3); - s &= 63; - s = (s >> i) | (s << (6 - i)); - inout[i + j] = s & 63; - } - } -} - -short LoRaDemod::toGray(short num) -{ - return (num >> 1) ^ num; -} - -// Ignore the FEC bits, just extract the data bits -void LoRaDemod::hamming6(char* c, int size) -{ - int i; - - for (i = 0; i < size; i++) { - c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<0) | ((c[i] & 4)>>0) | ((c[i] & 8)>>3); - i++; - c[i] = ((c[i] & 1)<<2) | ((c[i] & 2)<<2) | ((c[i] & 4)>>1) | ((c[i] & 8)>>3); - i++; - c[i] = ((c[i] &32)>>2) | ((c[i] & 2)<<1) | ((c[i] & 4)>>1) | ((c[i] & 8)>>3); - i++; - c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<1) | ((c[i] & 4)>>1) | ((c[i] & 8)>>3); - i++; - c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<1) | ((c[i] & 4)>>1) | ((c[i] &16)>>4); - i++; - c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<1) | ((c[i] & 4)>>2) | ((c[i] & 8)>>2); - } - c[i] = 0; -} - -// data whitening (6 bit) -void LoRaDemod::prng6(char* inout, int size) -{ - const char otp[] = { - //explicit mode - "cOGGg7CM2=b5a?<`i;T2of5jDAB=2DoQ9ko?h_RLQR4@Z\\`9jY\\PX89lHX8h_R]c_^@OB<0`W08ik?Mg>dQZf3kn5Je5R=R4h[dY?d9[n5Lg]b]R8hR<0`T008h9c9QJm[c?a:lQEGa;nU=b_WfUV2?V4@c=8h9B9njlQZDC@9Z. // /////////////////////////////////////////////////////////////////////////////////// +#include #include #include -#include +#include #include "dsp/downchannelizer.h" #include "dsp/threadedbasebandsamplesink.h" @@ -28,342 +29,87 @@ #include "device/deviceapi.h" #include "lorademod.h" -#include "lorabits.h" MESSAGE_CLASS_DEFINITION(LoRaDemod::MsgConfigureLoRaDemod, Message) -MESSAGE_CLASS_DEFINITION(LoRaDemod::MsgConfigureChannelizer, Message) const QString LoRaDemod::m_channelIdURI = "sdrangel.channel.lorademod"; const QString LoRaDemod::m_channelId = "LoRaDemod"; LoRaDemod::LoRaDemod(DeviceAPI* deviceAPI) : ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink), - m_deviceAPI(deviceAPI), - m_sampleSink(0), - m_settingsMutex(QMutex::Recursive) + m_deviceAPI(deviceAPI) { setObjectName(m_channelId); - m_Bandwidth = LoRaDemodSettings::bandwidths[0]; - m_sampleRate = 96000; - m_frequency = 0; - m_nco.setFreq(m_frequency, m_sampleRate); - m_interpolator.create(16, m_sampleRate, m_Bandwidth/1.9); - m_sampleDistanceRemain = (Real)m_sampleRate / m_Bandwidth; + m_thread = new QThread(this); + m_basebandSink = new LoRaDemodBaseband(); + m_basebandSink->moveToThread(m_thread); - m_chirp = 0; - m_angle = 0; - m_bin = 0; - m_result = 0; - m_count = 0; - m_header = 0; - m_time = 0; - m_tune = 0; + applySettings(m_settings, true); - loraFilter = new sfft(LORA_SFFT_LEN); - negaFilter = new sfft(LORA_SFFT_LEN); - - mov = new float[4*LORA_SFFT_LEN]; - history = new short[1024]; - finetune = new short[16]; - - m_channelizer = new DownChannelizer(this); - m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer); - m_deviceAPI->addChannelSink(m_threadedChannelizer); + m_deviceAPI->addChannelSink(this); m_deviceAPI->addChannelSinkAPI(this); } LoRaDemod::~LoRaDemod() { - if (loraFilter) - delete loraFilter; - if (negaFilter) - delete negaFilter; - if (mov) - delete [] mov; - if (history) - delete [] history; - if (finetune) - delete [] finetune; - - m_deviceAPI->removeChannelSinkAPI(this); - m_deviceAPI->removeChannelSink(m_threadedChannelizer); - delete m_threadedChannelizer; - delete m_channelizer; + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + delete m_basebandSink; + delete m_thread; } -void LoRaDemod::dumpRaw() -{ - short bin, j, max; - char text[256]; - - max = m_time / 4 - 3; - - if (max > 140) - { - max = 140; // about 2 symbols to each char - } - - for ( j=0; j < max; j++) - { - bin = (history[(j + 1) * 4] + m_tune ) & (LORA_SFFT_LEN - 1); - text[j] = toGray(bin >> 1); - } - - prng6(text, max); - // First block is always 8 symbols - interleave6(text, 6); - interleave6(&text[8], max); - hamming6(text, 6); - hamming6(&text[8], max); - - for ( j=0; j < max / 2; j++) - { - text[j] = (text[j * 2 + 1] << 4) | (0xf & text[j * 2 + 0]); - - if ((text[j] < 32 )||( text[j] > 126)) - { - text[j] = 0x5f; - } - } - - text[3] = text[2]; - text[2] = text[1]; - text[1] = text[0]; - text[j] = 0; - - printf("%s\n", &text[1]); -} - -short LoRaDemod::synch(short bin) -{ - short i, j; - - if (bin < 0) - { - if (m_time > 70) - { - dumpRaw(); - } - - m_time = 0; - return -1; - } - - history[m_time] = bin; - - if (m_time > 12) - { - if (bin == history[m_time - 6]) - { - if (bin == history[m_time - 12]) - { - m_tune = LORA_SFFT_LEN - bin; - j = 0; - - for (i=0; i<12; i++) - { - j += finetune[15 & (m_time - i)]; - } - - if (j < 0) - { - m_tune += 1; - } - - m_tune &= (LORA_SFFT_LEN - 1); - m_time = 0; - return -1; - } - } - } - - m_time++; - m_time &= 1023; - - if (m_time & 3) - { - return -1; - } - - return (bin + m_tune) & (LORA_SFFT_LEN - 1); -} - -int LoRaDemod::detect(Complex c, Complex a) -{ - int p, q; - short i, result, negresult, movpoint; - float peak, negpeak, tfloat; - float mag[LORA_SFFT_LEN]; - float rev[LORA_SFFT_LEN]; - - loraFilter->run(c * a); - negaFilter->run(c * conj(a)); - - // process spectrum twice in FFTLEN - if (++m_count & ((1 << DATA_BITS) - 1)) - { - return m_result; - } - - movpoint = 3 & (m_count >> DATA_BITS); - - loraFilter->fetch(mag); - negaFilter->fetch(rev); - peak = negpeak = 0.0f; - result = negresult = 0; - - for (i = 0; i < LORA_SFFT_LEN; i++) - { - if (rev[i] > negpeak) - { - negpeak = rev[i]; - negresult = i; - } - - tfloat = mov[i] + mov[LORA_SFFT_LEN + i] +mov[2 * LORA_SFFT_LEN + i] - + mov[3 * LORA_SFFT_LEN + i] + mag[i]; - - if (tfloat > peak) - { - peak = tfloat; - result = i; - } - - mov[movpoint * LORA_SFFT_LEN + i] = mag[i]; - } - - p = (result - 1 + LORA_SFFT_LEN) & (LORA_SFFT_LEN -1); - q = (result + 1) & (LORA_SFFT_LEN -1); - finetune[15 & m_time] = (mag[p] > mag[q]) ? -1 : 1; - - if (peak < negpeak * LORA_SQUELCH) - { - result = -1; - } - - result = synch(result); - - if (result >= 0) - { - m_result = result; - } - - return m_result; -} void LoRaDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool pO) { (void) pO; - int newangle; - Complex ci; - - m_sampleBuffer.clear(); - - m_settingsMutex.lock(); - - for(SampleVector::const_iterator it = begin; it < end; ++it) - { - Complex c(it->real() / SDR_RX_SCALEF, it->imag() / SDR_RX_SCALEF); - c *= m_nco.nextIQ(); - - if(m_interpolator.decimate(&m_sampleDistanceRemain, c, &ci)) - { - m_chirp = (m_chirp + 1) & (SPREADFACTOR - 1); - m_angle = (m_angle + m_chirp) & (SPREADFACTOR - 1); - Complex cangle(cos(M_PI*2*m_angle/SPREADFACTOR),-sin(M_PI*2*m_angle/SPREADFACTOR)); - newangle = detect(ci, cangle); - - m_bin = (m_bin + newangle) & (LORA_SFFT_LEN - 1); - Complex nangle(cos(M_PI*2*m_bin/LORA_SFFT_LEN),sin(M_PI*2*m_bin/LORA_SFFT_LEN)); - m_sampleBuffer.push_back(Sample(nangle.real() * 100, nangle.imag() * 100)); - m_sampleDistanceRemain += (Real)m_sampleRate / m_Bandwidth; - } - } - - if(m_sampleSink != 0) - { - m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), false); - } - - m_settingsMutex.unlock(); + m_basebandSink->feed(begin, end); } void LoRaDemod::start() { + qDebug() << "LoRaDemod::start"; + + if (m_basebandSampleRate != 0) { + m_basebandSink->setBasebandSampleRate(m_basebandSampleRate); + } + + m_basebandSink->reset(); + m_thread->start(); } void LoRaDemod::stop() { + qDebug() << "LoRaDemod::stop"; + m_thread->exit(); + m_thread->wait(); } bool LoRaDemod::handleMessage(const Message& cmd) { - qDebug() << "LoRaDemod::handleMessage"; - - if (DownChannelizer::MsgChannelizerNotification::match(cmd)) - { - DownChannelizer::MsgChannelizerNotification& notif = (DownChannelizer::MsgChannelizerNotification&) cmd; - - m_settingsMutex.lock(); - - m_sampleRate = notif.getSampleRate(); - m_nco.setFreq(-notif.getFrequencyOffset(), m_sampleRate); - m_interpolator.create(16, m_sampleRate, m_Bandwidth/1.9); - m_sampleDistanceRemain = m_sampleRate / m_Bandwidth; - - m_settingsMutex.unlock(); - - qDebug() << "LoRaDemod::handleMessage: MsgChannelizerNotification: m_sampleRate: " << m_sampleRate - << " frequencyOffset: " << notif.getFrequencyOffset(); - - return true; - } - else if (MsgConfigureChannelizer::match(cmd)) - { - MsgConfigureChannelizer& cfg = (MsgConfigureChannelizer&) cmd; - - m_channelizer->configure(m_channelizer->getInputMessageQueue(), - cfg.getSampleRate(), - cfg.getCenterFrequency()); - - qDebug() << "LoRaDemod::handleMessage: MsgConfigureChannelizer: sampleRate: " << cfg.getSampleRate() - << " centerFrequency: " << cfg.getCenterFrequency(); - - return true; - } - else if (MsgConfigureLoRaDemod::match(cmd)) + if (MsgConfigureLoRaDemod::match(cmd)) { + qDebug() << "LoRaDemod::handleMessage: MsgConfigureLoRaDemod"; MsgConfigureLoRaDemod& cfg = (MsgConfigureLoRaDemod&) cmd; - - m_settingsMutex.lock(); - LoRaDemodSettings settings = cfg.getSettings(); - - m_Bandwidth = LoRaDemodSettings::bandwidths[settings.m_bandwidthIndex]; - m_interpolator.create(16, m_sampleRate, m_Bandwidth/1.9); - - m_settingsMutex.unlock(); - - m_settings = settings; - qDebug() << "LoRaDemod::handleMessage: MsgConfigureLoRaDemod: m_Bandwidth: " << m_Bandwidth; + applySettings(settings, cfg.getForce()); return true; } else if (DSPSignalNotification::match(cmd)) { + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + m_basebandSampleRate = notif.getSampleRate(); + // Forward to the sink + DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy + qDebug() << "LoRaDemod::handleMessage: DSPSignalNotification"; + m_basebandSink->getInputMessageQueue()->push(rep); + return true; } else { - if(m_sampleSink != 0) - { - return m_sampleSink->handleMessage(cmd); - } - else - { - return false; - } + return false; } } @@ -389,3 +135,18 @@ bool LoRaDemod::deserialize(const QByteArray& data) } } +void LoRaDemod::applySettings(const LoRaDemodSettings& settings, bool force) +{ + qDebug() << "LoRaDemod::applySettings:" + << " m_centerFrequency: " << settings.m_centerFrequency + << " m_bandwidthIndex: " << settings.m_bandwidthIndex + << " m_spread: " << settings.m_spread + << " m_rgbColor: " << settings.m_rgbColor + << " m_title: " << settings.m_title + << " force: " << force; + + LoRaDemodBaseband::MsgConfigureLoRaDemodBaseband *msg = LoRaDemodBaseband::MsgConfigureLoRaDemodBaseband::create(settings, force); + m_basebandSink->getInputMessageQueue()->push(msg); + + m_settings = settings; +} \ No newline at end of file diff --git a/plugins/channelrx/demodlora/lorademod.h b/plugins/channelrx/demodlora/lorademod.h index 60f12d9d6..cca244ed6 100644 --- a/plugins/channelrx/demodlora/lorademod.h +++ b/plugins/channelrx/demodlora/lorademod.h @@ -16,30 +16,19 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#ifndef INCLUDE_LoRaDEMOD_H -#define INCLUDE_LoRaDEMOD_H +#ifndef INCLUDE_LORADEMOD_H +#define INCLUDE_LORADEMOD_H -#include #include #include "dsp/basebandsamplesink.h" #include "channel/channelapi.h" -#include "dsp/nco.h" -#include "dsp/interpolator.h" #include "util/message.h" -#include "dsp/fftfilt.h" -#include "lorademodsettings.h" - -#define DATA_BITS (6) -#define SAMPLEBITS (DATA_BITS + 2) -#define SPREADFACTOR (1 << SAMPLEBITS) -#define LORA_SFFT_LEN (SPREADFACTOR / 2) -#define LORA_SQUELCH (3) +#include "lorademodbaseband.h" class DeviceAPI; -class ThreadedBasebandSampleSink; -class DownChannelizer; +class QThread; class LoRaDemod : public BasebandSampleSink, public ChannelAPI { public: @@ -66,33 +55,10 @@ public: { } }; - class MsgConfigureChannelizer : public Message { - MESSAGE_CLASS_DECLARATION - - public: - int getSampleRate() const { return m_sampleRate; } - int getCenterFrequency() const { return m_centerFrequency; } - - static MsgConfigureChannelizer* create(int sampleRate, int centerFrequency) - { - return new MsgConfigureChannelizer(sampleRate, centerFrequency); - } - - private: - int m_sampleRate; - int m_centerFrequency; - - MsgConfigureChannelizer(int sampleRate, int centerFrequency) : - Message(), - m_sampleRate(sampleRate), - m_centerFrequency(centerFrequency) - { } - }; - LoRaDemod(DeviceAPI* deviceAPI); virtual ~LoRaDemod(); virtual void destroy() { delete this; } - void setSpectrumSink(BasebandSampleSink* sampleSink) { m_sampleSink = sampleSink; } + void setSpectrumSink(BasebandSampleSink* sampleSink) { m_basebandSink->setSpectrumSink(sampleSink); } virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool pO); virtual void start(); @@ -120,44 +86,13 @@ public: static const QString m_channelId; private: - int detect(Complex sample, Complex angle); - void dumpRaw(void); - short synch (short bin); - short toGray(short bin); - void interleave6(char* inout, int size); - void hamming6(char* inout, int size); - void prng6(char* inout, int size); - DeviceAPI *m_deviceAPI; - ThreadedBasebandSampleSink* m_threadedChannelizer; - DownChannelizer* m_channelizer; + QThread *m_thread; + LoRaDemodBaseband* m_basebandSink; LoRaDemodSettings m_settings; + int m_basebandSampleRate; - Real m_Bandwidth; - int m_sampleRate; - int m_frequency; - int m_chirp; - int m_angle; - int m_bin; - int m_result; - int m_count; - int m_header; - int m_time; - short m_tune; - - sfft* loraFilter; - sfft* negaFilter; - float* mov; - short* history; - short* finetune; - - NCO m_nco; - Interpolator m_interpolator; - Real m_sampleDistanceRemain; - - BasebandSampleSink* m_sampleSink; - SampleVector m_sampleBuffer; - QMutex m_settingsMutex; + void applySettings(const LoRaDemodSettings& settings, bool force = false); }; -#endif // INCLUDE_LoRaDEMOD_H +#endif // INCLUDE_LORADEMOD_H diff --git a/plugins/channelrx/demodlora/lorademodbaseband.cpp b/plugins/channelrx/demodlora/lorademodbaseband.cpp new file mode 100644 index 000000000..f3291bdaf --- /dev/null +++ b/plugins/channelrx/demodlora/lorademodbaseband.cpp @@ -0,0 +1,169 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "dsp/downsamplechannelizer.h" + +#include "lorademodbaseband.h" + +MESSAGE_CLASS_DEFINITION(LoRaDemodBaseband::MsgConfigureLoRaDemodBaseband, Message) + +LoRaDemodBaseband::LoRaDemodBaseband() : + m_mutex(QMutex::Recursive) +{ + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000)); + m_channelizer = new DownSampleChannelizer(&m_sink); + + qDebug("LoRaDemodBaseband::LoRaDemodBaseband"); + QObject::connect( + &m_sampleFifo, + &SampleSinkFifo::dataReady, + this, + &LoRaDemodBaseband::handleData, + Qt::QueuedConnection + ); + + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); +} + +LoRaDemodBaseband::~LoRaDemodBaseband() +{ + delete m_channelizer; +} + +void LoRaDemodBaseband::reset() +{ + QMutexLocker mutexLocker(&m_mutex); + m_sampleFifo.reset(); +} + +void LoRaDemodBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +{ + m_sampleFifo.write(begin, end); +} + +void LoRaDemodBaseband::handleData() +{ + QMutexLocker mutexLocker(&m_mutex); + + while ((m_sampleFifo.fill() > 0) && (m_inputMessageQueue.size() == 0)) + { + SampleVector::iterator part1begin; + SampleVector::iterator part1end; + SampleVector::iterator part2begin; + SampleVector::iterator part2end; + + std::size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end); + + // first part of FIFO data + if (part1begin != part1end) { + m_channelizer->feed(part1begin, part1end); + } + + // second part of FIFO data (used when block wraps around) + if(part2begin != part2end) { + m_channelizer->feed(part2begin, part2end); + } + + m_sampleFifo.readCommit((unsigned int) count); + } +} + +void LoRaDemodBaseband::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != nullptr) + { + if (handleMessage(*message)) { + delete message; + } + } +} + +bool LoRaDemodBaseband::handleMessage(const Message& cmd) +{ + if (MsgConfigureLoRaDemodBaseband::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + MsgConfigureLoRaDemodBaseband& cfg = (MsgConfigureLoRaDemodBaseband&) cmd; + qDebug() << "LoRaDemodBaseband::handleMessage: MsgConfigureLoRaDemodBaseband"; + + applySettings(cfg.getSettings(), cfg.getForce()); + + return true; + } + else if (DSPSignalNotification::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + qDebug() << "LoRaDemodBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate(); + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate())); + m_channelizer->setBasebandSampleRate(notif.getSampleRate()); + m_sink.applyChannelSettings( + m_channelizer->getChannelSampleRate(), + LoRaDemodSettings::bandwidths[m_settings.m_bandwidthIndex], + m_channelizer->getChannelFrequencyOffset() + ); + + return true; + } + else + { + return false; + } +} + +void LoRaDemodBaseband::applySettings(const LoRaDemodSettings& settings, bool force) +{ + if ((settings.m_bandwidthIndex != m_settings.m_bandwidthIndex) + || (settings.m_centerFrequency != m_settings.m_centerFrequency) || force) + { + m_channelizer->setChannelization( + LoRaDemodSettings::bandwidths[settings.m_bandwidthIndex], + settings.m_centerFrequency + ); + m_sink.applyChannelSettings( + m_channelizer->getChannelSampleRate(), + LoRaDemodSettings::bandwidths[settings.m_bandwidthIndex], + m_channelizer->getChannelFrequencyOffset() + ); + } + + m_sink.applySettings(settings, force); + + m_settings = settings; +} + +int LoRaDemodBaseband::getChannelSampleRate() const +{ + return m_channelizer->getChannelSampleRate(); +} + + +void LoRaDemodBaseband::setBasebandSampleRate(int sampleRate) +{ + m_channelizer->setBasebandSampleRate(sampleRate); + m_sink.applyChannelSettings( + m_channelizer->getChannelSampleRate(), + LoRaDemodSettings::bandwidths[m_settings.m_bandwidthIndex], + m_channelizer->getChannelFrequencyOffset() + ); +} diff --git a/plugins/channelrx/demodlora/lorademodbaseband.h b/plugins/channelrx/demodlora/lorademodbaseband.h new file mode 100644 index 000000000..22ba1f417 --- /dev/null +++ b/plugins/channelrx/demodlora/lorademodbaseband.h @@ -0,0 +1,84 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_LORADEMODBASEBAND_H +#define INCLUDE_LORADEMODBASEBAND_H + +#include +#include + +#include "dsp/samplesinkfifo.h" +#include "util/message.h" +#include "util/messagequeue.h" + +#include "lorademodsink.h" + +class DownSampleChannelizer; + +class LoRaDemodBaseband : public QObject +{ + Q_OBJECT +public: + class MsgConfigureLoRaDemodBaseband : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const LoRaDemodSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureLoRaDemodBaseband* create(const LoRaDemodSettings& settings, bool force) + { + return new MsgConfigureLoRaDemodBaseband(settings, force); + } + + private: + LoRaDemodSettings m_settings; + bool m_force; + + MsgConfigureLoRaDemodBaseband(const LoRaDemodSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + LoRaDemodBaseband(); + ~LoRaDemodBaseband(); + void reset(); + void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication + int getChannelSampleRate() const; + void setBasebandSampleRate(int sampleRate); + void setSpectrumSink(BasebandSampleSink* spectrumSink) { m_sink.setSpectrumSink(spectrumSink); } + +private: + SampleSinkFifo m_sampleFifo; + DownSampleChannelizer *m_channelizer; + LoRaDemodSink m_sink; + MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication + LoRaDemodSettings m_settings; + QMutex m_mutex; + + bool handleMessage(const Message& cmd); + void applySettings(const LoRaDemodSettings& settings, bool force = false); + +private slots: + void handleInputMessages(); + void handleData(); //!< Handle data when samples have to be processed +}; + +#endif // INCLUDE_LORADEMODBASEBAND_H diff --git a/plugins/channelrx/demodlora/lorademodgui.cpp b/plugins/channelrx/demodlora/lorademodgui.cpp index bd79129cc..72ee1d61d 100644 --- a/plugins/channelrx/demodlora/lorademodgui.cpp +++ b/plugins/channelrx/demodlora/lorademodgui.cpp @@ -165,12 +165,6 @@ void LoRaDemodGUI::applySettings(bool force) if (m_doApplySettings) { setTitleColor(m_channelMarker.getColor()); - - LoRaDemod::MsgConfigureChannelizer* channelConfigMsg = LoRaDemod::MsgConfigureChannelizer::create( - LoRaDemodSettings::bandwidths[m_settings.m_bandwidthIndex], - m_channelMarker.getCenterFrequency()); - m_LoRaDemod->getInputMessageQueue()->push(channelConfigMsg); - LoRaDemod::MsgConfigureLoRaDemod* message = LoRaDemod::MsgConfigureLoRaDemod::create( m_settings, force); m_LoRaDemod->getInputMessageQueue()->push(message); } diff --git a/plugins/channelrx/demodlora/lorademodsink.cpp b/plugins/channelrx/demodlora/lorademodsink.cpp new file mode 100644 index 000000000..4a420e5b5 --- /dev/null +++ b/plugins/channelrx/demodlora/lorademodsink.cpp @@ -0,0 +1,286 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include + +#include "dsp/dsptypes.h" +#include "dsp/basebandsamplesink.h" + +#include "lorademodsink.h" + +const int LoRaDemodSink::DATA_BITS = 6; +const int LoRaDemodSink::SAMPLEBITS = LoRaDemodSink::DATA_BITS + 2; +const int LoRaDemodSink::SPREADFACTOR = (1 << LoRaDemodSink::SAMPLEBITS); +const int LoRaDemodSink::LORA_SFFT_LEN = (LoRaDemodSink::SPREADFACTOR / 2); +const int LoRaDemodSink::LORA_SQUELCH = 3; + +LoRaDemodSink::LoRaDemodSink() : + m_spectrumSink(nullptr) +{ + m_Bandwidth = LoRaDemodSettings::bandwidths[0]; + m_channelSampleRate = 96000; + m_channelFrequencyOffset = 0; + m_nco.setFreq(m_channelFrequencyOffset, m_channelSampleRate); + m_interpolator.create(16, m_channelSampleRate, m_Bandwidth/1.9); + m_sampleDistanceRemain = (Real) m_channelSampleRate / m_Bandwidth; + + m_chirp = 0; + m_angle = 0; + m_bin = 0; + m_result = 0; + m_count = 0; + m_header = 0; + m_time = 0; + m_tune = 0; + + loraFilter = new sfft(LORA_SFFT_LEN); + negaFilter = new sfft(LORA_SFFT_LEN); + mov = new float[4*LORA_SFFT_LEN]; + history = new short[1024]; + finetune = new short[16]; +} + +LoRaDemodSink::~LoRaDemodSink() +{ + delete loraFilter; + delete negaFilter; + delete [] mov; + delete [] history; + delete [] finetune; +} + +void LoRaDemodSink::dumpRaw() +{ + short bin, j, max; + char text[256]; + + max = m_time / 4 - 3; + + if (max > 140) { + max = 140; // about 2 symbols to each char + } + + for ( j=0; j < max; j++) + { + bin = (history[(j + 1) * 4] + m_tune ) & (LORA_SFFT_LEN - 1); + text[j] = toGray(bin >> 1); + } + + prng6(text, max); + // First block is always 8 symbols + interleave6(text, 6); + interleave6(&text[8], max); + hamming6(text, 6); + hamming6(&text[8], max); + + for ( j=0; j < max / 2; j++) + { + text[j] = (text[j * 2 + 1] << 4) | (0xf & text[j * 2 + 0]); + + if ((text[j] < 32 )||( text[j] > 126)) { + text[j] = 0x5f; + } + } + + text[3] = text[2]; + text[2] = text[1]; + text[1] = text[0]; + text[j] = 0; + + qDebug("LoRaDemodSink::dumpRaw: %s", &text[1]); +} + +short LoRaDemodSink::synch(short bin) +{ + short i, j; + + if (bin < 0) + { + if (m_time > 70) { + dumpRaw(); + } + + m_time = 0; + return -1; + } + + history[m_time] = bin; + + if (m_time > 12) + { + if (bin == history[m_time - 6]) + { + if (bin == history[m_time - 12]) + { + m_tune = LORA_SFFT_LEN - bin; + j = 0; + + for (i=0; i<12; i++) { + j += finetune[15 & (m_time - i)]; + } + + if (j < 0) { + m_tune += 1; + } + + m_tune &= (LORA_SFFT_LEN - 1); + m_time = 0; + return -1; + } + } + } + + m_time++; + m_time &= 1023; + + if (m_time & 3) { + return -1; + } + + return (bin + m_tune) & (LORA_SFFT_LEN - 1); +} + +int LoRaDemodSink::detect(Complex c, Complex a) +{ + int p, q; + short i, result, negresult, movpoint; + float peak, negpeak, tfloat; + float mag[LORA_SFFT_LEN]; + float rev[LORA_SFFT_LEN]; + + loraFilter->run(c * a); + negaFilter->run(c * conj(a)); + + // process spectrum twice in FFTLEN + if (++m_count & ((1 << DATA_BITS) - 1)) { + return m_result; + } + + movpoint = 3 & (m_count >> DATA_BITS); + + loraFilter->fetch(mag); + negaFilter->fetch(rev); + peak = negpeak = 0.0f; + result = negresult = 0; + + for (i = 0; i < LORA_SFFT_LEN; i++) + { + if (rev[i] > negpeak) + { + negpeak = rev[i]; + negresult = i; + } + + tfloat = mov[i] + mov[LORA_SFFT_LEN + i] +mov[2 * LORA_SFFT_LEN + i] + + mov[3 * LORA_SFFT_LEN + i] + mag[i]; + + if (tfloat > peak) + { + peak = tfloat; + result = i; + } + + mov[movpoint * LORA_SFFT_LEN + i] = mag[i]; + } + + p = (result - 1 + LORA_SFFT_LEN) & (LORA_SFFT_LEN -1); + q = (result + 1) & (LORA_SFFT_LEN -1); + finetune[15 & m_time] = (mag[p] > mag[q]) ? -1 : 1; + + if (peak < negpeak * LORA_SQUELCH) + { + result = -1; + } + + result = synch(result); + + if (result >= 0) { + m_result = result; + } + + return m_result; +} + +void LoRaDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +{ + int newangle; + Complex ci; + + m_sampleBuffer.clear(); + + for (SampleVector::const_iterator it = begin; it < end; ++it) + { + Complex c(it->real() / SDR_RX_SCALEF, it->imag() / SDR_RX_SCALEF); + c *= m_nco.nextIQ(); + + if (m_interpolator.decimate(&m_sampleDistanceRemain, c, &ci)) + { + m_chirp = (m_chirp + 1) & (SPREADFACTOR - 1); + m_angle = (m_angle + m_chirp) & (SPREADFACTOR - 1); + Complex cangle(cos(M_PI*2*m_angle/SPREADFACTOR),-sin(M_PI*2*m_angle/SPREADFACTOR)); + newangle = detect(ci, cangle); + + m_bin = (m_bin + newangle) & (LORA_SFFT_LEN - 1); + Complex nangle(cos(M_PI*2*m_bin/LORA_SFFT_LEN),sin(M_PI*2*m_bin/LORA_SFFT_LEN)); + m_sampleBuffer.push_back(Sample(nangle.real() * 100, nangle.imag() * 100)); + m_sampleDistanceRemain += (Real) m_channelSampleRate / m_Bandwidth; + } + } + + if (m_spectrumSink) { + m_spectrumSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), false); + } +} + +void LoRaDemodSink::applyChannelSettings(int channelSampleRate, int bandwidth, int channelFrequencyOffset, bool force) +{ + qDebug() << "LoRaDemodSink::applyChannelSettings:" + << " channelSampleRate: " << channelSampleRate + << " channelFrequencyOffset: " << channelFrequencyOffset; + + if((channelFrequencyOffset != m_channelFrequencyOffset) || + (channelSampleRate != m_channelSampleRate) || force) + { + m_nco.setFreq(-channelFrequencyOffset, channelSampleRate); + } + + if ((channelSampleRate != m_channelSampleRate) || force) + { + qDebug() << "LoRaDemodSink::applyChannelSettings: m_interpolator.create"; + m_interpolator.create(16, channelSampleRate, bandwidth / 1.9f); + m_sampleDistanceRemain = (Real) channelSampleRate / bandwidth; + } + + m_channelSampleRate = channelSampleRate; + m_Bandwidth = bandwidth; + m_channelFrequencyOffset = channelFrequencyOffset; +} + +void LoRaDemodSink::applySettings(const LoRaDemodSettings& settings, bool force) +{ + qDebug() << "LoRaDemodSink::applySettings:" + << " m_centerFrequency: " << settings.m_centerFrequency + << " m_bandwidthIndex: " << settings.m_bandwidthIndex + << " m_spread: " << settings.m_spread + << " m_rgbColor: " << settings.m_rgbColor + << " m_title: " << settings.m_title + << " force: " << force; + + m_settings = settings; +} diff --git a/plugins/channelrx/demodlora/lorademodsink.h b/plugins/channelrx/demodlora/lorademodsink.h new file mode 100644 index 000000000..005ab71ea --- /dev/null +++ b/plugins/channelrx/demodlora/lorademodsink.h @@ -0,0 +1,163 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_LORADEMODSINK_H +#define INCLUDE_LORADEMODSINK_H + +#include + +#include "dsp/channelsamplesink.h" +#include "dsp/nco.h" +#include "dsp/interpolator.h" +#include "util/message.h" +#include "dsp/fftfilt.h" + +#include "lorademodsettings.h" + +class BasebandSampleSink; + +class LoRaDemodSink : public ChannelSampleSink { +public: + LoRaDemodSink(); + ~LoRaDemodSink(); + + virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + + void setSpectrumSink(BasebandSampleSink* spectrumSink) { m_spectrumSink = spectrumSink; } + void applyChannelSettings(int channelSampleRate, int bandwidth, int channelFrequencyOffset, bool force = false); + void applySettings(const LoRaDemodSettings& settings, bool force = false); + +private: + LoRaDemodSettings m_settings; + Real m_Bandwidth; + int m_channelSampleRate; + int m_channelFrequencyOffset; + int m_chirp; + int m_angle; + int m_bin; + int m_result; + int m_count; + int m_header; + int m_time; + short m_tune; + + sfft* loraFilter; + sfft* negaFilter; + float* mov; + short* history; + short* finetune; + + NCO m_nco; + Interpolator m_interpolator; + Real m_sampleDistanceRemain; + + BasebandSampleSink* m_spectrumSink; + SampleVector m_sampleBuffer; + + static const int DATA_BITS; + static const int SAMPLEBITS; + static const int SPREADFACTOR; + static const int LORA_SFFT_LEN; + static const int LORA_SQUELCH; + + int detect(Complex sample, Complex angle); + void dumpRaw(void); + short synch (short bin); + + /* + Interleaving is "easiest" if the same number of bits is used per symbol as for FEC + Chosen mode "spreading 8, low rate" has 6 bits per symbol, so use 4:6 FEC + + More spreading needs higher frequency resolution and longer time on air, increasing drift errors. + Want higher bandwidth when using more spreading, which needs more CPU and a better FFT. + + Six bit Hamming can only correct long runs of drift errors when not using interleaving. Interleaving defeats the point of using Gray code and puts multiple bit errors into single FEC blocks. Hardware decoding uses RSSI to detect the symbols most likely to be damaged, so that individual bits can be repaired after de-interleaving. + + Using Implicit Mode: explicit starts with a 4:8 block and seems to have a different whitening sequence. + */ + + // Six bits per symbol, six chars per block + inline void interleave6(char* inout, int size) + { + int i, j; + char in[6 * 2]; + short s; + + for (j = 0; j < size; j+=6) { + for (i = 0; i < 6; i++) + in[i] = in[i + 6] = inout[i + j]; + // top bits are swapped + for (i = 0; i < 6; i++) { + s = (32 & in[2 + i]) | (16 & in[1 + i]) | (8 & in[3 + i]) + | (4 & in[4 + i]) | (2 & in[5 + i]) | (1 & in[6 + i]); + // bits are also rotated + s = (s << 3) | (s >> 3); + s &= 63; + s = (s >> i) | (s << (6 - i)); + inout[i + j] = s & 63; + } + } + } + + inline short toGray(short num) + { + return (num >> 1) ^ num; + } + + // Ignore the FEC bits, just extract the data bits + inline void hamming6(char* c, int size) + { + int i; + + for (i = 0; i < size; i++) { + c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<0) | ((c[i] & 4)>>0) | ((c[i] & 8)>>3); + i++; + c[i] = ((c[i] & 1)<<2) | ((c[i] & 2)<<2) | ((c[i] & 4)>>1) | ((c[i] & 8)>>3); + i++; + c[i] = ((c[i] &32)>>2) | ((c[i] & 2)<<1) | ((c[i] & 4)>>1) | ((c[i] & 8)>>3); + i++; + c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<1) | ((c[i] & 4)>>1) | ((c[i] & 8)>>3); + i++; + c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<1) | ((c[i] & 4)>>1) | ((c[i] &16)>>4); + i++; + c[i] = ((c[i] & 1)<<3) | ((c[i] & 2)<<1) | ((c[i] & 4)>>2) | ((c[i] & 8)>>2); + } + c[i] = 0; + } + + // data whitening (6 bit) + inline void prng6(char* inout, int size) + { + const char otp[] = { + //explicit mode + "cOGGg7CM2=b5a?<`i;T2of5jDAB=2DoQ9ko?h_RLQR4@Z\\`9jY\\PX89lHX8h_R]c_^@OB<0`W08ik?Mg>dQZf3kn5Je5R=R4h[dY?d9[n5Lg]b]R8hR<0`T008h9c9QJm[c?a:lQEGa;nU=b_WfUV2?V4@c=8h9B9njlQZDC@9ZgetInputMessageQueue()->push(msg); diff --git a/plugins/channelrx/demodnfm/nfmdemod.h b/plugins/channelrx/demodnfm/nfmdemod.h index cc27056fc..6e2faa34e 100644 --- a/plugins/channelrx/demodnfm/nfmdemod.h +++ b/plugins/channelrx/demodnfm/nfmdemod.h @@ -16,8 +16,8 @@ // along with this program. If not, see . // /////////////////////////////////////////////////////////////////////////////////// -#ifndef INCLUDE_NFMTESTDEMOD_H -#define INCLUDE_NFMTESTDEMOD_H +#ifndef INCLUDE_NFMDEMOD_H +#define INCLUDE_NFMDEMOD_H #include @@ -122,11 +122,6 @@ public: static const QString m_channelId; private: - enum RateState { - RSInitialFill, - RSRunning - }; - DeviceAPI* m_deviceAPI; QThread *m_thread; NFMDemodBaseband* m_basebandSink; diff --git a/plugins/channelrx/demodssb/CMakeLists.txt b/plugins/channelrx/demodssb/CMakeLists.txt index 6853cec95..ae3862ccd 100644 --- a/plugins/channelrx/demodssb/CMakeLists.txt +++ b/plugins/channelrx/demodssb/CMakeLists.txt @@ -3,6 +3,8 @@ project(ssb) set(ssb_SOURCES ssbdemod.cpp ssbdemodsettings.cpp + ssbdemodsink.cpp + ssbdemodbaseband.cpp ssbdemodwebapiadapter.cpp ssbplugin.cpp ) @@ -10,6 +12,8 @@ set(ssb_SOURCES set(ssb_HEADERS ssbdemod.h ssbdemodsettings.h + ssbdemodsink.h + ssbdemodbaseband.h ssbdemodwebapiadapter.h ssbplugin.h ) @@ -22,14 +26,12 @@ if(NOT SERVER_MODE) set(ssb_SOURCES ${ssb_SOURCES} ssbdemodgui.cpp - ssbdemodgui.ui ) set(ssb_HEADERS ${ssb_HEADERS} ssbdemodgui.h ) - set(TARGET_NAME demodssb) set(TARGET_LIB "Qt5::Widgets") set(TARGET_LIB_GUI "sdrgui") @@ -46,11 +48,11 @@ add_library(${TARGET_NAME} SHARED ) target_link_libraries(${TARGET_NAME} - Qt5::Core - ${TARGET_LIB} + Qt5::Core + ${TARGET_LIB} sdrbase ${TARGET_LIB_GUI} - swagger + swagger ) install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER}) diff --git a/plugins/channelrx/demodssb/ssbdemod.cpp b/plugins/channelrx/demodssb/ssbdemod.cpp index 2ff37758f..04bd86368 100644 --- a/plugins/channelrx/demodssb/ssbdemod.cpp +++ b/plugins/channelrx/demodssb/ssbdemod.cpp @@ -25,16 +25,15 @@ #include #include #include +#include #include "SWGChannelSettings.h" #include "SWGSSBDemodSettings.h" #include "SWGChannelReport.h" #include "SWGSSBDemodReport.h" -#include "audio/audiooutput.h" #include "dsp/dspengine.h" #include "dsp/downchannelizer.h" -#include "dsp/threadedbasebandsamplesink.h" #include "dsp/dspcommands.h" #include "dsp/devicesamplemimo.h" #include "device/deviceapi.h" @@ -43,8 +42,6 @@ #include "ssbdemod.h" MESSAGE_CLASS_DEFINITION(SSBDemod::MsgConfigureSSBDemod, Message) -MESSAGE_CLASS_DEFINITION(SSBDemod::MsgConfigureSSBDemodPrivate, Message) -MESSAGE_CLASS_DEFINITION(SSBDemod::MsgConfigureChannelizer, Message) const QString SSBDemod::m_channelIdURI = "sdrangel.channel.ssbdemod"; const QString SSBDemod::m_channelId = "SSBDemod"; @@ -52,57 +49,17 @@ const QString SSBDemod::m_channelId = "SSBDemod"; SSBDemod::SSBDemod(DeviceAPI *deviceAPI) : ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink), m_deviceAPI(deviceAPI), - m_audioBinaual(false), - m_audioFlipChannels(false), - m_dsb(false), - m_audioMute(false), - m_agc(12000, agcTarget, 1e-2), - m_agcActive(false), - m_agcClamping(false), - m_agcNbSamples(12000), - m_agcPowerThreshold(1e-2), - m_agcThresholdGate(0), - m_squelchDelayLine(2*48000), - m_audioActive(false), - m_sampleSink(0), - m_audioFifo(24000), - m_settingsMutex(QMutex::Recursive) + m_basebandSampleRate(0) { setObjectName(m_channelId); - m_Bandwidth = 5000; - m_LowCutoff = 300; - m_volume = 2.0; - m_spanLog2 = 3; - m_inputSampleRate = 48000; - m_inputFrequencyOffset = 0; + m_thread = new QThread(this); + m_basebandSink = new SSBDemodBaseband(); + m_basebandSink->moveToThread(m_thread); - DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(&m_audioFifo, getInputMessageQueue()); - m_audioSampleRate = DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate(); - - m_audioBuffer.resize(1<<14); - m_audioBufferFill = 0; - m_undersampleCount = 0; - m_sum = 0; - - m_usb = true; - m_magsq = 0.0f; - m_magsqSum = 0.0f; - m_magsqPeak = 0.0f; - m_magsqCount = 0; - - m_agc.setClampMax(SDR_RX_SCALED/100.0); - m_agc.setClamping(m_agcClamping); - - SSBFilter = new fftfilt(m_LowCutoff / m_audioSampleRate, m_Bandwidth / m_audioSampleRate, ssbFftLen); - DSBFilter = new fftfilt((2.0f * m_Bandwidth) / m_audioSampleRate, 2 * ssbFftLen); - - applyChannelSettings(m_inputSampleRate, m_inputFrequencyOffset, true); applySettings(m_settings, true); - m_channelizer = new DownChannelizer(this); - m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); - m_deviceAPI->addChannelSink(m_threadedChannelizer); + m_deviceAPI->addChannelSink(this); m_deviceAPI->addChannelSinkAPI(this); m_networkManager = new QNetworkAccessManager(); @@ -113,46 +70,10 @@ SSBDemod::~SSBDemod() { disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); delete m_networkManager; - DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(&m_audioFifo); - m_deviceAPI->removeChannelSinkAPI(this); - m_deviceAPI->removeChannelSink(m_threadedChannelizer); - delete m_threadedChannelizer; - delete m_channelizer; - delete SSBFilter; - delete DSBFilter; -} - -void SSBDemod::configure(MessageQueue* messageQueue, - Real Bandwidth, - Real LowCutoff, - Real volume, - int spanLog2, - bool audioBinaural, - bool audioFlipChannel, - bool dsb, - bool audioMute, - bool agc, - bool agcClamping, - int agcTimeLog2, - int agcPowerThreshold, - int agcThresholdGate) -{ - Message* cmd = MsgConfigureSSBDemodPrivate::create( - Bandwidth, - LowCutoff, - volume, - spanLog2, - audioBinaural, - audioFlipChannel, - dsb, - audioMute, - agc, - agcClamping, - agcTimeLog2, - agcPowerThreshold, - agcThresholdGate); - messageQueue->push(cmd); + m_deviceAPI->removeChannelSink(this); + delete m_basebandSink; + delete m_thread; } uint32_t SSBDemod::getNumberOfDeviceStreams() const @@ -163,185 +84,31 @@ uint32_t SSBDemod::getNumberOfDeviceStreams() const void SSBDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly) { (void) positiveOnly; - Complex ci; - m_settingsMutex.lock(); - - for(SampleVector::const_iterator it = begin; it < end; ++it) - { - Complex c(it->real(), it->imag()); - c *= m_nco.nextIQ(); - - if (m_interpolatorDistance < 1.0f) // interpolate - { - while (!m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci)) - { - processOneSample(ci); - m_interpolatorDistanceRemain += m_interpolatorDistance; - } - } - else - { - if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci)) - { - processOneSample(ci); - m_interpolatorDistanceRemain += m_interpolatorDistance; - } - } - } - - m_settingsMutex.unlock(); -} - -void SSBDemod::processOneSample(Complex &ci) -{ - fftfilt::cmplx *sideband; - int n_out = 0; - int decim = 1<<(m_spanLog2 - 1); - unsigned char decim_mask = decim - 1; // counter LSB bit mask for decimation by 2^(m_scaleLog2 - 1) - - if (m_dsb) { - n_out = DSBFilter->runDSB(ci, &sideband); - } else { - n_out = SSBFilter->runSSB(ci, &sideband, m_usb); - } - - for (int i = 0; i < n_out; i++) - { - // Downsample by 2^(m_scaleLog2 - 1) for SSB band spectrum display - // smart decimation with bit gain using float arithmetic (23 bits significand) - - m_sum += sideband[i]; - - if (!(m_undersampleCount++ & decim_mask)) - { - Real avgr = m_sum.real() / decim; - Real avgi = m_sum.imag() / decim; - m_magsq = (avgr * avgr + avgi * avgi) / (SDR_RX_SCALED*SDR_RX_SCALED); - - m_magsqSum += m_magsq; - - if (m_magsq > m_magsqPeak) - { - m_magsqPeak = m_magsq; - } - - m_magsqCount++; - - if (!m_dsb & !m_usb) - { // invert spectrum for LSB - m_sampleBuffer.push_back(Sample(avgi, avgr)); - } - else - { - m_sampleBuffer.push_back(Sample(avgr, avgi)); - } - - m_sum.real(0.0); - m_sum.imag(0.0); - } - - float agcVal = m_agcActive ? m_agc.feedAndGetValue(sideband[i]) : 0.1; - fftfilt::cmplx& delayedSample = m_squelchDelayLine.readBack(m_agc.getStepDownDelay()); - m_audioActive = delayedSample.real() != 0.0; - m_squelchDelayLine.write(sideband[i]*agcVal); - - if (m_audioMute) - { - m_audioBuffer[m_audioBufferFill].r = 0; - m_audioBuffer[m_audioBufferFill].l = 0; - } - else - { - fftfilt::cmplx z = m_agcActive ? delayedSample * m_agc.getStepValue() : delayedSample; - - if (m_audioBinaual) - { - if (m_audioFlipChannels) - { - m_audioBuffer[m_audioBufferFill].r = (qint16)(z.imag() * m_volume); - m_audioBuffer[m_audioBufferFill].l = (qint16)(z.real() * m_volume); - } - else - { - m_audioBuffer[m_audioBufferFill].r = (qint16)(z.real() * m_volume); - m_audioBuffer[m_audioBufferFill].l = (qint16)(z.imag() * m_volume); - } - } - else - { - Real demod = (z.real() + z.imag()) * 0.7; - qint16 sample = (qint16)(demod * m_volume); - m_audioBuffer[m_audioBufferFill].l = sample; - m_audioBuffer[m_audioBufferFill].r = sample; - } - } - - ++m_audioBufferFill; - - if (m_audioBufferFill >= m_audioBuffer.size()) - { - uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); - - if (res != m_audioBufferFill) - { - qDebug("SSBDemod::feed: %u/%u samples written", res, m_audioBufferFill); - } - - m_audioBufferFill = 0; - } - } - - uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); - - if (res != m_audioBufferFill) - { - qDebug("SSBDemod::feed: %u/%u tail samples written", res, m_audioBufferFill); - } - - m_audioBufferFill = 0; - - if (m_sampleSink != 0) - { - m_sampleSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), !m_dsb); - } - - m_sampleBuffer.clear(); - + m_basebandSink->feed(begin, end); } void SSBDemod::start() { - applyChannelSettings(m_inputSampleRate, m_inputFrequencyOffset, true); + qDebug() << "SSBDemod::start"; + + if (m_basebandSampleRate != 0) { + m_basebandSink->setBasebandSampleRate(m_basebandSampleRate); + } + + m_basebandSink->reset(); + m_thread->start(); } void SSBDemod::stop() { + qDebug() << "SSBDemod::stop"; + m_thread->exit(); + m_thread->wait(); } bool SSBDemod::handleMessage(const Message& cmd) { - if (DownChannelizer::MsgChannelizerNotification::match(cmd)) - { - DownChannelizer::MsgChannelizerNotification& notif = (DownChannelizer::MsgChannelizerNotification&) cmd; - qDebug("SSBDemod::handleMessage: MsgChannelizerNotification: m_sampleRate"); - - applyChannelSettings(notif.getSampleRate(), notif.getFrequencyOffset()); - - return true; - } - else if (MsgConfigureChannelizer::match(cmd)) - { - MsgConfigureChannelizer& cfg = (MsgConfigureChannelizer&) cmd; - qDebug() << "SSBDemod::handleMessage: MsgConfigureChannelizer: sampleRate: " << cfg.getSampleRate() - << " centerFrequency: " << cfg.getCenterFrequency(); - - m_channelizer->configure(m_channelizer->getInputMessageQueue(), - cfg.getSampleRate(), - cfg.getCenterFrequency()); - - return true; - } - else if (MsgConfigureSSBDemod::match(cmd)) + if (MsgConfigureSSBDemod::match(cmd)) { MsgConfigureSSBDemod& cfg = (MsgConfigureSSBDemod&) cmd; qDebug("SSBDemod::handleMessage: MsgConfigureSSBDemod"); @@ -350,117 +117,23 @@ bool SSBDemod::handleMessage(const Message& cmd) return true; } - else if (BasebandSampleSink::MsgThreadedSink::match(cmd)) - { - BasebandSampleSink::MsgThreadedSink& cfg = (BasebandSampleSink::MsgThreadedSink&) cmd; - const QThread *thread = cfg.getThread(); - qDebug("SSBDemod::handleMessage: BasebandSampleSink::MsgThreadedSink: %p", thread); - return true; - } - else if (DSPConfigureAudio::match(cmd)) - { - DSPConfigureAudio& cfg = (DSPConfigureAudio&) cmd; - uint32_t sampleRate = cfg.getSampleRate(); - - qDebug() << "SSBDemod::handleMessage: DSPConfigureAudio:" - << " sampleRate: " << sampleRate; - - if (sampleRate != m_audioSampleRate) { - applyAudioSampleRate(sampleRate); - } - - return true; - } else if (DSPSignalNotification::match(cmd)) { + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + m_basebandSampleRate = notif.getSampleRate(); + // Forward to the sink + DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy + qDebug() << "SSBDemod::handleMessage: DSPSignalNotification"; + m_basebandSink->getInputMessageQueue()->push(rep); + return true; } else { - if(m_sampleSink != 0) - { - return m_sampleSink->handleMessage(cmd); - } - else - { - return false; - } + return false; } } -void SSBDemod::applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force) -{ - qDebug() << "SSBDemod::applyChannelSettings:" - << " inputSampleRate: " << inputSampleRate - << " inputFrequencyOffset: " << inputFrequencyOffset; - - if ((m_inputFrequencyOffset != inputFrequencyOffset) || - (m_inputSampleRate != inputSampleRate) || force) - { - m_nco.setFreq(-inputFrequencyOffset, inputSampleRate); - } - - if ((m_inputSampleRate != inputSampleRate) || force) - { - m_settingsMutex.lock(); - Real interpolatorBandwidth = (m_Bandwidth * 1.5f) > inputSampleRate ? inputSampleRate : (m_Bandwidth * 1.5f); - m_interpolator.create(16, inputSampleRate, interpolatorBandwidth, 2.0f); - m_interpolatorDistanceRemain = 0; - m_interpolatorDistance = (Real) inputSampleRate / (Real) m_audioSampleRate; - m_settingsMutex.unlock(); - } - - m_inputSampleRate = inputSampleRate; - m_inputFrequencyOffset = inputFrequencyOffset; -} - -void SSBDemod::applyAudioSampleRate(int sampleRate) -{ - qDebug("SSBDemod::applyAudioSampleRate: %d", sampleRate); - - MsgConfigureChannelizer* channelConfigMsg = MsgConfigureChannelizer::create( - sampleRate, m_settings.m_inputFrequencyOffset); - m_inputMessageQueue.push(channelConfigMsg); - - m_settingsMutex.lock(); - - Real interpolatorBandwidth = (m_Bandwidth * 1.5f) > m_inputSampleRate ? m_inputSampleRate : (m_Bandwidth * 1.5f); - m_interpolator.create(16, m_inputSampleRate, interpolatorBandwidth, 2.0f); - m_interpolatorDistanceRemain = 0; - m_interpolatorDistance = (Real) m_inputSampleRate / (Real) sampleRate; - - SSBFilter->create_filter(m_LowCutoff / (float) sampleRate, m_Bandwidth / (float) sampleRate); - DSBFilter->create_dsb_filter((2.0f * m_Bandwidth) / (float) sampleRate); - - int agcNbSamples = (sampleRate / 1000) * (1<push(cfg); - } -} - void SSBDemod::applySettings(const SSBDemodSettings& settings, bool force) { qDebug() << "SSBDemod::applySettings:" @@ -498,49 +171,9 @@ void SSBDemod::applySettings(const SSBDemodSettings& settings, bool force) if((m_settings.m_lowCutoff != settings.m_lowCutoff) || force) { reverseAPIKeys.append("lowCutoff"); } - - if((m_settings.m_rfBandwidth != settings.m_rfBandwidth) || - (m_settings.m_lowCutoff != settings.m_lowCutoff) || force) - { - float band, lowCutoff; - - band = settings.m_rfBandwidth; - lowCutoff = settings.m_lowCutoff; - - if (band < 0) { - band = -band; - lowCutoff = -lowCutoff; - m_usb = false; - } else { - m_usb = true; - } - - if (band < 100.0f) - { - band = 100.0f; - lowCutoff = 0; - } - - m_Bandwidth = band; - m_LowCutoff = lowCutoff; - - m_settingsMutex.lock(); - Real interpolatorBandwidth = (m_Bandwidth * 1.5f) > m_inputSampleRate ? m_inputSampleRate : (m_Bandwidth * 1.5f); - m_interpolator.create(16, m_inputSampleRate, interpolatorBandwidth, 2.0f); - m_interpolatorDistanceRemain = 0; - m_interpolatorDistance = (Real) m_inputSampleRate / (Real) m_audioSampleRate; - SSBFilter->create_filter(m_LowCutoff / (float) m_audioSampleRate, m_Bandwidth / (float) m_audioSampleRate); - DSBFilter->create_dsb_filter((2.0f * m_Bandwidth) / (float) m_audioSampleRate); - m_settingsMutex.unlock(); - } - - if ((m_settings.m_volume != settings.m_volume) || force) - { + if ((m_settings.m_volume != settings.m_volume) || force) { reverseAPIKeys.append("volume"); - m_volume = settings.m_volume; - m_volume /= 4.0; // for 3276.8 } - if ((m_settings.m_agcTimeLog2 != settings.m_agcTimeLog2) || force) { reverseAPIKeys.append("agcTimeLog2"); } @@ -553,65 +186,9 @@ void SSBDemod::applySettings(const SSBDemodSettings& settings, bool force) if ((m_settings.m_agcClamping != settings.m_agcClamping) || force) { reverseAPIKeys.append("agcClamping"); } - - if ((m_settings.m_agcTimeLog2 != settings.m_agcTimeLog2) || - (m_settings.m_agcPowerThreshold != settings.m_agcPowerThreshold) || - (m_settings.m_agcThresholdGate != settings.m_agcThresholdGate) || - (m_settings.m_agcClamping != settings.m_agcClamping) || force) - { - int agcNbSamples = (m_audioSampleRate / 1000) * (1<getAudioDeviceManager(); - int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_audioDeviceName); - audioDeviceManager->addAudioSink(&m_audioFifo, getInputMessageQueue(), audioDeviceIndex); - uint32_t audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex); - - if (m_audioSampleRate != audioSampleRate) { - applyAudioSampleRate(audioSampleRate); - } } - if ((m_settings.m_spanLog2 != settings.m_spanLog2) || force) { reverseAPIKeys.append("spanLog2"); } @@ -631,28 +208,22 @@ void SSBDemod::applySettings(const SSBDemodSettings& settings, bool force) reverseAPIKeys.append("agc"); } - m_spanLog2 = settings.m_spanLog2; - m_audioBinaual = settings.m_audioBinaural; - m_audioFlipChannels = settings.m_audioFlipChannels; - m_dsb = settings.m_dsb; - m_audioMute = settings.m_audioMute; - m_agcActive = settings.m_agc; - if (m_settings.m_streamIndex != settings.m_streamIndex) { if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only { m_deviceAPI->removeChannelSinkAPI(this, m_settings.m_streamIndex); - m_deviceAPI->removeChannelSink(m_threadedChannelizer, m_settings.m_streamIndex); - m_deviceAPI->addChannelSink(m_threadedChannelizer, settings.m_streamIndex); + m_deviceAPI->removeChannelSink(this, m_settings.m_streamIndex); + m_deviceAPI->addChannelSink(this, settings.m_streamIndex); m_deviceAPI->addChannelSinkAPI(this, settings.m_streamIndex); - // apply stream sample rate to itself - applyChannelSettings(m_deviceAPI->getSampleMIMO()->getSourceSampleRate(settings.m_streamIndex), m_inputFrequencyOffset); } reverseAPIKeys.append("streamIndex"); } + SSBDemodBaseband::MsgConfigureSSBDemodBaseband *msg = SSBDemodBaseband::MsgConfigureSSBDemodBaseband::create(settings, force); + m_basebandSink->getInputMessageQueue()->push(msg); + if (settings.m_useReverseAPI) { bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || @@ -709,13 +280,6 @@ int SSBDemod::webapiSettingsPutPatch( SSBDemodSettings settings = m_settings; webapiUpdateChannelSettings(settings, channelSettingsKeys, response); - if (settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) - { - MsgConfigureChannelizer* channelConfigMsg = MsgConfigureChannelizer::create( - m_audioSampleRate, settings.m_inputFrequencyOffset); - m_inputMessageQueue.push(channelConfigMsg); - } - MsgConfigureSSBDemod *msg = MsgConfigureSSBDemod::create(settings, force); m_inputMessageQueue.push(msg); @@ -870,9 +434,9 @@ void SSBDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples); response.getSsbDemodReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg)); - response.getSsbDemodReport()->setSquelch(m_audioActive ? 1 : 0); - response.getSsbDemodReport()->setAudioSampleRate(m_audioSampleRate); - response.getSsbDemodReport()->setChannelSampleRate(m_inputSampleRate); + response.getSsbDemodReport()->setSquelch(m_basebandSink->getAudioActive() ? 1 : 0); + response.getSsbDemodReport()->setAudioSampleRate(m_basebandSink->getAudioSampleRate()); + response.getSsbDemodReport()->setChannelSampleRate(m_basebandSink->getChannelSampleRate()); } void SSBDemod::webapiReverseSendSettings(QList& channelSettingsKeys, const SSBDemodSettings& settings, bool force) diff --git a/plugins/channelrx/demodssb/ssbdemod.h b/plugins/channelrx/demodssb/ssbdemod.h index cfac77fb9..d94239438 100644 --- a/plugins/channelrx/demodssb/ssbdemod.h +++ b/plugins/channelrx/demodssb/ssbdemod.h @@ -26,24 +26,15 @@ #include "dsp/basebandsamplesink.h" #include "channel/channelapi.h" -#include "dsp/ncof.h" -#include "dsp/interpolator.h" -#include "dsp/fftfilt.h" -#include "dsp/agc.h" -#include "audio/audiofifo.h" #include "util/message.h" -#include "util/doublebufferfifo.h" #include "ssbdemodsettings.h" - -#define ssbFftLen 1024 -#define agcTarget 3276.8 // -10 dB amplitude => -20 dB power: center of normal signal +#include "ssbdemodbaseband.h" class QNetworkAccessManager; class QNetworkReply; +class QThread; class DeviceAPI; -class ThreadedBasebandSampleSink; -class DownChannelizer; class SSBDemod : public BasebandSampleSink, public ChannelAPI { Q_OBJECT @@ -71,48 +62,10 @@ public: { } }; - class MsgConfigureChannelizer : public Message { - MESSAGE_CLASS_DECLARATION - - public: - int getSampleRate() const { return m_sampleRate; } - int getCenterFrequency() const { return m_centerFrequency; } - - static MsgConfigureChannelizer* create(int sampleRate, int centerFrequency) - { - return new MsgConfigureChannelizer(sampleRate, centerFrequency); - } - - private: - int m_sampleRate; - int m_centerFrequency; - - MsgConfigureChannelizer(int sampleRate, int centerFrequency) : - Message(), - m_sampleRate(sampleRate), - m_centerFrequency(centerFrequency) - { } - }; - SSBDemod(DeviceAPI *deviceAPI); virtual ~SSBDemod(); virtual void destroy() { delete this; } - void setSampleSink(BasebandSampleSink* sampleSink) { m_sampleSink = sampleSink; } - - void configure(MessageQueue* messageQueue, - Real Bandwidth, - Real LowCutoff, - Real volume, - int spanLog2, - bool audioBinaural, - bool audioFlipChannels, - bool dsb, - bool audioMute, - bool agc, - bool agcClamping, - int agcTimeLog2, - int agcPowerThreshold, - int agcThresholdGate); + void setSpectrumSink(BasebandSampleSink* spectrumSink) { m_basebandSink->setSpectrumSink(spectrumSink); } virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); virtual void start(); @@ -136,28 +89,13 @@ public: return m_settings.m_inputFrequencyOffset; } - uint32_t getAudioSampleRate() const { return m_audioSampleRate; } - uint32_t getInputSampleRate() const { return m_inputSampleRate; } - double getMagSq() const { return m_magsq; } - bool getAudioActive() const { return m_audioActive; } + void propagateMessageQueueToGUI() { m_basebandSink->setMessageQueueToGUI(getMessageQueueToGUI()); } + uint32_t getAudioSampleRate() const { return m_basebandSink->getAudioSampleRate(); } + uint32_t getChannelSampleRate() const { return m_basebandSink->getChannelSampleRate(); } + double getMagSq() const { return m_basebandSink->getMagSq(); } + bool getAudioActive() const { return m_basebandSink->getAudioActive(); } - void getMagSqLevels(double& avg, double& peak, int& nbSamples) - { - if (m_magsqCount > 0) - { - m_magsq = m_magsqSum / m_magsqCount; - m_magSqLevelStore.m_magsq = m_magsq; - m_magSqLevelStore.m_magsqPeak = m_magsqPeak; - } - - avg = m_magSqLevelStore.m_magsq; - peak = m_magSqLevelStore.m_magsqPeak; - nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount; - - m_magsqSum = 0.0f; - m_magsqPeak = 0.0f; - m_magsqCount = 0; - } + void getMagSqLevels(double& avg, double& peak, int& nbSamples) { m_basebandSink->getMagSqLevels(avg, peak, nbSamples); } virtual int webapiSettingsGet( SWGSDRangel::SWGChannelSettings& response, @@ -188,169 +126,19 @@ public: static const QString m_channelId; private: - struct MagSqLevelsStore - { - MagSqLevelsStore() : - m_magsq(1e-12), - m_magsqPeak(1e-12) - {} - double m_magsq; - double m_magsqPeak; - }; - - class MsgConfigureSSBDemodPrivate : public Message { - MESSAGE_CLASS_DECLARATION - - public: - Real getBandwidth() const { return m_Bandwidth; } - Real getLoCutoff() const { return m_LowCutoff; } - Real getVolume() const { return m_volume; } - int getSpanLog2() const { return m_spanLog2; } - bool getAudioBinaural() const { return m_audioBinaural; } - bool getAudioFlipChannels() const { return m_audioFlipChannels; } - bool getDSB() const { return m_dsb; } - bool getAudioMute() const { return m_audioMute; } - bool getAGC() const { return m_agc; } - bool getAGCClamping() const { return m_agcClamping; } - int getAGCTimeLog2() const { return m_agcTimeLog2; } - int getAGCPowerThershold() const { return m_agcPowerThreshold; } - int getAGCThersholdGate() const { return m_agcThresholdGate; } - - static MsgConfigureSSBDemodPrivate* create(Real Bandwidth, - Real LowCutoff, - Real volume, - int spanLog2, - bool audioBinaural, - bool audioFlipChannels, - bool dsb, - bool audioMute, - bool agc, - bool agcClamping, - int agcTimeLog2, - int agcPowerThreshold, - int agcThresholdGate) - { - return new MsgConfigureSSBDemodPrivate( - Bandwidth, - LowCutoff, - volume, - spanLog2, - audioBinaural, - audioFlipChannels, - dsb, - audioMute, - agc, - agcClamping, - agcTimeLog2, - agcPowerThreshold, - agcThresholdGate); - } - - private: - Real m_Bandwidth; - Real m_LowCutoff; - Real m_volume; - int m_spanLog2; - bool m_audioBinaural; - bool m_audioFlipChannels; - bool m_dsb; - bool m_audioMute; - bool m_agc; - bool m_agcClamping; - int m_agcTimeLog2; - int m_agcPowerThreshold; - int m_agcThresholdGate; - - MsgConfigureSSBDemodPrivate(Real Bandwidth, - Real LowCutoff, - Real volume, - int spanLog2, - bool audioBinaural, - bool audioFlipChannels, - bool dsb, - bool audioMute, - bool agc, - bool agcClamping, - int agcTimeLog2, - int agcPowerThreshold, - int agcThresholdGate) : - Message(), - m_Bandwidth(Bandwidth), - m_LowCutoff(LowCutoff), - m_volume(volume), - m_spanLog2(spanLog2), - m_audioBinaural(audioBinaural), - m_audioFlipChannels(audioFlipChannels), - m_dsb(dsb), - m_audioMute(audioMute), - m_agc(agc), - m_agcClamping(agcClamping), - m_agcTimeLog2(agcTimeLog2), - m_agcPowerThreshold(agcPowerThreshold), - m_agcThresholdGate(agcThresholdGate) - { } - }; - DeviceAPI *m_deviceAPI; - ThreadedBasebandSampleSink* m_threadedChannelizer; - DownChannelizer* m_channelizer; + QThread *m_thread; + SSBDemodBaseband* m_basebandSink; SSBDemodSettings m_settings; - - Real m_Bandwidth; - Real m_LowCutoff; - Real m_volume; - int m_spanLog2; - fftfilt::cmplx m_sum; - int m_undersampleCount; - int m_inputSampleRate; - int m_inputFrequencyOffset; - bool m_audioBinaual; - bool m_audioFlipChannels; - bool m_usb; - bool m_dsb; - bool m_audioMute; - double m_magsq; - double m_magsqSum; - double m_magsqPeak; - int m_magsqCount; - MagSqLevelsStore m_magSqLevelStore; - MagAGC m_agc; - bool m_agcActive; - bool m_agcClamping; - int m_agcNbSamples; //!< number of audio (48 kHz) samples for AGC averaging - double m_agcPowerThreshold; //!< AGC power threshold (linear) - int m_agcThresholdGate; //!< Gate length in number of samples befor threshold triggers - DoubleBufferFIFO m_squelchDelayLine; - bool m_audioActive; //!< True if an audio signal is produced (no AGC or AGC and above threshold) - - NCOF m_nco; - Interpolator m_interpolator; - Real m_interpolatorDistance; - Real m_interpolatorDistanceRemain; - fftfilt* SSBFilter; - fftfilt* DSBFilter; - - BasebandSampleSink* m_sampleSink; - SampleVector m_sampleBuffer; - - AudioVector m_audioBuffer; - uint m_audioBufferFill; - AudioFifo m_audioFifo; - quint32 m_audioSampleRate; + int m_basebandSampleRate; //!< stored from device message used when starting baseband sink QNetworkAccessManager *m_networkManager; QNetworkRequest m_networkRequest; - QMutex m_settingsMutex; - - void applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force = false); void applySettings(const SSBDemodSettings& settings, bool force = false); - void applyAudioSampleRate(int sampleRate); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); void webapiReverseSendSettings(QList& channelSettingsKeys, const SSBDemodSettings& settings, bool force); - void processOneSample(Complex &ci); - private slots: void networkManagerFinished(QNetworkReply *reply); }; diff --git a/plugins/channelrx/demodssb/ssbdemodbaseband.cpp b/plugins/channelrx/demodssb/ssbdemodbaseband.cpp new file mode 100644 index 000000000..b842e7399 --- /dev/null +++ b/plugins/channelrx/demodssb/ssbdemodbaseband.cpp @@ -0,0 +1,182 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "dsp/downsamplechannelizer.h" + +#include "ssbdemodbaseband.h" + +MESSAGE_CLASS_DEFINITION(SSBDemodBaseband::MsgConfigureSSBDemodBaseband, Message) + +SSBDemodBaseband::SSBDemodBaseband() : + m_messageQueueToGUI(nullptr), + m_mutex(QMutex::Recursive) +{ + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000)); + m_channelizer = new DownSampleChannelizer(&m_sink); + + qDebug("SSBDemodBaseband::SSBDemodBaseband"); + QObject::connect( + &m_sampleFifo, + &SampleSinkFifo::dataReady, + this, + &SSBDemodBaseband::handleData, + Qt::QueuedConnection + ); + + DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(m_sink.getAudioFifo(), getInputMessageQueue()); + m_audioSampleRate = DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate(); + m_sink.applyAudioSampleRate(m_audioSampleRate); + + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); +} + +SSBDemodBaseband::~SSBDemodBaseband() +{ + DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(m_sink.getAudioFifo()); + delete m_channelizer; +} + +void SSBDemodBaseband::reset() +{ + QMutexLocker mutexLocker(&m_mutex); + m_sink.applyAudioSampleRate(DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate()); + m_sampleFifo.reset(); +} + +void SSBDemodBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +{ + m_sampleFifo.write(begin, end); +} + +void SSBDemodBaseband::handleData() +{ + QMutexLocker mutexLocker(&m_mutex); + + while ((m_sampleFifo.fill() > 0) && (m_inputMessageQueue.size() == 0)) + { + SampleVector::iterator part1begin; + SampleVector::iterator part1end; + SampleVector::iterator part2begin; + SampleVector::iterator part2end; + + std::size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end); + + // first part of FIFO data + if (part1begin != part1end) { + m_channelizer->feed(part1begin, part1end); + } + + // second part of FIFO data (used when block wraps around) + if(part2begin != part2end) { + m_channelizer->feed(part2begin, part2end); + } + + m_sampleFifo.readCommit((unsigned int) count); + } +} + +void SSBDemodBaseband::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != nullptr) + { + if (handleMessage(*message)) { + delete message; + } + } +} + +bool SSBDemodBaseband::handleMessage(const Message& cmd) +{ + if (MsgConfigureSSBDemodBaseband::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + MsgConfigureSSBDemodBaseband& cfg = (MsgConfigureSSBDemodBaseband&) cmd; + qDebug() << "SSBDemodBaseband::handleMessage: MsgConfigureSSBDemodBaseband"; + + applySettings(cfg.getSettings(), cfg.getForce()); + + return true; + } + else if (DSPSignalNotification::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + qDebug() << "SSBDemodBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate(); + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate())); + m_channelizer->setBasebandSampleRate(notif.getSampleRate()); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + + return true; + } + else + { + return false; + } +} + +void SSBDemodBaseband::applySettings(const SSBDemodSettings& settings, bool force) +{ + if ((settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) + { + m_channelizer->setChannelization(m_audioSampleRate, settings.m_inputFrequencyOffset); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + } + + if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) + { + AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); + int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_audioDeviceName); + audioDeviceManager->addAudioSink(m_sink.getAudioFifo(), getInputMessageQueue(), audioDeviceIndex); + unsigned int audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex); + + if (m_audioSampleRate != audioSampleRate) + { + m_sink.applyAudioSampleRate(audioSampleRate); + m_channelizer->setChannelization(audioSampleRate, settings.m_inputFrequencyOffset); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + m_audioSampleRate = audioSampleRate; + + if (getMessageQueueToGUI()) + { + DSPConfigureAudio *msg = new DSPConfigureAudio((int) audioSampleRate, DSPConfigureAudio::AudioOutput); + getMessageQueueToGUI()->push(msg); + } + } + } + + m_sink.applySettings(settings, force); + + m_settings = settings; +} + +int SSBDemodBaseband::getChannelSampleRate() const +{ + return m_channelizer->getChannelSampleRate(); +} + + +void SSBDemodBaseband::setBasebandSampleRate(int sampleRate) +{ + m_channelizer->setBasebandSampleRate(sampleRate); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); +} \ No newline at end of file diff --git a/plugins/channelrx/demodssb/ssbdemodbaseband.h b/plugins/channelrx/demodssb/ssbdemodbaseband.h new file mode 100644 index 000000000..37d21d7ab --- /dev/null +++ b/plugins/channelrx/demodssb/ssbdemodbaseband.h @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_SSBDEMODBASEBAND_H +#define INCLUDE_SSBDEMODBASEBAND_H + +#include +#include + +#include "dsp/samplesinkfifo.h" +#include "util/message.h" +#include "util/messagequeue.h" + +#include "ssbdemodsink.h" + +class DownSampleChannelizer; + +class SSBDemodBaseband : public QObject +{ + Q_OBJECT +public: + class MsgConfigureSSBDemodBaseband : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const SSBDemodSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureSSBDemodBaseband* create(const SSBDemodSettings& settings, bool force) + { + return new MsgConfigureSSBDemodBaseband(settings, force); + } + + private: + SSBDemodSettings m_settings; + bool m_force; + + MsgConfigureSSBDemodBaseband(const SSBDemodSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + SSBDemodBaseband(); + ~SSBDemodBaseband(); + void reset(); + void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication + int getChannelSampleRate() const; + void setSpectrumSink(BasebandSampleSink* spectrumSink) { m_sink.setSpectrumSink(spectrumSink); } + double getMagSq() const { return m_sink.getMagSq(); } + void getMagSqLevels(double& avg, double& peak, int& nbSamples) { m_sink.getMagSqLevels(avg, peak, nbSamples); } + unsigned int getAudioSampleRate() const { return m_audioSampleRate; } + bool getAudioActive() const { return m_sink.getAudioActive(); } + void setBasebandSampleRate(int sampleRate); + void setMessageQueueToGUI(MessageQueue *messageQueue) { m_messageQueueToGUI = messageQueue; } + +private: + SampleSinkFifo m_sampleFifo; + DownSampleChannelizer *m_channelizer; + SSBDemodSink m_sink; + MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication + SSBDemodSettings m_settings; + unsigned int m_audioSampleRate; + MessageQueue *m_messageQueueToGUI; + QMutex m_mutex; + + bool handleMessage(const Message& cmd); + void applySettings(const SSBDemodSettings& settings, bool force = false); + MessageQueue *getMessageQueueToGUI() { return m_messageQueueToGUI; } + +private slots: + void handleInputMessages(); + void handleData(); //!< Handle data when samples have to be processed +}; + +#endif // INCLUDE_SSBDEMODBASEBAND_H \ No newline at end of file diff --git a/plugins/channelrx/demodssb/ssbdemodgui.cpp b/plugins/channelrx/demodssb/ssbdemodgui.cpp index 3bd249dea..c32c31b0b 100644 --- a/plugins/channelrx/demodssb/ssbdemodgui.cpp +++ b/plugins/channelrx/demodssb/ssbdemodgui.cpp @@ -219,11 +219,13 @@ void SSBDemodGUI::on_audioMute_toggled(bool checked) void SSBDemodGUI::on_spanLog2_valueChanged(int value) { - if ((value < 0) || (value > 4)) { + unsigned int s2max = spanLog2Max(); + + if ((value < 0) || (value > s2max-1)) { return; } - applyBandwidths(5 - ui->spanLog2->value()); + applyBandwidths(s2max - ui->spanLog2->value()); } void SSBDemodGUI::on_flipSidebands_clicked(bool checked) @@ -308,7 +310,8 @@ SSBDemodGUI::SSBDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban m_spectrumVis = new SpectrumVis(SDR_RX_SCALEF, ui->glSpectrum); m_ssbDemod = (SSBDemod*) rxChannel; //new SSBDemod(m_deviceUISet->m_deviceSourceAPI); m_ssbDemod->setMessageQueueToGUI(getInputMessageQueue()); - m_ssbDemod->setSampleSink(m_spectrumVis); + m_ssbDemod->propagateMessageQueueToGUI(); + m_ssbDemod->setSpectrumSink(m_spectrumVis); CRightClickEnabler *audioMuteRightClickEnabler = new CRightClickEnabler(ui->audioMute); connect(audioMuteRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(audioSelect())); @@ -378,34 +381,30 @@ void SSBDemodGUI::applySettings(bool force) { if (m_doApplySettings) { - SSBDemod::MsgConfigureChannelizer* channelConfigMsg = SSBDemod::MsgConfigureChannelizer::create( - m_ssbDemod->getAudioSampleRate(), m_channelMarker.getCenterFrequency()); - m_ssbDemod->getInputMessageQueue()->push(channelConfigMsg); - SSBDemod::MsgConfigureSSBDemod* message = SSBDemod::MsgConfigureSSBDemod::create( m_settings, force); m_ssbDemod->getInputMessageQueue()->push(message); } } -int SSBDemodGUI::spanLog2Limit(int spanLog2) +unsigned int SSBDemodGUI::spanLog2Max() { - while (((m_ssbDemod->getAudioSampleRate() / (1< m_ssbDemod->getInputSampleRate()) && (spanLog2 < 4)) { - spanLog2++; - } - - return spanLog2; + unsigned int spanLog2 = 0; + for (; m_ssbDemod->getAudioSampleRate() / (1<= 1000; spanLog2++); + return spanLog2 == 0 ? 0 : spanLog2-1; } -void SSBDemodGUI::applyBandwidths(int spanLog2, bool force) +void SSBDemodGUI::applyBandwidths(unsigned int spanLog2, bool force) { - spanLog2 = spanLog2Limit(spanLog2); - ui->spanLog2->setMaximum(5 - spanLog2Limit(1)); + unsigned int s2max = spanLog2Max(); + spanLog2 = spanLog2 > s2max ? s2max : spanLog2; + unsigned int limit = s2max < 1 ? 0 : s2max - 1; + ui->spanLog2->setMaximum(limit); bool dsb = ui->dsb->isChecked(); //int spanLog2 = ui->spanLog2->value(); m_spectrumRate = m_ssbDemod->getAudioSampleRate() / (1<BW->value(); int lw = ui->lowCut->value(); - int bwMax = std::min(m_ssbDemod->getAudioSampleRate() / (100*(1<getInputSampleRate()/100); + int bwMax = m_ssbDemod->getAudioSampleRate() / (100*(1<. // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include +#include + +#include "audio/audiooutput.h" +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "dsp/devicesamplemimo.h" +#include "dsp/basebandsamplesink.h" +#include "device/deviceapi.h" +#include "util/db.h" + +#include "ssbdemodsink.h" + +const int SSBDemodSink::m_ssbFftLen = 1024; +const int SSBDemodSink::m_agcTarget = 3276.8; // -10 dB amplitude => -20 dB power: center of normal signal + +SSBDemodSink::SSBDemodSink() : + m_audioBinaual(false), + m_audioFlipChannels(false), + m_dsb(false), + m_audioMute(false), + m_agc(12000, m_agcTarget, 1e-2), + m_agcActive(false), + m_agcClamping(false), + m_agcNbSamples(12000), + m_agcPowerThreshold(1e-2), + m_agcThresholdGate(0), + m_squelchDelayLine(2*48000), + m_audioActive(false), + m_spectrumSink(nullptr), + m_audioFifo(24000) +{ + m_Bandwidth = 5000; + m_LowCutoff = 300; + m_volume = 2.0; + m_spanLog2 = 3; + m_channelSampleRate = 48000; + m_channelFrequencyOffset = 0; + + m_audioBuffer.resize(1<<14); + m_audioBufferFill = 0; + m_undersampleCount = 0; + m_sum = 0; + + m_usb = true; + m_magsq = 0.0f; + m_magsqSum = 0.0f; + m_magsqPeak = 0.0f; + m_magsqCount = 0; + + m_agc.setClampMax(SDR_RX_SCALED/100.0); + m_agc.setClamping(m_agcClamping); + + SSBFilter = new fftfilt(m_LowCutoff / m_audioSampleRate, m_Bandwidth / m_audioSampleRate, m_ssbFftLen); + DSBFilter = new fftfilt((2.0f * m_Bandwidth) / m_audioSampleRate, 2 * m_ssbFftLen); + + applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true); + applySettings(m_settings, true); +} + +SSBDemodSink::~SSBDemodSink() +{ + delete SSBFilter; + delete DSBFilter; +} + +void SSBDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +{ + Complex ci; + + for(SampleVector::const_iterator it = begin; it < end; ++it) + { + Complex c(it->real(), it->imag()); + c *= m_nco.nextIQ(); + + if (m_interpolatorDistance < 1.0f) // interpolate + { + while (!m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci)) + { + processOneSample(ci); + m_interpolatorDistanceRemain += m_interpolatorDistance; + } + } + else + { + if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci)) + { + processOneSample(ci); + m_interpolatorDistanceRemain += m_interpolatorDistance; + } + } + } +} + +void SSBDemodSink::processOneSample(Complex &ci) +{ + fftfilt::cmplx *sideband; + int n_out = 0; + int decim = 1<<(m_spanLog2 - 1); + unsigned char decim_mask = decim - 1; // counter LSB bit mask for decimation by 2^(m_scaleLog2 - 1) + + if (m_dsb) { + n_out = DSBFilter->runDSB(ci, &sideband); + } else { + n_out = SSBFilter->runSSB(ci, &sideband, m_usb); + } + + for (int i = 0; i < n_out; i++) + { + // Downsample by 2^(m_scaleLog2 - 1) for SSB band spectrum display + // smart decimation with bit gain using float arithmetic (23 bits significand) + + m_sum += sideband[i]; + + if (!(m_undersampleCount++ & decim_mask)) + { + Real avgr = m_sum.real() / decim; + Real avgi = m_sum.imag() / decim; + m_magsq = (avgr * avgr + avgi * avgi) / (SDR_RX_SCALED*SDR_RX_SCALED); + + m_magsqSum += m_magsq; + + if (m_magsq > m_magsqPeak) + { + m_magsqPeak = m_magsq; + } + + m_magsqCount++; + + if (!m_dsb & !m_usb) + { // invert spectrum for LSB + m_sampleBuffer.push_back(Sample(avgi, avgr)); + } + else + { + m_sampleBuffer.push_back(Sample(avgr, avgi)); + } + + m_sum.real(0.0); + m_sum.imag(0.0); + } + + float agcVal = m_agcActive ? m_agc.feedAndGetValue(sideband[i]) : 0.1; + fftfilt::cmplx& delayedSample = m_squelchDelayLine.readBack(m_agc.getStepDownDelay()); + m_audioActive = delayedSample.real() != 0.0; + m_squelchDelayLine.write(sideband[i]*agcVal); + + if (m_audioMute) + { + m_audioBuffer[m_audioBufferFill].r = 0; + m_audioBuffer[m_audioBufferFill].l = 0; + } + else + { + fftfilt::cmplx z = m_agcActive ? delayedSample * m_agc.getStepValue() : delayedSample; + + if (m_audioBinaual) + { + if (m_audioFlipChannels) + { + m_audioBuffer[m_audioBufferFill].r = (qint16)(z.imag() * m_volume); + m_audioBuffer[m_audioBufferFill].l = (qint16)(z.real() * m_volume); + } + else + { + m_audioBuffer[m_audioBufferFill].r = (qint16)(z.real() * m_volume); + m_audioBuffer[m_audioBufferFill].l = (qint16)(z.imag() * m_volume); + } + } + else + { + Real demod = (z.real() + z.imag()) * 0.7; + qint16 sample = (qint16)(demod * m_volume); + m_audioBuffer[m_audioBufferFill].l = sample; + m_audioBuffer[m_audioBufferFill].r = sample; + } + } + + ++m_audioBufferFill; + + if (m_audioBufferFill >= m_audioBuffer.size()) + { + uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); + + if (res != m_audioBufferFill) { + qDebug("SSBDemodSink::feed: %u/%u samples written", res, m_audioBufferFill); + } + + m_audioBufferFill = 0; + } + } + + uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); + + if (res != m_audioBufferFill) { + qDebug("SSBDemodSink::feed: %u/%u tail samples written", res, m_audioBufferFill); + } + + m_audioBufferFill = 0; + + if (m_spectrumSink != 0) { + m_spectrumSink->feed(m_sampleBuffer.begin(), m_sampleBuffer.end(), !m_dsb); + } + + m_sampleBuffer.clear(); +} + +void SSBDemodSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force) +{ + qDebug() << "SSBDemodSink::applyChannelSettings:" + << " channelSampleRate: " << channelSampleRate + << " channelFrequencyOffset: " << channelFrequencyOffset; + + if ((m_channelFrequencyOffset != channelFrequencyOffset) || + (m_channelSampleRate != channelSampleRate) || force) + { + m_nco.setFreq(-channelFrequencyOffset, channelSampleRate); + } + + if ((m_channelSampleRate != channelSampleRate) || force) + { + Real interpolatorBandwidth = (m_Bandwidth * 1.5f) > channelSampleRate ? channelSampleRate : (m_Bandwidth * 1.5f); + m_interpolator.create(16, channelSampleRate, interpolatorBandwidth, 2.0f); + m_interpolatorDistanceRemain = 0; + m_interpolatorDistance = (Real) channelSampleRate / (Real) m_audioSampleRate; + } + + m_channelSampleRate = channelSampleRate; + m_channelFrequencyOffset = channelFrequencyOffset; +} + +void SSBDemodSink::applyAudioSampleRate(int sampleRate) +{ + qDebug("SSBDemodSink::applyAudioSampleRate: %d", sampleRate); + + Real interpolatorBandwidth = (m_Bandwidth * 1.5f) > m_channelSampleRate ? m_channelSampleRate : (m_Bandwidth * 1.5f); + m_interpolator.create(16, m_channelSampleRate, interpolatorBandwidth, 2.0f); + m_interpolatorDistanceRemain = 0; + m_interpolatorDistance = (Real) m_channelSampleRate / (Real) sampleRate; + + SSBFilter->create_filter(m_LowCutoff / (float) sampleRate, m_Bandwidth / (float) sampleRate); + DSBFilter->create_dsb_filter((2.0f * m_Bandwidth) / (float) sampleRate); + + int agcNbSamples = (sampleRate / 1000) * (1< m_channelSampleRate ? m_channelSampleRate : (m_Bandwidth * 1.5f); + m_interpolator.create(16, m_channelSampleRate, interpolatorBandwidth, 2.0f); + m_interpolatorDistanceRemain = 0; + m_interpolatorDistance = (Real) m_channelSampleRate / (Real) m_audioSampleRate; + SSBFilter->create_filter(m_LowCutoff / (float) m_audioSampleRate, m_Bandwidth / (float) m_audioSampleRate); + DSBFilter->create_dsb_filter((2.0f * m_Bandwidth) / (float) m_audioSampleRate); + } + + if ((m_settings.m_volume != settings.m_volume) || force) + { + m_volume = settings.m_volume; + m_volume /= 4.0; // for 3276.8 + } + + if ((m_settings.m_agcTimeLog2 != settings.m_agcTimeLog2) || + (m_settings.m_agcPowerThreshold != settings.m_agcPowerThreshold) || + (m_settings.m_agcThresholdGate != settings.m_agcThresholdGate) || + (m_settings.m_agcClamping != settings.m_agcClamping) || force) + { + int agcNbSamples = (m_audioSampleRate / 1000) * (1<. // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_SSBDEMODSINK_H +#define INCLUDE_SSBDEMODSINK_H + +#include + +#include "dsp/channelsamplesink.h" +#include "dsp/ncof.h" +#include "dsp/interpolator.h" +#include "dsp/fftfilt.h" +#include "dsp/agc.h" +#include "audio/audiofifo.h" +#include "util/doublebufferfifo.h" + +#include "ssbdemodsettings.h" + +class BasebandSampleSink; + +class SSBDemodSink : public ChannelSampleSink { +public: + SSBDemodSink(); + ~SSBDemodSink(); + + virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + + void setSpectrumSink(BasebandSampleSink* spectrumSink) { m_spectrumSink = spectrumSink; } + void applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force = false); + void applySettings(const SSBDemodSettings& settings, bool force = false); + void applyAudioSampleRate(int sampleRate); + + AudioFifo *getAudioFifo() { return &m_audioFifo; } + double getMagSq() const { return m_magsq; } + bool getAudioActive() const { return m_audioActive; } + + void getMagSqLevels(double& avg, double& peak, int& nbSamples) + { + if (m_magsqCount > 0) + { + m_magsq = m_magsqSum / m_magsqCount; + m_magSqLevelStore.m_magsq = m_magsq; + m_magSqLevelStore.m_magsqPeak = m_magsqPeak; + } + + avg = m_magSqLevelStore.m_magsq; + peak = m_magSqLevelStore.m_magsqPeak; + nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount; + + m_magsqSum = 0.0f; + m_magsqPeak = 0.0f; + m_magsqCount = 0; + } + +private: + struct MagSqLevelsStore + { + MagSqLevelsStore() : + m_magsq(1e-12), + m_magsqPeak(1e-12) + {} + double m_magsq; + double m_magsqPeak; + }; + + SSBDemodSettings m_settings; + + Real m_Bandwidth; + Real m_LowCutoff; + Real m_volume; + int m_spanLog2; + fftfilt::cmplx m_sum; + int m_undersampleCount; + int m_channelSampleRate; + int m_channelFrequencyOffset; + bool m_audioBinaual; + bool m_audioFlipChannels; + bool m_usb; + bool m_dsb; + bool m_audioMute; + double m_magsq; + double m_magsqSum; + double m_magsqPeak; + int m_magsqCount; + MagSqLevelsStore m_magSqLevelStore; + MagAGC m_agc; + bool m_agcActive; + bool m_agcClamping; + int m_agcNbSamples; //!< number of audio (48 kHz) samples for AGC averaging + double m_agcPowerThreshold; //!< AGC power threshold (linear) + int m_agcThresholdGate; //!< Gate length in number of samples befor threshold triggers + DoubleBufferFIFO m_squelchDelayLine; + bool m_audioActive; //!< True if an audio signal is produced (no AGC or AGC and above threshold) + + NCOF m_nco; + Interpolator m_interpolator; + Real m_interpolatorDistance; + Real m_interpolatorDistanceRemain; + fftfilt* SSBFilter; + fftfilt* DSBFilter; + + BasebandSampleSink* m_spectrumSink; + SampleVector m_sampleBuffer; + + AudioVector m_audioBuffer; + uint m_audioBufferFill; + AudioFifo m_audioFifo; + quint32 m_audioSampleRate; + + static const int m_ssbFftLen; + static const int m_agcTarget; + + void processOneSample(Complex &ci); +}; + +#endif // INCLUDE_SSBDEMODSINK_H \ No newline at end of file diff --git a/plugins/channelrx/demodssb/ssbplugin.cpp b/plugins/channelrx/demodssb/ssbplugin.cpp index 867829675..0473bfde8 100644 --- a/plugins/channelrx/demodssb/ssbplugin.cpp +++ b/plugins/channelrx/demodssb/ssbplugin.cpp @@ -11,7 +11,7 @@ const PluginDescriptor SSBPlugin::m_pluginDescriptor = { QString("SSB Demodulator"), - QString("4.11.6"), + QString("4.12.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/channelrx/demodwfm/CMakeLists.txt b/plugins/channelrx/demodwfm/CMakeLists.txt index 57e04f202..faf29c91c 100644 --- a/plugins/channelrx/demodwfm/CMakeLists.txt +++ b/plugins/channelrx/demodwfm/CMakeLists.txt @@ -3,6 +3,8 @@ project(wfm) set(wfm_SOURCES wfmdemod.cpp wfmdemodsettings.cpp + wfmdemodsink.cpp + wfmdemodbaseband.cpp wfmdemodwebapiadapter.cpp wfmplugin.cpp ) @@ -10,6 +12,8 @@ set(wfm_SOURCES set(wfm_HEADERS wfmdemod.h wfmdemodsettings.h + wfmdemodsink.h + wfmdemodbaseband.h wfmdemodwebapiadapter.h wfmplugin.h ) @@ -22,8 +26,7 @@ if(NOT SERVER_MODE) set(wfm_SOURCES ${wfm_SOURCES} wfmdemodgui.cpp - - wfmdemodgui.ui + wfmdemodgui.ui ) set(wfm_HEADERS ${wfm_HEADERS} @@ -46,8 +49,8 @@ add_library(${TARGET_NAME} SHARED ) target_link_libraries(${TARGET_NAME} - Qt5::Core - ${TARGET_LIB} + Qt5::Core + ${TARGET_LIB} sdrbase ${TARGET_LIB_GUI} ) diff --git a/plugins/channelrx/demodwfm/wfmdemod.cpp b/plugins/channelrx/demodwfm/wfmdemod.cpp index 6f4ad5515..48864e0a6 100644 --- a/plugins/channelrx/demodwfm/wfmdemod.cpp +++ b/plugins/channelrx/demodwfm/wfmdemod.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include "SWGChannelSettings.h" #include "SWGWFMDemodSettings.h" @@ -44,7 +45,6 @@ #include "wfmdemod.h" MESSAGE_CLASS_DEFINITION(WFMDemod::MsgConfigureWFMDemod, Message) -MESSAGE_CLASS_DEFINITION(WFMDemod::MsgConfigureChannelizer, Message) const QString WFMDemod::m_channelIdURI = "sdrangel.channel.wfmdemod"; const QString WFMDemod::m_channelId = "WFMDemod"; @@ -53,33 +53,17 @@ const int WFMDemod::m_udpBlockSize = 512; WFMDemod::WFMDemod(DeviceAPI* deviceAPI) : ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink), m_deviceAPI(deviceAPI), - m_inputSampleRate(384000), - m_inputFrequencyOffset(0), - m_squelchOpen(false), - m_magsq(0.0f), - m_magsqSum(0.0f), - m_magsqPeak(0.0f), - m_magsqCount(0), - m_audioFifo(250000), - m_settingsMutex(QMutex::Recursive) + m_basebandSampleRate(0) { setObjectName(m_channelId); - m_rfFilter = new fftfilt(-50000.0 / 384000.0, 50000.0 / 384000.0, rfFilterFftLength); - m_phaseDiscri.setFMScaling(384000/75000); + m_thread = new QThread(this); + m_basebandSink = new WFMDemodBaseband(); + m_basebandSink->moveToThread(m_thread); - m_audioBuffer.resize(16384); - m_audioBufferFill = 0; - - DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(&m_audioFifo, getInputMessageQueue()); - m_audioSampleRate = DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate(); - - applyChannelSettings(m_inputSampleRate, m_inputFrequencyOffset, true); applySettings(m_settings, true); - m_channelizer = new DownChannelizer(this); - m_threadedChannelizer = new ThreadedBasebandSampleSink(m_channelizer, this); - m_deviceAPI->addChannelSink(m_threadedChannelizer); + m_deviceAPI->addChannelSink(this); m_deviceAPI->addChannelSinkAPI(this); m_networkManager = new QNetworkAccessManager(); @@ -90,13 +74,11 @@ WFMDemod::~WFMDemod() { disconnect(m_networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkManagerFinished(QNetworkReply*))); delete m_networkManager; - DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(&m_audioFifo); - m_deviceAPI->removeChannelSinkAPI(this); - m_deviceAPI->removeChannelSink(m_threadedChannelizer); - delete m_threadedChannelizer; - delete m_channelizer; - delete m_rfFilter; + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this); + delete m_basebandSink; + delete m_thread; } uint32_t WFMDemod::getNumberOfDeviceStreams() const @@ -107,137 +89,31 @@ uint32_t WFMDemod::getNumberOfDeviceStreams() const void WFMDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst) { (void) firstOfBurst; - Complex ci; - fftfilt::cmplx *rf; - int rf_out; - Real demod; - double msq; - float fmDev; - - m_settingsMutex.lock(); - - for (SampleVector::const_iterator it = begin; it != end; ++it) - { - Complex c(it->real(), it->imag()); - c *= m_nco.nextIQ(); - - rf_out = m_rfFilter->runFilt(c, &rf); // filter RF before demod - - for (int i = 0 ; i < rf_out; i++) - { - msq = rf[i].real()*rf[i].real() + rf[i].imag()*rf[i].imag(); - Real magsq = msq / (SDR_RX_SCALED*SDR_RX_SCALED); - m_magsqSum += magsq; - m_movingAverage(magsq); - - if (magsq > m_magsqPeak) { - m_magsqPeak = magsq; - } - - m_magsqCount++; - - if (magsq >= m_squelchLevel) - { - if (m_squelchState < m_settings.m_rfBandwidth / 10) { // twice attack and decay rate - m_squelchState++; - } - } - else - { - if (m_squelchState > 0) { - m_squelchState--; - } - } - - m_squelchOpen = (m_squelchState > (m_settings.m_rfBandwidth / 20)); - - if (m_squelchOpen && !m_settings.m_audioMute) { // squelch open and not mute - demod = m_phaseDiscri.phaseDiscriminatorDelta(rf[i], msq, fmDev); - } else { - demod = 0; - } - - Complex e(demod, 0); - - if (m_interpolator.decimate(&m_interpolatorDistanceRemain, e, &ci)) - { - qint16 sample = (qint16)(ci.real() * 3276.8f * m_settings.m_volume); - m_sampleBuffer.push_back(Sample(sample, sample)); - m_audioBuffer[m_audioBufferFill].l = sample; - m_audioBuffer[m_audioBufferFill].r = sample; - - ++m_audioBufferFill; - - if(m_audioBufferFill >= m_audioBuffer.size()) - { - uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); - - if (res != m_audioBufferFill) { - qDebug("WFMDemod::feed: %u/%u audio samples written", res, m_audioBufferFill); - } - - m_audioBufferFill = 0; - } - - m_interpolatorDistanceRemain += m_interpolatorDistance; - } - } - } - - if (m_audioBufferFill > 0) - { - uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); - - if (res != m_audioBufferFill) { - qDebug("WFMDemod::feed: %u/%u tail samples written", res, m_audioBufferFill); - } - - m_audioBufferFill = 0; - } - - m_sampleBuffer.clear(); - - m_settingsMutex.unlock(); + m_basebandSink->feed(begin, end); } void WFMDemod::start() { - m_squelchState = 0; - m_audioFifo.clear(); - m_phaseDiscri.reset(); - applyChannelSettings(m_inputSampleRate, m_inputFrequencyOffset, true); + qDebug() << "WFMDemod::start"; + + if (m_basebandSampleRate != 0) { + m_basebandSink->setBasebandSampleRate(m_basebandSampleRate); + } + + m_basebandSink->reset(); + m_thread->start(); } void WFMDemod::stop() { + qDebug() << "WFMDemod::stop"; + m_thread->exit(); + m_thread->wait(); } bool WFMDemod::handleMessage(const Message& cmd) { - if (DownChannelizer::MsgChannelizerNotification::match(cmd)) - { - DownChannelizer::MsgChannelizerNotification& notif = (DownChannelizer::MsgChannelizerNotification&) cmd; - qDebug() << "WFMDemod::handleMessage: MsgChannelizerNotification: m_inputSampleRate: " << notif.getSampleRate() - << " m_inputFrequencyOffset: " << notif.getFrequencyOffset(); - - applyChannelSettings(notif.getSampleRate(), notif.getFrequencyOffset()); - - return true; - } - else if (MsgConfigureChannelizer::match(cmd)) - { - MsgConfigureChannelizer& cfg = (MsgConfigureChannelizer&) cmd; - qDebug() << "WFMDemod::handleMessage: MsgConfigureChannelizer:" - << " sampleRate: " << cfg.getSampleRate() - << " inputFrequencyOffset: " << cfg.getCenterFrequency(); - - m_channelizer->configure(m_channelizer->getInputMessageQueue(), - cfg.getSampleRate(), - cfg.getCenterFrequency()); - - return true; - } - else if (MsgConfigureWFMDemod::match(cmd)) + if (MsgConfigureWFMDemod::match(cmd)) { MsgConfigureWFMDemod& cfg = (MsgConfigureWFMDemod&) cmd; qDebug("WFMDemod::handleMessage: MsgConfigureWFMDemod"); @@ -246,29 +122,15 @@ bool WFMDemod::handleMessage(const Message& cmd) return true; } - else if (BasebandSampleSink::MsgThreadedSink::match(cmd)) - { - BasebandSampleSink::MsgThreadedSink& cfg = (BasebandSampleSink::MsgThreadedSink&) cmd; - const QThread *thread = cfg.getThread(); - qDebug("WFMDemod::handleMessage: BasebandSampleSink::MsgThreadedSink: %p", thread); - return true; - } - else if (DSPConfigureAudio::match(cmd)) - { - DSPConfigureAudio& cfg = (DSPConfigureAudio&) cmd; - uint32_t sampleRate = cfg.getSampleRate(); - - qDebug() << "WFMDemod::handleMessage: DSPConfigureAudio:" - << " sampleRate: " << sampleRate; - - if (sampleRate != m_audioSampleRate) { - applyAudioSampleRate(sampleRate); - } - - return true; - } else if (DSPSignalNotification::match(cmd)) { + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + m_basebandSampleRate = notif.getSampleRate(); + // Forward to the sink + DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy + qDebug() << "WFMDemod::handleMessage: DSPSignalNotification"; + m_basebandSink->getInputMessageQueue()->push(rep); + return true; } else @@ -277,54 +139,6 @@ bool WFMDemod::handleMessage(const Message& cmd) } } -void WFMDemod::applyAudioSampleRate(int sampleRate) -{ - qDebug("WFMDemod::applyAudioSampleRate: %d", sampleRate); - - m_settingsMutex.lock(); - - m_interpolator.create(16, m_inputSampleRate, m_settings.m_afBandwidth); - m_interpolatorDistanceRemain = (Real) m_inputSampleRate / sampleRate; - m_interpolatorDistance = (Real) m_inputSampleRate / (Real) sampleRate; - - m_settingsMutex.unlock(); - - m_audioSampleRate = sampleRate; -} - -void WFMDemod::applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force) -{ - qDebug() << "WFMDemod::applyChannelSettings:" - << " inputSampleRate: " << inputSampleRate - << " inputFrequencyOffset: " << inputFrequencyOffset; - - if((inputFrequencyOffset != m_inputFrequencyOffset) || - (inputSampleRate != m_inputSampleRate) || force) - { - m_nco.setFreq(-inputFrequencyOffset, inputSampleRate); - } - - if ((inputSampleRate != m_inputSampleRate) || force) - { - qDebug() << "WFMDemod::applyChannelSettings: m_interpolator.create"; - m_settingsMutex.lock(); - m_interpolator.create(16, inputSampleRate, m_settings.m_afBandwidth); - m_interpolatorDistanceRemain = (Real) inputSampleRate / (Real) m_audioSampleRate; - m_interpolatorDistance = (Real) inputSampleRate / (Real) m_audioSampleRate; - m_settingsMutex.unlock(); - qDebug() << "WFMDemod::applySettings: m_rfFilter->create_filter"; - Real lowCut = -(m_settings.m_rfBandwidth / 2.0) / inputSampleRate; - Real hiCut = (m_settings.m_rfBandwidth / 2.0) / inputSampleRate; - m_rfFilter->create_filter(lowCut, hiCut); - m_fmExcursion = m_settings.m_rfBandwidth / (Real) inputSampleRate; - m_phaseDiscri.setFMScaling(1.0f/m_fmExcursion); - qDebug("WFMDemod::applySettings: m_fmExcursion: %f", m_fmExcursion); - } - - m_inputSampleRate = inputSampleRate; - m_inputFrequencyOffset = inputFrequencyOffset; -} - void WFMDemod::applySettings(const WFMDemodSettings& settings, bool force) { qDebug() << "WFMDemod::applySettings:" @@ -373,58 +187,22 @@ void WFMDemod::applySettings(const WFMDemodSettings& settings, bool force) reverseAPIKeys.append("rgbColor"); } - if((settings.m_afBandwidth != m_settings.m_afBandwidth) || - (settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) - { - m_settingsMutex.lock(); - qDebug() << "WFMDemod::applySettings: m_interpolator.create"; - m_interpolator.create(16, m_inputSampleRate, settings.m_afBandwidth); - m_interpolatorDistanceRemain = (Real) m_inputSampleRate / (Real) m_audioSampleRate; - m_interpolatorDistance = (Real) m_inputSampleRate / (Real) m_audioSampleRate; - qDebug() << "WFMDemod::applySettings: m_rfFilter->create_filter"; - Real lowCut = -(settings.m_rfBandwidth / 2.0) / m_inputSampleRate; - Real hiCut = (settings.m_rfBandwidth / 2.0) / m_inputSampleRate; - m_rfFilter->create_filter(lowCut, hiCut); - m_fmExcursion = settings.m_rfBandwidth / (Real) m_inputSampleRate; - m_phaseDiscri.setFMScaling(1.0f/m_fmExcursion); - qDebug("WFMDemod::applySettings: m_fmExcursion: %f", m_fmExcursion); - m_settingsMutex.unlock(); - } - - if ((settings.m_squelch != m_settings.m_squelch) || force) - { - qDebug() << "WFMDemod::applySettings: set m_squelchLevel"; - m_squelchLevel = pow(10.0, settings.m_squelch / 10.0); - } - - if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) - { - AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); - int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_audioDeviceName); - //qDebug("AMDemod::applySettings: audioDeviceName: %s audioDeviceIndex: %d", qPrintable(settings.m_audioDeviceName), audioDeviceIndex); - audioDeviceManager->addAudioSink(&m_audioFifo, getInputMessageQueue(), audioDeviceIndex); - uint32_t audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex); - - if (m_audioSampleRate != audioSampleRate) { - applyAudioSampleRate(audioSampleRate); - } - } - if (m_settings.m_streamIndex != settings.m_streamIndex) { if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only { m_deviceAPI->removeChannelSinkAPI(this, m_settings.m_streamIndex); - m_deviceAPI->removeChannelSink(m_threadedChannelizer, m_settings.m_streamIndex); - m_deviceAPI->addChannelSink(m_threadedChannelizer, settings.m_streamIndex); + m_deviceAPI->removeChannelSink(this, m_settings.m_streamIndex); + m_deviceAPI->addChannelSink(this, settings.m_streamIndex); m_deviceAPI->addChannelSinkAPI(this, settings.m_streamIndex); - // apply stream sample rate to itself - applyChannelSettings(m_deviceAPI->getSampleMIMO()->getSourceSampleRate(settings.m_streamIndex), m_inputFrequencyOffset); } reverseAPIKeys.append("streamIndex"); } + WFMDemodBaseband::MsgConfigureWFMDemodBaseband *msg = WFMDemodBaseband::MsgConfigureWFMDemodBaseband::create(settings, force); + m_basebandSink->getInputMessageQueue()->push(msg); + if (settings.m_useReverseAPI) { bool fullUpdate = ((m_settings.m_useReverseAPI != settings.m_useReverseAPI) && settings.m_useReverseAPI) || @@ -481,13 +259,6 @@ int WFMDemod::webapiSettingsPutPatch( WFMDemodSettings settings = m_settings; webapiUpdateChannelSettings(settings, channelSettingsKeys, response); - if (settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) - { - MsgConfigureChannelizer* channelConfigMsg = MsgConfigureChannelizer::create( - requiredBW(settings.m_rfBandwidth), settings.m_inputFrequencyOffset); - m_inputMessageQueue.push(channelConfigMsg); - } - MsgConfigureWFMDemod *msg = MsgConfigureWFMDemod::create(settings, force); m_inputMessageQueue.push(msg); @@ -609,9 +380,9 @@ void WFMDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples); response.getWfmDemodReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg)); - response.getWfmDemodReport()->setSquelch(m_squelchState > 0 ? 1 : 0); - response.getWfmDemodReport()->setAudioSampleRate(m_audioSampleRate); - response.getWfmDemodReport()->setChannelSampleRate(m_inputSampleRate); + response.getWfmDemodReport()->setSquelch(m_basebandSink->getSquelchState() > 0 ? 1 : 0); + response.getWfmDemodReport()->setAudioSampleRate(m_basebandSink->getAudioSampleRate()); + response.getWfmDemodReport()->setChannelSampleRate(m_basebandSink->getChannelSampleRate()); } void WFMDemod::webapiReverseSendSettings(QList& channelSettingsKeys, const WFMDemodSettings& settings, bool force) diff --git a/plugins/channelrx/demodwfm/wfmdemod.h b/plugins/channelrx/demodwfm/wfmdemod.h index 147759ab7..72bb0dce6 100644 --- a/plugins/channelrx/demodwfm/wfmdemod.h +++ b/plugins/channelrx/demodwfm/wfmdemod.h @@ -26,23 +26,12 @@ #include "dsp/basebandsamplesink.h" #include "channel/channelapi.h" -#include "dsp/nco.h" -#include "dsp/interpolator.h" -#include "dsp/lowpass.h" -#include "util/movingaverage.h" -#include "dsp/fftfilt.h" -#include "dsp/phasediscri.h" -#include "audio/audiofifo.h" -#include "util/message.h" #include "wfmdemodsettings.h" - -#define rfFilterFftLength 1024 +#include "wfmdemodbaseband.h" class QNetworkAccessManager; class QNetworkReply; -class ThreadedBasebandSampleSink; -class DownChannelizer; class DeviceAPI; class WFMDemod : public BasebandSampleSink, public ChannelAPI { @@ -71,29 +60,6 @@ public: { } }; - class MsgConfigureChannelizer : public Message { - MESSAGE_CLASS_DECLARATION - - public: - int getSampleRate() const { return m_sampleRate; } - int getCenterFrequency() const { return m_centerFrequency; } - - static MsgConfigureChannelizer* create(int sampleRate, int centerFrequency) - { - return new MsgConfigureChannelizer(sampleRate, centerFrequency); - } - - private: - int m_sampleRate; - int m_centerFrequency; - - MsgConfigureChannelizer(int sampleRate, int centerFrequency) : - Message(), - m_sampleRate(sampleRate), - m_centerFrequency(centerFrequency) - { } - }; - WFMDemod(DeviceAPI *deviceAPI); virtual ~WFMDemod(); virtual void destroy() { delete this; } @@ -120,26 +86,10 @@ public: return m_settings.m_inputFrequencyOffset; } - double getMagSq() const { return m_movingAverage.asDouble(); } - bool getSquelchOpen() const { return m_squelchOpen; } + double getMagSq() const { return m_basebandSink->getMagSq(); } + bool getSquelchOpen() const { return m_basebandSink->getSquelchOpen(); } - void getMagSqLevels(double& avg, double& peak, int& nbSamples) - { - if (m_magsqCount > 0) - { - m_magsq = m_magsqSum / m_magsqCount; - m_magSqLevelStore.m_magsq = m_magsq; - m_magSqLevelStore.m_magsqPeak = m_magsqPeak; - } - - avg = m_magSqLevelStore.m_magsq; - peak = m_magSqLevelStore.m_magsqPeak; - nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount; - - m_magsqSum = 0.0f; - m_magsqPeak = 0.0f; - m_magsqCount = 0; - } + void getMagSqLevels(double& avg, double& peak, int& nbSamples) { m_basebandSink->getMagSqLevels(avg, peak, nbSamples); } virtual int webapiSettingsGet( SWGSDRangel::SWGChannelSettings& response, @@ -164,79 +114,23 @@ public: const QStringList& channelSettingsKeys, SWGSDRangel::SWGChannelSettings& response); - static int requiredBW(int rfBW) - { - if (rfBW <= 48000) { - return 48000; - } else { - return (3*rfBW)/2; - } - } - uint32_t getNumberOfDeviceStreams() const; static const QString m_channelIdURI; static const QString m_channelId; private: - struct MagSqLevelsStore - { - MagSqLevelsStore() : - m_magsq(1e-12), - m_magsqPeak(1e-12) - {} - double m_magsq; - double m_magsqPeak; - }; - - enum RateState { - RSInitialFill, - RSRunning - }; - DeviceAPI* m_deviceAPI; - ThreadedBasebandSampleSink* m_threadedChannelizer; - DownChannelizer* m_channelizer; - - int m_inputSampleRate; - int m_inputFrequencyOffset; + QThread *m_thread; + WFMDemodBaseband* m_basebandSink; WFMDemodSettings m_settings; - quint32 m_audioSampleRate; - - NCO m_nco; - Interpolator m_interpolator; //!< Interpolator between sample rate sent from DSP engine and requested RF bandwidth (rational) - Real m_interpolatorDistance; - Real m_interpolatorDistanceRemain; - fftfilt* m_rfFilter; - - Real m_squelchLevel; - int m_squelchState; - bool m_squelchOpen; - double m_magsq; //!< displayed averaged value - double m_magsqSum; - double m_magsqPeak; - int m_magsqCount; - MagSqLevelsStore m_magSqLevelStore; - - MovingAverageUtil m_movingAverage; - Real m_fmExcursion; - - AudioVector m_audioBuffer; - uint m_audioBufferFill; - - AudioFifo m_audioFifo; - SampleVector m_sampleBuffer; - QMutex m_settingsMutex; - - PhaseDiscriminators m_phaseDiscri; + int m_basebandSampleRate; //!< stored from device message used when starting baseband sink QNetworkAccessManager *m_networkManager; QNetworkRequest m_networkRequest; static const int m_udpBlockSize; - void applyAudioSampleRate(int sampleRate); - void applyChannelSettings(int inputSampleRate, int inputFrequencyOffset, bool force = false); void applySettings(const WFMDemodSettings& settings, bool force = false); void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); diff --git a/plugins/channelrx/demodwfm/wfmdemodbaseband.cpp b/plugins/channelrx/demodwfm/wfmdemodbaseband.cpp new file mode 100644 index 000000000..e61f28fb6 --- /dev/null +++ b/plugins/channelrx/demodwfm/wfmdemodbaseband.cpp @@ -0,0 +1,171 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "dsp/downsamplechannelizer.h" + +#include "wfmdemodbaseband.h" + +MESSAGE_CLASS_DEFINITION(WFMDemodBaseband::MsgConfigureWFMDemodBaseband, Message) + +WFMDemodBaseband::WFMDemodBaseband() : + m_mutex(QMutex::Recursive) +{ + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000)); + m_channelizer = new DownSampleChannelizer(&m_sink); + + qDebug("WFMDemodBaseband::WFMDemodBaseband"); + QObject::connect( + &m_sampleFifo, + &SampleSinkFifo::dataReady, + this, + &WFMDemodBaseband::handleData, + Qt::QueuedConnection + ); + + DSPEngine::instance()->getAudioDeviceManager()->addAudioSink(m_sink.getAudioFifo(), getInputMessageQueue()); + m_sink.applyAudioSampleRate(DSPEngine::instance()->getAudioDeviceManager()->getOutputSampleRate()); + + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); +} + +WFMDemodBaseband::~WFMDemodBaseband() +{ + DSPEngine::instance()->getAudioDeviceManager()->removeAudioSink(m_sink.getAudioFifo()); + delete m_channelizer; +} + +void WFMDemodBaseband::reset() +{ + QMutexLocker mutexLocker(&m_mutex); + m_sampleFifo.reset(); +} + +void WFMDemodBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +{ + m_sampleFifo.write(begin, end); +} + +void WFMDemodBaseband::handleData() +{ + QMutexLocker mutexLocker(&m_mutex); + + while ((m_sampleFifo.fill() > 0) && (m_inputMessageQueue.size() == 0)) + { + SampleVector::iterator part1begin; + SampleVector::iterator part1end; + SampleVector::iterator part2begin; + SampleVector::iterator part2end; + + std::size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end); + + // first part of FIFO data + if (part1begin != part1end) { + m_channelizer->feed(part1begin, part1end); + } + + // second part of FIFO data (used when block wraps around) + if(part2begin != part2end) { + m_channelizer->feed(part2begin, part2end); + } + + m_sampleFifo.readCommit((unsigned int) count); + } +} + +void WFMDemodBaseband::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != nullptr) + { + if (handleMessage(*message)) { + delete message; + } + } +} + +bool WFMDemodBaseband::handleMessage(const Message& cmd) +{ + if (MsgConfigureWFMDemodBaseband::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + MsgConfigureWFMDemodBaseband& cfg = (MsgConfigureWFMDemodBaseband&) cmd; + qDebug() << "WFMDemodBaseband::handleMessage: MsgConfigureWFMDemodBaseband"; + + applySettings(cfg.getSettings(), cfg.getForce()); + + return true; + } + else if (DSPSignalNotification::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + qDebug() << "WFMDemodBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate(); + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate())); + m_channelizer->setBasebandSampleRate(notif.getSampleRate()); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + + return true; + } + else + { + return false; + } +} + +void WFMDemodBaseband::applySettings(const WFMDemodSettings& settings, bool force) +{ + if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) + || (settings.m_inputFrequencyOffset != m_settings.m_inputFrequencyOffset) || force) + { + m_channelizer->setChannelization(WFMDemodSettings::requiredBW(settings.m_rfBandwidth), settings.m_inputFrequencyOffset); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + } + + if ((settings.m_audioDeviceName != m_settings.m_audioDeviceName) || force) + { + AudioDeviceManager *audioDeviceManager = DSPEngine::instance()->getAudioDeviceManager(); + int audioDeviceIndex = audioDeviceManager->getOutputDeviceIndex(settings.m_audioDeviceName); + //qDebug("AMDemod::applySettings: audioDeviceName: %s audioDeviceIndex: %d", qPrintable(settings.m_audioDeviceName), audioDeviceIndex); + audioDeviceManager->addAudioSink(m_sink.getAudioFifo(), getInputMessageQueue(), audioDeviceIndex); + uint32_t audioSampleRate = audioDeviceManager->getOutputSampleRate(audioDeviceIndex); + + if (m_sink.getAudioSampleRate() != audioSampleRate) { + m_sink.applyAudioSampleRate(audioSampleRate); + } + } + + m_sink.applySettings(settings, force); + + m_settings = settings; +} + +int WFMDemodBaseband::getChannelSampleRate() const +{ + return m_channelizer->getChannelSampleRate(); +} + + +void WFMDemodBaseband::setBasebandSampleRate(int sampleRate) +{ + m_channelizer->setBasebandSampleRate(sampleRate); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); +} diff --git a/plugins/channelrx/demodwfm/wfmdemodbaseband.h b/plugins/channelrx/demodwfm/wfmdemodbaseband.h new file mode 100644 index 000000000..be87f02b0 --- /dev/null +++ b/plugins/channelrx/demodwfm/wfmdemodbaseband.h @@ -0,0 +1,89 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_WFMDEMODBASEBAND_H +#define INCLUDE_WFMDEMODBASEBAND_H + +#include +#include + +#include "dsp/samplesinkfifo.h" +#include "util/message.h" +#include "util/messagequeue.h" + +#include "wfmdemodsink.h" + +class DownSampleChannelizer; + +class WFMDemodBaseband : public QObject +{ + Q_OBJECT +public: + class MsgConfigureWFMDemodBaseband : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const WFMDemodSettings& getSettings() const { return m_settings; } + bool getForce() const { return m_force; } + + static MsgConfigureWFMDemodBaseband* create(const WFMDemodSettings& settings, bool force) + { + return new MsgConfigureWFMDemodBaseband(settings, force); + } + + private: + WFMDemodSettings m_settings; + bool m_force; + + MsgConfigureWFMDemodBaseband(const WFMDemodSettings& settings, bool force) : + Message(), + m_settings(settings), + m_force(force) + { } + }; + + WFMDemodBaseband(); + ~WFMDemodBaseband(); + void reset(); + void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication + int getChannelSampleRate() const; + void setBasebandSampleRate(int sampleRate); + + unsigned int getAudioSampleRate() const { return m_sink.getAudioSampleRate(); } + double getMagSq() const { return m_sink.getMagSq(); } + bool getSquelchOpen() const { return m_sink.getSquelchOpen(); } + int getSquelchState() const { return m_sink.getSquelchState(); } + void getMagSqLevels(double& avg, double& peak, int& nbSamples) { m_sink.getMagSqLevels(avg, peak, nbSamples); } + +private: + SampleSinkFifo m_sampleFifo; + DownSampleChannelizer *m_channelizer; + WFMDemodSink m_sink; + MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication + WFMDemodSettings m_settings; + QMutex m_mutex; + + bool handleMessage(const Message& cmd); + void applySettings(const WFMDemodSettings& settings, bool force = false); + +private slots: + void handleInputMessages(); + void handleData(); //!< Handle data when samples have to be processed +}; + +#endif // INCLUDE_WFMDEMODBASEBAND_H diff --git a/plugins/channelrx/demodwfm/wfmdemodgui.cpp b/plugins/channelrx/demodwfm/wfmdemodgui.cpp index c1438d0ed..d365f4619 100644 --- a/plugins/channelrx/demodwfm/wfmdemodgui.cpp +++ b/plugins/channelrx/demodwfm/wfmdemodgui.cpp @@ -282,11 +282,6 @@ void WFMDemodGUI::applySettings(bool force) { if (m_doApplySettings) { - WFMDemod::MsgConfigureChannelizer *msgChan = WFMDemod::MsgConfigureChannelizer::create( - WFMDemod::requiredBW(m_settings.m_rfBandwidth), - m_channelMarker.getCenterFrequency()); - m_wfmDemod->getInputMessageQueue()->push(msgChan); - WFMDemod::MsgConfigureWFMDemod* msgConfig = WFMDemod::MsgConfigureWFMDemod::create( m_settings, force); m_wfmDemod->getInputMessageQueue()->push(msgConfig); } diff --git a/plugins/channelrx/demodwfm/wfmdemodsettings.h b/plugins/channelrx/demodwfm/wfmdemodsettings.h index a56ac1323..fb68dc772 100644 --- a/plugins/channelrx/demodwfm/wfmdemodsettings.h +++ b/plugins/channelrx/demodwfm/wfmdemodsettings.h @@ -52,6 +52,15 @@ struct WFMDemodSettings void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; } QByteArray serialize() const; bool deserialize(const QByteArray& data); + + static int requiredBW(int rfBW) + { + if (rfBW <= 48000) { + return 48000; + } else { + return (3*rfBW)/2; + } + } }; #endif /* PLUGINS_CHANNELRX_DEMODWFM_WFMDEMODSETTINGS_H_ */ diff --git a/plugins/channelrx/demodwfm/wfmdemodsink.cpp b/plugins/channelrx/demodwfm/wfmdemodsink.cpp new file mode 100644 index 000000000..c9c72ac4e --- /dev/null +++ b/plugins/channelrx/demodwfm/wfmdemodsink.cpp @@ -0,0 +1,232 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include +#include + +#include "audio/audiooutput.h" +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "dsp/devicesamplemimo.h" +#include "util/db.h" + +#include "wfmdemodsink.h" + +const unsigned int WFMDemodSink::m_rfFilterFftLength = 1024; + +WFMDemodSink::WFMDemodSink() : + m_channelSampleRate(384000), + m_channelFrequencyOffset(0), + m_squelchOpen(false), + m_magsq(0.0f), + m_magsqSum(0.0f), + m_magsqPeak(0.0f), + m_magsqCount(0), + m_audioFifo(250000) +{ + m_rfFilter = new fftfilt(-50000.0 / 384000.0, 50000.0 / 384000.0, m_rfFilterFftLength); + m_phaseDiscri.setFMScaling(384000/75000); + + m_audioBuffer.resize(16384); + m_audioBufferFill = 0; + + applySettings(m_settings, true); + applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true); +} + +WFMDemodSink::~WFMDemodSink() +{ + delete m_rfFilter; +} + +void WFMDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +{ + Complex ci; + fftfilt::cmplx *rf; + int rf_out; + Real demod; + double msq; + float fmDev; + + for (SampleVector::const_iterator it = begin; it != end; ++it) + { + Complex c(it->real(), it->imag()); + c *= m_nco.nextIQ(); + + rf_out = m_rfFilter->runFilt(c, &rf); // filter RF before demod + + for (int i = 0 ; i < rf_out; i++) + { + msq = rf[i].real()*rf[i].real() + rf[i].imag()*rf[i].imag(); + Real magsq = msq / (SDR_RX_SCALED*SDR_RX_SCALED); + m_magsqSum += magsq; + m_movingAverage(magsq); + + if (magsq > m_magsqPeak) { + m_magsqPeak = magsq; + } + + m_magsqCount++; + + if (magsq >= m_squelchLevel) + { + if (m_squelchState < m_settings.m_rfBandwidth / 10) { // twice attack and decay rate + m_squelchState++; + } + } + else + { + if (m_squelchState > 0) { + m_squelchState--; + } + } + + m_squelchOpen = (m_squelchState > (m_settings.m_rfBandwidth / 20)); + + if (m_squelchOpen && !m_settings.m_audioMute) { // squelch open and not mute + demod = m_phaseDiscri.phaseDiscriminatorDelta(rf[i], msq, fmDev); + } else { + demod = 0; + } + + Complex e(demod, 0); + + if (m_interpolator.decimate(&m_interpolatorDistanceRemain, e, &ci)) + { + qint16 sample = (qint16)(ci.real() * 3276.8f * m_settings.m_volume); + m_sampleBuffer.push_back(Sample(sample, sample)); + m_audioBuffer[m_audioBufferFill].l = sample; + m_audioBuffer[m_audioBufferFill].r = sample; + + ++m_audioBufferFill; + + if(m_audioBufferFill >= m_audioBuffer.size()) + { + uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); + + if (res != m_audioBufferFill) { + qDebug("WFMDemodSink::feed: %u/%u audio samples written", res, m_audioBufferFill); + } + + m_audioBufferFill = 0; + } + + m_interpolatorDistanceRemain += m_interpolatorDistance; + } + } + } + + if (m_audioBufferFill > 0) + { + uint res = m_audioFifo.write((const quint8*)&m_audioBuffer[0], m_audioBufferFill); + + if (res != m_audioBufferFill) { + qDebug("WFMDemodSink::feed: %u/%u tail samples written", res, m_audioBufferFill); + } + + m_audioBufferFill = 0; + } + + m_sampleBuffer.clear(); +} + +void WFMDemodSink::applyAudioSampleRate(unsigned int sampleRate) +{ + qDebug("WFMDemodSink::applyAudioSampleRate: %u", sampleRate); + + m_interpolator.create(16, m_channelSampleRate, m_settings.m_afBandwidth); + m_interpolatorDistanceRemain = (Real) m_channelSampleRate / sampleRate; + m_interpolatorDistance = (Real) m_channelSampleRate / (Real) sampleRate; + m_audioSampleRate = sampleRate; +} + +void WFMDemodSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force) +{ + qDebug() << "WFMDemodSink::applyChannelSettings:" + << " channelSampleRate: " << channelSampleRate + << " channelFrequencyOffset: " << channelFrequencyOffset; + + if((channelFrequencyOffset != m_channelFrequencyOffset) || + (channelSampleRate != m_channelSampleRate) || force) + { + m_nco.setFreq(-channelFrequencyOffset, channelSampleRate); + } + + if ((channelSampleRate != m_channelSampleRate) || force) + { + qDebug() << "WFMDemod::applyChannelSettings: m_interpolator.create"; + m_interpolator.create(16, channelSampleRate, m_settings.m_afBandwidth); + m_interpolatorDistanceRemain = (Real) channelSampleRate / (Real) m_audioSampleRate; + m_interpolatorDistance = (Real) channelSampleRate / (Real) m_audioSampleRate; + qDebug() << "WFMDemod::applySettings: m_rfFilter->create_filter"; + Real lowCut = -(m_settings.m_rfBandwidth / 2.0) / channelSampleRate; + Real hiCut = (m_settings.m_rfBandwidth / 2.0) / channelSampleRate; + m_rfFilter->create_filter(lowCut, hiCut); + m_fmExcursion = m_settings.m_rfBandwidth / (Real) channelSampleRate; + m_phaseDiscri.setFMScaling(1.0f/m_fmExcursion); + qDebug("WFMDemod::applySettings: m_fmExcursion: %f", m_fmExcursion); + } + + m_channelSampleRate = channelSampleRate; + m_channelFrequencyOffset = channelFrequencyOffset; +} + +void WFMDemodSink::applySettings(const WFMDemodSettings& settings, bool force) +{ + qDebug() << "WFMDemodSink::applySettings:" + << " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset + << " m_rfBandwidth: " << settings.m_rfBandwidth + << " m_afBandwidth: " << settings.m_afBandwidth + << " m_volume: " << settings.m_volume + << " m_squelch: " << settings.m_squelch + << " m_audioDeviceName: " << settings.m_audioDeviceName + << " m_audioMute: " << settings.m_audioMute + << " m_streamIndex: " << settings.m_streamIndex + << " m_useReverseAPI: " << settings.m_useReverseAPI + << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress + << " m_reverseAPIPort: " << settings.m_reverseAPIPort + << " m_reverseAPIDeviceIndex: " << settings.m_reverseAPIDeviceIndex + << " m_reverseAPIChannelIndex: " << settings.m_reverseAPIChannelIndex + << " force: " << force; + + if((settings.m_afBandwidth != m_settings.m_afBandwidth) || + (settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) + { + qDebug() << "WFMDemodSink::applySettings: m_interpolator.create"; + m_interpolator.create(16, m_channelSampleRate, settings.m_afBandwidth); + m_interpolatorDistanceRemain = (Real) m_channelSampleRate / (Real) m_audioSampleRate; + m_interpolatorDistance = (Real) m_channelSampleRate / (Real) m_audioSampleRate; + qDebug() << "WFMDemodSink::applySettings: m_rfFilter->create_filter"; + Real lowCut = -(settings.m_rfBandwidth / 2.0) / m_channelSampleRate; + Real hiCut = (settings.m_rfBandwidth / 2.0) / m_channelSampleRate; + m_rfFilter->create_filter(lowCut, hiCut); + m_fmExcursion = settings.m_rfBandwidth / (Real) m_channelSampleRate; + m_phaseDiscri.setFMScaling(1.0f/m_fmExcursion); + qDebug("WFMDemodSink::applySettings: m_fmExcursion: %f", m_fmExcursion); + } + + if ((settings.m_squelch != m_settings.m_squelch) || force) + { + qDebug() << "WFMDemodSink::applySettings: set m_squelchLevel"; + m_squelchLevel = pow(10.0, settings.m_squelch / 10.0); + } + + m_settings = settings; +} diff --git a/plugins/channelrx/demodwfm/wfmdemodsink.h b/plugins/channelrx/demodwfm/wfmdemodsink.h new file mode 100644 index 000000000..00c56ef76 --- /dev/null +++ b/plugins/channelrx/demodwfm/wfmdemodsink.h @@ -0,0 +1,121 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_WFMDEMODSINK_H +#define INCLUDE_WFMDEMODSINK_H + +#include + +#include "dsp/channelsamplesink.h" +#include "dsp/nco.h" +#include "dsp/interpolator.h" +#include "dsp/lowpass.h" +#include "util/movingaverage.h" +#include "dsp/fftfilt.h" +#include "dsp/phasediscri.h" +#include "audio/audiofifo.h" +#include "util/message.h" + +#include "wfmdemodsettings.h" + +class WFMDemodSink : public ChannelSampleSink { +public: + WFMDemodSink(); + ~WFMDemodSink(); + + virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + + double getMagSq() const { return m_movingAverage.asDouble(); } + bool getSquelchOpen() const { return m_squelchOpen; } + int getSquelchState() const { return m_squelchState; } + + void getMagSqLevels(double& avg, double& peak, int& nbSamples) + { + if (m_magsqCount > 0) + { + m_magsq = m_magsqSum / m_magsqCount; + m_magSqLevelStore.m_magsq = m_magsq; + m_magSqLevelStore.m_magsqPeak = m_magsqPeak; + } + + avg = m_magSqLevelStore.m_magsq; + peak = m_magSqLevelStore.m_magsqPeak; + nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount; + + m_magsqSum = 0.0f; + m_magsqPeak = 0.0f; + m_magsqCount = 0; + } + + void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false); + void applySettings(const WFMDemodSettings& settings, bool force = false); + + AudioFifo *getAudioFifo() { return &m_audioFifo; } + void applyAudioSampleRate(unsigned int sampleRate); + unsigned int getAudioSampleRate() const { return m_audioSampleRate; } + +private: + struct MagSqLevelsStore + { + MagSqLevelsStore() : + m_magsq(1e-12), + m_magsqPeak(1e-12) + {} + double m_magsq; + double m_magsqPeak; + }; + + enum RateState { + RSInitialFill, + RSRunning + }; + + int m_channelSampleRate; + int m_channelFrequencyOffset; + WFMDemodSettings m_settings; + + quint32 m_audioSampleRate; + + NCO m_nco; + Interpolator m_interpolator; //!< Interpolator between sample rate sent from DSP engine and requested RF bandwidth (rational) + Real m_interpolatorDistance; + Real m_interpolatorDistanceRemain; + fftfilt* m_rfFilter; + + Real m_squelchLevel; + int m_squelchState; + bool m_squelchOpen; + double m_magsq; //!< displayed averaged value + double m_magsqSum; + double m_magsqPeak; + int m_magsqCount; + MagSqLevelsStore m_magSqLevelStore; + + MovingAverageUtil m_movingAverage; + Real m_fmExcursion; + + AudioVector m_audioBuffer; + uint m_audioBufferFill; + + AudioFifo m_audioFifo; + SampleVector m_sampleBuffer; + PhaseDiscriminators m_phaseDiscri; + + static const unsigned int m_rfFilterFftLength; +}; + +#endif // INCLUDE_WFMDEMODSINK_H diff --git a/plugins/channelrx/demodwfm/wfmplugin.cpp b/plugins/channelrx/demodwfm/wfmplugin.cpp index 9882652a0..52c65fafc 100644 --- a/plugins/channelrx/demodwfm/wfmplugin.cpp +++ b/plugins/channelrx/demodwfm/wfmplugin.cpp @@ -12,7 +12,7 @@ const PluginDescriptor WFMPlugin::m_pluginDescriptor = { QString("WFM Demodulator"), - QString("4.11.6"), + QString("4.12.2"), QString("(c) Edouard Griffiths, F4EXB"), QString("https://github.com/f4exb/sdrangel"), true,