Merge branch 'cluster' into shuttle

half-duplex
Phil Taylor 2022-10-11 16:42:34 +01:00
commit 5d0f6e82c5
25 zmienionych plików z 2423 dodań i 711 usunięć

Wyświetl plik

@ -1,8 +1,81 @@
# CHANGELOG
- 20220929
Fix for squished screen
bump version to 1.51
- 20220928
Only request passband when there is a scope available
Change default passband colors.
Fix passband colorswatch
- 20220927
See if this works better for color pickers?
Ignore second VFO scope data (for now)
Silently ignore server audio issues if server is not enabled.
Always use 8bit encoding for audio device names
Make multimedia-plugins message only for Linux
- 20220926
Add PSK modes to IC-7610 and to passband.
Add passband for FM mode
Added click-drag tuning. Needs refinement but it's a start.
colorpicker: Move to 3 columns
First look at a passband indicator
- 20220925
Remove obsolete code
Remove logging of audio device realm
- 20220923
ready for v1.50
- 20220923
Delete quit confirmation checkbox
Use date/time for log name if none specified
Fix logfile/directory opening in Windows
Fixed glitch when tabs are changed prior to rigcaps.
Remove redundant CL args
Additional resize fixes for non-spectrum rigs.
Hide UI elements not needed for non-spectrum radios.
updated to v1.48
- 20220921
Fix that was stopping users being created when none existed
- 20220920
Added dialog box to the toFixed button where an edge can be selected.
Add quick fix for rigctld fmv issue
updated to v1.47

Wyświetl plik

@ -1,44 +1,25 @@
The following highlights are in this 1.41-release:
The following highlights are in this 1.51-release ince v1.50:
New major change is the audio transport mechanism. Lower latencies.
About box updated to include Patreon site
added 500 Hz step for VFO
Added clock and UTC toggle.
Added forced manual RTS setting
Add RIT function and other rigctl fixes
Adjusted window size for radios without spectrum. Thanks K5TUX.
Allow dynamic restarting of server
New Settings tab
Enable High DPI Scaling
More multi-radio support (mostly working!)
Split LAN waterfall data for N1MM+ Spectrum Scope support: There is a combobox that allows you to select
split/combine (or default). Split only makes sense for LAN and Combine for USB.
added radio status display with meters for audio (speaker/mic)
selector for audio: QT, PortAudio, RealTime audio
version bumped to 1.4 -- rethinking of a new version schema that makes more sense
temporary squashed logs; may redo later
audio fixes at exit
introduction of peak decays at the scope
resizing of top and bottom scope/waterfall
various fixes on the spectrum display
+ 1.41 added color picker support for all kinds of vsual elements
+ 1.42 added three additional second meter choices. RxAudio, TxAdio, and TxRxAudio
+ 1.43 fixed resizing issues.
+ 1.44 added logging window. you can send the logs to termbin.com and share the link on the forum
+ 1.45 some more log enhancements
moved connect button and added cancel option
add enable/disable audioSystemServerCombo
Fixed bug where the frequency dial skipped extra when crossing zero.
+ 1.46 Added controls for custom scope edges, hide/show scope controls
depending upon the scope mode the radio reports.
Fixed clear peaks button to work with the plasma underlay.
both audio system button are disabled when connected to radio and enabled when not.
Remove password from log
Fix server user handling
+ 1.47 Added dialog box to the toFixed button where an edge can be selected.
+ 1.51
Fix for squished screen
Only request passband when there is a scope available
Change default passband colors.
Fix passband colorswatch
Ignore second VFO scope data (for now)
Silently ignore server audio issues if server is not enabled.
Always use 8bit encoding for audio device names
Make multimedia-plugins message only for Linux
Add PSK modes to IC-7610 and to passband.
Add passband for FM mode
Added click-drag tuning. Needs refinement but it's a start.
colorpicker: Move to 3 columns
passband indicator
Remove logging of audio device realm
Notes:
- We know about high CPU usage on RPi.

Wyświetl plik

