1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2025-05-29 05:22:25 -04: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) MESSAGE_CLASS_DEFINITION(UDPSrc::MsgUDPSrcSpectrum, Message)
UDPSrc::UDPSrc(MessageQueue* uiMessageQueue, UDPSrcGUI* udpSrcGUI, BasebandSampleSink* spectrum) : 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_outMovingAverage(480, 1e-10),
m_inMovingAverage(480, 1e-10), m_inMovingAverage(480, 1e-10),
m_audioFifo(4, 24000), m_audioFifo(4, 24000),
m_squelch(1e-6),
m_squelchEnabled(true),
m_squelchOpen(false), m_squelchOpen(false),
m_squelchOpenCount(0), m_squelchOpenCount(0),
m_squelchCloseCount(0), m_squelchCloseCount(0),
@ -49,22 +41,17 @@ UDPSrc::UDPSrc(MessageQueue* uiMessageQueue, UDPSrcGUI* udpSrcGUI, BasebandSampl
{ {
setObjectName("UDPSrc"); setObjectName("UDPSrc");
m_udpBuffer = new UDPSink<Sample>(this, udpBlockSize, m_udpPort); m_udpBuffer = new UDPSink<Sample>(this, udpBlockSize, m_config.m_udpPort);
m_udpBufferMono = new UDPSink<FixReal>(this, udpBlockSize, m_udpPort); m_udpBufferMono = new UDPSink<FixReal>(this, udpBlockSize, m_config.m_udpPort);
m_audioSocket = new QUdpSocket(this); m_audioSocket = new QUdpSocket(this);
m_udpAudioBuf = new char[m_udpAudioPayloadSize]; m_udpAudioBuf = new char[m_udpAudioPayloadSize];
m_audioBuffer.resize(1<<9); m_audioBuffer.resize(1<<9);
m_audioBufferFill = 0; m_audioBufferFill = 0;
m_inputSampleRate = 96000; m_nco.setFreq(0, m_config.m_inputSampleRate);
m_sampleFormat = FormatS16LE; m_interpolator.create(16, m_config.m_inputSampleRate, m_config.m_rfBandwidth / 2.0);
m_outputSampleRate = 48000; m_sampleDistanceRemain = m_config.m_inputSampleRate / m_config.m_outputSampleRate;
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_uiMessageQueue = uiMessageQueue; m_uiMessageQueue = uiMessageQueue;
m_udpSrcGUI = udpSrcGUI; m_udpSrcGUI = udpSrcGUI;
m_spectrum = spectrum; m_spectrum = spectrum;
@ -77,13 +64,13 @@ UDPSrc::UDPSrc(MessageQueue* uiMessageQueue, UDPSrcGUI* udpSrcGUI, BasebandSampl
m_scale = 0; m_scale = 0;
m_magsq = 0; m_magsq = 0;
m_inMagsq = 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); connect(m_audioSocket, SIGNAL(readyRead()), this, SLOT(audioReadyRead()), Qt::QueuedConnection);
} }
else else
@ -101,9 +88,10 @@ UDPSrc::~UDPSrc()
delete m_udpBufferMono; delete m_udpBufferMono;
delete[] m_udpAudioBuf; delete[] m_udpAudioBuf;
if (UDPFilter) delete UDPFilter; 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, void UDPSrc::configure(MessageQueue* messageQueue,
SampleFormat sampleFormat, SampleFormat sampleFormat,
Real outputSampleRate, Real outputSampleRate,
@ -111,7 +99,8 @@ void UDPSrc::configure(MessageQueue* messageQueue,
int fmDeviation, int fmDeviation,
QString& udpAddress, QString& udpAddress,
int udpPort, int udpPort,
int audioPort) int audioPort,
bool force)
{ {
Message* cmd = MsgUDPSrcConfigure::create(sampleFormat, Message* cmd = MsgUDPSrcConfigure::create(sampleFormat,
outputSampleRate, outputSampleRate,
@ -119,17 +108,21 @@ void UDPSrc::configure(MessageQueue* messageQueue,
fmDeviation, fmDeviation,
udpAddress, udpAddress,
udpPort, udpPort,
audioPort); audioPort,
force);
messageQueue->push(cmd); messageQueue->push(cmd);
} }
/** changes applied immediately */
void UDPSrc::configureImmediate(MessageQueue* messageQueue, void UDPSrc::configureImmediate(MessageQueue* messageQueue,
bool audioActive, bool audioActive,
bool audioStereo, bool audioStereo,
Real boost, Real boost,
int volume, int volume,
Real squelchDB, Real squelchDB,
bool squelchEnabled) Real squelchGate,
bool squelchEnabled,
bool force)
{ {
Message* cmd = MsgUDPSrcConfigureImmediate::create( Message* cmd = MsgUDPSrcConfigureImmediate::create(
audioActive, audioActive,
@ -137,7 +130,9 @@ void UDPSrc::configureImmediate(MessageQueue* messageQueue,
boost, boost,
volume, volume,
squelchDB, squelchDB,
squelchEnabled); squelchGate,
squelchEnabled,
force);
messageQueue->push(cmd); messageQueue->push(cmd);
} }
@ -171,11 +166,11 @@ void UDPSrc::feed(const SampleVector::const_iterator& begin, const SampleVector:
Sample ss(ci.real(), ci.imag()); Sample ss(ci.real(), ci.imag());
m_sampleBuffer.push_back(ss); m_sampleBuffer.push_back(ss);
m_sampleDistanceRemain += m_inputSampleRate / m_outputSampleRate; m_sampleDistanceRemain += m_running.m_inputSampleRate / m_running.m_outputSampleRate;
calculateSquelch(m_inMagsq); 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); 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++) for (int i = 0; i < n_out; i++)
{ {
l = m_squelchOpen ? sideband[i].real() * m_gain : 0; l = m_squelchOpen ? sideband[i].real() * m_running.m_gain : 0;
r = m_squelchOpen ? sideband[i].imag() * m_gain : 0; r = m_squelchOpen ? sideband[i].imag() * m_running.m_gain : 0;
m_udpBuffer->write(Sample(l, r)); m_udpBuffer->write(Sample(l, r));
m_outMovingAverage.feed((l*l + r*r) / (1<<30)); 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); 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++) for (int i = 0; i < n_out; i++)
{ {
l = m_squelchOpen ? sideband[i].real() * m_gain : 0; l = m_squelchOpen ? sideband[i].real() * m_running.m_gain : 0;
r = m_squelchOpen ? sideband[i].imag() * m_gain : 0; r = m_squelchOpen ? sideband[i].imag() * m_running.m_gain : 0;
m_udpBuffer->write(Sample(l, r)); m_udpBuffer->write(Sample(l, r));
m_outMovingAverage.feed((l*l + r*r) / (1<<30)); 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_udpBuffer->write(Sample(demod, demod));
m_outMovingAverage.feed((demod * demod) / (1<<30)); 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_udpBufferMono->write(demod);
m_outMovingAverage.feed((demod * demod) / 1073741824.0); 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); 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++) 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_udpBufferMono->write(l);
m_outMovingAverage.feed((l * l) / (1<<30)); 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); 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++) 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_udpBufferMono->write(l);
m_outMovingAverage.feed((l * l) / (1<<30)); 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_udpBufferMono->write(demod);
m_outMovingAverage.feed((demod * demod) / (1<<30)); m_outMovingAverage.feed((demod * demod) / (1<<30));
} }
@ -255,9 +250,9 @@ void UDPSrc::feed(const SampleVector::const_iterator& begin, const SampleVector:
{ {
if (m_squelchOpen) 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_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 else
{ {
@ -298,68 +293,38 @@ bool UDPSrc::handleMessage(const Message& cmd)
{ {
DownChannelizer::MsgChannelizerNotification& notif = (DownChannelizer::MsgChannelizerNotification&) 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(); apply(false);
m_nco.setFreq(-notif.getFrequencyOffset(), m_inputSampleRate);
m_interpolator.create(16, m_inputSampleRate, m_rfBandwidth / 2.0);
m_sampleDistanceRemain = m_inputSampleRate / m_outputSampleRate;
m_settingsMutex.unlock(); qDebug() << "UDPSrc::handleMessage: MsgChannelizerNotification: m_inputSampleRate: " << m_config.m_inputSampleRate
<< " frequencyOffset: " << notif.getFrequencyOffset();
qDebug() << "UDPSrc::handleMessage: MsgChannelizerNotification: m_inputSampleRate: " << m_inputSampleRate return true;
<< " frequencyOffset: " << notif.getFrequencyOffset();
return true;
} }
else if (MsgUDPSrcConfigureImmediate::match(cmd)) else if (MsgUDPSrcConfigureImmediate::match(cmd))
{ {
MsgUDPSrcConfigureImmediate& cfg = (MsgUDPSrcConfigureImmediate&) 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) apply(cfg.getForce());
{
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();
qDebug() << "UDPSrc::handleMessage: MsgUDPSrcConfigureImmediate: " qDebug() << "UDPSrc::handleMessage: MsgUDPSrcConfigureImmediate: "
<< " m_audioActive: " << m_audioActive << " m_audioActive: " << m_config.m_audioActive
<< " m_audioStereo: " << m_audioStereo << " m_audioStereo: " << m_config.m_audioStereo
<< " m_gain: " << m_gain << " m_gain: " << m_config.m_gain
<< " m_squelchEnabled: " << m_squelchEnabled << " m_squelchEnabled: " << m_config.m_squelchEnabled
<< " m_squelch: " << m_squelch << " m_squelch: " << m_config.m_squelch
<< " getSquelchDB: " << cfg.getSquelchDB(); << " getSquelchDB: " << cfg.getSquelchDB()
<< " m_squelchGate" << m_config.m_squelchGate;
return true; return true;
@ -368,67 +333,22 @@ bool UDPSrc::handleMessage(const Message& cmd)
{ {
MsgUDPSrcConfigure& cfg = (MsgUDPSrcConfigure&) 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(); apply(cfg.getForce());
m_outputSampleRate = cfg.getOutputSampleRate();
m_rfBandwidth = cfg.getRFBandwidth();
if (cfg.getUDPAddress() != m_udpAddressStr) qDebug() << "UDPSrc::handleMessage: MsgUDPSrcConfigure: m_sampleFormat: " << m_config.m_sampleFormat
{ << " m_outputSampleRate: " << m_config.m_outputSampleRate
m_udpAddressStr = cfg.getUDPAddress(); << " m_rfBandwidth: " << m_config.m_rfBandwidth
m_udpBuffer->setAddress(m_udpAddressStr); << " m_udpAddressStr: " << m_config.m_udpAddressStr
m_udpBufferMono->setAddress(m_udpAddressStr); << " m_udpPort: " << m_config.m_udpPort
} << " m_audioPort: " << m_config.m_audioPort;
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;
return true; 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() void UDPSrc::audioReadyRead()
{ {
while (m_audioSocket->hasPendingDatagrams()) while (m_audioSocket->hasPendingDatagrams())
@ -463,16 +460,16 @@ void UDPSrc::audioReadyRead()
qint64 udpReadBytes = m_audioSocket->readDatagram(m_udpAudioBuf, pendingDataSize, 0, 0); qint64 udpReadBytes = m_audioSocket->readDatagram(m_udpAudioBuf, pendingDataSize, 0, 0);
//qDebug("UDPSrc::audioReadyRead: %lld", udpReadBytes); //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) for (int i = 0; i < udpReadBytes - 3; i += 4)
{ {
qint16 l_sample = (qint16) *(&m_udpAudioBuf[i]); qint16 l_sample = (qint16) *(&m_udpAudioBuf[i]);
qint16 r_sample = (qint16) *(&m_udpAudioBuf[i+2]); qint16 r_sample = (qint16) *(&m_udpAudioBuf[i+2]);
m_audioBuffer[m_audioBufferFill].l = l_sample * m_volume; m_audioBuffer[m_audioBufferFill].l = l_sample * m_running.m_volume;
m_audioBuffer[m_audioBufferFill].r = r_sample * m_volume; m_audioBuffer[m_audioBufferFill].r = r_sample * m_running.m_volume;
++m_audioBufferFill; ++m_audioBufferFill;
if (m_audioBufferFill >= m_audioBuffer.size()) if (m_audioBufferFill >= m_audioBuffer.size())
@ -493,8 +490,8 @@ void UDPSrc::audioReadyRead()
for (int i = 0; i < udpReadBytes - 1; i += 2) for (int i = 0; i < udpReadBytes - 1; i += 2)
{ {
qint16 sample = (qint16) *(&m_udpAudioBuf[i]); qint16 sample = (qint16) *(&m_udpAudioBuf[i]);
m_audioBuffer[m_audioBufferFill].l = sample * m_volume; m_audioBuffer[m_audioBufferFill].l = sample * m_running.m_volume;
m_audioBuffer[m_audioBufferFill].r = sample * m_volume; m_audioBuffer[m_audioBufferFill].r = sample * m_running.m_volume;
++m_audioBufferFill; ++m_audioBufferFill;
if (m_audioBufferFill >= m_audioBuffer.size()) if (m_audioBufferFill >= m_audioBuffer.size())

View File

@ -67,14 +67,17 @@ public:
int fmDeviation, int fmDeviation,
QString& udpAddress, QString& udpAddress,
int udpPort, int udpPort,
int audioPort); int audioPort,
bool force);
void configureImmediate(MessageQueue* messageQueue, void configureImmediate(MessageQueue* messageQueue,
bool audioActive, bool audioActive,
bool audioStereo, bool audioStereo,
Real gain, Real gain,
int volume, int volume,
Real squelchDB, Real squelchDB,
bool squelchEnabled); Real squelchGate,
bool squelchEnabled,
bool force);
void setSpectrum(MessageQueue* messageQueue, bool enabled); void setSpectrum(MessageQueue* messageQueue, bool enabled);
double getMagSq() const { return m_magsq; } double getMagSq() const { return m_magsq; }
double getInMagSq() const { return m_inMagsq; } double getInMagSq() const { return m_inMagsq; }
@ -102,6 +105,7 @@ protected:
const QString& getUDPAddress() const { return m_udpAddress; } const QString& getUDPAddress() const { return m_udpAddress; }
int getUDPPort() const { return m_udpPort; } int getUDPPort() const { return m_udpPort; }
int getAudioPort() const { return m_audioPort; } int getAudioPort() const { return m_audioPort; }
bool getForce() const { return m_force; }
static MsgUDPSrcConfigure* create(SampleFormat static MsgUDPSrcConfigure* create(SampleFormat
sampleFormat, sampleFormat,
@ -110,7 +114,8 @@ protected:
int fmDeviation, int fmDeviation,
QString& udpAddress, QString& udpAddress,
int udpPort, int udpPort,
int audioPort) int audioPort,
bool force)
{ {
return new MsgUDPSrcConfigure(sampleFormat, return new MsgUDPSrcConfigure(sampleFormat,
sampleRate, sampleRate,
@ -118,7 +123,8 @@ protected:
fmDeviation, fmDeviation,
udpAddress, udpAddress,
udpPort, udpPort,
audioPort); audioPort,
force);
} }
private: private:
@ -129,6 +135,7 @@ protected:
QString m_udpAddress; QString m_udpAddress;
int m_udpPort; int m_udpPort;
int m_audioPort; int m_audioPort;
bool m_force;
MsgUDPSrcConfigure(SampleFormat sampleFormat, MsgUDPSrcConfigure(SampleFormat sampleFormat,
Real outputSampleRate, Real outputSampleRate,
@ -136,7 +143,8 @@ protected:
int fmDeviation, int fmDeviation,
QString& udpAddress, QString& udpAddress,
int udpPort, int udpPort,
int audioPort) : int audioPort,
bool force) :
Message(), Message(),
m_sampleFormat(sampleFormat), m_sampleFormat(sampleFormat),
m_outputSampleRate(outputSampleRate), m_outputSampleRate(outputSampleRate),
@ -144,7 +152,8 @@ protected:
m_fmDeviation(fmDeviation), m_fmDeviation(fmDeviation),
m_udpAddress(udpAddress), m_udpAddress(udpAddress),
m_udpPort(udpPort), m_udpPort(udpPort),
m_audioPort(audioPort) m_audioPort(audioPort),
m_force(force)
{ } { }
}; };
@ -157,7 +166,9 @@ protected:
bool getAudioActive() const { return m_audioActive; } bool getAudioActive() const { return m_audioActive; }
bool getAudioStereo() const { return m_audioStereo; } bool getAudioStereo() const { return m_audioStereo; }
Real getSquelchDB() const { return m_squelchDB; } Real getSquelchDB() const { return m_squelchDB; }
Real getSquelchGate() const { return m_squelchGate; }
bool getSquelchEnabled() const { return m_squelchEnabled; } bool getSquelchEnabled() const { return m_squelchEnabled; }
bool getForce() const { return m_force; }
static MsgUDPSrcConfigureImmediate* create( static MsgUDPSrcConfigureImmediate* create(
bool audioActive, bool audioActive,
@ -165,7 +176,9 @@ protected:
int gain, int gain,
int volume, int volume,
Real squelchDB, Real squelchDB,
bool squelchEnabled) Real squelchGate,
bool squelchEnabled,
bool force)
{ {
return new MsgUDPSrcConfigureImmediate( return new MsgUDPSrcConfigureImmediate(
audioActive, audioActive,
@ -173,7 +186,9 @@ protected:
gain, gain,
volume, volume,
squelchDB, squelchDB,
squelchEnabled); squelchGate,
squelchEnabled,
force);
} }
private: private:
@ -182,7 +197,9 @@ protected:
bool m_audioActive; bool m_audioActive;
bool m_audioStereo; bool m_audioStereo;
Real m_squelchDB; Real m_squelchDB;
Real m_squelchGate; // seconds
bool m_squelchEnabled; bool m_squelchEnabled;
bool m_force;
MsgUDPSrcConfigureImmediate( MsgUDPSrcConfigureImmediate(
bool audioActive, bool audioActive,
@ -190,14 +207,18 @@ protected:
Real gain, Real gain,
int volume, int volume,
Real squelchDB, Real squelchDB,
bool squelchEnabled) : Real squelchGate,
bool squelchEnabled,
bool force) :
Message(), Message(),
m_gain(gain), m_gain(gain),
m_volume(volume), m_volume(volume),
m_audioActive(audioActive), m_audioActive(audioActive),
m_audioStereo(audioStereo), m_audioStereo(audioStereo),
m_squelchDB(squelchDB), 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; MessageQueue* m_uiMessageQueue;
UDPSrcGUI* m_udpSrcGUI; UDPSrcGUI* m_udpSrcGUI;
QUdpSocket *m_audioSocket; 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_magsq;
double m_inMagsq; double m_inMagsq;
MovingAverage<double> m_outMovingAverage; MovingAverage<double> m_outMovingAverage;
@ -270,34 +322,54 @@ protected:
PhaseDiscriminators m_phaseDiscri; PhaseDiscriminators m_phaseDiscri;
Real m_squelch; //!< squared magnitude
bool m_squelchEnabled;
bool m_squelchOpen; bool m_squelchOpen;
int m_squelchOpenCount; int m_squelchOpenCount;
int m_squelchCloseCount; int m_squelchCloseCount;
int m_squelchThreshold; int m_squelchThreshold; //!< number of samples computed from given gate
QMutex m_settingsMutex; QMutex m_settingsMutex;
void apply(bool force);
inline void calculateSquelch(double value) 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) { if (m_squelchThreshold == 0)
m_squelchOpenCount++; {
} else {
m_squelchCloseCount = m_squelchThreshold;
m_squelchOpen = true; m_squelchOpen = true;
} }
else
{
if (m_squelchOpenCount < m_squelchThreshold)
{
m_squelchOpenCount++;
}
else
{
m_squelchCloseCount = m_squelchThreshold;
m_squelchOpen = true;
}
}
} }
else else
{ {
if (m_squelchCloseCount > 0) { if (m_squelchThreshold == 0)
m_squelchCloseCount--; {
} else {
m_squelchOpenCount = 0;
m_squelchOpen = false; 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); blockApplySettings(false);
m_channelMarker.blockSignals(false); m_channelMarker.blockSignals(false);
applySettingsImmediate(); applySettingsImmediate(true);
applySettings(); applySettings(true);
return true; return true;
} }
else else
@ -297,8 +297,8 @@ UDPSrcGUI::UDPSrcGUI(PluginAPI* pluginAPI, DeviceSourceAPI *deviceAPI, QWidget*
ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum); ui->spectrumGUI->setBuddies(m_spectrumVis->getInputMessageQueue(), m_spectrumVis, ui->glSpectrum);
displaySettings(); displaySettings();
applySettingsImmediate(); applySettingsImmediate(true);
applySettings(); applySettings(true);
} }
UDPSrcGUI::~UDPSrcGUI() UDPSrcGUI::~UDPSrcGUI()
@ -323,9 +323,10 @@ void UDPSrcGUI::displaySettings()
ui->gainText->setText(tr("%1").arg(ui->gain->value()/10.0, 0, 'f', 1)); ui->gainText->setText(tr("%1").arg(ui->gain->value()/10.0, 0, 'f', 1));
ui->volumeText->setText(QString("%1").arg(ui->volume->value())); ui->volumeText->setText(QString("%1").arg(ui->volume->value()));
ui->squelchText->setText(tr("%1").arg(ui->squelch->value()*1.0, 0, 'f', 0)); 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) if (m_doApplySettings)
{ {
@ -340,12 +341,14 @@ void UDPSrcGUI::applySettingsImmediate()
m_gain, m_gain,
m_volume, m_volume,
ui->squelch->value() * 1.0f, 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) if (m_doApplySettings)
{ {
@ -461,7 +464,8 @@ void UDPSrcGUI::applySettings()
fmDeviation, fmDeviation,
m_udpAddress, m_udpAddress,
udpPort, udpPort,
audioPort); audioPort,
force);
ui->applyBtn->setEnabled(false); ui->applyBtn->setEnabled(false);
} }
@ -554,6 +558,12 @@ void UDPSrcGUI::on_squelch_valueChanged(int value)
applySettingsImmediate(); 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) void UDPSrcGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{ {
if ((widget == ui->spectrumBox) && (m_udpSrc != 0)) if ((widget == ui->spectrumBox) && (m_udpSrc != 0))

View File

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

View File

@ -236,6 +236,38 @@
</property> </property>
</widget> </widget>
</item> </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> </layout>
</item> </item>
<item row="6" column="0"> <item row="6" column="0">
@ -637,18 +669,40 @@
</widget> </widget>
</item> </item>
<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"> <property name="minimumSize">
<size> <size>
<width>40</width> <width>24</width>
<height>0</height> <height>0</height>
</size> </size>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Input power (dB) to which squelch applies</string> <string>Squelch gate (ms)</string>
</property> </property>
<property name="text"> <property name="text">
<string>-100.0</string> <string>000</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>