mirror of
https://github.com/f4exb/sdrangel.git
synced 2026-06-01 13:47:01 -04:00
Remote TCP updates:
Add support for public list of SDRangel servers that can be displayed on Map. Add FLAC and zlib IQ compression. Add IQ squelch for compression. Add remote device/antenna position and direction reporting. Add text messaging.
This commit is contained in:
@@ -40,16 +40,27 @@
|
||||
MESSAGE_CLASS_DEFINITION(RemoteTCPInput::MsgConfigureRemoteTCPInput, Message)
|
||||
MESSAGE_CLASS_DEFINITION(RemoteTCPInput::MsgStartStop, Message)
|
||||
MESSAGE_CLASS_DEFINITION(RemoteTCPInput::MsgReportTCPBuffer, Message)
|
||||
MESSAGE_CLASS_DEFINITION(RemoteTCPInput::MsgSaveReplay, Message)
|
||||
MESSAGE_CLASS_DEFINITION(RemoteTCPInput::MsgSendMessage, Message)
|
||||
MESSAGE_CLASS_DEFINITION(RemoteTCPInput::MsgReportPosition, Message)
|
||||
MESSAGE_CLASS_DEFINITION(RemoteTCPInput::MsgReportDirection, Message)
|
||||
|
||||
RemoteTCPInput::RemoteTCPInput(DeviceAPI *deviceAPI) :
|
||||
m_deviceAPI(deviceAPI),
|
||||
m_settings(),
|
||||
m_remoteInputTCPPHandler(nullptr),
|
||||
m_deviceDescription("RemoteTCPInput")
|
||||
m_deviceDescription("RemoteTCPInput"),
|
||||
m_running(false),
|
||||
m_latitude(std::numeric_limits<float>::quiet_NaN()),
|
||||
m_longitude(std::numeric_limits<float>::quiet_NaN()),
|
||||
m_altitude(std::numeric_limits<float>::quiet_NaN()),
|
||||
m_isotropic(false),
|
||||
m_azimuth(std::numeric_limits<float>::quiet_NaN()),
|
||||
m_elevation(std::numeric_limits<float>::quiet_NaN())
|
||||
{
|
||||
m_sampleFifo.setLabel(m_deviceDescription);
|
||||
m_sampleFifo.setSize(48000 * 8);
|
||||
m_remoteInputTCPPHandler = new RemoteTCPInputTCPHandler(&m_sampleFifo, m_deviceAPI);
|
||||
m_remoteInputTCPPHandler = new RemoteTCPInputTCPHandler(&m_sampleFifo, m_deviceAPI, &m_replayBuffer);
|
||||
m_remoteInputTCPPHandler->moveToThread(&m_thread);
|
||||
m_remoteInputTCPPHandler->setMessageQueueToInput(&m_inputMessageQueue);
|
||||
|
||||
@@ -66,6 +77,7 @@ RemoteTCPInput::RemoteTCPInput(DeviceAPI *deviceAPI) :
|
||||
|
||||
RemoteTCPInput::~RemoteTCPInput()
|
||||
{
|
||||
qDebug() << "RemoteTCPInput::~RemoteTCPInput";
|
||||
QObject::disconnect(
|
||||
m_networkManager,
|
||||
&QNetworkAccessManager::finished,
|
||||
@@ -84,25 +96,45 @@ void RemoteTCPInput::destroy()
|
||||
|
||||
void RemoteTCPInput::init()
|
||||
{
|
||||
qDebug() << "*************** RemoteTCPInput::init";
|
||||
applySettings(m_settings, QList<QString>(), true);
|
||||
}
|
||||
|
||||
bool RemoteTCPInput::start()
|
||||
{
|
||||
qDebug() << "RemoteTCPInput::start";
|
||||
if (m_running) {
|
||||
qDebug() << "RemoteTCPInput::stop - Already running";
|
||||
return true;
|
||||
}
|
||||
m_remoteInputTCPPHandler->reset();
|
||||
m_remoteInputTCPPHandler->start();
|
||||
qDebug() << "************ RemoteTCPInput::start" << m_settings.m_dataAddress;
|
||||
m_remoteInputTCPPHandler->getInputMessageQueue()->push(RemoteTCPInputTCPHandler::MsgConfigureTcpHandler::create(m_settings, QList<QString>(), true));
|
||||
m_thread.start();
|
||||
m_running = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemoteTCPInput::stop()
|
||||
{
|
||||
qDebug() << "RemoteTCPInput::stop";
|
||||
if (!m_running) {
|
||||
// For wasm, important not to call m_remoteInputTCPPHandler->stop() twice
|
||||
// as mutex can deadlock when this object is being deleted
|
||||
qDebug() << "RemoteTCPInput::stop - Not running";
|
||||
return;
|
||||
}
|
||||
m_remoteInputTCPPHandler->stop();
|
||||
qDebug() << "RemoteTCPInput::stop1";
|
||||
m_thread.quit();
|
||||
qDebug() << "RemoteTCPInput::stop2";
|
||||
#ifndef __EMSCRIPTEN__
|
||||
qDebug() << "RemoteTCPInput::stop3";
|
||||
m_thread.wait();
|
||||
#endif
|
||||
m_running = false;
|
||||
qDebug() << "RemoteTCPInput::stopped";
|
||||
}
|
||||
|
||||
QByteArray RemoteTCPInput::serialize() const
|
||||
@@ -120,6 +152,8 @@ bool RemoteTCPInput::deserialize(const QByteArray& data)
|
||||
success = false;
|
||||
}
|
||||
|
||||
qDebug() << "************** RemoteTCPInput::deserialize" << m_settings.m_dataAddress;
|
||||
|
||||
MsgConfigureRemoteTCPInput* message = MsgConfigureRemoteTCPInput::create(m_settings, QList<QString>(), true);
|
||||
m_inputMessageQueue.push(message);
|
||||
|
||||
@@ -196,6 +230,7 @@ bool RemoteTCPInput::handleMessage(const Message& message)
|
||||
{
|
||||
qDebug() << "RemoteTCPInput::handleMessage:" << message.getIdentifier();
|
||||
MsgConfigureRemoteTCPInput& conf = (MsgConfigureRemoteTCPInput&) message;
|
||||
qDebug() << "*********** RemoteTCPInput::handleMessage MsgConfigureRemoteTCPInput" << m_settings.m_dataAddress;
|
||||
applySettings(conf.getSettings(), conf.getSettingsKeys(), conf.getForce());
|
||||
return true;
|
||||
}
|
||||
@@ -210,6 +245,42 @@ bool RemoteTCPInput::handleMessage(const Message& message)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (MsgSaveReplay::match(message))
|
||||
{
|
||||
MsgSaveReplay& cmd = (MsgSaveReplay&) message;
|
||||
m_replayBuffer.save(cmd.getFilename(), m_settings.m_devSampleRate, getCenterFrequency());
|
||||
return true;
|
||||
}
|
||||
else if (MsgSendMessage::match(message))
|
||||
{
|
||||
MsgSendMessage& msg = (MsgSendMessage&) message;
|
||||
m_remoteInputTCPPHandler->getInputMessageQueue()->push(MsgSendMessage::create(msg.getCallsign(), msg.getText(), msg.getBroadcast()));
|
||||
return true;
|
||||
}
|
||||
else if (MsgReportPosition::match(message))
|
||||
{
|
||||
MsgReportPosition& report = (MsgReportPosition&) message;
|
||||
|
||||
m_latitude = report.getLatitude();
|
||||
m_longitude = report.getLongitude();
|
||||
m_altitude = report.getAltitude();
|
||||
|
||||
emit positionChanged(m_latitude, m_longitude, m_altitude);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MsgReportDirection::match(message))
|
||||
{
|
||||
MsgReportDirection& report = (MsgReportDirection&) message;
|
||||
|
||||
m_isotropic = report.getIsotropic();
|
||||
m_azimuth = report.getAzimuth();
|
||||
m_elevation = report.getElevation();
|
||||
|
||||
emit directionChanged(m_isotropic, m_azimuth, m_elevation);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
@@ -242,6 +313,12 @@ void RemoteTCPInput::applySettings(const RemoteTCPInputSettings& settings, const
|
||||
forwardChange = true;
|
||||
}
|
||||
|
||||
if ((settingsKeys.contains("channelSampleRate") || force)
|
||||
&& (settings.m_devSampleRate != m_settings.m_devSampleRate))
|
||||
{
|
||||
m_replayBuffer.clear();
|
||||
}
|
||||
|
||||
mutexLocker.unlock();
|
||||
|
||||
if (settings.m_useReverseAPI)
|
||||
@@ -265,6 +342,18 @@ void RemoteTCPInput::applySettings(const RemoteTCPInputSettings& settings, const
|
||||
m_settings.applySettings(settingsKeys, settings);
|
||||
}
|
||||
|
||||
if (settingsKeys.contains("replayLength") || settingsKeys.contains("devSampleRate") || force) {
|
||||
m_replayBuffer.setSize(m_settings.m_replayLength, m_settings.m_devSampleRate);
|
||||
}
|
||||
|
||||
if (settingsKeys.contains("replayOffset") || settingsKeys.contains("devSampleRate") || force) {
|
||||
m_replayBuffer.setReadOffset(((unsigned)(m_settings.m_replayOffset * m_settings.m_devSampleRate)) * 2);
|
||||
}
|
||||
|
||||
if (settingsKeys.contains("replayLoop") || force) {
|
||||
m_replayBuffer.setLoop(m_settings.m_replayLoop);
|
||||
}
|
||||
|
||||
m_remoteInputTCPPHandler->getInputMessageQueue()->push(RemoteTCPInputTCPHandler::MsgConfigureTcpHandler::create(m_settings, settingsKeys, force));
|
||||
}
|
||||
|
||||
@@ -459,6 +548,9 @@ int RemoteTCPInput::webapiReportGet(
|
||||
void RemoteTCPInput::webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response)
|
||||
{
|
||||
response.getRemoteTcpInputReport()->setSampleRate(m_settings.m_channelSampleRate);
|
||||
response.getRemoteTcpInputReport()->setLatitude(m_latitude);
|
||||
response.getRemoteTcpInputReport()->setLongitude(m_longitude);
|
||||
response.getRemoteTcpInputReport()->setAltitude(m_altitude);
|
||||
}
|
||||
|
||||
void RemoteTCPInput::webapiReverseSendSettings(const QList<QString>& deviceSettingsKeys, const RemoteTCPInputSettings& settings, bool force)
|
||||
|
||||
@@ -31,13 +31,14 @@
|
||||
#include <QNetworkRequest>
|
||||
|
||||
#include "dsp/devicesamplesource.h"
|
||||
#include "dsp/replaybuffer.h"
|
||||
|
||||
#include "remotetcpinputsettings.h"
|
||||
#include "remotetcpinputtcphandler.h"
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
class DeviceAPI;
|
||||
class RemoteTCPInputTCPHandler;
|
||||
|
||||
class RemoteTCPInput : public DeviceSampleSource {
|
||||
Q_OBJECT
|
||||
@@ -124,6 +125,100 @@ public:
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgSaveReplay : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
QString getFilename() const { return m_filename; }
|
||||
|
||||
static MsgSaveReplay* create(const QString& filename) {
|
||||
return new MsgSaveReplay(filename);
|
||||
}
|
||||
|
||||
protected:
|
||||
QString m_filename;
|
||||
|
||||
MsgSaveReplay(const QString& filename) :
|
||||
Message(),
|
||||
m_filename(filename)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgSendMessage : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const QString& getCallsign() const { return m_callsign; }
|
||||
const QString& getText() const { return m_text; }
|
||||
bool getBroadcast() const { return m_broadcast; }
|
||||
|
||||
static MsgSendMessage* create(const QString& callsign, const QString& text, bool broadcast) {
|
||||
return new MsgSendMessage(callsign, text, broadcast);
|
||||
}
|
||||
|
||||
protected:
|
||||
QString m_callsign;
|
||||
QString m_text;
|
||||
bool m_broadcast;
|
||||
|
||||
MsgSendMessage(const QString& callsign, const QString& text, bool broadcast) :
|
||||
Message(),
|
||||
m_callsign(callsign),
|
||||
m_text(text),
|
||||
m_broadcast(broadcast)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportPosition : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
float getLatitude() const { return m_latitude; }
|
||||
float getLongitude() const { return m_longitude; }
|
||||
float getAltitude() const { return m_altitude; }
|
||||
|
||||
static MsgReportPosition* create(float latitude, float longitude, float altitude) {
|
||||
return new MsgReportPosition(latitude, longitude, altitude);
|
||||
}
|
||||
|
||||
private:
|
||||
float m_latitude;
|
||||
float m_longitude;
|
||||
float m_altitude;
|
||||
|
||||
MsgReportPosition(float latitude, float longitude, float altitude) :
|
||||
Message(),
|
||||
m_latitude(latitude),
|
||||
m_longitude(longitude),
|
||||
m_altitude(altitude)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgReportDirection : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
bool getIsotropic() const { return m_isotropic; }
|
||||
float getAzimuth() const { return m_azimuth; }
|
||||
float getElevation() const { return m_elevation; }
|
||||
|
||||
static MsgReportDirection* create(bool isotropic, float azimuth, float elevation) {
|
||||
return new MsgReportDirection(isotropic, azimuth, elevation);
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_isotropic;
|
||||
float m_azimuth;
|
||||
float m_elevation;
|
||||
|
||||
MsgReportDirection(bool isotropic, float azimuth, float elevation) :
|
||||
Message(),
|
||||
m_isotropic(isotropic),
|
||||
m_azimuth(azimuth),
|
||||
m_elevation(elevation)
|
||||
{ }
|
||||
};
|
||||
|
||||
RemoteTCPInput(DeviceAPI *deviceAPI);
|
||||
virtual ~RemoteTCPInput();
|
||||
virtual void destroy();
|
||||
@@ -177,6 +272,15 @@ public:
|
||||
const QStringList& deviceSettingsKeys,
|
||||
SWGSDRangel::SWGDeviceSettings& response);
|
||||
|
||||
void getMagSqLevels(double& avg, double& peak, int& nbSamples)
|
||||
{
|
||||
if (m_remoteInputTCPPHandler) {
|
||||
m_remoteInputTCPPHandler->getMagSqLevels(avg, peak, nbSamples);
|
||||
} else {
|
||||
avg = 0.0; peak = 0.0; nbSamples = 1;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
DeviceAPI *m_deviceAPI;
|
||||
QRecursiveMutex m_mutex;
|
||||
@@ -185,7 +289,15 @@ private:
|
||||
QString m_deviceDescription;
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QNetworkRequest m_networkRequest;
|
||||
ReplayBuffer<FixReal> m_replayBuffer;
|
||||
QThread m_thread;
|
||||
bool m_running;
|
||||
float m_latitude; // Position of remote device (antenna)
|
||||
float m_longitude;
|
||||
float m_altitude;
|
||||
bool m_isotropic; // Direction of remote anntenna
|
||||
float m_azimuth;
|
||||
float m_elevation;
|
||||
|
||||
void applySettings(const RemoteTCPInputSettings& settings, const QList<QString>& settingsKeys, bool force = false);
|
||||
void webapiFormatDeviceReport(SWGSDRangel::SWGDeviceReport& response);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <QMessageBox>
|
||||
#include <QDateTime>
|
||||
#include <QString>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "ui_remotetcpinputgui.h"
|
||||
#include "gui/colormapper.h"
|
||||
@@ -30,17 +31,19 @@
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "device/deviceapi.h"
|
||||
#include "device/deviceuiset.h"
|
||||
#include "util/db.h"
|
||||
#include "remotetcpinputgui.h"
|
||||
#include "remotetcpinputtcphandler.h"
|
||||
#include "maincore.h"
|
||||
|
||||
RemoteTCPInputGui::RemoteTCPInputGui(DeviceUISet *deviceUISet, QWidget* parent) :
|
||||
DeviceGUI(parent),
|
||||
ui(new Ui::RemoteTCPInputGui),
|
||||
m_settings(),
|
||||
m_sampleSource(0),
|
||||
m_lastEngineState(DeviceAPI::StNotStarted),
|
||||
m_sampleSource(nullptr),
|
||||
m_sampleRate(0),
|
||||
m_centerFrequency(0),
|
||||
m_tickCount(0),
|
||||
m_doApplySettings(true),
|
||||
m_forceSettings(true),
|
||||
m_deviceGains(nullptr),
|
||||
@@ -71,12 +74,15 @@ RemoteTCPInputGui::RemoteTCPInputGui(DeviceUISet *deviceUISet, QWidget* parent)
|
||||
ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
|
||||
ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999);
|
||||
|
||||
ui->channelPowerMeter->setColorTheme(LevelMeterSignalDB::ColorGreenAndBlue);
|
||||
|
||||
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(openDeviceSettingsDialog(const QPoint &)));
|
||||
|
||||
displaySettings();
|
||||
|
||||
connect(&m_statusTimer, SIGNAL(timeout()), this, SLOT(updateStatus()));
|
||||
m_statusTimer.start(500);
|
||||
connect(deviceUISet->m_deviceAPI, &DeviceAPI::stateChanged, this, &RemoteTCPInputGui::updateStatus);
|
||||
updateStatus();
|
||||
|
||||
connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateHardware()));
|
||||
|
||||
m_sampleSource = (RemoteTCPInput*) m_deviceUISet->m_deviceAPI->getSampleSource();
|
||||
@@ -89,11 +95,12 @@ RemoteTCPInputGui::RemoteTCPInputGui(DeviceUISet *deviceUISet, QWidget* parent)
|
||||
makeUIConnections();
|
||||
DialPopup::addPopupsToChildDials(this);
|
||||
m_resizer.enableChildMouseTracking();
|
||||
|
||||
connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick()));
|
||||
}
|
||||
|
||||
RemoteTCPInputGui::~RemoteTCPInputGui()
|
||||
{
|
||||
m_statusTimer.stop();
|
||||
m_updateTimer.stop();
|
||||
delete ui;
|
||||
}
|
||||
@@ -110,6 +117,7 @@ void RemoteTCPInputGui::destroy()
|
||||
|
||||
void RemoteTCPInputGui::resetToDefaults()
|
||||
{
|
||||
qDebug() << "*************** RemoteTCPInputGui::resetToDefaults";
|
||||
m_settings.resetToDefaults();
|
||||
displaySettings();
|
||||
m_forceSettings = true;
|
||||
@@ -150,6 +158,7 @@ bool RemoteTCPInputGui::handleMessage(const Message& message)
|
||||
} else {
|
||||
m_settings.applySettings(cfg.getSettingsKeys(), cfg.getSettings());
|
||||
}
|
||||
qDebug() << "********* RemoteTCPInputGui::handleMessage MsgConfigureRemoteTCPInput" << m_settings.m_dataAddress;
|
||||
|
||||
blockApplySettings(true);
|
||||
displaySettings();
|
||||
@@ -224,24 +233,27 @@ bool RemoteTCPInputGui::handleMessage(const Message& message)
|
||||
ui->detectedProtocol->setText(QString("Protocol: %1").arg(report.getProtocol()));
|
||||
|
||||
// Update GUI so we only show widgets available for the protocol in use
|
||||
bool sdra = report.getProtocol() == "SDRA";
|
||||
bool spyServer = report.getProtocol() == "Spy Server";
|
||||
if (spyServer) {
|
||||
m_sdra = report.getProtocol() == "SDRA";
|
||||
m_spyServer = report.getProtocol() == "Spy Server";
|
||||
m_remoteControl = report.getRemoteControl();
|
||||
m_iqOnly = report.getIQOnly();
|
||||
|
||||
if (m_spyServer) {
|
||||
m_spyServerGains.m_gains[0].m_max = report.getMaxGain();
|
||||
}
|
||||
if ((sdra || spyServer) && (ui->sampleBits->count() < 4))
|
||||
if ((m_sdra || m_spyServer) && (ui->sampleBits->count() < 4))
|
||||
{
|
||||
ui->sampleBits->addItem("16");
|
||||
ui->sampleBits->addItem("24");
|
||||
ui->sampleBits->addItem("32");
|
||||
}
|
||||
else if (!(sdra || spyServer) && (ui->sampleBits->count() != 1))
|
||||
else if (!(m_sdra || m_spyServer) && (ui->sampleBits->count() != 1))
|
||||
{
|
||||
while (ui->sampleBits->count() > 1) {
|
||||
ui->sampleBits->removeItem(ui->sampleBits->count() - 1);
|
||||
}
|
||||
}
|
||||
if ((sdra || spyServer) && (ui->decim->count() != 7))
|
||||
if ((m_sdra || m_spyServer) && (ui->decim->count() != 7))
|
||||
{
|
||||
ui->decim->addItem("2");
|
||||
ui->decim->addItem("4");
|
||||
@@ -250,26 +262,19 @@ bool RemoteTCPInputGui::handleMessage(const Message& message)
|
||||
ui->decim->addItem("32");
|
||||
ui->decim->addItem("64");
|
||||
}
|
||||
else if (!(sdra || spyServer) && (ui->decim->count() != 1))
|
||||
else if (!(m_sdra || m_spyServer) && (ui->decim->count() != 1))
|
||||
{
|
||||
while (ui->decim->count() > 1) {
|
||||
ui->decim->removeItem(ui->decim->count() - 1);
|
||||
}
|
||||
}
|
||||
if (!sdra)
|
||||
if (!m_sdra)
|
||||
{
|
||||
ui->deltaFrequency->setValue(0);
|
||||
ui->channelGain->setValue(0);
|
||||
ui->decimation->setChecked(true);
|
||||
}
|
||||
ui->deltaFrequencyLabel->setEnabled(sdra);
|
||||
ui->deltaFrequency->setEnabled(sdra);
|
||||
ui->deltaUnits->setEnabled(sdra);
|
||||
ui->channelGainLabel->setEnabled(sdra);
|
||||
ui->channelGain->setEnabled(sdra);
|
||||
ui->channelGainText->setEnabled(sdra);
|
||||
ui->decimation->setEnabled(sdra);
|
||||
if (sdra) {
|
||||
if (m_sdra) {
|
||||
ui->centerFrequency->setValueRange(9, 0, 999999999); // Should add transverter control to protocol in the future
|
||||
} else {
|
||||
ui->centerFrequency->setValueRange(7, 0, 9999999);
|
||||
@@ -291,19 +296,7 @@ bool RemoteTCPInputGui::handleMessage(const Message& message)
|
||||
{
|
||||
ui->devSampleRate->setValueRange(8, 0, 99999999);
|
||||
}
|
||||
ui->devSampleRateLabel->setEnabled(!spyServer);
|
||||
ui->devSampleRate->setEnabled(!spyServer);
|
||||
ui->devSampleRateUnits->setEnabled(!spyServer);
|
||||
ui->agc->setEnabled(!spyServer);
|
||||
ui->rfBWLabel->setEnabled(!spyServer);
|
||||
ui->rfBW->setEnabled(!spyServer);
|
||||
ui->rfBWUnits->setEnabled(!spyServer);
|
||||
ui->dcOffset->setEnabled(!spyServer);
|
||||
ui->iqImbalance->setEnabled(!spyServer);
|
||||
ui->ppm->setEnabled(!spyServer);
|
||||
ui->ppmLabel->setEnabled(!spyServer);
|
||||
ui->ppmText->setEnabled(!spyServer);
|
||||
|
||||
displayEnabled();
|
||||
displayGains();
|
||||
return true;
|
||||
}
|
||||
@@ -314,13 +307,33 @@ bool RemoteTCPInputGui::handleMessage(const Message& message)
|
||||
if (report.getConnected())
|
||||
{
|
||||
m_connectionError = false;
|
||||
ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
|
||||
//ui->startStop->setStyleSheet("QToolButton { background-color : green; }");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_connectionError = true;
|
||||
ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
|
||||
//ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
|
||||
}
|
||||
updateStatus();
|
||||
return true;
|
||||
}
|
||||
else if (RemoteTCPInput::MsgSendMessage::match(message))
|
||||
{
|
||||
const RemoteTCPInput::MsgSendMessage& msg = (const RemoteTCPInput::MsgSendMessage&) message;
|
||||
|
||||
ui->messages->addItem(QString("%1> %2").arg(msg.getCallsign()).arg(msg.getText()));
|
||||
ui->messages->scrollToBottom();
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (RemoteTCPInput::MsgReportPosition::match(message))
|
||||
{
|
||||
// Could display in future
|
||||
return true;
|
||||
}
|
||||
else if (RemoteTCPInput::MsgReportDirection::match(message))
|
||||
{
|
||||
// Could display in future
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -329,6 +342,87 @@ bool RemoteTCPInputGui::handleMessage(const Message& message)
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::displayEnabled()
|
||||
{
|
||||
int state = m_deviceUISet->m_deviceAPI->state();
|
||||
bool remoteControl;
|
||||
bool enableMessages;
|
||||
bool enableSquelchEnable;
|
||||
bool enableSquelch;
|
||||
bool sdra;
|
||||
|
||||
if (state == DeviceAPI::StRunning)
|
||||
{
|
||||
sdra = m_sdra;
|
||||
remoteControl = m_remoteControl;
|
||||
enableMessages = !m_iqOnly;
|
||||
enableSquelchEnable = !m_iqOnly;
|
||||
enableSquelch = !m_iqOnly && m_settings.m_squelchEnabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
sdra = m_settings.m_protocol == "SDRangel";
|
||||
remoteControl = m_settings.m_overrideRemoteSettings;
|
||||
enableMessages = false;
|
||||
enableSquelchEnable = m_settings.m_overrideRemoteSettings;
|
||||
enableSquelch = m_settings.m_overrideRemoteSettings && m_settings.m_squelchEnabled;
|
||||
}
|
||||
|
||||
ui->deltaFrequencyLabel->setEnabled(sdra && remoteControl);
|
||||
ui->deltaFrequency->setEnabled(sdra && remoteControl);
|
||||
ui->deltaUnits->setEnabled(sdra && remoteControl);
|
||||
ui->channelGainLabel->setEnabled(sdra && remoteControl);
|
||||
ui->channelGain->setEnabled(sdra && remoteControl);
|
||||
ui->channelGainText->setEnabled(sdra && remoteControl);
|
||||
ui->decimation->setEnabled(sdra && remoteControl);
|
||||
|
||||
ui->channelSampleRate->setEnabled(m_settings.m_channelDecimation && sdra && remoteControl);
|
||||
ui->channelSampleRateLabel->setEnabled(m_settings.m_channelDecimation && sdra && remoteControl);
|
||||
ui->channelSampleRateUnit->setEnabled(m_settings.m_channelDecimation && sdra && remoteControl);
|
||||
|
||||
ui->devSampleRateLabel->setEnabled(!m_spyServer && remoteControl);
|
||||
ui->devSampleRate->setEnabled(!m_spyServer && remoteControl);
|
||||
ui->devSampleRateUnits->setEnabled(!m_spyServer && remoteControl);
|
||||
ui->agc->setEnabled(!m_spyServer && remoteControl);
|
||||
ui->rfBWLabel->setEnabled(!m_spyServer && remoteControl);
|
||||
ui->rfBW->setEnabled(!m_spyServer && remoteControl);
|
||||
ui->rfBWUnits->setEnabled(!m_spyServer && remoteControl);
|
||||
ui->dcOffset->setEnabled(!m_spyServer && remoteControl);
|
||||
ui->iqImbalance->setEnabled(!m_spyServer && remoteControl);
|
||||
ui->ppm->setEnabled(!m_spyServer && remoteControl);
|
||||
ui->ppmLabel->setEnabled(!m_spyServer && remoteControl);
|
||||
ui->ppmText->setEnabled(!m_spyServer && remoteControl);
|
||||
|
||||
ui->centerFrequency->setEnabled(remoteControl);
|
||||
ui->biasTee->setEnabled(remoteControl);
|
||||
ui->directSampling->setEnabled(remoteControl);
|
||||
ui->decimLabel->setEnabled(remoteControl);
|
||||
ui->decim->setEnabled(remoteControl);
|
||||
ui->gain1Label->setEnabled(remoteControl);
|
||||
ui->gain1->setEnabled(remoteControl);
|
||||
ui->gain1Text->setEnabled(remoteControl);
|
||||
ui->gain2Label->setEnabled(remoteControl);
|
||||
ui->gain2->setEnabled(remoteControl);
|
||||
ui->gain2Text->setEnabled(remoteControl);
|
||||
ui->gain3Label->setEnabled(remoteControl);
|
||||
ui->gain3->setEnabled(remoteControl);
|
||||
ui->gain3Text->setEnabled(remoteControl);
|
||||
ui->sampleBitsLabel->setEnabled(remoteControl);
|
||||
ui->sampleBits->setEnabled(remoteControl);
|
||||
ui->sampleBitsUnits->setEnabled(remoteControl);
|
||||
|
||||
ui->squelchEnabled->setEnabled(enableSquelchEnable);
|
||||
ui->squelch->setEnabled(enableSquelch);
|
||||
ui->squelchText->setEnabled(enableSquelch);
|
||||
ui->squelchUnits->setEnabled(enableSquelch);
|
||||
ui->squelchGate->setEnabled(enableSquelch);
|
||||
|
||||
ui->sendMessage->setEnabled(enableMessages);
|
||||
ui->txAddress->setEnabled(enableMessages);
|
||||
ui->txMessage->setEnabled(enableMessages);
|
||||
ui->messages->setEnabled(enableMessages);
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::handleInputMessages()
|
||||
{
|
||||
Message* message;
|
||||
@@ -386,12 +480,14 @@ void RemoteTCPInputGui::displaySettings()
|
||||
ui->channelSampleRate->setValue(m_settings.m_channelSampleRate);
|
||||
ui->deviceRateText->setText(tr("%1k").arg(m_settings.m_channelSampleRate / 1000.0));
|
||||
ui->decimation->setChecked(!m_settings.m_channelDecimation);
|
||||
ui->channelSampleRate->setEnabled(m_settings.m_channelDecimation);
|
||||
ui->channelSampleRateLabel->setEnabled(m_settings.m_channelDecimation);
|
||||
ui->channelSampleRateUnit->setEnabled(m_settings.m_channelDecimation);
|
||||
ui->sampleBits->setCurrentText(QString::number(m_settings.m_sampleBits));
|
||||
|
||||
ui->dataPort->setText(tr("%1").arg(m_settings.m_dataPort));
|
||||
ui->squelchEnabled->setChecked(m_settings.m_squelchEnabled);
|
||||
ui->squelch->setValue(m_settings.m_squelch);
|
||||
ui->squelchText->setText(QString::number(m_settings.m_squelch));
|
||||
ui->squelchGate->setValue(m_settings.m_squelchGate);
|
||||
|
||||
ui->dataPort->setValue(m_settings.m_dataPort);
|
||||
ui->dataAddress->blockSignals(true);
|
||||
ui->dataAddress->clear();
|
||||
for (const auto& address : m_settings.m_addressList) {
|
||||
@@ -412,6 +508,11 @@ void RemoteTCPInputGui::displaySettings()
|
||||
}
|
||||
|
||||
displayGains();
|
||||
displayReplayLength();
|
||||
displayReplayOffset();
|
||||
displayReplayStep();
|
||||
ui->replayLoop->setChecked(m_settings.m_replayLoop);
|
||||
displayEnabled();
|
||||
blockApplySettings(false);
|
||||
}
|
||||
|
||||
@@ -517,6 +618,7 @@ const QHash<RemoteTCPProtocol::Device, const RemoteTCPInputGui::DeviceGains *> R
|
||||
{
|
||||
{RemoteTCPProtocol::RTLSDR_E4000, &m_rtlSDRe4kGains},
|
||||
{RemoteTCPProtocol::RTLSDR_R820T, &m_rtlSDRR820Gains},
|
||||
{RemoteTCPProtocol::RTLSDR_R828D, &m_rtlSDRR820Gains},
|
||||
{RemoteTCPProtocol::AIRSPY, &m_airspyGains},
|
||||
{RemoteTCPProtocol::AIRSPY_HF, &m_airspyHFGains},
|
||||
{RemoteTCPProtocol::BLADE_RF1, &m_baldeRF1Gains},
|
||||
@@ -609,7 +711,12 @@ void RemoteTCPInputGui::on_startStop_toggled(bool checked)
|
||||
{
|
||||
if (m_doApplySettings)
|
||||
{
|
||||
m_connectionError = false;
|
||||
if (m_connectionError)
|
||||
{
|
||||
// Clear previous error
|
||||
m_connectionError = false;
|
||||
updateStatus();
|
||||
}
|
||||
RemoteTCPInput::MsgStartStop *message = RemoteTCPInput::MsgStartStop::create(checked);
|
||||
m_sampleSource->getInputMessageQueue()->push(message);
|
||||
}
|
||||
@@ -789,6 +896,29 @@ void RemoteTCPInputGui::on_sampleBits_currentIndexChanged(int index)
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_squelchEnabled_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_squelchEnabled = checked;
|
||||
m_settingsKeys.append("squelchEnabled");
|
||||
displayEnabled();
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_squelch_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_squelch = value;
|
||||
ui->squelchText->setText(QString::number(m_settings.m_squelch));
|
||||
m_settingsKeys.append("squelch");
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_squelchGate_valueChanged(double value)
|
||||
{
|
||||
m_settings.m_squelchGate = value;
|
||||
m_settingsKeys.append("squelchGate");
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_dataAddress_editingFinished()
|
||||
{
|
||||
m_settings.m_dataAddress = ui->dataAddress->currentText();
|
||||
@@ -810,17 +940,9 @@ void RemoteTCPInputGui::on_dataAddress_currentIndexChanged(int index)
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_dataPort_editingFinished()
|
||||
void RemoteTCPInputGui::on_dataPort_valueChanged(int value)
|
||||
{
|
||||
bool ok;
|
||||
quint16 udpPort = ui->dataPort->text().toInt(&ok);
|
||||
|
||||
if ((!ok) || (udpPort < 1024)) {
|
||||
udpPort = 9998;
|
||||
}
|
||||
|
||||
m_settings.m_dataPort = udpPort;
|
||||
ui->dataPort->setText(tr("%1").arg(m_settings.m_dataPort));
|
||||
m_settings.m_dataPort = value;
|
||||
m_settingsKeys.append("dataPort");
|
||||
|
||||
sendSettings();
|
||||
@@ -831,6 +953,7 @@ void RemoteTCPInputGui::on_overrideRemoteSettings_toggled(bool checked)
|
||||
m_settings.m_overrideRemoteSettings = checked;
|
||||
m_settingsKeys.append("overrideRemoteSettings");
|
||||
sendSettings();
|
||||
displayEnabled();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_preFill_valueChanged(int value)
|
||||
@@ -867,10 +990,14 @@ void RemoteTCPInputGui::updateHardware()
|
||||
|
||||
void RemoteTCPInputGui::updateStatus()
|
||||
{
|
||||
int state = m_deviceUISet->m_deviceAPI->state();
|
||||
|
||||
if (!m_connectionError && (m_lastEngineState != state))
|
||||
if (m_connectionError)
|
||||
{
|
||||
ui->startStop->setStyleSheet("QToolButton { background-color : red; }");
|
||||
}
|
||||
else
|
||||
{
|
||||
int state = m_deviceUISet->m_deviceAPI->state();
|
||||
|
||||
switch(state)
|
||||
{
|
||||
case DeviceAPI::StNotStarted:
|
||||
@@ -889,9 +1016,28 @@ void RemoteTCPInputGui::updateStatus()
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_lastEngineState = state;
|
||||
}
|
||||
displayEnabled();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::tick()
|
||||
{
|
||||
double magsqAvg, magsqPeak;
|
||||
int nbMagsqSamples;
|
||||
m_sampleSource->getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples);
|
||||
double powDbAvg = CalcDb::dbPower(magsqAvg);
|
||||
double powDbPeak = CalcDb::dbPower(magsqPeak);
|
||||
|
||||
ui->channelPowerMeter->levelChanged(
|
||||
(100.0f + powDbAvg) / 100.0f,
|
||||
(100.0f + powDbPeak) / 100.0f,
|
||||
nbMagsqSamples);
|
||||
|
||||
if (m_tickCount % 4 == 0) {
|
||||
ui->channelPower->setText(tr("%1").arg(powDbAvg, 0, 'f', 1));
|
||||
}
|
||||
|
||||
m_tickCount++;
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::openDeviceSettingsDialog(const QPoint& p)
|
||||
@@ -899,6 +1045,9 @@ void RemoteTCPInputGui::openDeviceSettingsDialog(const QPoint& p)
|
||||
if (m_contextMenuType == ContextMenuDeviceSettings)
|
||||
{
|
||||
BasicDeviceSettingsDialog dialog(this);
|
||||
dialog.setReplayBytesPerSecond(m_settings.m_devSampleRate * 2 * sizeof(FixReal));
|
||||
dialog.setReplayLength(m_settings.m_replayLength);
|
||||
dialog.setReplayStep(m_settings.m_replayStep);
|
||||
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
|
||||
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
|
||||
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
|
||||
@@ -908,6 +1057,11 @@ void RemoteTCPInputGui::openDeviceSettingsDialog(const QPoint& p)
|
||||
new DialogPositioner(&dialog, false);
|
||||
dialog.exec();
|
||||
|
||||
m_settings.m_replayLength = dialog.getReplayLength();
|
||||
m_settings.m_replayStep = dialog.getReplayStep();
|
||||
displayReplayLength();
|
||||
displayReplayOffset();
|
||||
displayReplayStep();
|
||||
m_settings.m_useReverseAPI = dialog.useReverseAPI();
|
||||
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
|
||||
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
|
||||
@@ -919,6 +1073,110 @@ void RemoteTCPInputGui::openDeviceSettingsDialog(const QPoint& p)
|
||||
resetContextMenuType();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::displayReplayLength()
|
||||
{
|
||||
bool replayEnabled = m_settings.m_replayLength > 0.0f;
|
||||
if (!replayEnabled) {
|
||||
ui->replayOffset->setMaximum(0);
|
||||
} else {
|
||||
ui->replayOffset->setMaximum(m_settings.m_replayLength * 10 - 1);
|
||||
}
|
||||
ui->replayLabel->setEnabled(replayEnabled);
|
||||
ui->replayOffset->setEnabled(replayEnabled);
|
||||
ui->replayOffsetText->setEnabled(replayEnabled);
|
||||
ui->replaySave->setEnabled(replayEnabled);
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::displayReplayOffset()
|
||||
{
|
||||
bool replayEnabled = m_settings.m_replayLength > 0.0f;
|
||||
ui->replayOffset->setValue(m_settings.m_replayOffset * 10);
|
||||
ui->replayOffsetText->setText(QString("%1s").arg(m_settings.m_replayOffset, 0, 'f', 1));
|
||||
ui->replayNow->setEnabled(replayEnabled && (m_settings.m_replayOffset > 0.0f));
|
||||
ui->replayPlus->setEnabled(replayEnabled && (std::round(m_settings.m_replayOffset * 10) < ui->replayOffset->maximum()));
|
||||
ui->replayMinus->setEnabled(replayEnabled && (m_settings.m_replayOffset > 0.0f));
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::displayReplayStep()
|
||||
{
|
||||
QString step;
|
||||
float intpart;
|
||||
float frac = modf(m_settings.m_replayStep, &intpart);
|
||||
if (frac == 0.0f) {
|
||||
step = QString::number((int)intpart);
|
||||
} else {
|
||||
step = QString::number(m_settings.m_replayStep, 'f', 1);
|
||||
}
|
||||
ui->replayPlus->setText(QString("+%1s").arg(step));
|
||||
ui->replayPlus->setToolTip(QString("Add %1 seconds to time delay").arg(step));
|
||||
ui->replayMinus->setText(QString("-%1s").arg(step));
|
||||
ui->replayMinus->setToolTip(QString("Remove %1 seconds from time delay").arg(step));
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_replayOffset_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_replayOffset = value / 10.0f;
|
||||
displayReplayOffset();
|
||||
m_settingsKeys.append("replayOffset");
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_replayNow_clicked()
|
||||
{
|
||||
ui->replayOffset->setValue(0);
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_replayPlus_clicked()
|
||||
{
|
||||
ui->replayOffset->setValue(ui->replayOffset->value() + m_settings.m_replayStep * 10);
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_replayMinus_clicked()
|
||||
{
|
||||
ui->replayOffset->setValue(ui->replayOffset->value() - m_settings.m_replayStep * 10);
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_replaySave_clicked()
|
||||
{
|
||||
QFileDialog fileDialog(nullptr, "Select file to save IQ data to", "", "*.wav");
|
||||
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||
if (fileDialog.exec())
|
||||
{
|
||||
QStringList fileNames = fileDialog.selectedFiles();
|
||||
if (fileNames.size() > 0)
|
||||
{
|
||||
RemoteTCPInput::MsgSaveReplay *message = RemoteTCPInput ::MsgSaveReplay::create(fileNames[0]);
|
||||
m_sampleSource->getInputMessageQueue()->push(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_replayLoop_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_replayLoop = checked;
|
||||
m_settingsKeys.append("replayLoop");
|
||||
sendSettings();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_sendMessage_clicked()
|
||||
{
|
||||
QString message = ui->txMessage->text().trimmed();
|
||||
if (!message.isEmpty())
|
||||
{
|
||||
ui->messages->addItem(QString("< %1").arg(message));
|
||||
ui->messages->scrollToBottom();
|
||||
bool broadcast = ui->txAddress->currentText() == "All";
|
||||
QString callsign = MainCore::instance()->getSettings().getStationName();
|
||||
m_sampleSource->getInputMessageQueue()->push(RemoteTCPInput::MsgSendMessage::create(callsign, message, broadcast));
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::on_txMessage_returnPressed()
|
||||
{
|
||||
on_sendMessage_clicked();
|
||||
ui->txMessage->selectAll();
|
||||
}
|
||||
|
||||
void RemoteTCPInputGui::makeUIConnections()
|
||||
{
|
||||
QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_startStop_toggled);
|
||||
@@ -940,10 +1198,21 @@ void RemoteTCPInputGui::makeUIConnections()
|
||||
QObject::connect(ui->channelSampleRate, &ValueDial::changed, this, &RemoteTCPInputGui::on_channelSampleRate_changed);
|
||||
QObject::connect(ui->decimation, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_decimation_toggled);
|
||||
QObject::connect(ui->sampleBits, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RemoteTCPInputGui::on_sampleBits_currentIndexChanged);
|
||||
QObject::connect(ui->squelchEnabled, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_squelchEnabled_toggled);
|
||||
QObject::connect(ui->squelch, &QDial::valueChanged, this, &RemoteTCPInputGui::on_squelch_valueChanged);
|
||||
QObject::connect(ui->squelchGate, &PeriodDial::valueChanged, this, &RemoteTCPInputGui::on_squelchGate_valueChanged);
|
||||
QObject::connect(ui->dataAddress->lineEdit(), &QLineEdit::editingFinished, this, &RemoteTCPInputGui::on_dataAddress_editingFinished);
|
||||
QObject::connect(ui->dataAddress, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RemoteTCPInputGui::on_dataAddress_currentIndexChanged);
|
||||
QObject::connect(ui->dataPort, &QLineEdit::editingFinished, this, &RemoteTCPInputGui::on_dataPort_editingFinished);
|
||||
QObject::connect(ui->dataPort, QOverload<int>::of(&QSpinBox::valueChanged), this, &RemoteTCPInputGui::on_dataPort_valueChanged);
|
||||
QObject::connect(ui->overrideRemoteSettings, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_overrideRemoteSettings_toggled);
|
||||
QObject::connect(ui->preFill, &QDial::valueChanged, this, &RemoteTCPInputGui::on_preFill_valueChanged);
|
||||
QObject::connect(ui->protocol, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &RemoteTCPInputGui::on_protocol_currentIndexChanged);
|
||||
QObject::connect(ui->replayOffset, &QSlider::valueChanged, this, &RemoteTCPInputGui::on_replayOffset_valueChanged);
|
||||
QObject::connect(ui->replayNow, &QToolButton::clicked, this, &RemoteTCPInputGui::on_replayNow_clicked);
|
||||
QObject::connect(ui->replayPlus, &QToolButton::clicked, this, &RemoteTCPInputGui::on_replayPlus_clicked);
|
||||
QObject::connect(ui->replayMinus, &QToolButton::clicked, this, &RemoteTCPInputGui::on_replayMinus_clicked);
|
||||
QObject::connect(ui->replaySave, &QToolButton::clicked, this, &RemoteTCPInputGui::on_replaySave_clicked);
|
||||
QObject::connect(ui->replayLoop, &ButtonSwitch::toggled, this, &RemoteTCPInputGui::on_replayLoop_toggled);
|
||||
QObject::connect(ui->sendMessage, &QToolButton::clicked, this, &RemoteTCPInputGui::on_sendMessage_clicked);
|
||||
QObject::connect(ui->txMessage, &QLineEdit::returnPressed, this, &RemoteTCPInputGui::on_txMessage_returnPressed);
|
||||
}
|
||||
|
||||
@@ -108,12 +108,11 @@ private:
|
||||
QList<QString> m_settingsKeys;
|
||||
RemoteTCPInput* m_sampleSource;
|
||||
QTimer m_updateTimer;
|
||||
QTimer m_statusTimer;
|
||||
int m_lastEngineState;
|
||||
MessageQueue m_inputMessageQueue;
|
||||
|
||||
int m_sampleRate;
|
||||
quint64 m_centerFrequency;
|
||||
uint32_t m_tickCount;
|
||||
|
||||
bool m_doApplySettings;
|
||||
bool m_forceSettings;
|
||||
@@ -125,6 +124,11 @@ private:
|
||||
DeviceGains::GainRange m_spyServerGainRange;
|
||||
DeviceGains m_spyServerGains;
|
||||
|
||||
bool m_sdra;
|
||||
bool m_spyServer;
|
||||
bool m_remoteControl;
|
||||
bool m_iqOnly;
|
||||
|
||||
static const DeviceGains::GainRange m_rtlSDR34kGainRange;
|
||||
static const DeviceGains m_rtlSDRe4kGains;
|
||||
static const DeviceGains::GainRange m_rtlSDRR820GainRange;
|
||||
@@ -175,9 +179,13 @@ private:
|
||||
void blockApplySettings(bool block);
|
||||
void displaySettings();
|
||||
QString gainText(int stage);
|
||||
void displayEnabled();
|
||||
void displayGains();
|
||||
void displayRemoteSettings();
|
||||
void displayRemoteShift();
|
||||
void displayReplayLength();
|
||||
void displayReplayOffset();
|
||||
void displayReplayStep();
|
||||
void sendSettings();
|
||||
void updateSampleRateAndFrequency();
|
||||
void applyDecimation();
|
||||
@@ -206,15 +214,27 @@ private slots:
|
||||
void on_channelSampleRate_changed(quint64 value);
|
||||
void on_decimation_toggled(bool checked);
|
||||
void on_sampleBits_currentIndexChanged(int index);
|
||||
void on_squelchEnabled_toggled(bool checked);
|
||||
void on_squelch_valueChanged(int value);
|
||||
void on_squelchGate_valueChanged(double value);
|
||||
void on_dataAddress_editingFinished();
|
||||
void on_dataAddress_currentIndexChanged(int index);
|
||||
void on_dataPort_editingFinished();
|
||||
void on_dataPort_valueChanged(int value);
|
||||
void on_overrideRemoteSettings_toggled(bool checked);
|
||||
void on_preFill_valueChanged(int value);
|
||||
void on_protocol_currentIndexChanged(int index);
|
||||
void on_replayOffset_valueChanged(int value);
|
||||
void on_replayNow_clicked();
|
||||
void on_replayPlus_clicked();
|
||||
void on_replayMinus_clicked();
|
||||
void on_replaySave_clicked();
|
||||
void on_replayLoop_toggled(bool checked);
|
||||
void on_sendMessage_clicked();
|
||||
void on_txMessage_returnPressed();
|
||||
void updateHardware();
|
||||
void updateStatus();
|
||||
void openDeviceSettingsDialog(const QPoint& p);
|
||||
void tick();
|
||||
};
|
||||
|
||||
#endif // INCLUDE_REMOTETCPINPUTGUI_H
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>360</width>
|
||||
<height>360</height>
|
||||
<height>586</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@@ -19,13 +19,13 @@
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>360</width>
|
||||
<height>360</height>
|
||||
<height>500</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>491</width>
|
||||
<height>360</height>
|
||||
<width>533</width>
|
||||
<height>610</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
@@ -863,6 +863,184 @@ Use to ensure full dynamic range of 8-bit data is used.</string>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_9">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="signalLevelLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="channelPowerMeterUnits">
|
||||
<property name="text">
|
||||
<string>dB</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="LevelMeterSignalDB" name="channelPowerMeter" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Mono</family>
|
||||
<pointsize>8</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Level meter (dB) top trace: average, bottom trace: instantaneous peak, tip: peak hold</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="squelchLayout">
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="squelchEnabled">
|
||||
<property name="toolTip">
|
||||
<string>Check to enable IQ squelch</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>SQ</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_12">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDial" name="squelch">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>24</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>IQ squelch power level in dB</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>-150</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="squelchText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>-150</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="squelchUnits">
|
||||
<property name="text">
|
||||
<string>dB</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_13">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="PeriodDial" name="squelchGate" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>IQ squelch gate time</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_14">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_8">
|
||||
<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="channelPower">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Channel power</string>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0.0</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="channelPowerUnits">
|
||||
<property name="text">
|
||||
<string> dB</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_6">
|
||||
<property name="orientation">
|
||||
@@ -914,33 +1092,18 @@ Use to ensure full dynamic range of 8-bit data is used.</string>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="dataPort">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<widget class="QSpinBox" name="dataPort">
|
||||
<property name="toolTip">
|
||||
<string>Remote data port (rtl_tcp defaults to 1234)</string>
|
||||
</property>
|
||||
<property name="inputMask">
|
||||
<string>00000</string>
|
||||
<property name="minimum">
|
||||
<number>1024</number>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0</string>
|
||||
<property name="maximum">
|
||||
<number>65535</number>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
<property name="value">
|
||||
<number>1234</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -961,7 +1124,7 @@ Use to ensure full dynamic range of 8-bit data is used.</string>
|
||||
<widget class="QComboBox" name="protocol">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>75</width>
|
||||
<width>92</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
@@ -1162,6 +1325,182 @@ This should typically be empty. If full, your CPU cannot keep up and data will b
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_8">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="txMessagesLayout">
|
||||
<item>
|
||||
<widget class="QToolButton" name="sendMessage">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Click to send message</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TX</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="txAddress">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Who to send message to</string>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Host</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>All</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="txMessage">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Message to transmit</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="messagesLayout">
|
||||
<item>
|
||||
<widget class="QListWidget" name="messages">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Messages</string>
|
||||
</property>
|
||||
<property name="movement">
|
||||
<enum>QListView::Static</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="replayLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="replayLabel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>65</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Time Delay</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="replayOffset">
|
||||
<property name="toolTip">
|
||||
<string>Replay time delay in seconds</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>500</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="replayOffsetText">
|
||||
<property name="toolTip">
|
||||
<string>Replay time delay in seconds</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0.0s</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="replayNow">
|
||||
<property name="toolTip">
|
||||
<string>Set time delay to 0 seconds</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Now</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="replayPlus">
|
||||
<property name="toolTip">
|
||||
<string>Add displayed number of seconds to time delay</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>+5s</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="replayMinus">
|
||||
<property name="toolTip">
|
||||
<string>Remove displayed number of seconds from time delay</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>-5s</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="replayLoop">
|
||||
<property name="toolTip">
|
||||
<string>Repeatedly replay data in replay buffer</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/playloop.png</normaloff>:/playloop.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="replaySave">
|
||||
<property name="toolTip">
|
||||
<string>Save replay buffer to a file</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/save.png</normaloff>:/save.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_5">
|
||||
<property name="orientation">
|
||||
@@ -1214,6 +1553,18 @@ This should typically be empty. If full, your CPU cannot keep up and data will b
|
||||
<header>gui/valuedial.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>LevelMeterSignalDB</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/levelmeter.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>PeriodDial</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>gui/perioddial.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>startStop</tabstop>
|
||||
@@ -1232,7 +1583,8 @@ This should typically be empty. If full, your CPU cannot keep up and data will b
|
||||
<tabstop>channelGain</tabstop>
|
||||
<tabstop>decimation</tabstop>
|
||||
<tabstop>sampleBits</tabstop>
|
||||
<tabstop>dataPort</tabstop>
|
||||
<tabstop>dataAddress</tabstop>
|
||||
<tabstop>protocol</tabstop>
|
||||
<tabstop>overrideRemoteSettings</tabstop>
|
||||
<tabstop>preFill</tabstop>
|
||||
</tabstops>
|
||||
|
||||
@@ -53,6 +53,13 @@ void RemoteTCPInputSettings::resetToDefaults()
|
||||
m_reverseAPIAddress = "127.0.0.1";
|
||||
m_reverseAPIPort = 8888;
|
||||
m_reverseAPIDeviceIndex = 0;
|
||||
m_replayOffset = 0.0f;
|
||||
m_replayLength = 20.0f;
|
||||
m_replayStep = 5.0f;
|
||||
m_replayLoop = false;
|
||||
m_squelchEnabled = false;
|
||||
m_squelch = -100.0f;
|
||||
m_squelchGate = 0.001f;
|
||||
}
|
||||
|
||||
QByteArray RemoteTCPInputSettings::serialize() const
|
||||
@@ -83,11 +90,19 @@ QByteArray RemoteTCPInputSettings::serialize() const
|
||||
s.writeU32(23, m_reverseAPIDeviceIndex);
|
||||
s.writeList(24, m_addressList);
|
||||
s.writeString(25, m_protocol);
|
||||
s.writeFloat(26, m_replayOffset);
|
||||
s.writeFloat(27, m_replayLength);
|
||||
s.writeFloat(28, m_replayStep);
|
||||
s.writeBool(29, m_replayLoop);
|
||||
|
||||
for (int i = 0; i < m_maxGains; i++) {
|
||||
s.writeS32(30+i, m_gain[i]);
|
||||
}
|
||||
|
||||
s.writeBool(40, m_squelchEnabled);
|
||||
s.writeFloat(41, m_squelch);
|
||||
s.writeFloat(42, m_squelchGate);
|
||||
|
||||
return s.final();
|
||||
}
|
||||
|
||||
@@ -140,10 +155,19 @@ bool RemoteTCPInputSettings::deserialize(const QByteArray& data)
|
||||
d.readList(24, &m_addressList);
|
||||
d.readString(25, &m_protocol, "SDRangel");
|
||||
|
||||
d.readFloat(26, &m_replayOffset, 0.0f);
|
||||
d.readFloat(27, &m_replayLength, 20.0f);
|
||||
d.readFloat(28, &m_replayStep, 5.0f);
|
||||
d.readBool(29, &m_replayLoop, false);
|
||||
|
||||
for (int i = 0; i < m_maxGains; i++) {
|
||||
d.readS32(30+i, &m_gain[i], 0);
|
||||
}
|
||||
|
||||
d.readBool(40, &m_squelchEnabled, false);
|
||||
d.readFloat(41, &m_squelch, -100.0f);
|
||||
d.readFloat(42, &m_squelchGate, 0.001f);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -212,7 +236,7 @@ void RemoteTCPInputSettings::applySettings(const QStringList& settingsKeys, cons
|
||||
if (settingsKeys.contains("preFill")) {
|
||||
m_preFill = settings.m_preFill;
|
||||
}
|
||||
if (settingsKeys.contains("_useReverseAPI")) {
|
||||
if (settingsKeys.contains("useReverseAPI")) {
|
||||
m_useReverseAPI = settings.m_useReverseAPI;
|
||||
}
|
||||
if (settingsKeys.contains("reverseAPIAddress")) {
|
||||
@@ -230,6 +254,27 @@ void RemoteTCPInputSettings::applySettings(const QStringList& settingsKeys, cons
|
||||
if (settingsKeys.contains("protocol")) {
|
||||
m_protocol = settings.m_protocol;
|
||||
}
|
||||
if (settingsKeys.contains("replayOffset")) {
|
||||
m_replayOffset = settings.m_replayOffset;
|
||||
}
|
||||
if (settingsKeys.contains("replayLength")) {
|
||||
m_replayLength = settings.m_replayLength;
|
||||
}
|
||||
if (settingsKeys.contains("replayStep")) {
|
||||
m_replayStep = settings.m_replayStep;
|
||||
}
|
||||
if (settingsKeys.contains("replayLoop")) {
|
||||
m_replayLoop = settings.m_replayLoop;
|
||||
}
|
||||
if (settingsKeys.contains("squelchEnabled")) {
|
||||
m_squelchEnabled = settings.m_squelchEnabled;
|
||||
}
|
||||
if (settingsKeys.contains("squelch")) {
|
||||
m_squelch = settings.m_squelch;
|
||||
}
|
||||
if (settingsKeys.contains("squelchGate")) {
|
||||
m_squelchGate = settings.m_squelchGate;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_maxGains; i++)
|
||||
{
|
||||
@@ -318,6 +363,27 @@ QString RemoteTCPInputSettings::getDebugString(const QStringList& settingsKeys,
|
||||
if (settingsKeys.contains("protocol") || force) {
|
||||
ostr << " m_protocol: " << m_protocol.toStdString();
|
||||
}
|
||||
if (settingsKeys.contains("replayOffset") || force) {
|
||||
ostr << " m_replayOffset: " << m_replayOffset;
|
||||
}
|
||||
if (settingsKeys.contains("replayLength") || force) {
|
||||
ostr << " m_replayLength: " << m_replayLength;
|
||||
}
|
||||
if (settingsKeys.contains("replayStep") || force) {
|
||||
ostr << " m_replayStep: " << m_replayStep;
|
||||
}
|
||||
if (settingsKeys.contains("replayLoop") || force) {
|
||||
ostr << " m_replayLoop: " << m_replayLoop;
|
||||
}
|
||||
if (settingsKeys.contains("squelchEnabled") || force) {
|
||||
ostr << " m_squelchEnabled: " << m_squelchEnabled;
|
||||
}
|
||||
if (settingsKeys.contains("squelch") || force) {
|
||||
ostr << " m_squelch: " << m_squelch;
|
||||
}
|
||||
if (settingsKeys.contains("squelchGate") || force) {
|
||||
ostr << " m_squelchGate: " << m_squelchGate;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_maxGains; i++)
|
||||
{
|
||||
|
||||
@@ -54,6 +54,13 @@ struct RemoteTCPInputSettings
|
||||
uint16_t m_reverseAPIDeviceIndex;
|
||||
QStringList m_addressList; // List of dataAddresses that have been used in the past
|
||||
QString m_protocol; // "SDRangel" or "Spy Server"
|
||||
float m_replayOffset; //!< Replay offset in seconds
|
||||
float m_replayLength; //!< Replay buffer size in seconds
|
||||
float m_replayStep; //!< Replay forward/back step size in seconds
|
||||
bool m_replayLoop; //!< Replay buffer repeatedly without recording new data
|
||||
bool m_squelchEnabled;
|
||||
float m_squelch;
|
||||
float m_squelchGate;
|
||||
|
||||
RemoteTCPInputSettings();
|
||||
void resetToDefaults();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,7 +26,12 @@
|
||||
#include <QRecursiveMutex>
|
||||
#include <QDateTime>
|
||||
|
||||
#include <FLAC/stream_decoder.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "util/messagequeue.h"
|
||||
#include "util/movingaverage.h"
|
||||
#include "dsp/replaybuffer.h"
|
||||
#include "remotetcpinputsettings.h"
|
||||
#include "../../channelrx/remotetcpsink/remotetcpprotocol.h"
|
||||
#include "spyserver.h"
|
||||
@@ -35,6 +40,31 @@ class SampleSinkFifo;
|
||||
class MessageQueue;
|
||||
class DeviceAPI;
|
||||
|
||||
class FIFO {
|
||||
public:
|
||||
|
||||
FIFO(qsizetype elements = 10);
|
||||
|
||||
qsizetype write(quint8 *data, qsizetype elements);
|
||||
qsizetype read(quint8 *data, qsizetype elements);
|
||||
qsizetype readPtr(quint8 **data, qsizetype elements);
|
||||
void read(qsizetype elements);
|
||||
void resize(qsizetype elements); // Sets capacity
|
||||
void clear();
|
||||
qsizetype fill() const { return m_fill; } // Number of elements in use
|
||||
bool empty() const { return m_fill == 0; }
|
||||
bool full() const { return m_fill == m_data.size(); }
|
||||
|
||||
private:
|
||||
|
||||
qsizetype m_readPtr;
|
||||
qsizetype m_writePtr;
|
||||
qsizetype m_fill;
|
||||
|
||||
QByteArray m_data;
|
||||
|
||||
};
|
||||
|
||||
class RemoteTCPInputTCPHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -71,22 +101,28 @@ public:
|
||||
public:
|
||||
RemoteTCPProtocol::Device getDevice() const { return m_device; }
|
||||
QString getProtocol() const { return m_protocol; }
|
||||
bool getIQOnly() const { return m_iqOnly; }
|
||||
bool getRemoteControl() const { return m_remoteControl; }
|
||||
int getMaxGain() const { return m_maxGain; }
|
||||
|
||||
static MsgReportRemoteDevice* create(RemoteTCPProtocol::Device device, const QString& protocol, int maxGain = 0)
|
||||
static MsgReportRemoteDevice* create(RemoteTCPProtocol::Device device, const QString& protocol, bool iqOnly, bool remoteControl, int maxGain = 0)
|
||||
{
|
||||
return new MsgReportRemoteDevice(device, protocol, maxGain);
|
||||
return new MsgReportRemoteDevice(device, protocol, iqOnly, remoteControl, maxGain);
|
||||
}
|
||||
|
||||
protected:
|
||||
RemoteTCPProtocol::Device m_device;
|
||||
QString m_protocol;
|
||||
bool m_iqOnly;
|
||||
bool m_remoteControl;
|
||||
int m_maxGain;
|
||||
|
||||
MsgReportRemoteDevice(RemoteTCPProtocol::Device device, const QString& protocol, int maxGain) :
|
||||
MsgReportRemoteDevice(RemoteTCPProtocol::Device device, const QString& protocol, bool iqOnly, bool remoteControl, int maxGain) :
|
||||
Message(),
|
||||
m_device(device),
|
||||
m_protocol(protocol),
|
||||
m_iqOnly(iqOnly),
|
||||
m_remoteControl(remoteControl),
|
||||
m_maxGain(maxGain)
|
||||
{ }
|
||||
};
|
||||
@@ -111,7 +147,7 @@ public:
|
||||
{ }
|
||||
};
|
||||
|
||||
RemoteTCPInputTCPHandler(SampleSinkFifo* sampleFifo, DeviceAPI *deviceAPI);
|
||||
RemoteTCPInputTCPHandler(SampleSinkFifo* sampleFifo, DeviceAPI *deviceAPI, ReplayBuffer<FixReal> *replayBuffer);
|
||||
~RemoteTCPInputTCPHandler();
|
||||
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
|
||||
void setMessageQueueToInput(MessageQueue *queue) { m_messageQueueToInput = queue; }
|
||||
@@ -120,6 +156,29 @@ public:
|
||||
void start();
|
||||
void stop();
|
||||
int getBufferGauge() const { return 0; }
|
||||
void processCommands();
|
||||
|
||||
FLAC__StreamDecoderReadStatus flacRead(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes);
|
||||
FLAC__StreamDecoderWriteStatus flacWrite(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[]);
|
||||
void flacError(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public slots:
|
||||
void dataReadyRead();
|
||||
@@ -129,11 +188,22 @@ public slots:
|
||||
|
||||
private:
|
||||
|
||||
struct MagSqLevelsStore
|
||||
{
|
||||
MagSqLevelsStore() :
|
||||
m_magsq(1e-12),
|
||||
m_magsqPeak(1e-12)
|
||||
{}
|
||||
double m_magsq;
|
||||
double m_magsqPeak;
|
||||
};
|
||||
|
||||
DeviceAPI *m_deviceAPI;
|
||||
bool m_running;
|
||||
QTcpSocket *m_dataSocket;
|
||||
char *m_tcpBuf;
|
||||
SampleSinkFifo *m_sampleFifo;
|
||||
ReplayBuffer<FixReal> *m_replayBuffer;
|
||||
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
|
||||
MessageQueue *m_messageQueueToInput;
|
||||
MessageQueue *m_messageQueueToGUI;
|
||||
@@ -148,6 +218,8 @@ private:
|
||||
SpyServerProtocol::Header m_spyServerHeader;
|
||||
enum {HEADER, DATA} m_state; //!< FSM for reading Spy Server packets
|
||||
|
||||
RemoteTCPProtocol::Command m_command;
|
||||
quint32 m_commandLength;
|
||||
|
||||
int32_t *m_converterBuffer;
|
||||
uint32_t m_converterBufferNbSamples;
|
||||
@@ -155,13 +227,38 @@ private:
|
||||
QRecursiveMutex m_mutex;
|
||||
RemoteTCPInputSettings m_settings;
|
||||
|
||||
void applyTCPLink(const QString& address, quint16 port);
|
||||
bool m_remoteControl;
|
||||
bool m_iqOnly;
|
||||
QByteArray m_compressedData;
|
||||
|
||||
// FLAC decompression
|
||||
qint64 m_compressedFrames;
|
||||
qint64 m_uncompressedFrames;
|
||||
FIFO m_uncompressedData;
|
||||
FLAC__StreamDecoder *m_decoder;
|
||||
int m_remainingSamples;
|
||||
|
||||
// Zlib decompression
|
||||
z_stream m_zStream;
|
||||
QByteArray m_zOutBuf;
|
||||
static const int m_zBufSize = 32768+128; //
|
||||
|
||||
bool m_blacklisted;
|
||||
|
||||
double m_magsq;
|
||||
double m_magsqSum;
|
||||
double m_magsqPeak;
|
||||
int m_magsqCount;
|
||||
MagSqLevelsStore m_magSqLevelStore;
|
||||
MovingAverageUtil<Real, double, 16> m_movingAverage;
|
||||
|
||||
bool handleMessage(const Message& message);
|
||||
void convert(int nbSamples);
|
||||
void connectToHost(const QString& address, quint16 port);
|
||||
void disconnectFromHost();
|
||||
//void disconnectFromHost();
|
||||
void cleanup();
|
||||
void clearBuffer();
|
||||
void sendCommand(RemoteTCPProtocol::Command cmd, quint32 value);
|
||||
void sendCommandFloat(RemoteTCPProtocol::Command cmd, float value);
|
||||
void setSampleRate(int sampleRate);
|
||||
void setCenterFrequency(quint64 frequency);
|
||||
void setTunerAGC(bool agc);
|
||||
@@ -180,6 +277,10 @@ private:
|
||||
void setChannelFreqOffset(int offset);
|
||||
void setChannelGain(int gain);
|
||||
void setSampleBitDepth(int sampleBits);
|
||||
void setSquelchEnabled(bool enabled);
|
||||
void setSquelch(float squelch);
|
||||
void setSquelchGate(float squelchGate);
|
||||
void sendMessage(const QString& callsign, const QString& text, bool broadcast);
|
||||
void applySettings(const RemoteTCPInputSettings& settings, const QList<QString>& settingsKeys, bool force = false);
|
||||
void processMetaData();
|
||||
void spyServerConnect();
|
||||
@@ -190,6 +291,11 @@ private:
|
||||
void processSpyServerDevice(const SpyServerProtocol::Device* ssDevice);
|
||||
void processSpyServerState(const SpyServerProtocol::State* ssState, bool initial);
|
||||
void processSpyServerData(int requiredBytes, bool clear);
|
||||
void processDecompressedData(int requiredSamples);
|
||||
void processUncompressedData(const char *inBuf, int nbSamples);
|
||||
void processDecompressedZlibData(const char *inBuf, int nbSamples);
|
||||
void calcPower(const Sample *iq, int nbSamples);
|
||||
void sendSettings(const RemoteTCPInputSettings& settings, const QStringList& settingsKeys);
|
||||
|
||||
private slots:
|
||||
void started();
|
||||
|
||||
Reference in New Issue
Block a user