@ -46,7 +46,11 @@ audioHandler::~audioHandler()
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "audio handler starting:" << setup.name;
if (setup.port.isNull())
{
qInfo(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found. You probably need to install libqt5multimedia-plugins.";
#ifdef Q_OS_LINUX
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "No audio device was found. You probably need to install libqt5multimedia-plugins.";
#else
qCritical(logAudio()) << (setup.isinput ? "Input" : "Output") << "Audio device is NULL, please check device selection in settings.";
#endif
return false;
}

291
cluster.cpp 100644
Wyświetl plik

@ -0,0 +1,291 @@
#include "cluster.h"
#include "logcategories.h"
dxClusterClient::dxClusterClient(QObject* parent):
QObject(parent)
{
qInfo(logCluster()) << "starting dxClusterClient()";
}
dxClusterClient::~dxClusterClient()
{
qInfo(logCluster()) << "closing dxClusterClient()";
enableUdp(false);
enableTcp(false);
#ifdef USESQL
database db;
db.close();
#else
QMap<QString, spotData*>::iterator spot = allSpots.begin();
while (spot != allSpots.end())
{
delete spot.value(); // Stop memory leak?
spot = allSpots.erase(spot);
}
#endif
}
void dxClusterClient::enableUdp(bool enable)
{
udpEnable = enable;
if (enable)
{
if (udpSocket == Q_NULLPTR)
{
udpSocket = new QUdpSocket(this);
bool result = udpSocket->bind(QHostAddress::AnyIPv4, udpPort);
qInfo(logCluster()) << "Starting udpSocket() on:" << udpPort << "Result:" << result;
connect(udpSocket, SIGNAL(readyRead()), this, SLOT(udpDataReceived()), Qt::QueuedConnection);
}
}
else {
if (udpSocket != Q_NULLPTR)
{
qInfo(logCluster()) << "Stopping udpSocket() on:" << udpPort;
udpSocket->disconnect();
delete udpSocket;
udpSocket = Q_NULLPTR;
}
}
}
void dxClusterClient::enableTcp(bool enable)
{
tcpEnable = enable;
if (enable)
{
tcpRegex = QRegularExpression("^DX de ([a-z|A-Z|0-9|/]+):\\s+([0-9|.]+)\\s+([a-z|A-Z|0-9|/]+)+\\s+(.*)\\s+(\\d{4}Z)");
if (tcpSocket == Q_NULLPTR)
{
tcpSocket = new QTcpSocket(this);
tcpSocket->connectToHost(tcpServerName, tcpPort);
qInfo(logCluster()) << "Starting tcpSocket() on:" << tcpPort;
connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(tcpDataReceived()), Qt::QueuedConnection);
connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(tcpDisconnected()));
tcpCleanupTimer = new QTimer(this);
tcpCleanupTimer->setInterval(1000 * 10); // Run once a minute
connect(tcpCleanupTimer, SIGNAL(timeout()), this, SLOT(tcpCleanup()));
tcpCleanupTimer->start();
}
}
else {
if (tcpSocket != Q_NULLPTR)
{
qInfo(logCluster()) << "Disconnecting tcpSocket() on:" << tcpPort;
if (tcpCleanupTimer != Q_NULLPTR)
{
tcpCleanupTimer->stop();
delete tcpCleanupTimer;
tcpCleanupTimer = Q_NULLPTR;
}
tcpSocket->disconnect();
delete tcpSocket;
tcpSocket = Q_NULLPTR;
//emit deleteOldSpots(0);
}
}
}
void dxClusterClient::udpDataReceived()
{
QHostAddress sender;
quint16 port;
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &port);
if (udpSpotReader.setContent(datagram))
{
QDomElement spot = udpSpotReader.firstChildElement("spot");
if (spot.nodeName() == "spot")
{
// This is a spot?
QString action = spot.firstChildElement("action").text();
if (action == "add") {
spotData* data = new spotData();
data->dxcall = spot.firstChildElement("dxcall").text();
data->frequency = spot.firstChildElement("frequency").text().toDouble() / 1000.0;
data->spottercall = spot.firstChildElement("spottercall").text();
data->timestamp = QDateTime::fromString(spot.firstChildElement("timestamp").text(),"yyyy-MM-dd hh:mm:ss");
data->mode = spot.firstChildElement("mode").text();
data->comment = spot.firstChildElement("comment").text();
#ifdef USESQL
database db = database();
db.query(QString("DELETE from spots where dxcall='%1'").arg(data->dxcall));
QString query = QString("INSERT INTO spots(type,spottercall,frequency,dxcall,mode,comment,timestamp) VALUES('%1','%2',%3,'%4','%5','%6','%7')\n")
.arg("UDP").arg(data->spottercall).arg(data->frequency).arg(data->dxcall).arg(data->mode).arg(data->comment).arg(data->timestamp.toString("yyyy-MM-dd hh:mm:ss"));
db.query(query);
#else
bool found = false;
QMap<QString, spotData*>::iterator spot = allSpots.find(data->dxcall);
while (spot != allSpots.end() && spot.key() == data->dxcall && spot.value()->frequency == data->frequency) {
found = true;
++spot;
}
if (found == false) {
allSpots.insert(data->dxcall, data);
}
#endif
emit sendOutput(QString("<spot><action>add</action><dxcall>%1</dxcall><spottercall>%2</spottercall><frequency>%3</frequency><comment>%4</comment></spot>\n")
.arg(data->dxcall).arg(data->spottercall).arg(data->frequency).arg(data->comment));
}
else if (action == "delete")
{
QString dxcall = spot.firstChildElement("dxcall").text();
double frequency = spot.firstChildElement("frequency").text().toDouble() / 1000.0;
#ifdef USESQL
database db = database();
QString query=QString("DELETE from spots where dxcall='%1' AND frequency=%2").arg(dxcall).arg(frequency);
db.query(query);
qInfo(logCluster()) << query;
#else
QMap<QString, spotData*>::iterator spot = allSpots.find(dxcall);
while (spot != allSpots.end() && spot.key() == dxcall && spot.value()->frequency == frequency)
{
delete spot.value(); // Stop memory leak?
spot = allSpots.erase(spot);
}
#endif
emit sendOutput(QString("<spot><action>delete</action><dxcall>%1</dxcall<frequency>%3</frequency></spot>\n")
.arg(dxcall).arg(frequency));
}
updateSpots();
}
}
}
void dxClusterClient::tcpDataReceived()
{
QString data = QString(tcpSocket->readAll());
emit sendOutput(data);
if (data.contains("login:")) {
sendTcpData(QString("%1\n").arg(tcpUserName));
return;
}
if (data.contains("password:")) {
sendTcpData(QString("%1\n").arg(tcpPassword));
return;
}
QRegularExpressionMatchIterator i = tcpRegex.globalMatch(data);
while (i.hasNext()) {
QRegularExpressionMatch match = i.next();
if (match.hasMatch()) {
spotData* data = new spotData();
data->spottercall = match.captured(1);
data->frequency = match.captured(2).toDouble() / 1000.0;
data->dxcall = match.captured(3);
data->comment = match.captured(4).trimmed();
data->timestamp = QDateTime::currentDateTimeUtc();
#ifdef USESQL
database db = database();
db.query(QString("DELETE from spots where dxcall='%1'").arg(data->dxcall));
QString query = QString("INSERT INTO spots(type,spottercall,frequency,dxcall,comment,timestamp) VALUES('%1','%2',%3,'%4','%5','%6')\n")
.arg("TCP").arg(data->spottercall).arg(data->frequency).arg(data->dxcall).arg(data->comment).arg(data->timestamp.toString("yyyy-MM-dd hh:mm:ss"));
db.query(query);
#else
bool found = false;
QMap<QString, spotData*>::iterator spot = allSpots.find(data->dxcall);
while (spot != allSpots.end() && spot.key() == data->dxcall && spot.value()->frequency == data->frequency) {
found = true;
++spot;
}
if (found == false) {
allSpots.insert(data->dxcall, data);
}
#endif
}
}
updateSpots();
}
void dxClusterClient::sendTcpData(QString data)
{
qInfo(logCluster()) << "Sending:" << data;
if (tcpSocket != Q_NULLPTR && tcpSocket->isValid() && tcpSocket->isOpen())
{
tcpSocket->write(data.toLatin1());
}
else
{
qInfo(logCluster()) << "socket not open!";
}
}
void dxClusterClient::tcpCleanup()
{
#ifdef USESQL
database db = database();
db.query(QString("DELETE FROM spots where timestamp < datetime('now', '-%1 minutes')").arg(tcpTimeout));
#else
QMap<QString, spotData*>::iterator spot = allSpots.begin();;
while (spot != allSpots.end()) {
if (spot.value()->timestamp.addSecs(tcpTimeout * 60) < QDateTime::currentDateTimeUtc())
{
delete spot.value(); // Stop memory leak?
spot = allSpots.erase(spot);
}
else
{
++spot;
}
}
#endif
}
void dxClusterClient::tcpDisconnected() {
qWarning(logCluster()) << "TCP Cluster server disconnected...";
// Need to start a timer and attempt reconnect.
}
void dxClusterClient::freqRange(double low, double high)
{
lowFreq = low;
highFreq = high;
//qInfo(logCluster) << "New range" << low << "-" << high;
updateSpots();
}
void dxClusterClient::updateSpots()
{
QList<spotData> spots;
#ifdef USESQL
// Set the required frequency range.
QString queryText = QString("SELECT * FROM spots WHERE frequency > %1 AND frequency < %2").arg(lowFreq).arg(highFreq);
//QString queryText = QString("SELECT * FROM spots");
database db;
auto query = db.query(queryText);
while (query.next()) {
// Step through all current spots within range
spotData s = spotData();
s.dxcall = query.value(query.record().indexOf("dxcall")).toString();
s.frequency = query.value(query.record().indexOf("frequency")).toDouble();
spots.append(s);
}
#else
QMap<QString, spotData*>::iterator spot = allSpots.begin();;
while (spot != allSpots.end()) {
if (spot.value()->frequency > lowFreq && spot.value()->frequency < highFreq)
{
spots.append(**spot);
}
++spot;
}
#endif
emit sendSpots(spots);
}

