2017-08-13 19:39:26 -04:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Copyright (C) 2017 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 //
|
|
|
|
// //
|
|
|
|
// 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 <http://www.gnu.org/licenses/>. //
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#ifndef PLUGINS_CHANNELTX_UDPSINK_UDPSINK_H_
|
|
|
|
#define PLUGINS_CHANNELTX_UDPSINK_UDPSINK_H_
|
|
|
|
|
|
|
|
#include <QObject>
|
|
|
|
|
|
|
|
#include "dsp/basebandsamplesource.h"
|
|
|
|
#include "dsp/basebandsamplesink.h"
|
2017-08-14 04:59:05 -04:00
|
|
|
#include "dsp/interpolator.h"
|
2017-08-14 16:50:28 -04:00
|
|
|
#include "dsp/movingaverage.h"
|
2017-08-14 04:59:05 -04:00
|
|
|
#include "dsp/nco.h"
|
2017-08-18 21:42:56 -04:00
|
|
|
#include "dsp/fftfilt.h"
|
2017-08-13 19:39:26 -04:00
|
|
|
#include "util/message.h"
|
|
|
|
|
2017-08-15 14:23:49 -04:00
|
|
|
#include "udpsinkudphandler.h"
|
|
|
|
|
2017-08-13 19:39:26 -04:00
|
|
|
class UDPSinkGUI;
|
|
|
|
|
|
|
|
class UDPSink : public BasebandSampleSource {
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
public:
|
|
|
|
enum SampleFormat {
|
|
|
|
FormatS16LE,
|
|
|
|
FormatNFM,
|
|
|
|
FormatNFMMono,
|
|
|
|
FormatLSB,
|
|
|
|
FormatUSB,
|
|
|
|
FormatLSBMono,
|
|
|
|
FormatUSBMono,
|
|
|
|
FormatAMMono,
|
|
|
|
FormatNone
|
|
|
|
};
|
|
|
|
|
|
|
|
UDPSink(MessageQueue* uiMessageQueue, UDPSinkGUI* udpSinkGUI, BasebandSampleSink* spectrum);
|
|
|
|
virtual ~UDPSink();
|
|
|
|
|
|
|
|
virtual void start();
|
|
|
|
virtual void stop();
|
|
|
|
virtual void pull(Sample& sample);
|
|
|
|
virtual bool handleMessage(const Message& cmd);
|
|
|
|
|
2017-08-14 16:50:28 -04:00
|
|
|
double getMagSq() const { return m_magsq; }
|
2017-08-16 22:23:36 -04:00
|
|
|
double getInMagSq() const { return m_inMagsq; }
|
2017-08-15 21:33:05 -04:00
|
|
|
int32_t getBufferGauge() const { return m_udpHandler.getBufferGauge(); }
|
2017-08-16 22:23:36 -04:00
|
|
|
bool getSquelchOpen() const { return m_squelchOpen; }
|
2017-08-14 16:50:28 -04:00
|
|
|
|
2017-08-13 19:39:26 -04:00
|
|
|
void configure(MessageQueue* messageQueue,
|
|
|
|
SampleFormat sampleFormat,
|
|
|
|
Real inputSampleRate,
|
|
|
|
Real rfBandwidth,
|
|
|
|
int fmDeviation,
|
2017-08-18 19:46:52 -04:00
|
|
|
Real amModFactor,
|
2017-08-13 19:39:26 -04:00
|
|
|
QString& udpAddress,
|
2017-08-14 04:59:05 -04:00
|
|
|
int udpPort,
|
2017-08-16 03:53:23 -04:00
|
|
|
bool channelMute,
|
2017-08-17 06:57:45 -04:00
|
|
|
Real gain,
|
2017-08-16 22:23:36 -04:00
|
|
|
Real squelchDB,
|
2017-08-18 18:52:10 -04:00
|
|
|
Real squelchGate,
|
2017-08-17 14:23:17 -04:00
|
|
|
bool squelchEnabled,
|
2017-08-16 03:53:23 -04:00
|
|
|
bool force = false);
|
2017-08-14 10:09:56 -04:00
|
|
|
void setSpectrum(MessageQueue* messageQueue, bool enabled);
|
2017-08-18 11:51:11 -04:00
|
|
|
void resetReadIndex(MessageQueue* messageQueue);
|
2017-08-13 19:39:26 -04:00
|
|
|
|
2017-08-16 17:37:01 -04:00
|
|
|
signals:
|
|
|
|
/**
|
|
|
|
* Level changed
|
|
|
|
* \param rmsLevel RMS level in range 0.0 - 1.0
|
|
|
|
* \param peakLevel Peak level in range 0.0 - 1.0
|
|
|
|
* \param numSamples Number of audio samples analyzed
|
|
|
|
*/
|
|
|
|
void levelChanged(qreal rmsLevel, qreal peakLevel, int numSamples);
|
|
|
|
|
2017-08-13 19:39:26 -04:00
|
|
|
private:
|
|
|
|
class MsgUDPSinkConfigure : public Message {
|
|
|
|
MESSAGE_CLASS_DECLARATION
|
|
|
|
|
|
|
|
public:
|
|
|
|
SampleFormat getSampleFormat() const { return m_sampleFormat; }
|
|
|
|
Real getInputSampleRate() const { return m_inputSampleRate; }
|
|
|
|
Real getRFBandwidth() const { return m_rfBandwidth; }
|
|
|
|
int getFMDeviation() const { return m_fmDeviation; }
|
2017-08-18 19:46:52 -04:00
|
|
|
Real getAMModFactor() const { return m_amModFactor; }
|
2017-08-13 19:39:26 -04:00
|
|
|
const QString& getUDPAddress() const { return m_udpAddress; }
|
|
|
|
int getUDPPort() const { return m_udpPort; }
|
2017-08-14 04:59:05 -04:00
|
|
|
bool getChannelMute() const { return m_channelMute; }
|
2017-08-17 06:57:45 -04:00
|
|
|
Real getGain() const { return m_gain; }
|
2017-08-16 22:23:36 -04:00
|
|
|
Real getSquelchDB() const { return m_squelchDB; }
|
2017-08-18 18:52:10 -04:00
|
|
|
Real getSquelchGate() const { return m_squelchGate; }
|
2017-08-17 14:23:17 -04:00
|
|
|
bool getSquelchEnabled() const { return m_squelchEnabled; }
|
2017-08-16 03:53:23 -04:00
|
|
|
bool getForce() const { return m_force; }
|
2017-08-13 19:39:26 -04:00
|
|
|
|
|
|
|
static MsgUDPSinkConfigure* create(SampleFormat
|
|
|
|
sampleFormat,
|
|
|
|
Real inputSampleRate,
|
|
|
|
Real rfBandwidth,
|
|
|
|
int fmDeviation,
|
2017-08-18 19:46:52 -04:00
|
|
|
Real amModFactor,
|
2017-08-13 19:39:26 -04:00
|
|
|
QString& udpAddress,
|
2017-08-14 04:59:05 -04:00
|
|
|
int udpPort,
|
2017-08-16 03:53:23 -04:00
|
|
|
bool channelMute,
|
2017-08-17 06:57:45 -04:00
|
|
|
Real gain,
|
2017-08-16 22:23:36 -04:00
|
|
|
Real squelchDB,
|
2017-08-18 18:52:10 -04:00
|
|
|
Real squelchGate,
|
2017-08-17 14:23:17 -04:00
|
|
|
bool squelchEnabled,
|
2017-08-16 03:53:23 -04:00
|
|
|
bool force)
|
2017-08-13 19:39:26 -04:00
|
|
|
{
|
|
|
|
return new MsgUDPSinkConfigure(sampleFormat,
|
|
|
|
inputSampleRate,
|
|
|
|
rfBandwidth,
|
|
|
|
fmDeviation,
|
2017-08-18 19:46:52 -04:00
|
|
|
amModFactor,
|
2017-08-13 19:39:26 -04:00
|
|
|
udpAddress,
|
2017-08-14 04:59:05 -04:00
|
|
|
udpPort,
|
2017-08-16 03:53:23 -04:00
|
|
|
channelMute,
|
2017-08-17 06:57:45 -04:00
|
|
|
gain,
|
2017-08-16 22:23:36 -04:00
|
|
|
squelchDB,
|
2017-08-18 18:52:10 -04:00
|
|
|
squelchGate,
|
2017-08-17 14:23:17 -04:00
|
|
|
squelchEnabled,
|
2017-08-16 03:53:23 -04:00
|
|
|
force);
|
2017-08-13 19:39:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
SampleFormat m_sampleFormat;
|
|
|
|
Real m_inputSampleRate;
|
|
|
|
Real m_rfBandwidth;
|
|
|
|
int m_fmDeviation;
|
2017-08-18 19:46:52 -04:00
|
|
|
Real m_amModFactor;
|
2017-08-13 19:39:26 -04:00
|
|
|
QString m_udpAddress;
|
|
|
|
int m_udpPort;
|
2017-08-14 04:59:05 -04:00
|
|
|
bool m_channelMute;
|
2017-08-17 06:57:45 -04:00
|
|
|
Real m_gain;
|
2017-08-16 22:23:36 -04:00
|
|
|
Real m_squelchDB;
|
2017-08-18 18:52:10 -04:00
|
|
|
Real m_squelchGate;
|
2017-08-17 14:23:17 -04:00
|
|
|
bool m_squelchEnabled;
|
2017-08-16 03:53:23 -04:00
|
|
|
bool m_force;
|
2017-08-13 19:39:26 -04:00
|
|
|
|
|
|
|
MsgUDPSinkConfigure(SampleFormat sampleFormat,
|
|
|
|
Real inputSampleRate,
|
|
|
|
Real rfBandwidth,
|
|
|
|
int fmDeviation,
|
2017-08-18 19:46:52 -04:00
|
|
|
Real amModFactor,
|
2017-08-13 19:39:26 -04:00
|
|
|
QString& udpAddress,
|
2017-08-14 04:59:05 -04:00
|
|
|
int udpPort,
|
2017-08-16 03:53:23 -04:00
|
|
|
bool channelMute,
|
2017-08-17 06:57:45 -04:00
|
|
|
Real gain,
|
2017-08-16 22:23:36 -04:00
|
|
|
Real squelchDB,
|
2017-08-18 18:52:10 -04:00
|
|
|
Real squelchGate,
|
2017-08-17 14:23:17 -04:00
|
|
|
bool squelchEnabled,
|
2017-08-16 03:53:23 -04:00
|
|
|
bool force) :
|
2017-08-13 19:39:26 -04:00
|
|
|
Message(),
|
|
|
|
m_sampleFormat(sampleFormat),
|
|
|
|
m_inputSampleRate(inputSampleRate),
|
|
|
|
m_rfBandwidth(rfBandwidth),
|
|
|
|
m_fmDeviation(fmDeviation),
|
2017-08-18 19:46:52 -04:00
|
|
|
m_amModFactor(amModFactor),
|
2017-08-13 19:39:26 -04:00
|
|
|
m_udpAddress(udpAddress),
|
2017-08-14 04:59:05 -04:00
|
|
|
m_udpPort(udpPort),
|
2017-08-16 03:53:23 -04:00
|
|
|
m_channelMute(channelMute),
|
2017-08-17 06:57:45 -04:00
|
|
|
m_gain(gain),
|
2017-08-16 22:23:36 -04:00
|
|
|
m_squelchDB(squelchDB),
|
2017-08-18 18:52:10 -04:00
|
|
|
m_squelchGate(squelchGate),
|
2017-08-17 14:23:17 -04:00
|
|
|
m_squelchEnabled(squelchEnabled),
|
2017-08-16 03:53:23 -04:00
|
|
|
m_force(force)
|
2017-08-13 19:39:26 -04:00
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
2017-08-14 10:09:56 -04:00
|
|
|
class MsgUDPSinkSpectrum : public Message {
|
|
|
|
MESSAGE_CLASS_DECLARATION
|
|
|
|
|
|
|
|
public:
|
|
|
|
bool getEnabled() const { return m_enabled; }
|
|
|
|
|
|
|
|
static MsgUDPSinkSpectrum* create(bool enabled)
|
|
|
|
{
|
|
|
|
return new MsgUDPSinkSpectrum(enabled);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool m_enabled;
|
|
|
|
|
|
|
|
MsgUDPSinkSpectrum(bool enabled) :
|
|
|
|
Message(),
|
|
|
|
m_enabled(enabled)
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
2017-08-18 11:51:11 -04:00
|
|
|
class MsgResetReadIndex : public Message {
|
|
|
|
MESSAGE_CLASS_DECLARATION
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
static MsgResetReadIndex* create()
|
|
|
|
{
|
|
|
|
return new MsgResetReadIndex();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
|
|
MsgResetReadIndex() :
|
|
|
|
Message()
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
2017-08-13 19:39:26 -04:00
|
|
|
struct Config {
|
|
|
|
int m_basebandSampleRate;
|
|
|
|
Real m_outputSampleRate;
|
|
|
|
int m_sampleFormat;
|
|
|
|
Real m_inputSampleRate;
|
|
|
|
qint64 m_inputFrequencyOffset;
|
|
|
|
Real m_rfBandwidth;
|
2017-08-18 21:42:56 -04:00
|
|
|
Real m_lowCutoff;
|
2017-08-13 19:39:26 -04:00
|
|
|
int m_fmDeviation;
|
2017-08-18 19:46:52 -04:00
|
|
|
Real m_amModFactor;
|
2017-08-14 04:59:05 -04:00
|
|
|
bool m_channelMute;
|
2017-08-17 06:57:45 -04:00
|
|
|
Real m_gain;
|
2017-08-16 22:23:36 -04:00
|
|
|
Real m_squelch; //!< squared magnitude
|
2017-08-18 18:52:10 -04:00
|
|
|
Real m_squelchGate; //!< seconds
|
2017-08-17 14:23:17 -04:00
|
|
|
bool m_squelchEnabled;
|
2017-08-13 19:39:26 -04:00
|
|
|
|
|
|
|
QString m_udpAddressStr;
|
|
|
|
quint16 m_udpPort;
|
|
|
|
|
|
|
|
Config() :
|
2017-08-14 04:59:05 -04:00
|
|
|
m_basebandSampleRate(48000),
|
|
|
|
m_outputSampleRate(48000),
|
2017-08-13 19:39:26 -04:00
|
|
|
m_sampleFormat(0),
|
2017-08-14 04:59:05 -04:00
|
|
|
m_inputSampleRate(48000),
|
2017-08-13 19:39:26 -04:00
|
|
|
m_inputFrequencyOffset(0),
|
2017-08-14 04:59:05 -04:00
|
|
|
m_rfBandwidth(12500),
|
2017-08-18 21:42:56 -04:00
|
|
|
m_lowCutoff(300),
|
2017-08-14 04:59:05 -04:00
|
|
|
m_fmDeviation(1.0),
|
2017-08-18 19:46:52 -04:00
|
|
|
m_amModFactor(0.95),
|
2017-08-14 04:59:05 -04:00
|
|
|
m_channelMute(false),
|
2017-08-17 06:57:45 -04:00
|
|
|
m_gain(1.0),
|
2017-08-16 22:23:36 -04:00
|
|
|
m_squelch(-50.0),
|
2017-08-18 18:52:10 -04:00
|
|
|
m_squelchGate(0.05),
|
2017-08-17 14:23:17 -04:00
|
|
|
m_squelchEnabled(true),
|
2017-08-13 19:39:26 -04:00
|
|
|
m_udpAddressStr("127.0.0.1"),
|
|
|
|
m_udpPort(9999)
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
|
|
|
Config m_config;
|
|
|
|
Config m_running;
|
|
|
|
|
2017-08-14 04:59:05 -04:00
|
|
|
NCO m_carrierNco;
|
|
|
|
Complex m_modSample;
|
|
|
|
|
2017-08-13 19:39:26 -04:00
|
|
|
MessageQueue* m_uiMessageQueue;
|
|
|
|
UDPSinkGUI* m_udpSinkGUI;
|
|
|
|
BasebandSampleSink* m_spectrum;
|
2017-08-16 16:49:19 -04:00
|
|
|
bool m_spectrumEnabled;
|
|
|
|
SampleVector m_sampleBuffer;
|
|
|
|
int m_spectrumChunkSize;
|
|
|
|
int m_spectrumChunkCounter;
|
2017-08-13 19:39:26 -04:00
|
|
|
|
2017-08-14 04:59:05 -04:00
|
|
|
Interpolator m_interpolator;
|
|
|
|
Real m_interpolatorDistance;
|
|
|
|
Real m_interpolatorDistanceRemain;
|
|
|
|
bool m_interpolatorConsumed;
|
|
|
|
|
2017-08-14 16:50:28 -04:00
|
|
|
double m_magsq;
|
2017-08-16 22:23:36 -04:00
|
|
|
double m_inMagsq;
|
2017-08-14 16:50:28 -04:00
|
|
|
MovingAverage<double> m_movingAverage;
|
2017-08-16 22:23:36 -04:00
|
|
|
MovingAverage<double> m_inMovingAverage;
|
2017-08-14 16:50:28 -04:00
|
|
|
|
2017-08-15 14:23:49 -04:00
|
|
|
UDPSinkUDPHandler m_udpHandler;
|
2017-08-16 05:35:47 -04:00
|
|
|
Real m_actualInputSampleRate; //!< sample rate with UDP buffer skew compensation
|
2017-08-16 10:09:35 -04:00
|
|
|
double m_sampleRateSum;
|
|
|
|
int m_sampleRateAvgCounter;
|
2017-08-15 14:23:49 -04:00
|
|
|
|
2017-08-16 17:37:01 -04:00
|
|
|
int m_levelCalcCount;
|
|
|
|
Real m_peakLevel;
|
|
|
|
double m_levelSum;
|
|
|
|
int m_levelNbSamples;
|
|
|
|
|
2017-08-16 22:23:36 -04:00
|
|
|
bool m_squelchOpen;
|
|
|
|
int m_squelchOpenCount;
|
|
|
|
int m_squelchCloseCount;
|
|
|
|
int m_squelchThreshold;
|
|
|
|
|
2017-08-18 21:42:56 -04:00
|
|
|
float m_modPhasor; //!< Phasor for FM modulation
|
|
|
|
fftfilt* m_SSBFilter; //!< Complex filter for SSB modulation
|
|
|
|
Complex* m_SSBFilterBuffer;
|
|
|
|
int m_SSBFilterBufferIndex;
|
2017-08-18 06:30:51 -04:00
|
|
|
|
2017-08-13 19:39:26 -04:00
|
|
|
QMutex m_settingsMutex;
|
2017-08-14 04:59:05 -04:00
|
|
|
|
2017-08-16 10:09:35 -04:00
|
|
|
static const int m_sampleRateAverageItems = 17;
|
2017-08-18 21:42:56 -04:00
|
|
|
static const int m_ssbFftLen = 1024;
|
2017-08-16 10:09:35 -04:00
|
|
|
|
2017-08-14 04:59:05 -04:00
|
|
|
void apply(bool force);
|
|
|
|
void modulateSample();
|
2017-08-16 17:37:01 -04:00
|
|
|
void calculateLevel(Real sample);
|
|
|
|
void calculateLevel(Complex sample);
|
2017-08-16 22:23:36 -04:00
|
|
|
|
|
|
|
inline void calculateSquelch(double value)
|
|
|
|
{
|
2017-08-17 14:23:17 -04:00
|
|
|
if ((!m_running.m_squelchEnabled) || (value > m_running.m_squelch))
|
2017-08-16 22:23:36 -04:00
|
|
|
{
|
2017-08-18 18:52:10 -04:00
|
|
|
if (m_squelchThreshold == 0)
|
|
|
|
{
|
2017-08-16 22:23:36 -04:00
|
|
|
m_squelchOpen = true;
|
|
|
|
}
|
2017-08-18 18:52:10 -04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (m_squelchOpenCount < m_squelchThreshold)
|
|
|
|
{
|
|
|
|
m_squelchOpenCount++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_squelchCloseCount = m_squelchThreshold;
|
|
|
|
m_squelchOpen = true;
|
|
|
|
}
|
|
|
|
}
|
2017-08-16 22:23:36 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-08-18 18:52:10 -04:00
|
|
|
if (m_squelchThreshold == 0)
|
|
|
|
{
|
2017-08-16 22:23:36 -04:00
|
|
|
m_squelchOpen = false;
|
|
|
|
}
|
2017-08-18 18:52:10 -04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (m_squelchCloseCount > 0)
|
|
|
|
{
|
|
|
|
m_squelchCloseCount--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_squelchOpenCount = 0;
|
|
|
|
m_squelchOpen = false;
|
|
|
|
}
|
|
|
|
}
|
2017-08-16 22:23:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2017-08-13 19:39:26 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* PLUGINS_CHANNELTX_UDPSINK_UDPSINK_H_ */
|