1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-26 17:58:43 -05:00

UDP source plugin: implemented squelch gate

This commit is contained in:
f4exb 2017-08-19 00:12:56 +02:00
parent 2c3f7fe690
commit 587d38665d
5 changed files with 345 additions and 211 deletions

View File

@ -30,17 +30,9 @@ MESSAGE_CLASS_DEFINITION(UDPSrc::MsgUDPSrcConfigureImmediate, Message)
MESSAGE_CLASS_DEFINITION(UDPSrc::MsgUDPSrcSpectrum, Message)
UDPSrc::UDPSrc(MessageQueue* uiMessageQueue, UDPSrcGUI* udpSrcGUI, BasebandSampleSink* spectrum) :
m_udpPort(9999),
m_gain(1.0),
m_audioActive(false),
m_audioStereo(false),
m_volume(20),
m_fmDeviation(2500),
m_outMovingAverage(480, 1e-10),
m_inMovingAverage(480, 1e-10),
m_audioFifo(4, 24000),
m_squelch(1e-6),
m_squelchEnabled(true),
m_squelchOpen(false),
m_squelchOpenCount(0),
m_squelchCloseCount(0),
@ -49,22 +41,17 @@ UDPSrc::UDPSrc(MessageQueue* uiMessageQueue, UDPSrcGUI* udpSrcGUI, BasebandSampl
{
setObjectName("UDPSrc");
m_udpBuffer = new UDPSink<Sample>(this, udpBlockSize, m_udpPort);
m_udpBufferMono = new UDPSink<FixReal>(this, udpBlockSize, m_udpPort);
m_udpBuffer = new UDPSink<Sample>(this, udpBlockSize, m_config.m_udpPort);
m_udpBufferMono = new UDPSink<FixReal>(this, udpBlockSize, m_config.m_udpPort);
m_audioSocket = new QUdpSocket(this);
m_udpAudioBuf = new char[m_udpAudioPayloadSize];
m_audioBuffer.resize(1<<9);
m_audioBufferFill = 0;
m_inputSampleRate = 96000;
m_sampleFormat = FormatS16LE;
m_outputSampleRate = 48000;
m_rfBandwidth = 32000;
m_audioPort = m_udpPort - 1;
m_nco.setFreq(0, m_inputSampleRate);
m_interpolator.create(16, m_inputSampleRate, m_rfBandwidth / 2.0);
m_sampleDistanceRemain = m_inputSampleRate / m_outputSampleRate;
m_nco.setFreq(0, m_config.m_inputSampleRate);
m_interpolator.create(16, m_config.m_inputSampleRate, m_config.m_rfBandwidth / 2.0);
m_sampleDistanceRemain = m_config.m_inputSampleRate / m_config.m_outputSampleRate;
m_uiMessageQueue = uiMessageQueue;
m_udpSrcGUI = udpSrcGUI;
m_spectrum = spectrum;
@ -77,13 +64,13 @@ UDPSrc::UDPSrc(MessageQueue* uiMessageQueue, UDPSrcGUI* udpSrcGUI, BasebandSampl
m_scale = 0;
m_magsq = 0;
m_inMagsq = 0;
UDPFilter = new fftfilt(0.0, (m_rfBandwidth / 2.0) / m_outputSampleRate, udpBlockSize);
UDPFilter = new fftfilt(0.0, (m_config.m_rfBandwidth / 2.0) / m_config.m_outputSampleRate, udpBlockSize);
m_phaseDiscri.setFMScaling((float) m_outputSampleRate / (2.0f * m_fmDeviation));
m_phaseDiscri.setFMScaling((float) m_config. m_outputSampleRate / (2.0f * m_config.m_fmDeviation));
if (m_audioSocket->bind(QHostAddress::LocalHost, m_audioPort))
if (m_audioSocket->bind(QHostAddress::LocalHost, m_config.m_audioPort))
{
qDebug("UDPSrc::UDPSrc: bind audio socket to port %d", m_audioPort);
qDebug("UDPSrc::UDPSrc: bind audio socket to port %d", m_config.m_audioPort);
connect(m_audioSocket, SIGNAL(readyRead()), this, SLOT(audioReadyRead()), Qt::QueuedConnection);
}
else
@ -101,9 +88,10 @@ UDPSrc::~UDPSrc()
delete m_udpBufferMono;
delete[] m_udpAudioBuf;
if (UDPFilter) delete UDPFilter;
if (m_audioActive) DSPEngine::instance()->removeAudioSink(&m_audioFifo);
if (m_running.m_audioActive) DSPEngine::instance()->removeAudioSink(&m_audioFifo);
}
/** what needs the "apply" button validation */
void UDPSrc::configure(MessageQueue* messageQueue,
SampleFormat sampleFormat,
Real outputSampleRate,
@ -111,7 +99,8 @@ void UDPSrc::configure(MessageQueue* messageQueue,
int fmDeviation,
QString& udpAddress,
int udpPort,
int audioPort)
int audioPort,
bool force)
{
Message* cmd = MsgUDPSrcConfigure::create(sampleFormat,
outputSampleRate,
@ -119,17 +108,21 @@ void UDPSrc::configure(MessageQueue* messageQueue,
fmDeviation,
udpAddress,
udpPort,
audioPort);
audioPort,
force);
messageQueue->push(cmd);
}
/** changes applied immediately */
void UDPSrc::configureImmediate(MessageQueue* messageQueue,
bool audioActive,
bool audioStereo,
Real boost,
int volume,
Real squelchDB,
bool squelchEnabled)
Real squelchGate,
bool squelchEnabled,
bool force)
{
Message* cmd = MsgUDPSrcConfigureImmediate::create(
audioActive,
@ -137,7 +130,9 @@ void UDPSrc::configureImmediate(MessageQueue* messageQueue,
boost,
volume,
squelchDB,
squelchEnabled);
squelchGate,
squelchEnabled,
force);
messageQueue->push(cmd);
}
@ -171,11 +166,11 @@ void UDPSrc::feed(const SampleVector::const_iterator& begin, const SampleVector:
Sample ss(ci.real(), ci.imag());
m_sampleBuffer.push_back(ss);
m_sampleDistanceRemain += m_inputSampleRate / m_outputSampleRate;
m_sampleDistanceRemain += m_running.m_inputSampleRate / m_running.m_outputSampleRate;
calculateSquelch(m_inMagsq);
if (m_sampleFormat == FormatLSB) // binaural LSB
if (m_running.m_sampleFormat == FormatLSB) // binaural LSB
{
int n_out = UDPFilter->runSSB(ci, &sideband, false);
@ -183,14 +178,14 @@ void UDPSrc::feed(const SampleVector::const_iterator& begin, const SampleVector:
{
for (int i = 0; i < n_out; i++)
{
l = m_squelchOpen ? sideband[i].real() * m_gain : 0;
r = m_squelchOpen ? sideband[i].imag() * m_gain : 0;
l = m_squelchOpen ? sideband[i].real() * m_running.m_gain : 0;
r = m_squelchOpen ? sideband[i].imag() * m_running.m_gain : 0;
m_udpBuffer->write(Sample(l, r));
m_outMovingAverage.feed((l*l + r*r) / (1<<30));
}
}
}
if (m_sampleFormat == FormatUSB) // binaural USB
if (m_running.m_sampleFormat == FormatUSB) // binaural USB
{
int n_out = UDPFilter->runSSB(ci, &sideband, true);
@ -198,26 +193,26 @@ void UDPSrc::feed(const SampleVector::const_iterator& begin, const SampleVector:
{
for (int i = 0; i < n_out; i++)
{
l = m_squelchOpen ? sideband[i].real() * m_gain : 0;
r = m_squelchOpen ? sideband[i].imag() * m_gain : 0;
l = m_squelchOpen ? sideband[i].real() * m_running.m_gain : 0;
r = m_squelchOpen ? sideband[i].imag() * m_running.m_gain : 0;
m_udpBuffer->write(Sample(l, r));
m_outMovingAverage.feed((l*l + r*r) / (1<<30));
}
}
}
else if (m_sampleFormat == FormatNFM)
else if (m_running.m_sampleFormat == FormatNFM)
{
double demod = m_squelchOpen ? 32768.0 * m_phaseDiscri.phaseDiscriminator(ci) * m_gain : 0;
double demod = m_squelchOpen ? 32768.0 * m_phaseDiscri.phaseDiscriminator(ci) * m_running.m_gain : 0;
m_udpBuffer->write(Sample(demod, demod));
m_outMovingAverage.feed((demod * demod) / (1<<30));
}
else if (m_sampleFormat == FormatNFMMono)
else if (m_running.m_sampleFormat == FormatNFMMono)
{
FixReal demod = m_squelchOpen ? (FixReal) (32768.0f * m_phaseDiscri.phaseDiscriminator(ci) * m_gain) : 0;
FixReal demod = m_squelchOpen ? (FixReal) (32768.0f * m_phaseDiscri.phaseDiscriminator(ci) * m_running.m_gain) : 0;
m_udpBufferMono->write(demod);
m_outMovingAverage.feed((demod * demod) / 1073741824.0);
}
else if (m_sampleFormat == FormatLSBMono) // Monaural LSB
else if (m_running.m_sampleFormat == FormatLSBMono) // Monaural LSB
{
int n_out = UDPFilter->runSSB(ci, &sideband, false);
@ -225,13 +220,13 @@ void UDPSrc::feed(const SampleVector::const_iterator& begin, const SampleVector:
{
for (int i = 0; i < n_out; i++)
{
l = m_squelchOpen ? (sideband[i].real() + sideband[i].imag()) * 0.7 * m_gain : 0;
l = m_squelchOpen ? (sideband[i].real() + sideband[i].imag()) * 0.7 * m_running.m_gain : 0;
m_udpBufferMono->write(l);
m_outMovingAverage.feed((l * l) / (1<<30));
}
}
}
else if (m_sampleFormat == FormatUSBMono) // Monaural USB
else if (m_running.m_sampleFormat == FormatUSBMono) // Monaural USB
{
int n_out = UDPFilter->runSSB(ci, &sideband, true);
@ -239,15 +234,15 @@ void UDPSrc::feed(const SampleVector::const_iterator& begin, const SampleVector:
{
for (int i = 0; i < n_out; i++)
{
l = m_squelchOpen ? (sideband[i].real() + sideband[i].imag()) * 0.7 * m_gain : 0;
l = m_squelchOpen ? (sideband[i].real() + sideband[i].imag()) * 0.7 * m_running.m_gain : 0;
m_udpBufferMono->write(l);
m_outMovingAverage.feed((l * l) / (1<<30));
}
}
}
else if (m_sampleFormat == FormatAMMono)
else if (m_running.m_sampleFormat == FormatAMMono)
{
FixReal demod = m_squelchOpen ? (FixReal) (sqrt(inMagSq) * m_gain) : 0;
FixReal demod = m_squelchOpen ? (FixReal) (sqrt(inMagSq) * m_running.m_gain) : 0;
m_udpBufferMono->write(demod);
m_outMovingAverage.feed((demod * demod) / (1<<30));
}
@ -255,9 +250,9 @@ void UDPSrc::feed(const SampleVector::const_iterator& begin, const SampleVector:
{
if (m_squelchOpen)
{
Sample s(ci.real() * m_gain, ci.imag() * m_gain);
Sample s(ci.real() * m_running.m_gain, ci.imag() * m_running.m_gain);
m_udpBuffer->write(s);
m_outMovingAverage.feed((inMagSq*m_gain*m_gain) / (1<<30));
m_outMovingAverage.feed((inMagSq*m_running.m_gain*m_running.m_gain) / (1<<30));
}
else
{
@ -298,68 +293,38 @@ bool UDPSrc::handleMessage(const Message& cmd)
{
DownChannelizer::MsgChannelizerNotification& notif = (DownChannelizer::MsgChannelizerNotification&) cmd;
m_settingsMutex.lock();
m_config.m_inputSampleRate = notif.getSampleRate();
m_config.m_inputFrequencyOffset = notif.getFrequencyOffset();
m_inputSampleRate = notif.getSampleRate();
m_nco.setFreq(-notif.getFrequencyOffset(), m_inputSampleRate);
m_interpolator.create(16, m_inputSampleRate, m_rfBandwidth / 2.0);
m_sampleDistanceRemain = m_inputSampleRate / m_outputSampleRate;
apply(false);
m_settingsMutex.unlock();
qDebug() << "UDPSrc::handleMessage: MsgChannelizerNotification: m_inputSampleRate: " << m_config.m_inputSampleRate
<< " frequencyOffset: " << notif.getFrequencyOffset();
qDebug() << "UDPSrc::handleMessage: MsgChannelizerNotification: m_inputSampleRate: " << m_inputSampleRate
<< " frequencyOffset: " << notif.getFrequencyOffset();
return true;
return true;
}
else if (MsgUDPSrcConfigureImmediate::match(cmd))
{
MsgUDPSrcConfigureImmediate& cfg = (MsgUDPSrcConfigureImmediate&) cmd;
m_settingsMutex.lock();
m_config.m_audioActive = cfg.getAudioActive();
m_config.m_audioStereo = cfg.getAudioStereo();
m_config.m_gain = cfg.getGain();
m_config.m_volume = cfg.getVolume();
m_config.m_squelch = CalcDb::powerFromdB(cfg.getSquelchDB());
m_config.m_squelchGate = cfg.getSquelchGate();
m_config.m_squelchEnabled = cfg.getSquelchEnabled();
if (cfg.getAudioActive() != m_audioActive)
{
m_audioActive = cfg.getAudioActive();
if (m_audioActive)
{
m_audioBufferFill = 0;
DSPEngine::instance()->addAudioSink(&m_audioFifo);
}
else
{
DSPEngine::instance()->removeAudioSink(&m_audioFifo);
}
}
if (cfg.getAudioStereo() != m_audioStereo)
{
m_audioStereo = cfg.getAudioStereo();
}
if (cfg.getGain() != m_gain)
{
m_gain = cfg.getGain();
}
if (cfg.getVolume() != m_volume)
{
m_volume = cfg.getVolume();
}
m_squelch = CalcDb::powerFromdB(cfg.getSquelchDB());
m_squelchEnabled = cfg.getSquelchEnabled();
m_settingsMutex.unlock();
apply(cfg.getForce());
qDebug() << "UDPSrc::handleMessage: MsgUDPSrcConfigureImmediate: "
<< " m_audioActive: " << m_audioActive
<< " m_audioStereo: " << m_audioStereo
<< " m_gain: " << m_gain
<< " m_squelchEnabled: " << m_squelchEnabled
<< " m_squelch: " << m_squelch
<< " getSquelchDB: " << cfg.getSquelchDB();
<< " m_audioActive: " << m_config.m_audioActive
<< " m_audioStereo: " << m_config.m_audioStereo
<< " m_gain: " << m_config.m_gain
<< " m_squelchEnabled: " << m_config.m_squelchEnabled
<< " m_squelch: " << m_config.m_squelch
<< " getSquelchDB: " << cfg.getSquelchDB()
<< " m_squelchGate" << m_config.m_squelchGate;
return true;
@ -368,67 +333,22 @@ bool UDPSrc::handleMessage(const Message& cmd)
{
MsgUDPSrcConfigure& cfg = (MsgUDPSrcConfigure&) cmd;
m_settingsMutex.lock();
m_config.m_sampleFormat = cfg.getSampleFormat();
m_config.m_outputSampleRate = cfg.getOutputSampleRate();
m_config.m_rfBandwidth = cfg.getRFBandwidth();
m_config.m_udpAddressStr = cfg.getUDPAddress();
m_config.m_udpPort = cfg.getUDPPort();
m_config.m_audioPort = cfg.getAudioPort();
m_config.m_fmDeviation = cfg.getFMDeviation();
m_sampleFormat = cfg.getSampleFormat();
m_outputSampleRate = cfg.getOutputSampleRate();
m_rfBandwidth = cfg.getRFBandwidth();
apply(cfg.getForce());
if (cfg.getUDPAddress() != m_udpAddressStr)
{
m_udpAddressStr = cfg.getUDPAddress();
m_udpBuffer->setAddress(m_udpAddressStr);
m_udpBufferMono->setAddress(m_udpAddressStr);
}
if (cfg.getUDPPort() != m_udpPort)
{
m_udpPort = cfg.getUDPPort();
m_udpBuffer->setPort(m_udpPort);
m_udpBufferMono->setPort(m_udpPort);
}
if (cfg.getAudioPort() != m_audioPort)
{
m_audioPort = cfg.getAudioPort();
disconnect(m_audioSocket, SIGNAL(readyRead()), this, SLOT(audioReadyRead()));
delete m_audioSocket;
m_audioSocket = new QUdpSocket(this);
if (m_audioSocket->bind(QHostAddress::LocalHost, m_audioPort))
{
connect(m_audioSocket, SIGNAL(readyRead()), this, SLOT(audioReadyRead()), Qt::QueuedConnection);
qDebug("UDPSrc::handleMessage: audio socket bound to port %d", m_audioPort);
}
else
{
qWarning("UDPSrc::handleMessage: cannot bind audio socket");
}
}
if (cfg.getFMDeviation() != m_fmDeviation)
{
m_fmDeviation = cfg.getFMDeviation();
m_phaseDiscri.setFMScaling((float) m_outputSampleRate / (2.0f * m_fmDeviation));
}
m_interpolator.create(16, m_inputSampleRate, m_rfBandwidth / 2.0);
m_sampleDistanceRemain = m_inputSampleRate / m_outputSampleRate;
UDPFilter->create_filter(0.0, (m_rfBandwidth / 2.0) / m_outputSampleRate);
m_inMovingAverage.resize(m_outputSampleRate * 0.01, 1e-10); // 10 ms
m_outMovingAverage.resize(m_outputSampleRate * 0.01, 1e-10); // 10 ms
m_squelchThreshold = m_outputSampleRate * 0.05; // 50 ms
m_settingsMutex.unlock();
qDebug() << "UDPSrc::handleMessage: MsgUDPSrcConfigure: m_sampleFormat: " << m_sampleFormat
<< " m_outputSampleRate: " << m_outputSampleRate
<< " m_rfBandwidth: " << m_rfBandwidth
<< " m_udpAddressStr: " << m_udpAddressStr
<< " m_udpPort: " << m_udpPort
<< " m_audioPort: " << m_audioPort;
qDebug() << "UDPSrc::handleMessage: MsgUDPSrcConfigure: m_sampleFormat: " << m_config.m_sampleFormat
<< " m_outputSampleRate: " << m_config.m_outputSampleRate
<< " m_rfBandwidth: " << m_config.m_rfBandwidth
<< " m_udpAddressStr: " << m_config.m_udpAddressStr
<< " m_udpPort: " << m_config.m_udpPort
<< " m_audioPort: " << m_config.m_audioPort;
return true;
}
@ -455,6 +375,83 @@ bool UDPSrc::handleMessage(const Message& cmd)
}
}
void UDPSrc::apply(bool force)
{
m_settingsMutex.lock();
if ((m_config.m_inputSampleRate != m_running.m_inputSampleRate) ||
(m_config.m_inputFrequencyOffset != m_running.m_inputFrequencyOffset) ||
(m_config.m_rfBandwidth != m_running.m_rfBandwidth) ||
(m_config.m_outputSampleRate != m_running.m_outputSampleRate) || force)
{
m_nco.setFreq(-m_config.m_inputFrequencyOffset, m_config.m_inputSampleRate);
m_interpolator.create(16, m_config.m_inputSampleRate, m_config.m_rfBandwidth / 2.0);
m_sampleDistanceRemain = m_config.m_inputSampleRate / m_config.m_outputSampleRate;
m_squelchThreshold = m_config.m_outputSampleRate * m_config.m_squelchGate;
initSquelch(m_squelchOpen);
m_inMovingAverage.resize(m_config.m_outputSampleRate * 0.01, 1e-10); // 10 ms
m_outMovingAverage.resize(m_config.m_outputSampleRate * 0.01, 1e-10); // 10 ms
}
if ((m_config.m_audioActive != m_config.m_audioActive) || force)
{
if (m_config.m_audioActive)
{
m_audioBufferFill = 0;
DSPEngine::instance()->addAudioSink(&m_audioFifo);
}
else
{
DSPEngine::instance()->removeAudioSink(&m_audioFifo);
}
}
if ((m_config.m_squelchGate != m_running.m_squelchGate) || force)
{
m_squelchThreshold = m_config.m_outputSampleRate * m_config.m_squelchGate;
}
if ((m_config.m_udpAddressStr != m_running.m_udpAddressStr) || force)
{
m_udpBuffer->setAddress(m_config.m_udpAddressStr);
m_udpBufferMono->setAddress(m_config.m_udpAddressStr);
}
if ((m_config.m_udpPort != m_running.m_udpPort) || force)
{
m_udpBuffer->setPort(m_config.m_udpPort);
m_udpBufferMono->setPort(m_config.m_udpPort);
}
if ((m_config.m_audioPort != m_running.m_audioPort) || force)
{
disconnect(m_audioSocket, SIGNAL(readyRead()), this, SLOT(audioReadyRead()));
delete m_audioSocket;
m_audioSocket = new QUdpSocket(this);
if (m_audioSocket->bind(QHostAddress::LocalHost, m_config.m_audioPort))
{
connect(m_audioSocket, SIGNAL(readyRead()), this, SLOT(audioReadyRead()), Qt::QueuedConnection);
qDebug("UDPSrc::handleMessage: audio socket bound to port %d", m_config.m_audioPort);
}
else
{
qWarning("UDPSrc::handleMessage: cannot bind audio socket");
}
}
if ((m_config.m_fmDeviation != m_running.m_fmDeviation) || force)
{
m_phaseDiscri.setFMScaling((float) m_config.m_outputSampleRate / (2.0f * m_config.m_fmDeviation));
}
m_settingsMutex.unlock();
m_running = m_config;
}
void UDPSrc::audioReadyRead()
{
while (m_audioSocket->hasPendingDatagrams())
@ -463,16 +460,16 @@ void UDPSrc::audioReadyRead()
qint64 udpReadBytes = m_audioSocket->readDatagram(m_udpAudioBuf, pendingDataSize, 0, 0);
//qDebug("UDPSrc::audioReadyRead: %lld", udpReadBytes);
if (m_audioActive)
if (m_running.m_audioActive)
{
if (m_audioStereo)
if (m_running.m_audioStereo)
{
for (int i = 0; i < udpReadBytes - 3; i += 4)
{
qint16 l_sample = (qint16) *(&m_udpAudioBuf[i]);
qint16 r_sample = (qint16) *(&m_udpAudioBuf[i+2]);
m_audioBuffer[m_audioBufferFill].l = l_sample * m_volume;
m_audioBuffer[m_audioBufferFill].r = r_sample * m_volume;
m_audioBuffer[m_audioBufferFill].l = l_sample * m_running.m_volume;
m_audioBuffer[m_audioBufferFill].r = r_sample * m_running.m_volume;
++m_audioBufferFill;
if (m_audioBufferFill >= m_audioBuffer.size())
@ -493,8 +490,8 @@ void UDPSrc::audioReadyRead()
for (int i = 0; i < udpReadBytes - 1; i += 2)
{
qint16 sample = (qint16) *(&m_udpAudioBuf[i]);
m_audioBuffer[m_audioBufferFill].l = sample * m_volume;
m_audioBuffer[m_audioBufferFill].r = sample * m_volume;
m_audioBuffer[m_audioBufferFill].l = sample * m_running.m_volume;
m_audioBuffer[m_audioBufferFill].r = sample * m_running.m_volume;
++m_audioBufferFill;
if (m_audioBufferFill >= m_audioBuffer.size())

View File

@ -67,14 +67,17 @@ public:
int fmDeviation,
QString& udpAddress,
int udpPort,
int audioPort);
int audioPort,
bool force);
void configureImmediate(MessageQueue* messageQueue,
bool audioActive,
bool audioStereo,
Real gain,
int volume,
Real squelchDB,
bool squelchEnabled);
Real squelchGate,
bool squelchEnabled,
bool force);
void setSpectrum(MessageQueue* messageQueue, bool enabled);
double getMagSq() const { return m_magsq; }
double getInMagSq() const { return m_inMagsq; }
@ -102,6 +105,7 @@ protected:
const QString& getUDPAddress() const { return m_udpAddress; }
int getUDPPort() const { return m_udpPort; }
int getAudioPort() const { return m_audioPort; }
bool getForce() const { return m_force; }
static MsgUDPSrcConfigure* create(SampleFormat
sampleFormat,
@ -110,7 +114,8 @@ protected:
int fmDeviation,
QString& udpAddress,
int udpPort,
int audioPort)
int audioPort,
bool force)
{
return new MsgUDPSrcConfigure(sampleFormat,
sampleRate,
@ -118,7 +123,8 @@ protected:
fmDeviation,
udpAddress,
udpPort,
audioPort);
audioPort,
force);
}
private:
@ -129,6 +135,7 @@ protected:
QString m_udpAddress;
int m_udpPort;
int m_audioPort;
bool m_force;
MsgUDPSrcConfigure(SampleFormat sampleFormat,
Real outputSampleRate,
@ -136,7 +143,8 @@ protected:
int fmDeviation,
QString& udpAddress,
int udpPort,
int audioPort) :
int audioPort,
bool force) :
Message(),
m_sampleFormat(sampleFormat),
m_outputSampleRate(outputSampleRate),
@ -144,7 +152,8 @@ protected:
m_fmDeviation(fmDeviation),
m_udpAddress(udpAddress),
m_udpPort(udpPort),
m_audioPort(audioPort)
m_audioPort(audioPort),
m_force(force)
{ }
};
@ -157,7 +166,9 @@ protected:
bool getAudioActive() const { return m_audioActive; }
bool getAudioStereo() const { return m_audioStereo; }
Real getSquelchDB() const { return m_squelchDB; }
Real getSquelchGate() const { return m_squelchGate; }
bool getSquelchEnabled() const { return m_squelchEnabled; }
bool getForce() const { return m_force; }
static MsgUDPSrcConfigureImmediate* create(
bool audioActive,
@ -165,7 +176,9 @@ protected:
int gain,
int volume,
Real squelchDB,
bool squelchEnabled)
Real squelchGate,
bool squelchEnabled,
bool force)
{
return new MsgUDPSrcConfigureImmediate(
audioActive,
@ -173,7 +186,9 @@ protected:
gain,
volume,
squelchDB,
squelchEnabled);
squelchGate,
squelchEnabled,
force);
}
private:
@ -182,7 +197,9 @@ protected:
bool m_audioActive;
bool m_audioStereo;
Real m_squelchDB;
Real m_squelchGate; // seconds
bool m_squelchEnabled;
bool m_force;
MsgUDPSrcConfigureImmediate(
bool audioActive,
@ -190,14 +207,18 @@ protected:
Real gain,
int volume,
Real squelchDB,
bool squelchEnabled) :
Real squelchGate,
bool squelchEnabled,
bool force) :
Message(),
m_gain(gain),
m_volume(volume),
m_audioActive(audioActive),
m_audioStereo(audioStereo),
m_squelchDB(squelchDB),
m_squelchEnabled(squelchEnabled)
m_squelchGate(squelchGate),
m_squelchEnabled(squelchEnabled),
m_force(force)
{ }
};
@ -221,23 +242,54 @@ protected:
{ }
};
struct Config {
Real m_outputSampleRate;
SampleFormat m_sampleFormat;
Real m_inputSampleRate;
qint64 m_inputFrequencyOffset;
Real m_rfBandwidth;
int m_fmDeviation;
bool m_channelMute;
Real m_gain;
Real m_squelch; //!< squared magnitude
Real m_squelchGate; //!< seconds
bool m_squelchEnabled;
bool m_audioActive;
bool m_audioStereo;
int m_volume;
QString m_udpAddressStr;
quint16 m_udpPort;
quint16 m_audioPort;
Config() :
m_outputSampleRate(48000),
m_sampleFormat(FormatS16LE),
m_inputSampleRate(48000),
m_inputFrequencyOffset(0),
m_rfBandwidth(12500),
m_fmDeviation(2500),
m_channelMute(false),
m_gain(1.0),
m_squelch(1e-6),
m_squelchGate(0.0),
m_squelchEnabled(true),
m_audioActive(false),
m_audioStereo(false),
m_volume(20),
m_udpAddressStr("127.0.0.1"),
m_udpPort(9999),
m_audioPort(9998)
{}
};
Config m_config;
Config m_running;
MessageQueue* m_uiMessageQueue;
UDPSrcGUI* m_udpSrcGUI;
QUdpSocket *m_audioSocket;
int m_inputSampleRate;
int m_sampleFormat;
Real m_outputSampleRate;
Real m_rfBandwidth;
QString m_udpAddressStr;
quint16 m_udpPort;
quint16 m_audioPort;
Real m_gain;
bool m_audioActive;
bool m_audioStereo;
int m_volume;
int m_fmDeviation;
double m_magsq;
double m_inMagsq;
MovingAverage<double> m_outMovingAverage;
@ -270,34 +322,54 @@ protected:
PhaseDiscriminators m_phaseDiscri;
Real m_squelch; //!< squared magnitude
bool m_squelchEnabled;
bool m_squelchOpen;
int m_squelchOpenCount;
int m_squelchCloseCount;
int m_squelchThreshold;
int m_squelchThreshold; //!< number of samples computed from given gate
QMutex m_settingsMutex;
void apply(bool force);
inline void calculateSquelch(double value)
{
if ((!m_squelchEnabled) || (value > m_squelch))
if ((!m_running.m_squelchEnabled) || (value > m_running.m_squelch))
{
if (m_squelchOpenCount < m_squelchThreshold) {
m_squelchOpenCount++;
} else {
m_squelchCloseCount = m_squelchThreshold;
if (m_squelchThreshold == 0)
{
m_squelchOpen = true;
}
else
{
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;
if (m_squelchThreshold == 0)
{
m_squelchOpen = false;
}
else
{
if (m_squelchCloseCount > 0)
{
m_squelchCloseCount--;
}
else
{
m_squelchOpenCount = 0;
m_squelchOpen = false;
}
}
}
}

View File

@ -197,8 +197,8 @@ bool UDPSrcGUI::deserialize(const QByteArray& data)
blockApplySettings(false);
m_channelMarker.blockSignals(false);
applySettingsImmediate();
applySettings();
applySettingsImmediate(true);
applySettings(true);
return true;
}
else
@ -297,8 +297,8 @@ UDPSrcGUI::UDPSrcGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget*
ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum);
displaySettings();
applySettingsImmediate();
applySettings();
applySettingsImmediate(true);
applySettings(true);
}
UDPSrcGUI::~UDPSrcGUI()
@ -323,9 +323,10 @@ void UDPSrcGUI::displaySettings()
ui->gainText->setText(tr("%1").arg(ui->gain->value()/10.0, 0, 'f', 1));
ui->volumeText->setText(QString("%1").arg(ui->volume->value()));
ui->squelchText->setText(tr("%1").arg(ui->squelch->value()*1.0, 0, 'f', 0));
ui->squelchGateText->setText(tr("%1").arg(ui->squelchGate->value()*10.0, 0, 'f', 0));
}
void UDPSrcGUI::applySettingsImmediate()
void UDPSrcGUI::applySettingsImmediate(bool force)
{
if (m_doApplySettings)
{
@ -340,12 +341,14 @@ void UDPSrcGUI::applySettingsImmediate()
m_gain,
m_volume,
ui->squelch->value() * 1.0f,
ui->squelch->value() != -100);
ui->squelchGate->value() * 0.01f,
ui->squelch->value() != -100,
force);
}
}
void UDPSrcGUI::applySettings()
void UDPSrcGUI::applySettings(bool force)
{
if (m_doApplySettings)
{
@ -461,7 +464,8 @@ void UDPSrcGUI::applySettings()
fmDeviation,
m_udpAddress,
udpPort,
audioPort);
audioPort,
force);
ui->applyBtn->setEnabled(false);
}
@ -554,6 +558,12 @@ void UDPSrcGUI::on_squelch_valueChanged(int value)
applySettingsImmediate();
}
void UDPSrcGUI::on_squelchGate_valueChanged(int value)
{
ui->squelchGateText->setText(tr("%1").arg(value*10.0, 0, 'f', 0));
applySettingsImmediate();
}
void UDPSrcGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
if ((widget == ui->spectrumBox) && (m_udpSrc != 0))