104
cluster.h 100644
Wyświetl plik

@ -0,0 +1,104 @@
#ifndef CLUSTER_H
#define CLUSTER_H
#include <QObject>
#include <QDebug>
#include <QUdpSocket>
#include <QTcpSocket>
#include <QDomDocument>
#include <QMutex>
#include <QMutexLocker>
#include <QDateTime>
#include <QRegularExpression>
#include <QTimer>
#ifdef USESQL
#include <QSqlDatabase>
#include <QSqlQuery>
#endif
#include <qcustomplot.h>
#ifdef USESQL
#include "database.h"
#endif
struct spotData {
QString dxcall;
double frequency;
QString spottercall;
QDateTime timestamp;
QString mode;
QString comment;
QCPItemText* text = Q_NULLPTR;
bool current = false;
};
struct clusterSettings {
QString server;
int port=7300;
QString userName;
QString password;
int timeout=30;
bool isdefault;
};
class dxClusterClient : public QObject
{
Q_OBJECT
public:
explicit dxClusterClient(QObject* parent = nullptr);
virtual ~dxClusterClient();
signals:
void addSpot(spotData* spot);
void deleteSpot(QString dxcall);
void deleteOldSpots(int minutes);
void sendOutput(QString text);
void sendSpots(QList<spotData> spots);
public slots:
void udpDataReceived();
void tcpDataReceived();
void tcpDisconnected();
void enableUdp(bool enable);
void enableTcp(bool enable);
void setUdpPort(int p) { udpPort = p; }
void setTcpServerName(QString s) { tcpServerName = s; }
void setTcpPort(int p) { tcpPort = p; }
void setTcpUserName(QString s) { tcpUserName = s; }
void setTcpPassword(QString s) { tcpPassword = s; }
void setTcpTimeout(int p) { tcpTimeout = p; }
void tcpCleanup();
void freqRange(double low, double high);
private:
void sendTcpData(QString data);
bool databaseOpen();
void updateSpots();
bool udpEnable;
bool tcpEnable;
QUdpSocket* udpSocket=Q_NULLPTR;
QTcpSocket* tcpSocket=Q_NULLPTR;
int udpPort;
QString tcpServerName;
int tcpPort;
QString tcpUserName;
QString tcpPassword;
int tcpTimeout;
QDomDocument udpSpotReader;
QRegularExpression tcpRegex;
QMutex mutex;
bool authenticated=false;
QTimer* tcpCleanupTimer=Q_NULLPTR;
#ifdef USESQL
QSqlDatabase db;
#endif
double lowFreq;
double highFreq;
QMap<QString,spotData*> allSpots;
};
#endif

