diff --git a/plugins/channeltx/udpsink/udpsink.cpp b/plugins/channeltx/udpsink/udpsink.cpp index dccb8ad77..afd08ecd5 100644 --- a/plugins/channeltx/udpsink/udpsink.cpp +++ b/plugins/channeltx/udpsink/udpsink.cpp @@ -17,6 +17,7 @@ #include #include "dsp/upchannelizer.h" +#include "util/db.h" #include "udpsinkmsg.h" #include "udpsink.h" @@ -31,13 +32,18 @@ UDPSink::UDPSink(MessageQueue* uiMessageQueue, UDPSinkGUI* udpSinkGUI, BasebandS m_spectrumChunkSize(2160), m_spectrumChunkCounter(0), m_magsq(1e-10), - m_movingAverage(16, 0), + m_movingAverage(16, 1e-10), + m_inMovingAverage(480, 1e-10), m_sampleRateSum(0), m_sampleRateAvgCounter(0), m_levelCalcCount(0), m_peakLevel(0.0f), m_levelSum(0.0f), m_levelNbSamples(480), + m_squelchOpen(false), + m_squelchOpenCount(0), + m_squelchCloseCount(0), + m_squelchThreshold(4800), m_settingsMutex(QMutex::Recursive) { setObjectName("UDPSink"); @@ -65,6 +71,7 @@ void UDPSink::pull(Sample& sample) { sample.m_real = 0.0f; sample.m_imag = 0.0f; + initSquelch(false); return; } @@ -95,7 +102,7 @@ void UDPSink::pull(Sample& sample) m_settingsMutex.unlock(); - Real magsq = ci.real() * ci.real() + ci.imag() * ci.imag(); + double magsq = ci.real() * ci.real() + ci.imag() * ci.imag(); magsq /= (1<<30); m_movingAverage.feed(magsq); m_magsq = m_movingAverage.average(); @@ -112,15 +119,30 @@ void UDPSink::modulateSample() if (m_running.m_sampleFormat == FormatS16LE) { m_udpHandler.readSample(s); - m_modSample.real(s.m_real * m_running.m_volume); - m_modSample.imag(s.m_imag * m_running.m_volume); - calculateLevel(m_modSample); + + uint64_t magsq = s.m_real * s.m_real + s.m_imag * s.m_imag; + m_inMovingAverage.feed(magsq/1073741824.0); + m_inMagsq = m_inMovingAverage.average(); + + calculateSquelch(m_inMagsq); + + if (m_squelchOpen) + { + m_modSample.real(s.m_real * m_running.m_volume); + m_modSample.imag(s.m_imag * m_running.m_volume); + calculateLevel(m_modSample); + } + else + { + m_modSample.real(0.0f); + m_modSample.imag(0.0f); + } } else { m_modSample.real(0.0f); m_modSample.imag(0.0f); - calculateLevel(1e-10); + initSquelch(false); } if (m_spectrum && m_spectrumEnabled && (m_spectrumChunkCounter < m_spectrumChunkSize - 1)) @@ -149,7 +171,7 @@ void UDPSink::calculateLevel(Real sample) } else { - qreal rmsLevel = sqrt(m_levelSum / m_levelNbSamples); + qreal rmsLevel = m_levelSum > 0.0 ? sqrt(m_levelSum / m_levelNbSamples) : 0.0; //qDebug("NFMMod::calculateLevel: %f %f", rmsLevel, m_peakLevel); emit levelChanged(rmsLevel, m_peakLevel, m_levelNbSamples); m_peakLevel = 0.0f; @@ -170,7 +192,7 @@ void UDPSink::calculateLevel(Complex sample) } else { - qreal rmsLevel = sqrt((m_levelSum/(1<<30)) / m_levelNbSamples); + qreal rmsLevel = m_levelSum > 0.0 ? sqrt((m_levelSum/(1<<30)) / m_levelNbSamples) : 0.0; emit levelChanged(rmsLevel, m_peakLevel / 32768.0, m_levelNbSamples); m_peakLevel = 0.0f; m_levelSum = 0.0f; @@ -209,6 +231,7 @@ bool UDPSink::handleMessage(const Message& cmd) m_config.m_udpPort = cfg.getUDPPort(); m_config.m_channelMute = cfg.getChannelMute(); m_config.m_volume = cfg.getVolume(); + m_config.m_squelch = CalcDb::powerFromdB(cfg.getSquelchDB()); apply(cfg.getForce()); @@ -220,7 +243,9 @@ bool UDPSink::handleMessage(const Message& cmd) << " m_udpAddressStr: " << m_config.m_udpAddressStr << " m_udpPort: " << m_config.m_udpPort << " m_channelMute: " << m_config.m_channelMute - << " m_volume: " << m_config.m_volume; + << " m_volume: " << m_config.m_volume + << " squelchDB: " << cfg.getSquelchDB() + << " m_squelch: " << m_config.m_squelch; return true; } @@ -306,6 +331,7 @@ void UDPSink::configure(MessageQueue* messageQueue, int udpPort, bool channelMute, Real volume, + Real squelchDB, bool force) { Message* cmd = MsgUDPSinkConfigure::create(sampleFormat, @@ -316,6 +342,7 @@ void UDPSink::configure(MessageQueue* messageQueue, udpPort, channelMute, volume, + squelchDB, force); messageQueue->push(cmd); } @@ -357,6 +384,8 @@ void UDPSink::apply(bool force) m_peakLevel = 0.0f; m_levelSum = 0.0f; m_udpHandler.resizeBuffer(m_config.m_inputSampleRate); + m_inMovingAverage.resize(m_config.m_inputSampleRate * 0.01, 1e-10); // 10 ms + m_squelchThreshold = m_config.m_inputSampleRate * 0.1; // 100 ms m_settingsMutex.unlock(); } diff --git a/plugins/channeltx/udpsink/udpsink.h b/plugins/channeltx/udpsink/udpsink.h index ff057add1..440c49067 100644 --- a/plugins/channeltx/udpsink/udpsink.h +++ b/plugins/channeltx/udpsink/udpsink.h @@ -55,7 +55,9 @@ public: virtual bool handleMessage(const Message& cmd); double getMagSq() const { return m_magsq; } + double getInMagSq() const { return m_inMagsq; } int32_t getBufferGauge() const { return m_udpHandler.getBufferGauge(); } + bool getSquelchOpen() const { return m_squelchOpen; } void configure(MessageQueue* messageQueue, SampleFormat sampleFormat, @@ -66,6 +68,7 @@ public: int udpPort, bool channelMute, Real volume, + Real squelchDB, bool force = false); void setSpectrum(MessageQueue* messageQueue, bool enabled); @@ -91,6 +94,7 @@ private: int getUDPPort() const { return m_udpPort; } bool getChannelMute() const { return m_channelMute; } Real getVolume() const { return m_volume; } + Real getSquelchDB() const { return m_squelchDB; } bool getForce() const { return m_force; } static MsgUDPSinkConfigure* create(SampleFormat @@ -102,6 +106,7 @@ private: int udpPort, bool channelMute, Real volume, + Real squelchDB, bool force) { return new MsgUDPSinkConfigure(sampleFormat, @@ -112,6 +117,7 @@ private: udpPort, channelMute, volume, + squelchDB, force); } @@ -124,6 +130,7 @@ private: int m_udpPort; bool m_channelMute; Real m_volume; + Real m_squelchDB; bool m_force; MsgUDPSinkConfigure(SampleFormat sampleFormat, @@ -134,6 +141,7 @@ private: int udpPort, bool channelMute, Real volume, + Real squelchDB, bool force) : Message(), m_sampleFormat(sampleFormat), @@ -144,6 +152,7 @@ private: m_udpPort(udpPort), m_channelMute(channelMute), m_volume(volume), + m_squelchDB(squelchDB), m_force(force) { } }; @@ -178,6 +187,7 @@ private: int m_fmDeviation; bool m_channelMute; Real m_volume; + Real m_squelch; //!< squared magnitude QString m_udpAddressStr; quint16 m_udpPort; @@ -192,6 +202,7 @@ private: m_fmDeviation(1.0), m_channelMute(false), m_volume(1.0), + m_squelch(-50.0), m_udpAddressStr("127.0.0.1"), m_udpPort(9999) {} @@ -217,7 +228,9 @@ private: bool m_interpolatorConsumed; double m_magsq; + double m_inMagsq; MovingAverage m_movingAverage; + MovingAverage m_inMovingAverage; UDPSinkUDPHandler m_udpHandler; Real m_actualInputSampleRate; //!< sample rate with UDP buffer skew compensation @@ -229,6 +242,11 @@ private: double m_levelSum; int m_levelNbSamples; + bool m_squelchOpen; + int m_squelchOpenCount; + int m_squelchCloseCount; + int m_squelchThreshold; + QMutex m_settingsMutex; static const int m_sampleRateAverageItems = 17; @@ -237,6 +255,44 @@ private: void modulateSample(); void calculateLevel(Real sample); void calculateLevel(Complex sample); + + inline void calculateSquelch(double value) + { + if (value > m_running.m_squelch) + { + if (m_squelchOpenCount < m_squelchThreshold) { + m_squelchOpenCount++; + } else { + m_squelchCloseCount = m_squelchThreshold; + m_squelchOpen = true; + } + } + else + { + if (m_squelchCloseCount > 0) { + m_squelchCloseCount--; + } else { + m_squelchOpenCount = 0; + m_squelchOpen = false; + } + } + } + + inline void initSquelch(bool open) + { + if (open) + { + m_squelchOpen = true; + m_squelchOpenCount = m_squelchThreshold; + m_squelchCloseCount = m_squelchThreshold; + } + else + { + m_squelchOpen = false; + m_squelchOpenCount = 0; + m_squelchCloseCount = 0; + } + } }; diff --git a/plugins/channeltx/udpsink/udpsinkgui.cpp b/plugins/channeltx/udpsink/udpsinkgui.cpp index 77e78dea5..ea5172c94 100644 --- a/plugins/channeltx/udpsink/udpsinkgui.cpp +++ b/plugins/channeltx/udpsink/udpsinkgui.cpp @@ -93,6 +93,7 @@ QByteArray UDPSinkGUI::serialize() const s.writeS32(11, m_fmDeviation); s.writeU32(12, m_channelMarker.getColor().rgb()); s.writeString(13, m_channelMarker.getTitle()); + s.writeS32(14, ui->squelch->value()); return s.final(); } @@ -176,6 +177,10 @@ bool UDPSinkGUI::deserialize(const QByteArray& data) m_channelMarker.setTitle(strtmp); this->setWindowTitle(m_channelMarker.getTitle()); + d.readS32(14, &s32tmp, 10); + ui->squelch->setValue(s32tmp); + ui->squelchText->setText(tr("%1").arg(s32tmp*1.0, 0, 'f', 0)); + blockApplySettings(false); m_channelMarker.blockSignals(false); @@ -213,7 +218,9 @@ UDPSinkGUI::UDPSinkGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* ui(new Ui::UDPSinkGUI), m_pluginAPI(pluginAPI), m_deviceAPI(deviceAPI), - m_channelPowerDbAvg(20,0), + m_channelPowerAvg(20, 1e-10), + m_inPowerAvg(50, 1e-10), + m_powDisplayCount(0), m_channelMarker(this), m_basicSettingsShown(false), m_doApplySettings(true) @@ -389,12 +396,19 @@ void UDPSinkGUI::applySettings(bool force) udpPort, ui->channelMute->isChecked(), ui->volume->value() / 10.0f, + ui->squelch->value() * 1.0f, force); ui->applyBtn->setEnabled(false); } } +void UDPSinkGUI::displaySettings() +{ + ui->volumeText->setText(tr("%1").arg(ui->volume->value()/10.0, 0, 'f', 1)); + ui->squelchText->setText(tr("%1").arg(ui->squelch->value()*1.0, 0, 'f', 0)); +} + void UDPSinkGUI::channelMarkerChanged() { this->setWindowTitle(m_channelMarker.getTitle()); @@ -448,6 +462,12 @@ void UDPSinkGUI::on_volume_valueChanged(int value) applySettings(); } +void UDPSinkGUI::on_squelch_valueChanged(int value) +{ + ui->squelchText->setText(tr("%1").arg(value*1.0, 0, 'f', 0)); + applySettings(); +} + void UDPSinkGUI::on_channelMute_toggled(bool checked __attribute__((unused))) { applySettings(); @@ -492,9 +512,22 @@ void UDPSinkGUI::enterEvent(QEvent*) void UDPSinkGUI::tick() { - double powDb = CalcDb::dbPower(m_udpSink->getMagSq()); - m_channelPowerDbAvg.feed(powDb); - ui->channelPower->setText(tr("%1 dB").arg(m_channelPowerDbAvg.average(), 0, 'f', 1)); + m_channelPowerAvg.feed(m_udpSink->getMagSq()); + m_inPowerAvg.feed(m_udpSink->getInMagSq()); + + if (m_powDisplayCount < 3) + { + m_powDisplayCount++; + } + else + { + double powDb = CalcDb::dbPower(m_channelPowerAvg.average()); + ui->channelPower->setText(tr("%1 dB").arg(powDb, 0, 'f', 1)); + double inPowDb = CalcDb::dbPower(m_inPowerAvg.average()); + ui->inputPower->setText(tr("%1").arg(inPowDb, 0, 'f', 1)); + + m_powDisplayCount = 0; + } int32_t bufferGauge = m_udpSink->getBufferGauge(); ui->bufferGaugeNegative->setValue((bufferGauge < 0 ? -bufferGauge : 0)); @@ -502,5 +535,10 @@ void UDPSinkGUI::tick() QString s = QString::number(bufferGauge, 'f', 0); ui->bufferRWBalanceText->setText(tr("%1").arg(s)); + if (m_udpSink->getSquelchOpen()) { + ui->channelMute->setStyleSheet("QToolButton { background-color : green; }"); + } else { + ui->channelMute->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + } } diff --git a/plugins/channeltx/udpsink/udpsinkgui.h b/plugins/channeltx/udpsink/udpsinkgui.h index 767b60bc4..0f102e254 100644 --- a/plugins/channeltx/udpsink/udpsinkgui.h +++ b/plugins/channeltx/udpsink/udpsinkgui.h @@ -68,6 +68,7 @@ private slots: void onWidgetRolled(QWidget* widget, bool rollDown); void onMenuDoubleClicked(); void on_volume_valueChanged(int value); + void on_squelch_valueChanged(int value); void on_channelMute_toggled(bool checked); void tick(); @@ -79,7 +80,9 @@ private: UpChannelizer* m_channelizer; SpectrumVis* m_spectrumVis; UDPSink* m_udpSink; - MovingAverage m_channelPowerDbAvg; + MovingAverage m_channelPowerAvg; + MovingAverage m_inPowerAvg; + int m_powDisplayCount; ChannelMarker m_channelMarker; // settings @@ -97,6 +100,7 @@ private: void blockApplySettings(bool block); void applySettings(bool force = false); + void displaySettings(); void leaveEvent(QEvent*); void enterEvent(QEvent*); diff --git a/plugins/channeltx/udpsink/udpsinkgui.ui b/plugins/channeltx/udpsink/udpsinkgui.ui index 6bb298ce2..74a57d508 100644 --- a/plugins/channeltx/udpsink/udpsinkgui.ui +++ b/plugins/channeltx/udpsink/udpsinkgui.ui @@ -7,7 +7,7 @@ 0 0 348 - 355 + 400 @@ -71,6 +71,114 @@ 3 + + + + + + + 0 + 0 + + + + + + + + + + + + FMd + + + + + + + FM deviation in Hz (for S16LE NFM format) + + + 00000 + + + 2500 + + + + + + + Sq + + + + + + + + 22 + 22 + + + + Power squelch threshold (dB) + + + -100 + + + 0 + + + 1 + + + -50 + + + + + + + + 22 + 0 + + + + Power squelch threshold (dB) + + + -00 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 40 + 0 + + + + Input power (dB) + + + -100.0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + @@ -299,100 +407,6 @@ - - - - - - FMd (Hz) - - - - - - - FM deviation in Hz (for S16LE NFM format) - - - 00000 - - - 2500 - - - - - - - Sq - - - - - - - - 22 - 22 - - - - Power squelch threshold (dB) - - - - - - - Power squelch threshold (dB) - - - -00 - - - - - - - - - - - Vol - - - - - - - - 22 - 22 - - - - 50 - - - 1 - - - 10 - - - - - - - 0.0 - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - @@ -520,33 +534,6 @@ - - - - false - - - Apply text input and/or samples format - - - Apply - - - - - - - - - - 0 - 0 - - - - - - @@ -650,6 +637,78 @@ + + + + + + Vol + + + + + + + + 22 + 22 + + + + 100 + + + 1 + + + 10 + + + + + + + + 32 + 0 + + + + 10.0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + false + + + Apply text input and/or samples format + + + Apply + + +