View File

@ -75,6 +75,7 @@ private slots:
void on_gain_valueChanged(int value);
void on_volume_valueChanged(int value);
void on_squelch_valueChanged(int value);
void on_squelchGate_valueChanged(int value);
void tick();
private:
@ -111,8 +112,8 @@ private:
virtual ~UDPSrcGUI();
void blockApplySettings(bool block);
void applySettings();
void applySettingsImmediate();
void applySettings(bool force = false);
void applySettingsImmediate(bool force = false);
void displaySettings();
void leaveEvent(QEvent*);

View File

@ -236,6 +236,38 @@
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="inputPower">
<property name="minimumSize">
<size>
<width>40</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Input power (dB) to which squelch applies</string>
</property>
<property name="text">
<string>-100.0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item row="6" column="0">
@ -637,18 +669,40 @@
</widget>
</item>
<item>
<widget class="QLabel" name="inputPower">
<widget class="QDial" name="squelchGate">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="toolTip">
<string>Squelch gate (ms)</string>
</property>
<property name="maximum">
<number>50</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>5</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="squelchGateText">
<property name="minimumSize">
<size>
<width>40</width>
<width>24</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Input power (dB) to which squelch applies</string>
<string>Squelch gate (ms)</string>
</property>
<property name="text">
<string>-100.0</string>
<string>000</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>