Wyświetl plik

@ -18,6 +18,7 @@ struct colorPrefsType{
QColor underlayFill;
QColor plotBackground;
QColor tuningLine;
QColor passband;
// Waterfall:
QColor wfBackground;
@ -32,6 +33,10 @@ struct colorPrefsType{
QColor meterPeakScale;
QColor meterLowerLine;
QColor meterLowText;
// Assorted
QColor clusterSpots;
};

107
database.cpp 100644
Wyświetl plik

@ -0,0 +1,107 @@
#ifdef USESQL
#include "database.h"
#include "logcategories.h"
database::database()
{
open();
}
database::~database()
{
}
bool database::open()
{
auto name = "my_db_" + QString::number((quint64)QThread::currentThread(), 16);
if (QSqlDatabase::contains(name))
{
db = QSqlDatabase::database(name);
qu = QSqlQuery(db);
return true;
}
else {
qInfo(logCluster()) << "Creating new connection" << name;
db = QSqlDatabase::addDatabase("QSQLITE", name);
qu = QSqlQuery(db);
}
//QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/" + "wfview.db";
QString path = ":memory:";
qInfo(logCluster()) << "DB Filename" << path;
db.setDatabaseName(path);
if (db.isValid())
{
db.open();
if (check()) {
return true;
}
}
qWarning(logCluster()) << "Database is not valid!";
return false;
}
void database::close()
{
auto name = "my_db_" + QString::number((quint64)QThread::currentThread(), 16);
qInfo(logCluster()) << "Closing database connection:" << name;
db.close();
}
QSqlQuery database::query(QString query)
{
if (!db.isOpen())
{
qWarning(logCluster()) << "Query Database is not open!";
db.open();
}
qu.exec(query);
return qu;
}
bool database::check()
{
if (db.isOpen()) {
for (const auto& table : db.tables())
{
if (table == "spots")
{
qInfo(logCluster()) << "DB Contains spots table";
return true;
}
}
qInfo(logCluster()) << "Creating spots table";
// Spots table does not exist, need to create it.
/*
QString dxcall;
double frequency;
QString spottercall;
QDateTime timestamp;
QString mode;
QString comment;
QCPItemText* text = Q_NULLPTR;*/
qu.exec("CREATE TABLE spots "
"(id INTEGER PRIMARY KEY, "
"type VARCHAR(3),"
"dxcall VARCHAR(30),"
"spottercall VARCHAR(30),"
"frequency DOUBLE,"
"timestamp DATETIME,"
"mode VARCHAR(30),"
"comment VARCHAR(255) )");
qu.exec("CREATE INDEX spots_index ON spots(type,dxcall,frequency,timestamp)");
return true;
}
else {
qWarning(logCluster()) << "Database is not open";
}
return false;
}
#endif

36
database.h 100644
Wyświetl plik

@ -0,0 +1,36 @@
#ifdef USESQL
#ifndef DATABASE_H
#define DATABASE_H
#include <QObject>
#include <QDebug>
#include <QDateTime>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlRecord>
#include <QThread>
#include <QStandardPaths>
class database
{
public:
explicit database();
virtual ~database();
bool open();
void close();
QSqlQuery query(QString query);
signals:
public slots:
private:
bool check();
QSqlDatabase db;
QSqlQuery qu;
};
#endif
#endif

Wyświetl plik

@ -17,6 +17,8 @@ enum mode_kind {
modeFM=0x05,
modeCW_R=0x07,
modeRTTY_R=0x08,
modePSK = 0x12,
modePSK_R = 0x13,
modeLSB_D=0x80,
modeUSB_D=0x81,
modeDV=0x17,
@ -29,9 +31,7 @@ enum mode_kind {
modedPMR,
modeNXDN_VN,
modeNXDN_N,
modeDCR,
modePSK,
modePSK_R
modeDCR
};
struct mode_info {

Wyświetl plik

@ -13,3 +13,4 @@ Q_LOGGING_CATEGORY(logRigCtlD, "rigctld")
Q_LOGGING_CATEGORY(logTcpServer, "tcpserver")
Q_LOGGING_CATEGORY(logUsbControl, "usbcontrol")
Q_LOGGING_CATEGORY(logAudioConverter, "audioconverter")
Q_LOGGING_CATEGORY(logCluster, "cluster")

Wyświetl plik

@ -16,6 +16,7 @@ Q_DECLARE_LOGGING_CATEGORY(logRigCtlD)
Q_DECLARE_LOGGING_CATEGORY(logTcpServer)
Q_DECLARE_LOGGING_CATEGORY(logUsbControl)
Q_DECLARE_LOGGING_CATEGORY(logAudioConverter)
Q_DECLARE_LOGGING_CATEGORY(logCluster)
#if defined(Q_OS_WIN) && !defined(__PRETTY_FUNCTION__)

Wyświetl plik

@ -1,9 +1,10 @@
#include "loggingwindow.h"
#include "ui_loggingwindow.h"
loggingWindow::loggingWindow(QWidget *parent) :
loggingWindow::loggingWindow(QString logFilename, QWidget *parent) :
QWidget(parent),
ui(new Ui::loggingWindow)
ui(new Ui::loggingWindow),
logFilename(logFilename)
{
ui->setupUi(this);
this->setWindowTitle("Log");
@ -12,20 +13,15 @@ loggingWindow::loggingWindow(QWidget *parent) :
ui->annotateBtn->setDefault(true);
ui->logTextDisplay->setFocusPolicy(Qt::NoFocus);
ui->annotateBtn->setFocusPolicy(Qt::NoFocus);
QDir d = QFileInfo(logFilename).absoluteDir();
logDirectory = d.absolutePath();
QFont font("Monospace");
font.setStyleHint(QFont::TypeWriter);
ui->logTextDisplay->setFont(font);
ui->userAnnotationText->setFont(font);
#ifdef Q_OS_MAC
logFilename = QStandardPaths::standardLocations(QStandardPaths::DownloadLocation)[0] + "/wfview.log";
logDirectory = QStandardPaths::standardLocations(QStandardPaths::DownloadLocation)[0];
#else
logFilename= QStandardPaths::standardLocations(QStandardPaths::TempLocation)[0] + "/wfview.log";
logDirectory = QStandardPaths::standardLocations(QStandardPaths::TempLocation)[0];
#endif
clipboard = QApplication::clipboard();
socket = new QTcpSocket(this);
connect(socket, SIGNAL(connected()), this, SLOT(connectedToHost()));
@ -142,37 +138,39 @@ void loggingWindow::on_clearDisplayBtn_clicked()
void loggingWindow::on_openDirBtn_clicked()
{
QString cmd;
int rtnval = 0;
#ifdef Q_OS_MAC
cmd = "open " + logDirectory;
#endif
bool rtn = false;
QStringList arg;
const QFileInfo dir(logDirectory);
#ifdef Q_OS_LINUX
cmd = "xdg-open " + logDirectory;
cmd = "xdg-open";
#elif defined(Q_OS_WIN)
cmd = QStandardPaths::findExecutable("explorer.exe");
if (!dir.isDir())
arg += QLatin1String("/select,");
#else
cmd = "open";
#endif
#ifdef Q_OS_WIN
cmd = "start " + logDirectory;
#endif
rtnval = system(cmd.toLocal8Bit().data());
if(rtnval)
qInfo(logLogger()) << "Error, open log directory command returned error code " << rtnval;
arg += QDir::toNativeSeparators(dir.canonicalFilePath());;
rtn = QProcess::startDetached(cmd, arg);
if(!rtn)
qInfo(logLogger()) << "Error, open log directory" << logDirectory << "command failed";
}
void loggingWindow::on_openLogFileBtn_clicked()
{
QString cmd;
int rtnval = 0;
#ifdef Q_OS_MAC
cmd = "open " + logFilename;
#endif
bool rtn = false;
#ifdef Q_OS_LINUX
cmd = "xdg-open " + logFilename;
cmd = "xdg-open";
#elif defined(Q_OS_WIN)
cmd = QStandardPaths::findExecutable("notepad.exe");
#else
cmd = "open";
#endif
#ifdef Q_OS_WIN
cmd = "notepad " + logFilename;
#endif
rtnval = system(cmd.toLocal8Bit().data());
if(rtnval)
qInfo(logLogger()) << "Error, open log file command returned error code " << rtnval;
rtn = QProcess::startDetached(cmd, { logFilename });
if(!rtn)
qInfo(logLogger()) << "Error, open log file command failed";
}
void loggingWindow::on_sendToPasteBtn_clicked()

Wyświetl plik

@ -10,6 +10,9 @@
#include <QTextStream>
#include <QMessageBox>
#include <QScrollBar>
#include <QProcess>
#include <QFileInfo>
#include <QDir>
#include "logcategories.h"
@ -22,7 +25,7 @@ class loggingWindow : public QWidget
Q_OBJECT
public:
explicit loggingWindow(QWidget *parent = nullptr);
explicit loggingWindow(QString logFilename, QWidget *parent = 0);
~loggingWindow();
void acceptLogText(QString text);
@ -57,14 +60,14 @@ signals:
void setDebugMode(bool debugOn);
private:
Ui::loggingWindow *ui;
Ui::loggingWindow* ui;
QString logFilename;
QString logDirectory;
QClipboard *clipboard;
QMessageBox URLmsgBox;
QScrollBar *vertLogScroll;
QScrollBar *horizLogScroll;
QMutex textMutex;
QString logFilename;
QString logDirectory;
QTcpSocket *socket;
void sendToTermbin();
};

Wyświetl plik

@ -71,19 +71,15 @@ int main(int argc, char *argv[])
//debugMode = true;
#endif
QString serialPortCL;
QString hostCL;
QString civCL;
#ifdef Q_OS_MAC
QString logFilename= QStandardPaths::standardLocations(QStandardPaths::DownloadLocation)[0] + "/wfview.log";
#else
QString logFilename= QStandardPaths::standardLocations(QStandardPaths::TempLocation)[0] + "/wfview.log";
#endif
QDateTime date = QDateTime::currentDateTime();
QString formattedTime = date.toString("dd.MM.yyyy hh:mm:ss");
QString logFilename = (QString("%1/%2-%3.log").arg(QStandardPaths::standardLocations(QStandardPaths::TempLocation)[0]).arg(a.applicationName()).arg(date.toString("yyyyMMddhhmmss")));
QString settingsFile = NULL;
QString currentArg;
const QString helpText = QString("\nUsage: -p --port /dev/port, -h --host remotehostname, -c --civ 0xAddr, -l --logfile filename.log, -s --settings filename.ini, -d --debug, -v --version\n"); // TODO...
const QString helpText = QString("\nUsage: -l --logfile filename.log, -s --settings filename.ini, -d --debug, -v --version\n"); // TODO...
#ifdef BUILD_WFSERVER
const QString version = QString("wfserver version: %1 (Git:%2 on %3 at %4 by %5@%6)\nOperating System: %7 (%8)\nBuild Qt Version %9. Current Qt Version: %10\n")
.arg(QString(WFVIEW_VERSION))
@ -102,34 +98,10 @@ int main(int argc, char *argv[])
//qInfo() << "Argc: " << c << " argument: " << argv[c];
currentArg = QString(argv[c]);
if ((currentArg == "-p") || (currentArg == "--port"))
{
if (argc > c)
{
serialPortCL = argv[c + 1];
c += 1;
}
}
else if ((currentArg == "-d") || (currentArg == "--debug"))
if ((currentArg == "-d") || (currentArg == "--debug"))
{
debugMode = true;
}
else if ((currentArg == "-h") || (currentArg == "--host"))
{
if(argc > c)
{
hostCL = argv[c+1];
c+=1;
}
}
else if ((currentArg == "-c") || (currentArg == "--civ"))
{
if (argc > c)
{
civCL = argv[c + 1];
c += 1;
}
}
else if ((currentArg == "-l") || (currentArg == "--logfile"))
{
if (argc > c)
@ -173,9 +145,7 @@ int main(int argc, char *argv[])
qInstallMessageHandler(messageHandler);
qInfo(logSystem()) << version;
qDebug(logSystem()) << QString("SerialPortCL as set by parser: %1").arg(serialPortCL);
qDebug(logSystem()) << QString("remote host as set by parser: %1").arg(hostCL);
qDebug(logSystem()) << QString("CIV as set by parser: %1").arg(civCL);
#endif
#ifdef BUILD_WFSERVER
#ifdef Q_OS_WIN
@ -185,10 +155,10 @@ int main(int argc, char *argv[])
signal(SIGTERM, cleanup);
signal(SIGKILL, cleanup);
#endif
w = new servermain(serialPortCL, hostCL, settingsFile);
w = new servermain(logFilename, settingsFile);
#else
a.setWheelScrollLines(1); // one line per wheel click
wfmain w(serialPortCL, hostCL, settingsFile, debugMode);
wfmain w(settingsFile, logFilename, debugMode);
w.show();
#endif

Wyświetl plik

@ -872,6 +872,13 @@ void rigCommander::getDuplexMode()
prepDataAndSend(payload);
}
void rigCommander::getPassband()
{
QByteArray payload;
payload.setRawData("\x1A\x03", 2);
prepDataAndSend(payload);
}
void rigCommander::getTransmitFrequency()
{
QByteArray payload;
@ -2511,6 +2518,9 @@ void rigCommander::parseRegisters1A()
// band stacking register
parseBandStackReg();
break;
case '\x03':
emit havePassband(bcdHexToUChar((quint8)payloadIn[2]));
break;
case '\x04':
state.set(AGC, (quint8)payloadIn[2], false);
break;
@ -3239,8 +3249,8 @@ void rigCommander::determineRigCaps()
rigCaps.bands.push_back(bandGen);
rigCaps.bsr[bandGen] = 0x11;
rigCaps.modes = commonModes;
rigCaps.modes.insert(rigCaps.modes.end(), {createMode(modePSK, 0x12, "PSK"),
createMode(modePSK_R, 0x13, "PSK-R")});
rigCaps.modes.insert(rigCaps.modes.end(), { createMode(modePSK, 0x12, "PSK"),
createMode(modePSK_R, 0x13, "PSK-R") });
rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x05\x00\x97");
break;
case model7610:
@ -3273,6 +3283,8 @@ void rigCommander::determineRigCaps()
rigCaps.bands.push_back(band630m);
rigCaps.bands.push_back(band2200m);
rigCaps.modes = commonModes;
rigCaps.modes.insert(rigCaps.modes.end(), { createMode(modePSK, 0x12, "PSK"),
createMode(modePSK_R, 0x13, "PSK-R") });
rigCaps.hasRXAntenna = true;
rigCaps.transceiveCommand = QByteArrayLiteral("\x1a\x05\x01\x12");
break;
@ -3771,9 +3783,16 @@ void rigCommander::parseSpectrum()
freqt fStart;
freqt fEnd;
unsigned char vfo = bcdHexToUChar(payloadIn[02]);
unsigned char sequence = bcdHexToUChar(payloadIn[03]);
//unsigned char sequenceMax = bcdHexToDecimal(payloadIn[04]);
if (vfo == 1)
{
// This is for the second VFO!
return;
}
// unsigned char waveInfo = payloadIn[06]; // really just one byte?
//qInfo(logRig()) << "Spectrum Data received: " << sequence << "/" << sequenceMax << " mode: " << scopeMode << " waveInfo: " << waveInfo << " length: " << payloadIn.length();

Wyświetl plik

@ -157,6 +157,8 @@ public slots:
void setManualNotch(bool enabled);
void getManualNotch();
void getPassband();
// Repeater:
void setDuplexMode(duplexMode dm);
void getDuplexMode();
@ -321,6 +323,7 @@ signals:
void haveBandStackReg(freqt f, char mode, char filter, bool dataOn);
void haveRitEnabled(bool ritEnabled);
void haveRitFrequency(int ritHz);
void havePassband(quint8 pass);
// Repeater:
void haveDuplexMode(duplexMode);

Wyświetl plik

@ -107,7 +107,7 @@ struct rigCapabilities {
QVector<rigInput> inputs;
bool hasSpectrum;
bool hasSpectrum=true;
quint8 spectSeqMax;
quint16 spectAmpMax;
quint16 spectLenMax;

Wyświetl plik

@ -8,10 +8,8 @@
// This code is copyright 2017-2020 Elliott H. Liggett
// All rights reserved
servermain::servermain(const QString serialPortCL, const QString hostCL, const QString settingsFile)
servermain::servermain(const QString settingsFile, const QString logFile)
{
this->serialPortCL = serialPortCL;
this->hostCL = hostCL;
qRegisterMetaType <udpPreferences>(); // Needs to be registered early.
qRegisterMetaType <rigCapabilities>();

Wyświetl plik

@ -46,9 +46,7 @@ class servermain : public QObject
Q_OBJECT
public:
servermain(const QString serialPortCL, const QString hostCL, const QString settingsFile);
QString serialPortCL;
QString hostCL;
servermain(const QString logFile, const QString settingsFile);
~servermain();
signals:

Plik diff jest za duży Load Diff

Wyświetl plik

@ -40,6 +40,7 @@
#include "selectradio.h"
#include "colorprefs.h"
#include "loggingwindow.h"
#include "cluster.h"
#include <qcustomplot.h>
#include <qserialportinfo.h>
@ -67,9 +68,7 @@ class wfmain : public QMainWindow
Q_OBJECT
public:
explicit wfmain(const QString serialPortCL, const QString hostCL, const QString settingsFile, bool debugMode, QWidget *parent = 0);
QString serialPortCL;
QString hostCL;
explicit wfmain(const QString settingsFile, const QString logFile, bool debugMode, QWidget *parent = 0);
~wfmain();
static void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg);
void handleLogText(QString text);
@ -80,7 +79,7 @@ signals:
void setRigID(unsigned char rigID);
void setRTSforPTT(bool enabled);
// Power
void sendPowerOn();
void sendPowerOff();
@ -103,6 +102,7 @@ signals:
// Repeater:
void getDuplexMode();
void getPassband();
void getTone();
void getTSQL();
void getDTCS();
@ -196,7 +196,16 @@ signals:
void shuttleLed(bool, unsigned char);
void sendUsbControllerCommands(QVector<COMMAND>* cmds);
void sendUsbControllerButtons(QVector<BUTTON>* buts);
void setClusterUdpPort(int port);
void setClusterEnableUdp(bool udp);
void setClusterEnableTcp(bool tcp);
void setClusterServerName(QString name);
void setClusterTcpPort(int port);
void setClusterUserName(QString name);
void setClusterPassword(QString pass);
void setClusterTimeout(int timeout);
void setFrequencyRange(double low, double high);
private slots:
void updateSizes(int tabIndex);
void shortcutF1();
@ -252,7 +261,7 @@ private slots:
void receiveRITValue(int ritValHz);
void receiveModInput(rigInput input, bool dataOn);
//void receiveDuplexMode(duplexMode dm);
void receivePassband(quint8 pass);
// Levels:
@ -295,6 +304,8 @@ private slots:
void receiveStatusUpdate(networkStatus status);
void receiveNetworkAudioLevels(networkAudioLevels l);
void handlePlotClick(QMouseEvent *);
void handlePlotMouseRelease(QMouseEvent *);
void handlePlotMouseMove(QMouseEvent *);
void handlePlotDoubleClick(QMouseEvent *);
void handleWFClick(QMouseEvent *);
void handleWFDoubleClick(QMouseEvent *);
@ -632,6 +643,10 @@ private slots:
void on_colorEditTuningLine_editingFinished();
void on_colorSetBtnPassband_clicked();
void on_colorEditPassband_editingFinished();
void on_colorSetBtnMeterLevel_clicked();
void on_colorEditMeterLevel_editingFinished();
@ -648,6 +663,10 @@ private slots:
void on_colorEditMeterText_editingFinished();
void on_colorSetBtnClusterSpots_clicked();
void on_colorEditClusterSpots_editingFinished();
void on_colorRenamePresetBtn_clicked();
void on_colorRevertPresetBtn_clicked();
@ -668,9 +687,26 @@ private slots:
void on_customEdgeBtn_clicked();
void on_clusterUdpEnable_clicked(bool enable);
void on_clusterTcpEnable_clicked(bool enable);
void on_clusterUdpPortLineEdit_editingFinished();
void on_clusterServerNameCombo_currentTextChanged(QString text);
void on_clusterServerNameCombo_currentIndexChanged(int index);
void on_clusterTcpPortLineEdit_editingFinished();
void on_clusterUsernameLineEdit_editingFinished();
void on_clusterPasswordLineEdit_editingFinished();
void on_clusterTimeoutLineEdit_editingFinished();
void on_clusterPopOutBtn_clicked();
void on_clickDragTuningEnableChk_clicked(bool checked);
void receiveClusterOutput(QString text);
void receiveSpots(QList<spotData> spots);
private:
Ui::wfmain *ui;
void closeEvent(QCloseEvent *event);
QString logFilename;
bool debugMode;
QString version;
QSettings *settings=Q_NULLPTR;
@ -683,11 +719,11 @@ private:
void initLogging();
QTimer logCheckingTimer;
int logCheckingOldPosition = 0;
QString logFilename;
QCustomPlot *plot; // line plot
QCustomPlot *wf; // waterfall image
QCPItemLine * freqIndicatorLine;
QCPItemRect* passbandIndicator;
void setAppTheme(bool isCustom);
void prepareWf();
void prepareWf(unsigned int wfLength);
@ -803,6 +839,10 @@ private:
double wfCeiling = 160;
double oldPlotFloor = -1;
double oldPlotCeiling = 999;
double passBand = 0.0;
double mousePressFreq = 0.0;
double mouseReleaseFreq = 0.0;
QVector <QByteArray> wfimage;
unsigned int wfLengthMax;
@ -825,7 +865,7 @@ private:
cmdGetDataMode, cmdSetModeFilter, cmdSetDataModeOn, cmdSetDataModeOff, cmdGetRitEnabled, cmdGetRitValue,
cmdSpecOn, cmdSpecOff, cmdDispEnable, cmdDispDisable, cmdGetRxGain, cmdSetRxRfGain, cmdGetAfGain, cmdSetAfGain,
cmdGetSql, cmdSetSql, cmdGetIFShift, cmdSetIFShift, cmdGetTPBFInner, cmdSetTPBFInner,
cmdGetTPBFOuter, cmdSetTPBFOuter, cmdGetATUStatus,
cmdGetTPBFOuter, cmdSetTPBFOuter, cmdGetATUStatus, cmdGetPassband,
cmdSetATU, cmdStartATU, cmdGetSpectrumMode,
cmdGetSpectrumSpan, cmdScopeCenterMode, cmdScopeFixedMode, cmdGetPTT, cmdSetPTT,cmdPTTToggle,
cmdGetTxPower, cmdSetTxPower, cmdGetMicGain, cmdSetMicGain, cmdSetModLevel,
@ -918,6 +958,14 @@ private:
quint16 tcpPort;
quint8 waterfallFormat;
audioType audioSystem;
bool clusterUdpEnable;
bool clusterTcpEnable;
int clusterUdpPort;
QString clusterTcpServerName;
QString clusterTcpUserName;
QString clusterTcpPassword;
int clusterTimeout;
bool clickDragTuningEnable;
} prefs;
preferences defPrefs;
@ -1063,6 +1111,14 @@ private:
QVector<COMMAND> usbCommands;
QVector<BUTTON> usbButtons;
dxClusterClient* cluster = Q_NULLPTR;
QThread* clusterThread = Q_NULLPTR;
QMap<QString, spotData*> clusterSpots;
QTimer clusterTimer;
QCPItemText* text=Q_NULLPTR;
QList<clusterSettings> clusters;
QMutex clusterMutex;
QColor clusterColor;
};
Q_DECLARE_METATYPE(struct rigCapabilities)
@ -1076,10 +1132,13 @@ Q_DECLARE_METATYPE(struct timekind)
Q_DECLARE_METATYPE(struct datekind)
Q_DECLARE_METATYPE(struct networkStatus)
Q_DECLARE_METATYPE(struct networkAudioLevels)
Q_DECLARE_METATYPE(struct spotData)
Q_DECLARE_METATYPE(enum rigInput)
Q_DECLARE_METATYPE(enum meterKind)
Q_DECLARE_METATYPE(enum spectrumMode)
Q_DECLARE_METATYPE(enum mode_kind)
Q_DECLARE_METATYPE(QList<radio_cap_packet>)
Q_DECLARE_METATYPE(QList<spotData>)
Q_DECLARE_METATYPE(rigstate*)
Q_DECLARE_METATYPE(QVector <BUTTON>*)
Q_DECLARE_METATYPE(struct BUTTON*)

1354
wfmain.ui

Plik diff jest za duży Load Diff

Wyświetl plik

@ -13,7 +13,7 @@ TEMPLATE = app
CONFIG += console
DEFINES += WFVIEW_VERSION=\\\"1.46\\\"
DEFINES += WFVIEW_VERSION=\\\"1.51\\\"
DEFINES += BUILD_WFSERVER

0
wfview 100644
Wyświetl plik

Wyświetl plik

@ -4,14 +4,17 @@
#
#-------------------------------------------------
QT += core gui serialport network multimedia
QT += core gui serialport network multimedia xml
#QT += sql
#DEFINES += USESQL
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets printsupport
TARGET = wfview
TEMPLATE = app
DEFINES += WFVIEW_VERSION=\\\"1.46\\\"
DEFINES += WFVIEW_VERSION=\\\"1.51\\\"
DEFINES += BUILD_WFVIEW
@ -182,6 +185,8 @@ SOURCES += main.cpp\
transceiveradjustments.cpp \
selectradio.cpp \
tcpserver.cpp \
cluster.cpp \
database.cpp \
aboutbox.cpp
HEADERS += wfmain.h \
@ -220,6 +225,8 @@ HEADERS += wfmain.h \
audiotaper.h \
selectradio.h \
tcpserver.h \
cluster.h \
database.h \
aboutbox.h
FORMS += wfmain.ui \