wfview/wfmain.cpp

3475 wiersze
107 KiB
C++

#include "wfmain.h"
#include "ui_wfmain.h"
#include "commhandler.h"
#include "rigidentities.h"
#include "logcategories.h"
// This code is copyright 2017-2020 Elliott H. Liggett
// All rights reserved
wfmain::wfmain(const QString serialPortCL, const QString hostCL, QWidget *parent ) :
QMainWindow(parent),
ui(new Ui::wfmain)
{
QGuiApplication::setApplicationDisplayName("wfview");
QGuiApplication::setApplicationName(QString("wfview"));
setWindowIcon(QIcon( QString(":resources/wfview.png")));
ui->setupUi(this);
theParent = parent;
setWindowTitle(QString("wfview"));
this->serialPortCL = serialPortCL;
this->hostCL = hostCL;
cal = new calibrationWindow();
sat = new satelliteSetup();
srv = new udpServerSetup();
connect(this, SIGNAL(sendServerConfig(SERVERCONFIG)), srv, SLOT(receiveServerConfig(SERVERCONFIG)));
connect(srv, SIGNAL(serverConfig(SERVERCONFIG, bool)), this, SLOT(serverConfigRequested(SERVERCONFIG, bool)));
qRegisterMetaType<udpPreferences>(); // Needs to be registered early.
haveRigCaps = false;
ui->bandStkLastUsedBtn->setVisible(false);
ui->bandStkVoiceBtn->setVisible(false);
ui->bandStkDataBtn->setVisible(false);
ui->bandStkCWBtn->setVisible(false);
keyF1 = new QShortcut(this);
keyF1->setKey(Qt::Key_F1);
connect(keyF1, SIGNAL(activated()), this, SLOT(shortcutF1()));
keyF2 = new QShortcut(this);
keyF2->setKey(Qt::Key_F2);
connect(keyF2, SIGNAL(activated()), this, SLOT(shortcutF2()));
keyF3 = new QShortcut(this);
keyF3->setKey(Qt::Key_F3);
connect(keyF3, SIGNAL(activated()), this, SLOT(shortcutF3()));
keyF4 = new QShortcut(this);
keyF4->setKey(Qt::Key_F4);
connect(keyF4, SIGNAL(activated()), this, SLOT(shortcutF4()));
keyF5 = new QShortcut(this);
keyF5->setKey(Qt::Key_F5);
connect(keyF5, SIGNAL(activated()), this, SLOT(shortcutF5()));
keyF6 = new QShortcut(this);
keyF6->setKey(Qt::Key_F6);
connect(keyF6, SIGNAL(activated()), this, SLOT(shortcutF6()));
keyF7 = new QShortcut(this);
keyF7->setKey(Qt::Key_F7);
connect(keyF7, SIGNAL(activated()), this, SLOT(shortcutF7()));
keyF8 = new QShortcut(this);
keyF8->setKey(Qt::Key_F8);
connect(keyF8, SIGNAL(activated()), this, SLOT(shortcutF8()));
keyF9 = new QShortcut(this);
keyF9->setKey(Qt::Key_F9);
connect(keyF9, SIGNAL(activated()), this, SLOT(shortcutF9()));
keyF10 = new QShortcut(this);
keyF10->setKey(Qt::Key_F10);
connect(keyF10, SIGNAL(activated()), this, SLOT(shortcutF10()));
keyF11 = new QShortcut(this);
keyF11->setKey(Qt::Key_F11);
connect(keyF11, SIGNAL(activated()), this, SLOT(shortcutF11()));
keyF12 = new QShortcut(this);
keyF12->setKey(Qt::Key_F12);
connect(keyF12, SIGNAL(activated()), this, SLOT(shortcutF12()));
keyControlT = new QShortcut(this);
keyControlT->setKey(Qt::CTRL + Qt::Key_T);
connect(keyControlT, SIGNAL(activated()), this, SLOT(shortcutControlT()));
keyControlR = new QShortcut(this);
keyControlR->setKey(Qt::CTRL + Qt::Key_R);
connect(keyControlR, SIGNAL(activated()), this, SLOT(shortcutControlR()));
keyControlI = new QShortcut(this);
keyControlI->setKey(Qt::CTRL + Qt::Key_I);
connect(keyControlI, SIGNAL(activated()), this, SLOT(shortcutControlI()));
keyControlU = new QShortcut(this);
keyControlU->setKey(Qt::CTRL + Qt::Key_U);
connect(keyControlU, SIGNAL(activated()), this, SLOT(shortcutControlU()));
keyStar = new QShortcut(this);
keyStar->setKey(Qt::Key_Asterisk);
connect(keyStar, SIGNAL(activated()), this, SLOT(shortcutStar()));
keySlash = new QShortcut(this);
keySlash->setKey(Qt::Key_Slash);
connect(keySlash, SIGNAL(activated()), this, SLOT(shortcutSlash()));
keyMinus = new QShortcut(this);
keyMinus->setKey(Qt::Key_Minus);
connect(keyMinus, SIGNAL(activated()), this, SLOT(shortcutMinus()));
keyPlus = new QShortcut(this);
keyPlus->setKey(Qt::Key_Plus);
connect(keyPlus, SIGNAL(activated()), this, SLOT(shortcutPlus()));
keyShiftMinus = new QShortcut(this);
keyShiftMinus->setKey(Qt::SHIFT + Qt::Key_Minus);
connect(keyShiftMinus, SIGNAL(activated()), this, SLOT(shortcutShiftMinus()));
keyShiftPlus = new QShortcut(this);
keyShiftPlus->setKey(Qt::SHIFT + Qt::Key_Plus);
connect(keyShiftPlus, SIGNAL(activated()), this, SLOT(shortcutShiftPlus()));
keyControlMinus = new QShortcut(this);
keyControlMinus->setKey(Qt::CTRL + Qt::Key_Minus);
connect(keyControlMinus, SIGNAL(activated()), this, SLOT(shortcutControlMinus()));
keyControlPlus = new QShortcut(this);
keyControlPlus->setKey(Qt::CTRL + Qt::Key_Plus);
connect(keyControlPlus, SIGNAL(activated()), this, SLOT(shortcutControlPlus()));
keyQuit = new QShortcut(this);
keyQuit->setKey(Qt::CTRL + Qt::Key_Q);
connect(keyQuit, SIGNAL(activated()), this, SLOT(on_exitBtn_clicked()));
keyPageUp = new QShortcut(this);
keyPageUp->setKey(Qt::Key_PageUp);
connect(keyPageUp, SIGNAL(activated()), this, SLOT(shortcutPageUp()));
keyPageDown = new QShortcut(this);
keyPageDown->setKey(Qt::Key_PageDown);
connect(keyPageDown, SIGNAL(activated()), this, SLOT(shortcutPageDown()));
keyF = new QShortcut(this);
keyF->setKey(Qt::Key_F);
connect(keyF, SIGNAL(activated()), this, SLOT(shortcutF()));
keyM = new QShortcut(this);
keyM->setKey(Qt::Key_M);
connect(keyM, SIGNAL(activated()), this, SLOT(shortcutM()));
// Enumerate audio devices, need to do before settings are loaded.
const auto audioOutputs = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
for (const QAudioDeviceInfo& deviceInfo : audioOutputs) {
ui->audioOutputCombo->addItem(deviceInfo.deviceName());
}
const auto audioInputs = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
for (const QAudioDeviceInfo& deviceInfo : audioInputs) {
ui->audioInputCombo->addItem(deviceInfo.deviceName());
}
setDefaultColors(); // set of UI colors with defaults populated
setDefPrefs(); // other default options
loadSettings(); // Look for saved preferences
setTuningSteps(); // TODO: Combine into preferences
ui->spectrumModeCombo->addItem("Center", (spectrumMode)spectModeCenter);
ui->spectrumModeCombo->addItem("Fixed", (spectrumMode)spectModeFixed);
ui->spectrumModeCombo->addItem("Scroll-C", (spectrumMode)spectModeScrollC);
ui->spectrumModeCombo->addItem("Scroll-F", (spectrumMode)spectModeScrollF);
// if setting for serial port is "auto" then...
// if(prefs.serialPortRadio == QString("auto"))
// {
// // Find the ICOM IC-7300.
// qDebug(logSystem()) << "Searching for serial port...";
// QDirIterator it("/dev/serial", QStringList() << "*IC-7300*", QDir::Files, QDirIterator::Subdirectories);
// while (it.hasNext())
// qDebug(logSystem()) << it.next();
// // if (it.isEmpty()) // fail or default to ttyUSB0 if present
// // iterator might not make sense
// serialPortRig = it.filePath(); // first? last?
// if(serialPortRig.isEmpty())
// {
// qDebug(logSystem()) << "Cannot find IC-7300 serial port. Trying /dev/ttyUSB0";
// serialPortRig = QString("/dev/ttyUSB0");
// }
// // end finding the 7300 code
// } else {
// serialPortRig = prefs.serialPortRadio;
// }
// Start server if enabled in config
if (serverConfig.enabled) {
udp = new udpServer(serverConfig);
serverThread = new QThread(this);
udp->moveToThread(serverThread);
connect(this, SIGNAL(initServer()), udp, SLOT(init()));
connect(serverThread, SIGNAL(finished()), udp, SLOT(deleteLater()));
serverThread->start();
emit initServer();
connect(this, SIGNAL(sendRigCaps(rigCapabilities)), udp, SLOT(receiveRigCaps(rigCapabilities)));
}
plot = ui->plot; // rename it waterfall.
wf = ui->waterfall;
tracer = new QCPItemTracer(plot);
//tracer->setGraphKey(5.5);
tracer->setInterpolating(true);
tracer->setStyle(QCPItemTracer::tsCrosshair);
tracer->setPen(QPen(Qt::green));
tracer->setBrush(Qt::green);
tracer->setSize(30);
// spectWidth = 475; // fixed for now
// wfLength = 160; // fixed for now, time-length of waterfall
// // Initialize before use!
// QByteArray empty((int)spectWidth, '\x01');
// spectrumPeaks = QByteArray( (int)spectWidth, '\x01' );
// for(quint16 i=0; i<wfLength; i++)
// {
// wfimage.append(empty);
// }
ui->modeSelectCombo->addItem("LSB", 0x00);
ui->modeSelectCombo->addItem("USB", 0x01);
ui->modeSelectCombo->addItem("AM", 0x02);
ui->modeSelectCombo->addItem("CW", 0x03);
ui->modeSelectCombo->addItem("RTTY", 0x04);
ui->modeSelectCombo->addItem("FM", 0x05);
ui->modeSelectCombo->addItem("CW-R", 0x07);
ui->modeSelectCombo->addItem("RTTY-R", 0x08);
ui->modeFilterCombo->addItem("1", 1);
ui->modeFilterCombo->addItem("2", 2);
ui->modeFilterCombo->addItem("3", 3);
ui->modeFilterCombo->addItem("Setup...", 99);
ui->tuningStepCombo->blockSignals(true);
ui->tuningStepCombo->addItem("1 Hz", 0.000001f);
ui->tuningStepCombo->addItem("10 Hz", 0.000010f);
ui->tuningStepCombo->addItem("100 Hz", 0.000100f);
ui->tuningStepCombo->addItem("1 kHz", 0.001000f);
ui->tuningStepCombo->addItem("2.5 kHz", 0.002500f);
ui->tuningStepCombo->addItem("5 kHz", 0.005000f);
ui->tuningStepCombo->addItem("10 kHz", 0.010000f);
ui->tuningStepCombo->addItem("12.5 kHz", 0.012500f);
ui->tuningStepCombo->addItem("100 kHz", 0.100000f);
ui->tuningStepCombo->addItem("250 kHz", 0.250000f);
ui->tuningStepCombo->setCurrentIndex(2);
ui->tuningStepCombo->blockSignals(false);
spans << "2.5k" << "5.0k" << "10k" << "25k";
spans << "50k" << "100k" << "250k" << "500k";
ui->scopeBWCombo->insertItems(0, spans);
edges << "1" << "2" << "3" << "4"; // yep
ui->scopeEdgeCombo->insertItems(0, edges);
ui->splitter->setHandleWidth(5);
ui->rfGainSlider->setTickInterval(100);
ui->rfGainSlider->setSingleStep(100);
ui->afGainSlider->setSingleStep(100);
ui->afGainSlider->setSingleStep(100);
rigStatus = new QLabel(this);
ui->statusBar->addPermanentWidget(rigStatus);
ui->statusBar->showMessage("Connecting to rig...", 1000);
pttLed = new QLedLabel(this);
ui->statusBar->addPermanentWidget(pttLed);
pttLed->setState(QLedLabel::State::StateOk);
connectedLed = new QLedLabel(this);
ui->statusBar->addPermanentWidget(connectedLed);
rigName = new QLabel(this);
ui->statusBar->addPermanentWidget(rigName);
rigName->setText("NONE");
rigName->setFixedWidth(50);
delayedCommand = new QTimer(this);
delayedCommand->setInterval(250); // 250ms until we find rig civ and id, then 100ms.
delayedCommand->setSingleShot(true);
connect(delayedCommand, SIGNAL(timeout()), this, SLOT(runDelayedCommand()));
periodicPollingTimer = new QTimer(this);
periodicPollingTimer->setInterval(10);
periodicPollingTimer->setSingleShot(false);
connect(periodicPollingTimer, SIGNAL(timeout()), this, SLOT(runPeriodicCommands()));
openRig();
qRegisterMetaType<rigCapabilities>();
qRegisterMetaType<duplexMode>();
qRegisterMetaType<rigInput>();
qRegisterMetaType<meterKind>();
qRegisterMetaType<spectrumMode>();
connect(rig, SIGNAL(haveFrequency(double)), this, SLOT(receiveFreq(double)));
connect(this, SIGNAL(getFrequency()), rig, SLOT(getFrequency()));
connect(this, SIGNAL(getMode()), rig, SLOT(getMode()));
connect(this, SIGNAL(getDataMode()), rig, SLOT(getDataMode()));
connect(this, SIGNAL(setDataMode(bool)), rig, SLOT(setDataMode(bool)));
connect(this, SIGNAL(getBandStackReg(char,char)), rig, SLOT(getBandStackReg(char,char)));
connect(rig, SIGNAL(havePTTStatus(bool)), this, SLOT(receivePTTstatus(bool)));
connect(this, SIGNAL(setPTT(bool)), rig, SLOT(setPTT(bool)));
connect(this, SIGNAL(getPTT()), rig, SLOT(getPTT()));
connect(rig, SIGNAL(haveBandStackReg(float,char,bool)), this, SLOT(receiveBandStackReg(float,char,bool)));
connect(this, SIGNAL(getDebug()), rig, SLOT(getDebug()));
connect(this, SIGNAL(spectOutputDisable()), rig, SLOT(disableSpectOutput()));
connect(this, SIGNAL(spectOutputEnable()), rig, SLOT(enableSpectOutput()));
connect(this, SIGNAL(scopeDisplayDisable()), rig, SLOT(disableSpectrumDisplay()));
connect(this, SIGNAL(scopeDisplayEnable()), rig, SLOT(enableSpectrumDisplay()));
connect(rig, SIGNAL(haveMode(unsigned char, unsigned char)), this, SLOT(receiveMode(unsigned char, unsigned char)));
connect(rig, SIGNAL(haveDataMode(bool)), this, SLOT(receiveDataModeStatus(bool)));
connect(this, SIGNAL(getDuplexMode()), rig, SLOT(getDuplexMode()));
connect(this, SIGNAL(setDuplexMode(duplexMode)), rig, SLOT(setDuplexMode(duplexMode)));
connect(rig, SIGNAL(haveDuplexMode(duplexMode)), this, SLOT(receiveDuplexMode(duplexMode)));
connect(this, SIGNAL(getModInput(bool)), rig, SLOT(getModInput(bool)));
connect(rig, SIGNAL(haveModInput(rigInput,bool)), this, SLOT(receiveModInput(rigInput, bool)));
connect(this, SIGNAL(setModInput(rigInput, bool)), rig, SLOT(setModInput(rigInput,bool)));
connect(rig, SIGNAL(haveSpectrumData(QByteArray, double, double)), this, SLOT(receiveSpectrumData(QByteArray, double, double)));
connect(rig, SIGNAL(haveSpectrumMode(spectrumMode)), this, SLOT(receiveSpectrumMode(spectrumMode)));
connect(this, SIGNAL(setScopeMode(spectrumMode)), rig, SLOT(setSpectrumMode(spectrumMode)));
connect(this, SIGNAL(getScopeMode()), rig, SLOT(getScopeMode()));
connect(this, SIGNAL(setFrequency(double)), rig, SLOT(setFrequency(double)));
connect(this, SIGNAL(setScopeEdge(char)), rig, SLOT(setScopeEdge(char)));
connect(this, SIGNAL(setScopeSpan(char)), rig, SLOT(setScopeSpan(char)));
connect(this, SIGNAL(getScopeMode()), rig, SLOT(getScopeMode()));
connect(this, SIGNAL(getScopeEdge()), rig, SLOT(getScopeEdge()));
connect(this, SIGNAL(getScopeSpan()), rig, SLOT(getScopeSpan()));
connect(this, SIGNAL(setScopeFixedEdge(double,double,unsigned char)), rig, SLOT(setSpectrumBounds(double,double,unsigned char)));
connect(this, SIGNAL(setMode(unsigned char, unsigned char)), rig, SLOT(setMode(unsigned char, unsigned char)));
// Levels (read and write)
// Levels: Query:
connect(this, SIGNAL(getLevels()), rig, SLOT(getLevels()));
connect(this, SIGNAL(getRfGain()), rig, SLOT(getRfGain()));
connect(this, SIGNAL(getAfGain()), rig, SLOT(getAfGain()));
connect(this, SIGNAL(getSql()), rig, SLOT(getSql()));
connect(this, SIGNAL(getTxPower()), rig, SLOT(getTxLevel()));
connect(this, SIGNAL(getMicGain()), rig, SLOT(getMicGain()));
connect(this, SIGNAL(getSpectrumRefLevel()), rig, SLOT(getSpectrumRefLevel()));
connect(this, SIGNAL(getModInputLevel(rigInput)), rig, SLOT(getModInputLevel(rigInput)));
// Levels: Set:
connect(this, SIGNAL(setRfGain(unsigned char)), rig, SLOT(setRfGain(unsigned char)));
connect(this, SIGNAL(setAfGain(unsigned char)), rig, SLOT(setAfGain(unsigned char)));
connect(this, SIGNAL(setSql(unsigned char)), rig, SLOT(setSquelch(unsigned char)));
connect(this, SIGNAL(setTxPower(unsigned char)), rig, SLOT(setTxPower(unsigned char)));
connect(this, SIGNAL(setMicGain(unsigned char)), rig, SLOT(setMicGain(unsigned char)));
connect(this, SIGNAL(setMonitorLevel(unsigned char)), rig, SLOT(setMonitorLevel(unsigned char)));
connect(this, SIGNAL(setVoxGain(unsigned char)), rig, SLOT(setVoxGain(unsigned char)));
connect(this, SIGNAL(setAntiVoxGain(unsigned char)), rig, SLOT(setAntiVoxGain(unsigned char)));
connect(this, SIGNAL(setSpectrumRefLevel(int)), rig, SLOT(setSpectrumRefLevel(int)));
connect(this, SIGNAL(setModLevel(rigInput,unsigned char)), rig, SLOT(setModInputLevel(rigInput,unsigned char)));
// Levels: handle return on query:
connect(rig, SIGNAL(haveRfGain(unsigned char)), this, SLOT(receiveRfGain(unsigned char)));
connect(rig, SIGNAL(haveAfGain(unsigned char)), this, SLOT(receiveAfGain(unsigned char)));
connect(rig, SIGNAL(haveSql(unsigned char)), this, SLOT(receiveSql(unsigned char)));
connect(rig, SIGNAL(haveTxPower(unsigned char)), this, SLOT(receiveTxPower(unsigned char)));
connect(rig, SIGNAL(haveMicGain(unsigned char)), this, SLOT(receiveMicGain(unsigned char)));
connect(rig, SIGNAL(haveSpectrumRefLevel(int)), this, SLOT(receiveSpectrumRefLevel(int)));
connect(rig, SIGNAL(haveACCGain(unsigned char,unsigned char)), this, SLOT(receiveACCGain(unsigned char,unsigned char)));
connect(rig, SIGNAL(haveUSBGain(unsigned char)), this, SLOT(receiveUSBGain(unsigned char)));
connect(rig, SIGNAL(haveLANGain(unsigned char)), this, SLOT(receiveLANGain(unsigned char)));
//Metering:
connect(this, SIGNAL(getMeters(meterKind)), rig, SLOT(getMeters(meterKind)));
connect(rig, SIGNAL(haveMeter(meterKind,unsigned char)), this, SLOT(receiveMeter(meterKind,unsigned char)));
// Rig and ATU info:
connect(this, SIGNAL(startATU()), rig, SLOT(startATU()));
connect(this, SIGNAL(setATU(bool)), rig, SLOT(setATU(bool)));
connect(this, SIGNAL(getATUStatus()), rig, SLOT(getATUStatus()));
connect(this, SIGNAL(getRigID()), rig, SLOT(getRigID()));
connect(rig, SIGNAL(haveATUStatus(unsigned char)), this, SLOT(receiveATUStatus(unsigned char)));
connect(rig, SIGNAL(haveRigID(rigCapabilities)), this, SLOT(receiveRigID(rigCapabilities)));
// Speech (emitted from rig speaker)
connect(this, SIGNAL(sayAll()), rig, SLOT(sayAll()));
connect(this, SIGNAL(sayFrequency()), rig, SLOT(sayFrequency()));
connect(this, SIGNAL(sayMode()), rig, SLOT(sayMode()));
// calibration window:
connect(cal, SIGNAL(requestRefAdjustCourse()), rig, SLOT(getRefAdjustCourse()));
connect(cal, SIGNAL(requestRefAdjustFine()), rig, SLOT(getRefAdjustFine()));
connect(rig, SIGNAL(haveRefAdjustCourse(unsigned char)), cal, SLOT(handleRefAdjustCourse(unsigned char)));
connect(rig, SIGNAL(haveRefAdjustFine(unsigned char)), cal, SLOT(handleRefAdjustFine(unsigned char)));
connect(cal, SIGNAL(setRefAdjustCourse(unsigned char)), rig, SLOT(setRefAdjustCourse(unsigned char)));
connect(cal, SIGNAL(setRefAdjustFine(unsigned char)), rig, SLOT(setRefAdjustFine(unsigned char)));
// Plot user interaction
connect(plot, SIGNAL(mouseDoubleClick(QMouseEvent*)), this, SLOT(handlePlotDoubleClick(QMouseEvent*)));
connect(wf, SIGNAL(mouseDoubleClick(QMouseEvent*)), this, SLOT(handleWFDoubleClick(QMouseEvent*)));
connect(plot, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(handlePlotClick(QMouseEvent*)));
connect(wf, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(handleWFClick(QMouseEvent*)));
connect(wf, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(handleWFScroll(QWheelEvent*)));
connect(plot, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(handlePlotScroll(QWheelEvent*)));
if (serverConfig.enabled && udp != Q_NULLPTR) {
// Server
connect(rig, SIGNAL(haveAudioData(audioPacket)), udp, SLOT(receiveAudioData(audioPacket)));
connect(rig, SIGNAL(haveDataForServer(QByteArray)), udp, SLOT(dataForServer(QByteArray)));
connect(udp, SIGNAL(haveDataFromServer(QByteArray)), rig, SLOT(dataFromServer(QByteArray)));
}
ui->plot->addGraph(); // primary
ui->plot->addGraph(0, 0); // secondary, peaks, same axis as first?
ui->waterfall->addGraph();
tracer->setGraph(plot->graph(0));
colorMap = new QCPColorMap(wf->xAxis, wf->yAxis);
colorMapData = NULL;
#if QCUSTOMPLOT_VERSION < 0x020001
wf->addPlottable(colorMap);
#endif
colorScale = new QCPColorScale(wf);
ui->tabWidget->setCurrentIndex(0);
QColor color(20+200/4.0*1,70*(1.6-1/4.0), 150, 150);
plot->graph(1)->setLineStyle(QCPGraph::lsLine);
plot->graph(1)->setPen(QPen(color.lighter(200)));
plot->graph(1)->setBrush(QBrush(color));
drawPeaks = false;
SMeterReadings.fill(0,30);
powerMeterReadings.fill(0,30);
ui->freqMhzLineEdit->setValidator( new QDoubleValidator(0, 100, 6, this));
ui->controlPortTxt->setValidator(new QIntValidator(this));
pttTimer = new QTimer(this);
pttTimer->setInterval(180*1000); // 3 minute max transmit time in ms
pttTimer->setSingleShot(true);
connect(pttTimer, SIGNAL(timeout()), this, SLOT(handlePttLimit()));
amTransmitting = false;
ui->tuneLockChk->setChecked(false);
freqLock = false;
// Not needed since we automate this now.
/*
foreach (const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts())
{
portList.append(serialPortInfo.portName());
// ui->commPortDrop->addItem(serialPortInfo.portName());
}
*/
#ifdef QT_DEBUG
qDebug(logSystem()) << "Running with debugging options enabled.";
ui->debugBtn->setVisible(true);
#else
ui->debugBtn->setVisible(false);
#endif
// Initial state of UI:
ui->fullScreenChk->setChecked(prefs.useFullScreen);
on_fullScreenChk_clicked(prefs.useFullScreen);
ui->useDarkThemeChk->setChecked(prefs.useDarkMode);
on_useDarkThemeChk_clicked(prefs.useDarkMode);
ui->useSystemThemeChk->setChecked(prefs.useSystemTheme);
on_useSystemThemeChk_clicked(prefs.useSystemTheme);
ui->drawPeakChk->setChecked(prefs.drawPeaks);
on_drawPeakChk_clicked(prefs.drawPeaks);
drawPeaks = prefs.drawPeaks;
//getInitialRigState();
oldFreqDialVal = ui->freqDial->value();
}
wfmain::~wfmain()
{
rigThread->quit();
rigThread->wait();
if (serverThread != Q_NULLPTR) {
serverThread->quit();
serverThread->wait();
}
delete ui;
}
void wfmain::openRig()
{
// This function is intended to handle opening a connection to the rig.
// the connection can be either serial or network,
// and this function is also responsible for initiating the search for a rig model and capabilities.
// Any errors, such as unable to open connection or unable to open port, are to be reported to the user.
//TODO: if(hasRunPreviously)
//TODO: if(useNetwork){...
// } else {
// if (prefs.fileWasNotFound) {
// showRigSettings(); // rig setting dialog box for network/serial, CIV, hostname, port, baud rate, serial device, etc
// TODO: How do we know if the setting was loaded?
// TODO: Use these if they are found
#ifdef QT_DEBUG
if(!serialPortCL.isEmpty())
{
qDebug(logSystem()) << "Serial port specified by user: " << serialPortCL;
} else {
qDebug(logSystem()) << "Serial port not specified. ";
}
if(!hostCL.isEmpty())
{
qDebug(logSystem()) << "Remote host name specified by user: " << hostCL;
}
#endif
if (rigThread == Q_NULLPTR)
{
rig = new rigCommander();
rigThread = new QThread(this);
rig->moveToThread(rigThread);
connect(rigThread, SIGNAL(started()), rig, SLOT(process()));
connect(rigThread, SIGNAL(finished()), rig, SLOT(deleteLater()));
rigThread->start();
connect(rig, SIGNAL(haveSerialPortError(QString, QString)), this, SLOT(receiveSerialPortError(QString, QString)));
connect(rig, SIGNAL(haveStatusUpdate(QString)), this, SLOT(receiveStatusUpdate(QString)));
connect(this, SIGNAL(sendCommSetup(unsigned char, udpPreferences)), rig, SLOT(commSetup(unsigned char, udpPreferences)));
connect(this, SIGNAL(sendCommSetup(unsigned char, QString, quint32)), rig, SLOT(commSetup(unsigned char, QString, quint32)));
connect(this, SIGNAL(sendCloseComm()), rig, SLOT(closeComm()));
connect(this, SIGNAL(sendChangeLatency(quint16)), rig, SLOT(changeLatency(quint16)));
connect(this, SIGNAL(getRigCIV()), rig, SLOT(findRigs()));
connect(rig, SIGNAL(discoveredRigID(rigCapabilities)), this, SLOT(receiveFoundRigID(rigCapabilities)));
connect(rig, SIGNAL(commReady()), this, SLOT(receiveCommReady()));
}
if (prefs.enableLAN)
{
ui->lanEnableBtn->setChecked(true);
emit sendCommSetup(prefs.radioCIVAddr, udpPrefs);
} else {
ui->serialEnableBtn->setChecked(true);
if( (prefs.serialPortRadio == QString("auto")) && (serialPortCL.isEmpty()))
{
// Find the ICOM
// qDebug(logSystem()) << "Searching for serial port...";
QDirIterator it73("/dev/serial", QStringList() << "*IC-7300*", QDir::Files, QDirIterator::Subdirectories);
QDirIterator it97("/dev/serial", QStringList() << "*IC-9700*A*", QDir::Files, QDirIterator::Subdirectories);
QDirIterator it785x("/dev/serial", QStringList() << "*IC-785*A*", QDir::Files, QDirIterator::Subdirectories);
QDirIterator it705("/dev/serial", QStringList() << "*IC-705*A", QDir::Files, QDirIterator::Subdirectories);
if(!it73.filePath().isEmpty())
{
// use
serialPortRig = it73.filePath(); // first
} else if(!it97.filePath().isEmpty())
{
// IC-9700 port
serialPortRig = it97.filePath();
} else if(!it785x.filePath().isEmpty())
{
// IC-785x port
serialPortRig = it785x.filePath();
} else if(!it705.filePath().isEmpty())
{
// IC-705
serialPortRig = it705.filePath();
} else {
//fall back:
qDebug(logSystem()) << "Could not find Icom serial port. Falling back to OS default. Use --port to specify, or modify preferences.";
#ifdef Q_OS_MAC
serialPortRig = QString("/dev/tty.SLAB_USBtoUART");
#endif
#ifdef Q_OS_LINUX
serialPortRig = QString("/dev/ttyUSB0");
#endif
#ifdef Q_OS_WIN
serialPortRig = QString("COM1");
#endif
}
} else {
if(serialPortCL.isEmpty())
{
serialPortRig = prefs.serialPortRadio;
} else {
serialPortRig = serialPortCL;
}
}
// Here, the radioCIVAddr is being set from a default preference, which is for the 7300.
// However, we will not use it initially. OTOH, if it is set explicitedly to a value in the prefs,
// then we skip auto detection.
emit sendCommSetup(prefs.radioCIVAddr, serialPortRig, prefs.serialPortBaud);
}
ui->statusBar->showMessage(QString("Connecting to rig using serial port ").append(serialPortRig), 1000);
/*
if(prefs.radioCIVAddr == 0)
{
// tell rigCommander to broadcast a request for all rig IDs.
// qDebug(logSystem()) << "Beginning search from wfview for rigCIV (auto-detection broadcast)";
ui->statusBar->showMessage(QString("Searching CIV bus for connected radios."), 1000);
emit getRigCIV();
cmdOutQue.append(cmdGetRigCIV);
delayedCommand->start();
} else {
// don't bother, they told us the CIV they want, stick with it.
// We still query the rigID to find the model, but at least we know the CIV.
qDebug(logSystem()) << "Skipping automatic CIV, using user-supplied value of " << prefs.radioCIVAddr;
getInitialRigState();
}
*/
}
void wfmain::receiveCommReady()
{
qDebug(logSystem()) << "Received CommReady!! ";
// taken from above:
if(prefs.radioCIVAddr == 0)
{
// tell rigCommander to broadcast a request for all rig IDs.
// qDebug(logSystem()) << "Beginning search from wfview for rigCIV (auto-detection broadcast)";
ui->statusBar->showMessage(QString("Searching CIV bus for connected radios."), 1000);
emit getRigCIV();
cmdOutQue.append(cmdGetRigCIV);
delayedCommand->start();
} else {
// don't bother, they told us the CIV they want, stick with it.
// We still query the rigID to find the model, but at least we know the CIV.
qDebug(logSystem()) << "Skipping automatic CIV, using user-supplied value of " << prefs.radioCIVAddr;
getInitialRigState();
}
}
void wfmain::receiveFoundRigID(rigCapabilities rigCaps)
{
// Entry point for unknown rig being identified at the start of the program.
//now we know what the rig ID is:
//qDebug(logSystem()) << "In wfview, we now have a reply to our request for rig identity sent to CIV BROADCAST.";
delayedCommand->setInterval(100); // faster polling is ok now.
receiveRigID(rigCaps);
getInitialRigState();
//QString message = QString("Found model: ").append(rigCaps.modelName);
//ui->statusBar->showMessage(message, 0);
return;
}
void wfmain::receiveSerialPortError(QString port, QString errorText)
{
qDebug(logSystem()) << "wfmain: received serial port error for port: " << port << " with message: " << errorText;
ui->statusBar->showMessage(QString("ERROR: using port ").append(port).append(": ").append(errorText), 10000);
// TODO: Dialog box, exit, etc
}
void wfmain::receiveStatusUpdate(QString text)
{
this->rigStatus->setText(text);
}
void wfmain::setDefPrefs()
{
defPrefs.useFullScreen = false;
defPrefs.useDarkMode = true;
defPrefs.useSystemTheme = false;
defPrefs.drawPeaks = true;
defPrefs.stylesheetPath = QString("qdarkstyle/style.qss");
defPrefs.radioCIVAddr = 0x00; // previously was 0x94 for 7300.
defPrefs.serialPortRadio = QString("auto");
defPrefs.serialPortBaud = 115200;
defPrefs.enablePTT = false;
defPrefs.niceTS = true;
udpDefPrefs.ipAddress = QString("");
udpDefPrefs.controlLANPort = 50001;
udpDefPrefs.serialLANPort = 50002;
udpDefPrefs.audioLANPort = 50003;
udpDefPrefs.username = QString("");
udpDefPrefs.password = QString("");
udpDefPrefs.audioOutput = QAudioDeviceInfo::defaultOutputDevice().deviceName();
udpDefPrefs.audioInput = QAudioDeviceInfo::defaultInputDevice().deviceName();
udpDefPrefs.audioRXLatency = 150;
udpDefPrefs.audioTXLatency = 150;
udpDefPrefs.audioRXSampleRate = 48000;
udpDefPrefs.audioRXCodec = 4;
udpDefPrefs.audioTXSampleRate = 48000;
udpDefPrefs.audioTXCodec = 4;
}
void wfmain::loadSettings()
{
qDebug(logSystem()) << "Loading settings from " << settings.fileName();
// Basic things to load:
// UI: (full screen, dark theme, draw peaks, colors, etc)
settings.beginGroup("Interface");
prefs.useFullScreen = settings.value("UseFullScreen", defPrefs.useFullScreen).toBool();
prefs.useDarkMode = settings.value("UseDarkMode", defPrefs.useDarkMode).toBool();
prefs.useSystemTheme = settings.value("UseSystemTheme", defPrefs.useSystemTheme).toBool();
prefs.drawPeaks = settings.value("DrawPeaks", defPrefs.drawPeaks).toBool();
prefs.stylesheetPath = settings.value("StylesheetPath", defPrefs.stylesheetPath).toString();
ui->splitter->restoreState(settings.value("splitter").toByteArray());
restoreGeometry(settings.value("windowGeometry").toByteArray());
restoreState(settings.value("windowState").toByteArray());
setWindowState(Qt::WindowActive); // Works around QT bug to returns window+keyboard focus.
settings.endGroup();
// Radio and Comms: C-IV addr, port to use
settings.beginGroup("Radio");
prefs.radioCIVAddr = (unsigned char) settings.value("RigCIVuInt", defPrefs.radioCIVAddr).toInt();
prefs.serialPortRadio = settings.value("SerialPortRadio", defPrefs.serialPortRadio).toString();
prefs.serialPortBaud = (quint32) settings.value("SerialPortBaud", defPrefs.serialPortBaud).toInt();
settings.endGroup();
// Misc. user settings (enable PTT, draw peaks, etc)
settings.beginGroup("Controls");
prefs.enablePTT = settings.value("EnablePTT", defPrefs.enablePTT).toBool();
ui->pttEnableChk->setChecked(prefs.enablePTT);
prefs.niceTS = settings.value("NiceTS", defPrefs.niceTS).toBool();
settings.endGroup();
settings.beginGroup("LAN");
prefs.enableLAN = settings.value("EnableLAN", defPrefs.enableLAN).toBool();
ui->lanEnableBtn->setChecked(prefs.enableLAN);
ui->connectBtn->setEnabled(prefs.enableLAN);
udpPrefs.ipAddress = settings.value("IPAddress", udpDefPrefs.ipAddress).toString();
ui->ipAddressTxt->setEnabled(ui->lanEnableBtn->isChecked());
ui->ipAddressTxt->setText(udpPrefs.ipAddress);
udpPrefs.controlLANPort = settings.value("ControlLANPort", udpDefPrefs.controlLANPort).toInt();
ui->controlPortTxt->setEnabled(ui->lanEnableBtn->isChecked());
ui->controlPortTxt->setText(QString("%1").arg(udpPrefs.controlLANPort));
udpPrefs.username = settings.value("Username", udpDefPrefs.username).toString();
ui->usernameTxt->setEnabled(ui->lanEnableBtn->isChecked());
ui->usernameTxt->setText(QString("%1").arg(udpPrefs.username));
udpPrefs.password = settings.value("Password", udpDefPrefs.password).toString();
ui->passwordTxt->setEnabled(ui->lanEnableBtn->isChecked());
ui->passwordTxt->setText(QString("%1").arg(udpPrefs.password));
udpPrefs.audioRXLatency = settings.value("AudioRXLatency", udpDefPrefs.audioRXLatency).toInt();
ui->rxLatencySlider->setEnabled(ui->lanEnableBtn->isChecked());
ui->rxLatencySlider->setValue(udpPrefs.audioRXLatency);
ui->rxLatencySlider->setTracking(false); // Stop it sending value on every change.
udpPrefs.audioTXLatency = settings.value("AudioTXLatency", udpDefPrefs.audioTXLatency).toInt();
ui->txLatencySlider->setEnabled(ui->lanEnableBtn->isChecked());
ui->txLatencySlider->setValue(udpPrefs.audioTXLatency);
ui->txLatencySlider->setTracking(false); // Stop it sending value on every change.
udpPrefs.audioRXSampleRate = settings.value("AudioRXSampleRate", udpDefPrefs.audioRXSampleRate).toInt();
udpPrefs.audioTXSampleRate = settings.value("AudioTXSampleRate",udpDefPrefs.audioTXSampleRate).toInt();
ui->audioSampleRateCombo->setEnabled(ui->lanEnableBtn->isChecked());
int audioSampleRateIndex = ui->audioSampleRateCombo->findText(QString::number(udpDefPrefs.audioRXSampleRate));
if (audioSampleRateIndex != -1) {
ui->audioOutputCombo->setCurrentIndex(audioSampleRateIndex);
}
// Add codec combobox items here so that we can add userdata!
ui->audioRXCodecCombo->addItem("LPCM 1ch 16bit", 4);
ui->audioRXCodecCombo->addItem("LPCM 1ch 8bit", 2);
ui->audioRXCodecCombo->addItem("uLaw 1ch 8bit", 1);
ui->audioRXCodecCombo->addItem("LPCM 2ch 16bit", 16);
ui->audioRXCodecCombo->addItem("uLaw 2ch 8bit", 32);
ui->audioRXCodecCombo->addItem("PCM 2ch 8bit", 8);
udpPrefs.audioRXCodec = settings.value("AudioRXCodec", udpDefPrefs.audioRXCodec).toInt();
ui->audioRXCodecCombo->setEnabled(ui->lanEnableBtn->isChecked());
for (int f = 0; f < ui->audioRXCodecCombo->count(); f++)
if (ui->audioRXCodecCombo->itemData(f).toInt() == udpPrefs.audioRXCodec)
ui->audioRXCodecCombo->setCurrentIndex(f);
ui->audioTXCodecCombo->addItem("LPCM 1ch 16bit", 4);
ui->audioTXCodecCombo->addItem("LPCM 1ch 8bit", 2);
ui->audioTXCodecCombo->addItem("uLaw 1ch 8bit", 1);
udpPrefs.audioTXCodec = settings.value("AudioTXCodec", udpDefPrefs.audioTXCodec).toInt();
ui->audioTXCodecCombo->setEnabled(ui->lanEnableBtn->isChecked());
for (int f = 0; f < ui->audioTXCodecCombo->count(); f++)
if (ui->audioTXCodecCombo->itemData(f).toInt() == udpPrefs.audioTXCodec)
ui->audioTXCodecCombo->setCurrentIndex(f);
udpPrefs.audioOutput = settings.value("AudioOutput", udpDefPrefs.audioOutput).toString();
ui->audioOutputCombo->setEnabled(ui->lanEnableBtn->isChecked());
int audioOutputIndex = ui->audioOutputCombo->findText(udpPrefs.audioOutput);
if (audioOutputIndex != -1)
ui->audioOutputCombo->setCurrentIndex(audioOutputIndex);
udpPrefs.audioInput = settings.value("AudioInput", udpDefPrefs.audioInput).toString();
ui->audioInputCombo->setEnabled(ui->lanEnableBtn->isChecked());
int audioInputIndex = ui->audioInputCombo->findText(udpPrefs.audioInput);
if (audioInputIndex != - 1)
ui->audioOutputCombo->setCurrentIndex(audioInputIndex);
settings.endGroup();
settings.beginGroup("Server");
serverConfig.enabled = settings.value("ServerEnabled", false).toBool();
serverConfig.controlPort = settings.value("ServerControlPort", 50001).toInt();
serverConfig.civPort = settings.value("ServerCivPort", 50002).toInt();
serverConfig.audioPort = settings.value("ServerAudioPort", 50003).toInt();
int numUsers = settings.value("ServerNumUsers", 2).toInt();
serverConfig.users.clear();
for (int f = 0; f < numUsers; f++)
{
SERVERUSER user;
user.username = settings.value("ServerUsername_" + QString::number(f), "").toString();
user.password = settings.value("ServerPassword_" + QString::number(f), "").toString();
user.userType = settings.value("ServerUserType_" + QString::number(f), 0).toInt();
serverConfig.users.append(user);
}
settings.endGroup();
// Memory channels
settings.beginGroup("Memory");
int size = settings.beginReadArray("Channel");
int chan = 0;
double freq;
unsigned char mode;
bool isSet;
// Annoying: QSettings will write the array to the
// preference file starting the array at 1 and ending at 100.
// Thus, we are writing the channel number each time.
// It is also annoying that they get written with their array
// numbers in alphabetical order without zero padding.
// Also annoying that the preference groups are not written in
// the order they are specified here.
for(int i=0; i < size; i++)
{
settings.setArrayIndex(i);
chan = settings.value("chan", 0).toInt();
freq = settings.value("freq", 12.345).toDouble();
mode = settings.value("mode", 0).toInt();
isSet = settings.value("isSet", false).toBool();
if(isSet)
{
mem.setPreset(chan, freq, (mode_kind)mode);
}
}
settings.endArray();
settings.endGroup();
emit sendServerConfig(serverConfig);
}
void wfmain::saveSettings()
{
qDebug(logSystem()) << "Saving settings to " << settings.fileName();
// Basic things to load:
// UI: (full screen, dark theme, draw peaks, colors, etc)
settings.beginGroup("Interface");
settings.setValue("UseFullScreen", prefs.useFullScreen);
settings.setValue("UseSystemTheme", prefs.useSystemTheme);
settings.setValue("UseDarkMode", prefs.useDarkMode);
settings.setValue("DrawPeaks", prefs.drawPeaks);
settings.setValue("StylesheetPath", prefs.stylesheetPath);
settings.setValue("splitter", ui->splitter->saveState());
settings.setValue("windowGeometry", saveGeometry());
settings.setValue("windowState", saveState());
settings.endGroup();
// Radio and Comms: C-IV addr, port to use
settings.beginGroup("Radio");
settings.setValue("RigCIVuInt", prefs.radioCIVAddr);
settings.setValue("SerialPortRadio", prefs.serialPortRadio);
settings.setValue("SerialPortBaud", prefs.serialPortBaud);
settings.endGroup();
// Misc. user settings (enable PTT, draw peaks, etc)
settings.beginGroup("Controls");
settings.setValue("EnablePTT", prefs.enablePTT);
settings.setValue("NiceTS", prefs.niceTS);
settings.endGroup();
settings.beginGroup("LAN");
settings.setValue("EnableLAN", prefs.enableLAN);
settings.setValue("IPAddress", udpPrefs.ipAddress);
settings.setValue("ControlLANPort", udpPrefs.controlLANPort);
settings.setValue("SerialLANPort", udpPrefs.serialLANPort);
settings.setValue("AudioLANPort", udpPrefs.audioLANPort);
settings.setValue("Username", udpPrefs.username);
settings.setValue("Password", udpPrefs.password);
settings.setValue("AudioRXLatency", udpPrefs.audioRXLatency);
settings.setValue("AudioTXLatency", udpPrefs.audioTXLatency);
settings.setValue("AudioRXSampleRate", udpPrefs.audioRXSampleRate);
settings.setValue("AudioRXCodec", udpPrefs.audioRXCodec);
settings.setValue("AudioTXSampleRate", udpPrefs.audioRXSampleRate);
settings.setValue("AudioTXCodec", udpPrefs.audioTXCodec);
settings.setValue("AudioOutput", udpPrefs.audioOutput);
settings.setValue("AudioInput", udpPrefs.audioInput);
settings.endGroup();
// Memory channels
settings.beginGroup("Memory");
settings.beginWriteArray("Channel", (int)mem.getNumPresets());
preset_kind temp;
for(int i=0; i < (int)mem.getNumPresets(); i++)
{
temp = mem.getPreset((int)i);
settings.setArrayIndex(i);
settings.setValue("chan", i);
settings.setValue("freq", temp.frequency);
settings.setValue("mode", temp.mode);
settings.setValue("isSet", temp.isSet);
}
settings.endArray();
settings.endGroup();
// Note: X and Y get the same colors. See setPlotTheme() function
settings.beginGroup("DarkColors");
settings.setValue("Dark_PlotBackground", QColor(0,0,0,255));
settings.setValue("Dark_PlotAxisPen", QColor(75,75,75,255));
settings.setValue("Dark_PlotLegendTextColor", QColor(255,255,255,255));
settings.setValue("Dark_PlotLegendBorderPen", QColor(255,255,255,255));
settings.setValue("Dark_PlotLegendBrush", QColor(0,0,0,200));
settings.setValue("Dark_PlotTickLabel", QColor(Qt::white));
settings.setValue("Dark_PlotBasePen", QColor(Qt::white));
settings.setValue("Dark_PlotTickPen", QColor(Qt::white));
settings.setValue("Dark_PlotFreqTracer", QColor(Qt::yellow));
settings.endGroup();
settings.beginGroup("LightColors");
settings.setValue("Light_PlotBackground", QColor(255,255,255,255));
settings.setValue("Light_PlotAxisPen", QColor(200,200,200,255));
settings.setValue("Light_PlotLegendTextColor", QColor(0,0,0,255));
settings.setValue("Light_PlotLegendBorderPen", QColor(0,0,0,255));
settings.setValue("Light_PlotLegendBrush", QColor(255,255,255,200));
settings.setValue("Light_PlotTickLabel", QColor(Qt::black));
settings.setValue("Light_PlotBasePen", QColor(Qt::black));
settings.setValue("Light_PlotTickPen", QColor(Qt::black));
settings.setValue("Light_PlotFreqTracer", QColor(Qt::blue));
settings.endGroup();
// This is a reference to see how the preference file is encoded.
settings.beginGroup("StandardColors");
settings.setValue("white", QColor(Qt::white));
settings.setValue("black", QColor(Qt::black));
settings.setValue("red_opaque", QColor(Qt::red));
settings.setValue("red_translucent", QColor(255,0,0,128));
settings.setValue("green_opaque", QColor(Qt::green));
settings.setValue("green_translucent", QColor(0,255,0,128));
settings.setValue("blue_opaque", QColor(Qt::blue));
settings.setValue("blue_translucent", QColor(0,0,255,128));
settings.endGroup();
settings.beginGroup("Server");
settings.setValue("ServerEnabled", serverConfig.enabled);
settings.setValue("ServerControlPort", serverConfig.controlPort);
settings.setValue("ServerCivPort", serverConfig.civPort);
settings.setValue("ServerAudioPort", serverConfig.audioPort);
settings.setValue("ServerNumUsers", serverConfig.users.count());
for (int f = 0; f < serverConfig.users.count(); f++)
{
settings.setValue("ServerUsername_" + QString::number(f), serverConfig.users[f].username);
settings.setValue("ServerPassword_" + QString::number(f), serverConfig.users[f].password);
settings.setValue("ServerUserType_" + QString::number(f), serverConfig.users[f].userType);
}
settings.endGroup();
settings.sync(); // Automatic, not needed (supposedly)
}
void wfmain::prepareWf()
{
// All this code gets moved in from the constructor of wfmain.
if(haveRigCaps)
{
// do things
spectWidth = rigCaps.spectLenMax; // was fixed at 475
wfLength = 160; // fixed for now, time-length of waterfall
// Initialize before use!
QByteArray empty((int)spectWidth, '\x01');
spectrumPeaks = QByteArray( (int)spectWidth, '\x01' );
for(quint16 i=0; i<wfLength; i++)
{
wfimage.append(empty);
}
// from line 305-313:
colorMap->data()->setValueRange(QCPRange(0, wfLength-1));
colorMap->data()->setKeyRange(QCPRange(0, spectWidth-1));
colorMap->setDataRange(QCPRange(0, rigCaps.spectAmpMax));
colorMap->setGradient(QCPColorGradient::gpJet); // TODO: Add preference
colorMapData = new QCPColorMapData(spectWidth, wfLength, QCPRange(0, spectWidth-1), QCPRange(0, wfLength-1));
colorMap->setData(colorMapData);
spectRowCurrent = 0;
wf->yAxis->setRangeReversed(true);
wf->xAxis->setVisible(false);
rigName->setText(rigCaps.modelName);
} else {
qDebug(logSystem()) << "Cannot prepare WF view without rigCaps. Waiting on this.";
return;
}
}
// Key shortcuts (hotkeys)
void wfmain::shortcutF11()
{
if(onFullscreen)
{
this->showNormal();
onFullscreen = false;
} else {
this->showFullScreen();
onFullscreen = true;
}
ui->fullScreenChk->setChecked(onFullscreen);
}
void wfmain::shortcutF1()
{
ui->tabWidget->setCurrentIndex(0);
}
void wfmain::shortcutF2()
{
ui->tabWidget->setCurrentIndex(1);
}
void wfmain::shortcutF3()
{
ui->tabWidget->setCurrentIndex(2);
ui->freqMhzLineEdit->clear();
ui->freqMhzLineEdit->setFocus();
}
void wfmain::shortcutF4()
{
ui->tabWidget->setCurrentIndex(3);
}
// Mode switch keys:
void wfmain::shortcutF5()
{
// LSB
changeMode(modeLSB, false);
}
void wfmain::shortcutF6()
{
// USB
changeMode(modeUSB, false);
}
void wfmain::shortcutF7()
{
// AM
changeMode(modeAM, false);
}
void wfmain::shortcutF8()
{
// CW
changeMode(modeCW, false);
}
void wfmain::shortcutF9()
{
// USB-D
changeMode(modeUSB, true);
}
void wfmain::shortcutF10()
{
// FM
changeMode(modeFM, false);
}
void wfmain::shortcutF12()
{
// Speak current frequency and mode via IC-7300
showStatusBarText("Sending speech command to radio.");
emit sayAll();
}
void wfmain::shortcutControlT()
{
// Transmit
qDebug(logSystem()) << "Activated Control-T shortcut";
showStatusBarText(QString("Transmitting. Press Control-R to receive."));
ui->pttOnBtn->click();
}
void wfmain::shortcutControlR()
{
// Receive
emit setPTT(false);
issueDelayedCommand(cmdGetPTT);
}
void wfmain::shortcutControlI()
{
// Enable ATU
ui->tuneEnableChk->click();
}
void wfmain::shortcutControlU()
{
// Run ATU tuning cycle
ui->tuneNowBtn->click();
}
void wfmain::shortcutStar()
{
// Jump to frequency tab from Asterisk key on keypad
ui->tabWidget->setCurrentIndex(2);
ui->freqMhzLineEdit->clear();
ui->freqMhzLineEdit->setFocus();
}
void wfmain::shortcutSlash()
{
// Cycle through available modes
ui->modeSelectCombo->setCurrentIndex( (ui->modeSelectCombo->currentIndex()+1) % ui->modeSelectCombo->count() );
on_modeSelectCombo_activated( ui->modeSelectCombo->currentIndex() );
}
void wfmain::setTuningSteps()
{
// TODO: interact with preferences, tuning step drop down box, and current operating mode
// Units are MHz:
tsPlusControl = 0.010f;
tsPlus = 0.001f;
tsPlusShift = 0.0001f;
tsPage = 1.0f;
tsPageShift = 0.5f; // TODO, unbind this keystroke from the dial
tsWfScroll = 0.0001f;
tsKnobMHz = 0.0001f;
}
void wfmain::on_tuningStepCombo_currentIndexChanged(int index)
{
tsWfScroll = ui->tuningStepCombo->itemData(index).toFloat();
tsKnobMHz = ui->tuningStepCombo->itemData(index).toFloat();
}
double wfmain::roundFrequency(double frequency)
{
return round(frequency*1000000) / 1000000.0;
}
void wfmain::shortcutMinus()
{
if(freqLock) return;
freqMhz = roundFrequency(freqMhz - tsPlus);
knobFreqMhz = freqMhz;
emit setFrequency(freqMhz);
issueDelayedCommand(cmdGetFreq);
//ui->freqDial->setValue( ui->freqDial->value() - ui->freqDial->singleStep() );
}
void wfmain::shortcutPlus()
{
if(freqLock) return;
knobFreqMhz = roundFrequency(freqMhz + tsPlus);
freqMhz = knobFreqMhz;
emit setFrequency(freqMhz);
issueDelayedCommand(cmdGetFreq);
//ui->freqDial->setValue( ui->freqDial->value() + ui->freqDial->singleStep() );
}
void wfmain::shortcutShiftMinus()
{
if(freqLock) return;
freqMhz= roundFrequency(freqMhz-tsPlusShift);
knobFreqMhz = freqMhz;
emit setFrequency(freqMhz);
issueDelayedCommand(cmdGetFreq);
//ui->freqDial->setValue( ui->freqDial->value() - ui->freqDial->pageStep() );
}
void wfmain::shortcutShiftPlus()
{
if(freqLock) return;
freqMhz= roundFrequency(freqMhz+tsPlusShift);
knobFreqMhz = freqMhz;
emit setFrequency(freqMhz);
issueDelayedCommand(cmdGetFreq);
//ui->freqDial->setValue( ui->freqDial->value() + ui->freqDial->pageStep() );
}
void wfmain::shortcutControlMinus()
{
if(freqLock) return;
freqMhz= roundFrequency(freqMhz-tsPlusControl);
knobFreqMhz = freqMhz;
emit setFrequency(freqMhz);
issueDelayedCommand(cmdGetFreq);
//ui->freqDial->setValue( ui->freqDial->value() - ui->freqDial->pageStep() );
}
void wfmain::shortcutControlPlus()
{
if(freqLock) return;
freqMhz= roundFrequency(freqMhz+tsPlusControl);
knobFreqMhz = freqMhz;
emit setFrequency(freqMhz);
issueDelayedCommand(cmdGetFreq);
//ui->freqDial->setValue( ui->freqDial->value() + ui->freqDial->pageStep() );
}
void wfmain::shortcutPageUp()
{
if(freqLock) return;
freqMhz = roundFrequency(freqMhz + tsPage);
knobFreqMhz = freqMhz;
emit setFrequency(freqMhz);
issueDelayedCommand(cmdGetFreq);
}
void wfmain::shortcutPageDown()
{
if(freqLock) return;
freqMhz = roundFrequency(freqMhz - tsPage);
knobFreqMhz = freqMhz;
emit setFrequency(freqMhz);
issueDelayedCommand(cmdGetFreq);
}
void wfmain::shortcutF()
{
showStatusBarText("Sending speech command (frequency) to radio.");
emit sayFrequency();
}
void wfmain::shortcutM()
{
showStatusBarText("Sending speech command (mode) to radio.");
emit sayMode();
}
void wfmain:: getInitialRigState()
{
// Initial list of queries to the radio.
// These are made when the program starts up
// and are used to adjust the UI to match the radio settings
// the polling interval is set at 200ms. Faster is possible but slower
// computers will glitch occassionally.
cmdOutQue.append(cmdGetFreq);
cmdOutQue.append(cmdGetMode);
cmdOutQue.append(cmdNone);
cmdOutQue.append(cmdGetFreq);
cmdOutQue.append(cmdGetMode);
// From left to right in the UI:
cmdOutQue.append(cmdGetDataMode);
cmdOutQue.append(cmdGetModInput);
cmdOutQue.append(cmdGetModDataInput);
cmdOutQue.append(cmdGetRxGain);
cmdOutQue.append(cmdGetAfGain);
cmdOutQue.append(cmdGetSql);
cmdOutQue.append(cmdGetTxPower);
cmdOutQue.append(cmdGetCurrentModLevel); // level for currently selected mod sources
cmdOutQue.append(cmdGetSpectrumRefLevel);
cmdOutQue.append(cmdGetDuplexMode);
cmdOutQue.append(cmdDispEnable);
cmdOutQue.append(cmdSpecOn);
cmdOutQue.append(cmdGetModInput);
cmdOutQue.append(cmdGetModDataInput);
cmdOutQue.append(cmdNone);
cmdOutQue.append(cmdStartRegularPolling);
if(rigCaps.hasATU)
{
cmdOutQue.append(cmdGetATUStatus);
}
cmdOut = cmdNone;
delayedCommand->start();
}
void wfmain::showStatusBarText(QString text)
{
ui->statusBar->showMessage(text, 5000);
}
void wfmain::on_useDarkThemeChk_clicked(bool checked)
{
//setAppTheme(checked);
setPlotTheme(wf, checked);
setPlotTheme(plot, checked);
prefs.useDarkMode = checked;
}
void wfmain::on_useSystemThemeChk_clicked(bool checked)
{
setAppTheme(!checked);
prefs.useSystemTheme = checked;
}
void wfmain::setAppTheme(bool isCustom)
{
if(isCustom)
{
#ifdef Q_OS_WIN
QFile f(":"+prefs.stylesheetPath); // built-in resource
#else
QFile f("/usr/share/wfview/stylesheets/" + prefs.stylesheetPath);
#endif
if (!f.exists())
{
printf("Unable to set stylesheet, file not found\n");
printf("Tried to load: [%s]\n", QString( QString("/usr/share/wfview/stylesheets/") + prefs.stylesheetPath).toStdString().c_str() );
}
else
{
f.open(QFile::ReadOnly | QFile::Text);
QTextStream ts(&f);
qApp->setStyleSheet(ts.readAll());
}
} else {
qApp->setStyleSheet("");
}
}
void wfmain::setDefaultColors()
{
defaultColors.Dark_PlotBackground = QColor(0,0,0,255);
defaultColors.Dark_PlotAxisPen = QColor(75,75,75,255);
defaultColors.Dark_PlotLegendTextColor = QColor(255,255,255,255);
defaultColors.Dark_PlotLegendBorderPen = QColor(255,255,255,255);
defaultColors.Dark_PlotLegendBrush = QColor(0,0,0,200);
defaultColors.Dark_PlotTickLabel = QColor(Qt::white);
defaultColors.Dark_PlotBasePen = QColor(Qt::white);
defaultColors.Dark_PlotTickPen = QColor(Qt::white);
defaultColors.Dark_PlotFreqTracer = QColor(Qt::yellow);
defaultColors.Light_PlotBackground = QColor(255,255,255,255);
defaultColors.Light_PlotAxisPen = QColor(200,200,200,255);
defaultColors.Light_PlotLegendTextColor = QColor(0,0,0,255);
defaultColors.Light_PlotLegendBorderPen = QColor(0,0,0,255);
defaultColors.Light_PlotLegendBrush = QColor(255,255,255,200);
defaultColors.Light_PlotTickLabel = QColor(Qt::black);
defaultColors.Light_PlotBasePen = QColor(Qt::black);
defaultColors.Light_PlotTickPen = QColor(Qt::black);
defaultColors.Light_PlotFreqTracer = QColor(Qt::blue);
}
void wfmain::setPlotTheme(QCustomPlot *plot, bool isDark)
{
if(isDark)
{
plot->setBackground(QColor(0,0,0,255));
plot->xAxis->grid()->setPen(QColor(75,75,75,255));
plot->yAxis->grid()->setPen(QColor(75,75,75,255));
plot->legend->setTextColor(QColor(255,255,255,255));
plot->legend->setBorderPen(QColor(255,255,255,255));
plot->legend->setBrush(QColor(0,0,0,200));
plot->xAxis->setTickLabelColor(Qt::white);
plot->xAxis->setLabelColor(Qt::white);
plot->yAxis->setTickLabelColor(Qt::white);
plot->yAxis->setLabelColor(Qt::white);
plot->xAxis->setBasePen(QPen(Qt::white));
plot->xAxis->setTickPen(QPen(Qt::white));
plot->yAxis->setBasePen(QPen(Qt::white));
plot->yAxis->setTickPen(QPen(Qt::white));
plot->graph(0)->setPen(QPen(Qt::yellow)); // magenta, yellow, green, lightGray
} else {
//color = ui->groupBox->palette().color(QPalette::Button);
plot->setBackground(QColor(255,255,255,255));
//plot->setBackground(color);
plot->xAxis->grid()->setPen(QColor(200,200,200,255));
plot->yAxis->grid()->setPen(QColor(200,200,200,255));
plot->legend->setTextColor(QColor(0,0,0,255));
plot->legend->setBorderPen(QColor(0,0,0,255));
plot->legend->setBrush(QColor(255,255,255,200));
plot->xAxis->setTickLabelColor(Qt::black);
plot->xAxis->setLabelColor(Qt::black);
plot->yAxis->setTickLabelColor(Qt::black);
plot->yAxis->setLabelColor(Qt::black);
plot->xAxis->setBasePen(QPen(Qt::black));
plot->xAxis->setTickPen(QPen(Qt::black));
plot->yAxis->setBasePen(QPen(Qt::black));
plot->yAxis->setTickPen(QPen(Qt::black));
plot->graph(0)->setPen(QPen(Qt::blue));
}
}
void wfmain::runPeriodicCommands()
{
// These commands are run at a regular interval. They are to be used sparingly.
// For general radio state queries, use the runDelayedCommand() queue,
// accessed by the insertPeriodicCommands() function.
// To insert commands to this queue, uset the insertPeriodicCommands() function.
// TODO: Queue should not remove items, just hit a different item each time.
int nCmds = periodicCmdQueue.length();
cmds pcmd;
if(!periodicCmdQueue.isEmpty())
{
pcmd = periodicCmdQueue.at( (pCmdNum++)%nCmds );
switch(pcmd)
{
case cmdNone:
break;
// Metering commands:
case cmdGetSMeter:
if(!amTransmitting)
emit getMeters(meterS);
break;
case cmdGetPowerMeter:
if(amTransmitting)
emit getMeters(meterPower);
break;
case cmdGetIdMeter:
emit getMeters(meterCurrent);
break;
case cmdGetVdMeter:
emit getMeters(meterVoltage);
break;
case cmdGetALCMeter:
if(amTransmitting)
emit getMeters(meterALC);
break;
case cmdGetCompMeter:
if(amTransmitting)
emit getMeters(meterComp);
break;
// Standard commands we are already checking:
case cmdGetRigID:
emit getRigID();
break;
case cmdGetRigCIV:
// if(!know rig civ already)
if(!haveRigCaps)
{
emit getRigCIV();
cmdOutQue.append(cmdGetRigCIV); // This way, we stay here until we get an answer.
}
break;
case cmdGetFreq:
emit getFrequency();
break;
case cmdGetMode:
emit getMode();
break;
case cmdGetDataMode:
// qDebug(logSystem()) << "Sending query for data mode";
emit getDataMode();
break;
case cmdSetDataModeOff:
emit setDataMode(false);
break;
case cmdSetDataModeOn:
emit setDataMode(true);
break;
case cmdGetModInput:
emit getModInput(false);
break;
case cmdGetModDataInput:
emit getModInput(true);
break;
case cmdGetCurrentModLevel:
emit getModInputLevel(currentModSrc);
emit getModInputLevel(currentModDataSrc);
break;
case cmdGetDuplexMode:
emit getDuplexMode();
break;
case cmdDispEnable:
emit scopeDisplayEnable();
break;
case cmdDispDisable:
emit scopeDisplayDisable();
break;
case cmdSpecOn:
emit spectOutputEnable();
break;
case cmdSpecOff:
emit spectOutputDisable();
break;
case cmdGetRxGain:
emit getRfGain();
break;
case cmdGetAfGain:
emit getAfGain();
break;
case cmdGetSql:
emit getSql();
break;
case cmdGetTxPower:
emit getTxPower();
break;
case cmdGetMicGain:
emit getMicGain();
break;
case cmdGetSpectrumRefLevel:
emit getSpectrumRefLevel();
break;
case cmdGetATUStatus:
emit getATUStatus();
break;
case cmdScopeCenterMode:
emit setScopeMode(spectModeCenter);
break;
case cmdScopeFixedMode:
emit setScopeMode(spectModeFixed);
break;
case cmdGetPTT:
emit getPTT();
break;
case cmdStartRegularPolling:
periodicPollingTimer->start();
break;
case cmdStopRegularPolling:
periodicPollingTimer->stop();
break;
default:
break;
}
}
}
void wfmain::runDelayedCommand()
{
cmds qdCmd;
// Note: This cmdOut queue will be removed entirely soon and only the cmdOutQue will be available.
switch (cmdOut)
{
case cmdGetFreq:
emit getFrequency();
break;
case cmdGetMode:
emit getMode();
break;
default:
break;
}
cmdOut = cmdNone; // yep. Hope this wasn't called twice in a row rapidly.
// Note: All command should use this queue. There is no need to use the above system.
if(!cmdOutQue.isEmpty())
{
qdCmd = cmdOutQue.takeFirst();
switch(qdCmd)
{
case cmdNone:
//qDebug(logSystem()) << "NOOP";
break;
case cmdGetRigID:
emit getRigID();
break;
case cmdGetRigCIV:
// if(!know rig civ already)
if(!haveRigCaps)
{
emit getRigCIV();
cmdOutQue.append(cmdGetRigCIV); // This way, we stay here until we get an answer.
}
break;
case cmdGetFreq:
emit getFrequency();
break;
case cmdGetMode:
emit getMode();
break;
case cmdGetDataMode:
// qDebug(logSystem()) << "Sending query for data mode";
emit getDataMode();
break;
case cmdSetDataModeOff:
emit setDataMode(false);
break;
case cmdSetDataModeOn:
emit setDataMode(true);
break;
case cmdGetModInput:
emit getModInput(false);
break;
case cmdGetModDataInput:
emit getModInput(true);
break;
case cmdGetCurrentModLevel:
emit getModInputLevel(currentModSrc);
emit getModInputLevel(currentModDataSrc);
break;
case cmdGetDuplexMode:
emit getDuplexMode();
break;
case cmdDispEnable:
emit scopeDisplayEnable();
break;
case cmdDispDisable:
emit scopeDisplayDisable();
break;
case cmdGetSpectrumMode:
emit getScopeMode();
break;
case cmdSpecOn:
emit spectOutputEnable();
break;
case cmdSpecOff:
emit spectOutputDisable();
break;
case cmdGetRxGain:
emit getRfGain();
break;
case cmdGetAfGain:
emit getAfGain();
break;
case cmdGetSql:
emit getSql();
break;
case cmdGetTxPower:
emit getTxPower();
break;
case cmdGetMicGain:
emit getMicGain();
break;
case cmdGetSpectrumRefLevel:
emit getSpectrumRefLevel();
break;
case cmdGetATUStatus:
emit getATUStatus();
break;
case cmdScopeCenterMode:
emit setScopeMode(spectModeCenter);
break;
case cmdScopeFixedMode:
emit setScopeMode(spectModeFixed);
break;
case cmdGetPTT:
emit getPTT();
break;
case cmdStartRegularPolling:
periodicPollingTimer->start();
break;
case cmdStopRegularPolling:
periodicPollingTimer->stop();
break;
default:
break;
}
}
if(cmdOutQue.isEmpty())
{
// done
} else {
// next
// TODO: If we always do ->start, then it will not be necessary for
// every command insertion to include a ->start.... probably worth doing.
delayedCommand->start();
}
}
void wfmain::issueDelayedCommand(cmds cmd)
{
cmdOutQue.append(cmd);
delayedCommand->start();
}
void wfmain::issueDelayedCommandPriority(cmds cmd)
{
// Places the new command at the top of the queue
// Use only when needed.
cmdOutQue.prepend(cmd);
delayedCommand->start();
}
void wfmain::receiveRigID(rigCapabilities rigCaps)
{
// Note: We intentionally request rigID several times
// because without rigID, we can't do anything with the waterfall.
if(haveRigCaps)
{
return;
} else {
#ifdef QT_DEBUG
qDebug(logSystem()) << "Rig name: " << rigCaps.modelName;
qDebug(logSystem()) << "Has LAN capabilities: " << rigCaps.hasLan;
qDebug(logSystem()) << "Rig ID received into wfmain: spectLenMax: " << rigCaps.spectLenMax;
qDebug(logSystem()) << "Rig ID received into wfmain: spectAmpMax: " << rigCaps.spectAmpMax;
qDebug(logSystem()) << "Rig ID received into wfmain: spectSeqMax: " << rigCaps.spectSeqMax;
qDebug(logSystem()) << "Rig ID received into wfmain: hasSpectrum: " << rigCaps.hasSpectrum;
#endif
this->rigCaps = rigCaps;
this->spectWidth = rigCaps.spectLenMax; // used once haveRigCaps is true.
haveRigCaps = true;
// Added so that server receives rig capabilities.
emit sendRigCaps(rigCaps);
if(rigCaps.model==model7850)
{
ui->modeSelectCombo->addItem("PSK", 0x12);
ui->modeSelectCombo->addItem("PSK-R", 0x13);
}
if(rigCaps.hasDV)
{
ui->modeSelectCombo->addItem("DV", 0x17);
}
if(rigCaps.hasDD)
{
ui->modeSelectCombo->addItem("DD", 0x22);
}
if(rigCaps.model == model9700)
{
ui->satOpsBtn->setDisabled(false);
ui->adjRefBtn->setDisabled(false);
} else {
ui->satOpsBtn->setDisabled(true);
ui->adjRefBtn->setDisabled(true);
}
QString inName;
for(int i=0; i < rigCaps.inputs.length(); i++)
{
switch(rigCaps.inputs.at(i))
{
case inputMic:
inName = "Mic";
break;
case inputLAN:
inName = "LAN";
break;
case inputUSB:
inName = "USB";
break;
case inputACC:
inName = "ACC";
break;
case inputACCA:
inName = "ACCA";
break;
case inputACCB:
inName = "ACCB";
break;
default:
inName = "Unknown";
break;
}
ui->modInputCombo->addItem(inName, rigCaps.inputs.at(i));
ui->modInputDataCombo->addItem(inName, rigCaps.inputs.at(i));
}
if(rigCaps.inputs.length() == 0)
{
ui->modInputCombo->addItem("None", inputNone);
ui->modInputDataCombo->addItem("None", inputNone);
}
ui->tuneEnableChk->setEnabled(rigCaps.hasATU);
ui->tuneNowBtn->setEnabled(rigCaps.hasATU);
ui->connectBtn->setText("Disconnect"); // We must be connected now.
prepareWf();
// Adding these here because clearly at this point we have valid
// rig comms. In the future, we should establish comms and then
// do all the initial grabs. For now, this hack of adding them here and there:
cmdOutQue.append(cmdGetFreq);
cmdOutQue.append(cmdGetMode);
initPeriodicCommands();
}
}
void wfmain::initPeriodicCommands()
{
// This function places periodic polling commands into a queue.
// The commands are run using a timer,
// and the timer is started by the delayed command cmdStartPeriodicTimer.
insertPeriodicCommand(cmdGetSMeter, 128);
insertPeriodicCommand(cmdGetPowerMeter, 128);
}
void wfmain::insertPeriodicCommand(cmds cmd, unsigned char priority)
{
// TODO: meaningful priority
if(priority < 10)
{
periodicCmdQueue.prepend(cmd);
} else {
periodicCmdQueue.append(cmd);
}
}
void wfmain::receiveFreq(double freqMhz)
{
//qDebug(logSystem()) << "HEY WE GOT A Frequency: " << freqMhz;
ui->freqLabel->setText(QString("%1").arg(freqMhz, 0, 'f'));
this->freqMhz = freqMhz;
this->knobFreqMhz = freqMhz;
//showStatusBarText(QString("Frequency: %1").arg(freqMhz));
}
void wfmain::receivePTTstatus(bool pttOn)
{
// This is the only place where amTransmitting and the transmit button text should be changed:
qDebug(logSystem()) << "PTT status: " << pttOn;
if (pttOn && !amTransmitting)
{
pttLed->setState(QLedLabel::State::StateError);
}
else if (!pttOn && amTransmitting)
{
pttLed->setState(QLedLabel::State::StateOk);
}
amTransmitting = pttOn;
changeTxBtn();
}
void wfmain::changeTxBtn()
{
if(amTransmitting)
{
ui->transmitBtn->setText("Receive");
} else {
ui->transmitBtn->setText("Transmit");
}
}
void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double endFreq)
{
if(!haveRigCaps)
{
#ifdef QT_DEBUG
qDebug(logSystem()) << "Spectrum received, but RigID incomplete.";
#endif
return;
}
if((startFreq != oldLowerFreq) || (endFreq != oldUpperFreq))
{
// If the frequency changed and we were drawing peaks, now is the time to clearn them
if(drawPeaks)
{
// TODO: create non-button function to do this
// This will break if the button is ever moved or renamed.
on_clearPeakBtn_clicked();
}
}
oldLowerFreq = startFreq;
oldUpperFreq = endFreq;
//qDebug(logSystem()) << "start: " << startFreq << " end: " << endFreq;
quint16 specLen = spectrum.length();
//qDebug(logSystem()) << "Spectrum data received at UI! Length: " << specLen;
//if( (specLen != 475) || (specLen!=689) )
if( specLen != rigCaps.spectLenMax )
{
#ifdef QT_DEBUG
qDebug(logSystem()) << "-------------------------------------------";
qDebug(logSystem()) << "------ Unusual spectrum received, length: " << specLen;
qDebug(logSystem()) << "------ Expected spectrum length: " << rigCaps.spectLenMax;
qDebug(logSystem()) << "------ This should happen once at most. ";
#endif
return; // safe. Using these unusual length things is a problem.
}
QVector <double> x(spectWidth), y(spectWidth), y2(spectWidth);
for(int i=0; i < spectWidth; i++)
{
x[i] = (i * (endFreq-startFreq)/spectWidth) + startFreq;
}
for(int i=0; i<specLen; i++)
{
//x[i] = (i * (endFreq-startFreq)/specLen) + startFreq;
y[i] = (unsigned char)spectrum.at(i);
if(drawPeaks)
{
if((unsigned char)spectrum.at(i) > (unsigned char)spectrumPeaks.at(i))
{
spectrumPeaks[i] = spectrum.at(i);
}
y2[i] = (unsigned char)spectrumPeaks.at(i);
}
}
//ui->qcp->addGraph();
plot->graph(0)->setData(x,y);
if((freqMhz < endFreq) && (freqMhz > startFreq))
{
// tracer->setGraphKey(freqMhz);
tracer->setGraphKey(knobFreqMhz);
}
if(drawPeaks)
{
plot->graph(1)->setData(x,y2); // peaks
}
plot->yAxis->setRange(0, rigCaps.spectAmpMax+1);
plot->xAxis->setRange(startFreq, endFreq);
plot->replot();
if(specLen == spectWidth)
{
wfimage.prepend(spectrum);
if(wfimage.length() > wfLength)
{
wfimage.remove(wfLength);
}
// Waterfall:
for(int row = 0; row < wfLength; row++)
{
for(int col = 0; col < spectWidth; col++)
{
//colorMap->data()->cellToCoord(xIndex, yIndex, &x, &y)
// Very fast but doesn't roll downward:
//colorMap->data()->setCell( col, spectRowCurrent, spectrum.at(col) );
// Slow but rolls:
colorMap->data()->setCell( col, row, wfimage.at(row).at(col));
}
}
//colorMap->data()->setRange(QCPRange(startFreq, endFreq), QCPRange(0,wfLength-1));
wf->yAxis->setRange(0,wfLength - 1);
wf->xAxis->setRange(0, spectWidth-1);
wf->replot();
spectRowCurrent = (spectRowCurrent + 1) % wfLength;
//qDebug(logSystem()) << "updating spectrum, new row is: " << spectRowCurrent;
}
}
void wfmain::receiveSpectrumMode(spectrumMode spectMode)
{
for(int i=0; i < ui->spectrumModeCombo->count(); i++)
{
if(static_cast<spectrumMode>(ui->spectrumModeCombo->itemData(i).toInt()) == spectMode)
{
ui->spectrumModeCombo->blockSignals(true);
ui->spectrumModeCombo->setCurrentIndex(i);
ui->spectrumModeCombo->blockSignals(false);
}
}
}
void wfmain::handlePlotDoubleClick(QMouseEvent *me)
{
double x;
//double y;
//double px;
if(!freqLock)
{
//y = plot->yAxis->pixelToCoord(me->pos().y());
x = plot->xAxis->pixelToCoord(me->pos().x());
emit setFrequency(x);
issueDelayedCommand(cmdGetFreq);
showStatusBarText(QString("Going to %1 MHz").arg(x));
}
}
void wfmain::handleWFDoubleClick(QMouseEvent *me)
{
double x;
//double y;
//x = wf->xAxis->pixelToCoord(me->pos().x());
//y = wf->yAxis->pixelToCoord(me->pos().y());
// cheap trick until I figure out how the axis works on the WF:
if(!freqLock)
{
x = plot->xAxis->pixelToCoord(me->pos().x());
emit setFrequency(x);
issueDelayedCommand(cmdGetFreq);
showStatusBarText(QString("Going to %1 MHz").arg(x));
}
}
void wfmain::handlePlotClick(QMouseEvent *me)
{
double x = plot->xAxis->pixelToCoord(me->pos().x());
showStatusBarText(QString("Selected %1 MHz").arg(x));
}
void wfmain::handleWFClick(QMouseEvent *me)
{
double x = plot->xAxis->pixelToCoord(me->pos().x());
showStatusBarText(QString("Selected %1 MHz").arg(x));
}
void wfmain::handleWFScroll(QWheelEvent *we)
{
// The wheel event is typically
// .y() and is +/- 120.
// We will click the dial once for every 120 received.
//QPoint delta = we->angleDelta();
if(freqLock)
return;
int clicks = we->angleDelta().y() / 120;
float steps = tsWfScroll * clicks;
Qt::KeyboardModifiers key= we->modifiers();
if (key == Qt::ShiftModifier)
{
steps /= 10;
} else if (key == Qt::ControlModifier)
{
steps *=10;
}
freqMhz = roundFrequency(freqMhz - steps);
knobFreqMhz = freqMhz;
emit setFrequency(freqMhz);
ui->freqLabel->setText(QString("%1").arg(freqMhz, 0, 'f'));
issueDelayedCommand(cmdGetFreq);
}
void wfmain::handlePlotScroll(QWheelEvent *we)
{
if(freqLock)
return;
int clicks = we->angleDelta().y() / 120;
float steps = tsWfScroll * clicks;
Qt::KeyboardModifiers key= we->modifiers();
if (key == Qt::ShiftModifier)
{
steps /= 10;
} else if (key == Qt::ControlModifier)
{
steps *=10;
}
freqMhz = roundFrequency(freqMhz - steps);
knobFreqMhz = freqMhz;
emit setFrequency(freqMhz);
ui->freqLabel->setText(QString("%1").arg(freqMhz, 0, 'f'));
issueDelayedCommand(cmdGetFreq);
}
void wfmain::on_scopeEnableWFBtn_clicked(bool checked)
{
if(checked)
{
emit spectOutputEnable();
} else {
emit spectOutputDisable();
}
}
void wfmain::receiveMode(unsigned char mode, unsigned char filter)
{
//qDebug(logSystem()) << __func__ << "Received mode " << mode << " current mode: " << currentModeIndex;
bool found=false;
if(mode < 0x23)
{
for(int i=0; i < ui->modeSelectCombo->count(); i++)
{
if(ui->modeSelectCombo->itemData(i).toInt() == mode)
{
ui->modeSelectCombo->blockSignals(true);
ui->modeSelectCombo->setCurrentIndex(i);
ui->modeSelectCombo->blockSignals(false);
found = true;
}
}
currentModeIndex = mode;
} else {
qDebug(logSystem()) << __func__ << "Invalid mode " << mode << " received. ";
}
if(!found)
{
qDebug(logSystem()) << __func__ << "Received mode " << mode << " but could not match to any index within the modeSelectCombo. ";
}
if( (filter) && (filter < 4)){
ui->modeFilterCombo->blockSignals(true);
ui->modeFilterCombo->setCurrentIndex(filter-1);
ui->modeFilterCombo->blockSignals(false);
}
(void)filter;
// Note: we need to know if the DATA mode is active to reach mode-D
// some kind of queued query:
cmdOutQue.append(cmdGetDataMode);
delayedCommand->start(); // why was that commented out?
}
void wfmain::receiveDataModeStatus(bool dataEnabled)
{
ui->dataModeBtn->blockSignals(true);
ui->dataModeBtn->setChecked(dataEnabled);
ui->dataModeBtn->blockSignals(false);
usingDataMode = dataEnabled;
}
void wfmain::on_clearPeakBtn_clicked()
{
if(haveRigCaps)
{
spectrumPeaks = QByteArray( (int)spectWidth, '\x01' );
}
return;
}
void wfmain::on_drawPeakChk_clicked(bool checked)
{
if(checked)
{
on_clearPeakBtn_clicked(); // clear
drawPeaks = true;
} else {
drawPeaks = false;
#if QCUSTOMPLOT_VERSION >= 0x020000
plot->graph(1)->data()->clear();
#else
plot->graph(1)->clearData();
#endif
}
prefs.drawPeaks = checked;
}
void wfmain::on_fullScreenChk_clicked(bool checked)
{
if(checked)
{
this->showFullScreen();
onFullscreen = true;
} else {
this->showNormal();
onFullscreen = false;
}
prefs.useFullScreen = checked;
}
void wfmain::on_goFreqBtn_clicked()
{
bool ok = false;
double freq = ui->freqMhzLineEdit->text().toDouble(&ok);
if(ok)
{
emit setFrequency(freq);
issueDelayedCommand(cmdGetFreq);
}
ui->freqMhzLineEdit->selectAll();
freqTextSelected = true;
ui->tabWidget->setCurrentIndex(0);
}
void wfmain::checkFreqSel()
{
if(freqTextSelected)
{
freqTextSelected = false;
ui->freqMhzLineEdit->clear();
}
}
void wfmain::on_f0btn_clicked()
{
checkFreqSel();
ui->freqMhzLineEdit->setText(ui->freqMhzLineEdit->text().append("0"));
}
void wfmain::on_f1btn_clicked()
{
checkFreqSel();
ui->freqMhzLineEdit->setText(ui->freqMhzLineEdit->text().append("1"));
}
void wfmain::on_f2btn_clicked()
{
checkFreqSel();
ui->freqMhzLineEdit->setText(ui->freqMhzLineEdit->text().append("2"));
}
void wfmain::on_f3btn_clicked()
{
ui->freqMhzLineEdit->setText(ui->freqMhzLineEdit->text().append("3"));
}
void wfmain::on_f4btn_clicked()
{
checkFreqSel();
ui->freqMhzLineEdit->setText(ui->freqMhzLineEdit->text().append("4"));
}
void wfmain::on_f5btn_clicked()
{
checkFreqSel();
ui->freqMhzLineEdit->setText(ui->freqMhzLineEdit->text().append("5"));
}
void wfmain::on_f6btn_clicked()
{
checkFreqSel();
ui->freqMhzLineEdit->setText(ui->freqMhzLineEdit->text().append("6"));
}
void wfmain::on_f7btn_clicked()
{
checkFreqSel();
ui->freqMhzLineEdit->setText(ui->freqMhzLineEdit->text().append("7"));
}
void wfmain::on_f8btn_clicked()
{
checkFreqSel();
ui->freqMhzLineEdit->setText(ui->freqMhzLineEdit->text().append("8"));
}
void wfmain::on_f9btn_clicked()
{
checkFreqSel();
ui->freqMhzLineEdit->setText(ui->freqMhzLineEdit->text().append("9"));
}
void wfmain::on_fDotbtn_clicked()
{
checkFreqSel();
ui->freqMhzLineEdit->setText(ui->freqMhzLineEdit->text().append("."));
}
void wfmain::on_fBackbtn_clicked()
{
QString currentFreq = ui->freqMhzLineEdit->text();
currentFreq.chop(1);
ui->freqMhzLineEdit->setText(currentFreq);
}
void wfmain::on_fCEbtn_clicked()
{
ui->freqMhzLineEdit->clear();
freqTextSelected = false;
}
void wfmain::on_spectrumModeCombo_currentIndexChanged(int index)
{
emit setScopeMode(static_cast<spectrumMode>(ui->spectrumModeCombo->itemData(index).toInt()));
}
void wfmain::on_fEnterBtn_clicked()
{
// TODO: do not jump to main tab on enter, only on return
// or something.
// Maybe this should be an option in settings.
on_goFreqBtn_clicked();
}
void wfmain::on_scopeBWCombo_currentIndexChanged(int index)
{
emit setScopeSpan((char)index);
}
void wfmain::on_scopeEdgeCombo_currentIndexChanged(int index)
{
emit setScopeEdge((char)index+1);
}
void wfmain::changeMode(mode_kind mode)
{
bool dataOn = false;
if(((unsigned char) mode >> 4) == 0x08)
{
dataOn = true;
mode = (mode_kind)((int)mode & 0x0f);
}
changeMode(mode, dataOn);
}
void wfmain::changeMode(mode_kind mode, bool dataOn)
{
int filter = ui->modeFilterCombo->currentData().toInt();
emit setMode((unsigned char)mode, filter);
if(dataOn)
{
issueDelayedCommand(cmdSetDataModeOn);
ui->dataModeBtn->blockSignals(true);
ui->dataModeBtn->setChecked(true);
ui->dataModeBtn->blockSignals(false);
} else {
issueDelayedCommand(cmdSetDataModeOff);
ui->dataModeBtn->blockSignals(true);
ui->dataModeBtn->setChecked(false);
ui->dataModeBtn->blockSignals(false);
}
issueDelayedCommand(cmdGetMode);
}
void wfmain::on_modeSelectCombo_activated(int index)
{
// The "acticvated" signal means the user initiated a mode change.
// This function is not called if code initated the change.
unsigned char newMode = static_cast<unsigned char>(ui->modeSelectCombo->itemData(index).toUInt());
currentModeIndex = newMode;
int filterSelection = ui->modeFilterCombo->currentData().toInt();
if(filterSelection == 99)
{
// oops, we forgot to reset the combo box
} else {
qDebug(logSystem()) << __func__ << " at index " << index << " has newMode: " << newMode;
emit setMode(newMode, filterSelection);
}
}
void wfmain::on_freqDial_valueChanged(int value)
{
int maxVal = ui->freqDial->maximum();
double stepSize = (double)tsKnobMHz;
double newFreqMhz = 0;
volatile int delta = 0;
int directPath = 0;
int crossingPath = 0;
int distToMaxNew = 0;
int distToMaxOld = 0;
if(freqLock)
{
ui->freqDial->blockSignals(true);
ui->freqDial->setValue(oldFreqDialVal);
ui->freqDial->blockSignals(false);
return;
}
// qDebug(logSystem()) << "Old value: " << oldFreqDialVal << " New value: " << value ;
if(value == 0)
{
distToMaxNew = 0;
} else {
distToMaxNew = maxVal - value;
}
if(oldFreqDialVal != 0)
{
distToMaxOld = maxVal - oldFreqDialVal;
} else {
distToMaxOld = 0;
}
directPath = abs(value - oldFreqDialVal);
if(value < maxVal / 2)
{
crossingPath = value + distToMaxOld;
} else {
crossingPath = distToMaxNew + oldFreqDialVal;
}
if(directPath > crossingPath)
{
// use crossing path, it is shorter
delta = crossingPath;
// mnow calculate the direction:
if( value > oldFreqDialVal)
{
// CW
delta = delta;
} else {
// CCW
delta *= -1;
}
} else {
// use direct path
// crossing path is larger than direct path, use direct path
//delta = directPath;
// now calculate the direction
delta = value - oldFreqDialVal;
}
newFreqMhz = knobFreqMhz + (delta * stepSize);
// qDebug(logSystem()) << "old freq: " << knobFreqMhz << " new freq: " << newFreqMhz << "knobDelta: " << delta << " freq delta: " << newFreqMhz - knobFreqMhz;
if(ui->tuningFloorZerosChk->isChecked())
{
newFreqMhz = (double)round(newFreqMhz*10000) / 10000.0;
}
this->knobFreqMhz = newFreqMhz; // the frequency we think we should be on.
oldFreqDialVal = value;
ui->freqLabel->setText(QString("%1").arg(knobFreqMhz, 0, 'f'));
this->freqMhz = knobFreqMhz;
emit setFrequency(newFreqMhz);
//emit getFrequency();
}
void wfmain::receiveBandStackReg(float freq, char mode, bool dataOn)
{
// read the band stack and apply by sending out commands
setFrequency(freq);
int filterSelection = ui->modeFilterCombo->currentData().toInt();
setMode(mode, (unsigned char)filterSelection); // make sure this is what you think it is
// setDataMode(dataOn); // signal out
if(dataOn)
{
cmdOutQue.append(cmdSetDataModeOn);
} else {
cmdOutQue.append(cmdSetDataModeOff);
}
cmdOutQue.append(cmdGetFreq);
cmdOutQue.append(cmdGetMode);
ui->tabWidget->setCurrentIndex(0);
delayedCommand->start();
}
void wfmain::bandStackBtnClick()
{
bandStkRegCode = ui->bandStkPopdown->currentIndex() + 1;
waitingForBandStackRtn = true; // so that when the return is parsed we jump to this frequency/mode info
emit getBandStackReg(bandStkBand, bandStkRegCode);
}
void wfmain::on_band6mbtn_clicked()
{
bandStkBand = 0x10; // 6 meters
bandStackBtnClick();
}
void wfmain::on_band10mbtn_clicked()
{
bandStkBand = 0x09; // 10 meters
bandStackBtnClick();
}
void wfmain::on_band12mbtn_clicked()
{
bandStkBand = 0x08; // 12 meters
bandStackBtnClick();
}
void wfmain::on_band15mbtn_clicked()
{
bandStkBand = 0x07; // 15 meters
bandStackBtnClick();
}
void wfmain::on_band17mbtn_clicked()
{
bandStkBand = 0x06; // 17 meters
bandStackBtnClick();
}
void wfmain::on_band20mbtn_clicked()
{
bandStkBand = 0x05; // 20 meters
bandStackBtnClick();
}
void wfmain::on_band30mbtn_clicked()
{
bandStkBand = 0x04; // 30 meters
bandStackBtnClick();
}
void wfmain::on_band40mbtn_clicked()
{
bandStkBand = 0x03; // 40 meters
bandStackBtnClick();
}
void wfmain::on_band60mbtn_clicked()
{
// This one is tricky. There isn't a band stack register on the
// 7300 for 60 meters, so we just drop to the middle of the band:
// Channel 1: 5330.5 kHz
// Channel 2: 5346.5 kHz
// Channel 3: 5357.0 kHz
// Channel 4: 5371.5 kHz
// Channel 5: 5403.5 kHz
// Really not sure what the best strategy here is, don't want to
// clutter the UI with 60M channel buttons...
setFrequency(5.3305);
}
void wfmain::on_band80mbtn_clicked()
{
bandStkBand = 0x02; // 80 meters
bandStackBtnClick();
}
void wfmain::on_band160mbtn_clicked()
{
bandStkBand = 0x01; // 160 meters
bandStackBtnClick();
}
void wfmain::on_bandGenbtn_clicked()
{
// "GENE" general coverage frequency outside the ham bands
// which does probably include any 60 meter frequencies used.
bandStkBand = 0x11; // GEN
bandStackBtnClick();
}
void wfmain::on_aboutBtn_clicked()
{
// Show.....
// Build date, time, git checksum (short)
// QT library version
// stylesheet credit
// contact information
QMessageBox msgBox(this);
msgBox.setWindowTitle("Abou wfview");
msgBox.setTextFormat(Qt::RichText);
msgBox.setWindowIcon(QIcon(":resources/wfview.png"));
// TODO: change style of link color based on current CSS sheet.
QString head = QString("<html><head></head><body>");
QString copyright = QString("Copyright 2017-2021 Elliott H. Liggett, W6EL. All rights reserved.");
QString nacode = QString("<br/><br/>Networking and audio code written by Phil Taylor, M0VSE");
QString doctest = QString("<br/><br/>Testing, documentation, bug fixes, and development mentorship from Roeland Jansen, PA3MET, and Jim Nijkamp, PA8E.");
QString ssCredit = QString("<br/><br/>Stylesheet qdarkstyle used under MIT license, stored in /usr/share/wfview/stylesheets/.");
QString website = QString("<br/><br/>Get the latest version from our gitlab repo: <a href='https://gitlab.com/eliggett/wfview' style='color: cyan;'>https://gitlab.com/eliggett/wfview</a>");
QString docs = QString("<br/>Also see the <a href='https://gitlab.com/eliggett/wfview/-/wikis/home' style='color: cyan;'>wiki</a> for the <a href='https://gitlab.com/eliggett/wfview/-/wikis/User-FAQ' style='color: cyan;'>FAQ</a>, <a href='https://gitlab.com/eliggett/wfview/-/wikis/Keystrokes' style='color: cyan;'>Keystrokes</a>, and more.");
QString contact = QString("<br/>email the author: kilocharlie8@gmail.com or W6EL on the air!");
QString buildInfo = QString("<br/><br/>Build " + QString(GITSHORT) + " on " + QString(__DATE__) + " at " + __TIME__ + " by " + UNAME + "@" + HOST);
QString end = QString("</body></html>");
QString aboutText = head + copyright + "\n" + nacode + "\n" + doctest + "\n" + ssCredit + "\n";
aboutText.append(website + "\n"+ docs + contact +"\n" + buildInfo + end);
msgBox.setText(aboutText);
msgBox.exec();
}
void wfmain::on_fStoBtn_clicked()
{
// sequence:
// type frequency
// press Enter or Go
// change mode if desired
// type in index number 0 through 99
// press STO
bool ok;
QString freqString;
int preset_number = ui->freqMhzLineEdit->text().toInt(&ok);
if(ok && (preset_number >= 0) && (preset_number < 100))
{
// TODO: keep an enum around with the current mode
mem.setPreset(preset_number, freqMhz, (mode_kind)ui->modeSelectCombo->currentIndex());
showStatusBarText( QString("Storing frequency %1 to memory location %2").arg( freqMhz ).arg(preset_number) );
} else {
showStatusBarText(QString("Could not store preset to %1. Valid preset numbers are 0 to 99").arg(preset_number));
}
}
void wfmain::on_fRclBtn_clicked()
{
// Sequence:
// type memory location 0 through 99
// press RCL
// Program recalls data stored in vector at position specified
// drop contents into text box, press go button
// add delayed command for mode and data mode
preset_kind temp;
bool ok;
QString freqString;
int preset_number = ui->freqMhzLineEdit->text().toInt(&ok);
if(ok && (preset_number >= 0) && (preset_number < 100))
{
temp = mem.getPreset(preset_number);
freqString = QString("%1").arg(temp.frequency);
ui->freqMhzLineEdit->setText( freqString );
ui->goFreqBtn->click();
} else {
qDebug(logSystem()) << "Could not recall preset. Valid presets are 0 through 99.";
}
}
void wfmain::on_rfGainSlider_valueChanged(int value)
{
emit setRfGain((unsigned char) value);
}
void wfmain::on_afGainSlider_valueChanged(int value)
{
// qDebug(logSystem()) << "Setting AF gain to " << value;
emit setAfGain((unsigned char) value);
}
void wfmain::receiveRfGain(unsigned char level)
{
// qDebug(logSystem()) << "Receive RF level of" << (int)level << " = " << 100*level/255.0 << "%";
ui->rfGainSlider->blockSignals(true);
ui->rfGainSlider->setValue(level);
ui->rfGainSlider->blockSignals(false);
}
void wfmain::receiveAfGain(unsigned char level)
{
// qDebug(logSystem()) << "Receive AF level of" << (int)level << " = " << 100*level/255.0 << "%";
ui->afGainSlider->blockSignals(true);
ui->afGainSlider->setValue(level);
ui->afGainSlider->blockSignals(false);
}
void wfmain::receiveSql(unsigned char level)
{
ui->sqlSlider->setValue(level);
}
void wfmain::on_drawTracerChk_toggled(bool checked)
{
tracer->setVisible(checked);
prefs.drawTracer = checked;
}
void wfmain::on_tuneNowBtn_clicked()
{
emit startATU();
showStatusBarText("Starting ATU tuning cycle...");
cmdOutQue.append(cmdGetATUStatus);
delayedCommand->start();
}
void wfmain::on_tuneEnableChk_clicked(bool checked)
{
emit setATU(checked);
if(checked)
{
showStatusBarText("Turning on ATU");
} else {
showStatusBarText("Turning off ATU");
}
}
void wfmain::on_exitBtn_clicked()
{
// Are you sure?
QApplication::exit();
}
void wfmain::on_pttOnBtn_clicked()
{
// is it enabled?
if(!ui->pttEnableChk->isChecked())
{
showStatusBarText("PTT is disabled, not sending command. Change under Settings tab.");
return;
}
// Are we already PTT? Not a big deal, just send again anyway.
showStatusBarText("Sending PTT ON command. Use Control-R to receive.");
emit setPTT(true);
// send PTT
// Start 3 minute timer
pttTimer->start();
issueDelayedCommand(cmdGetPTT);
}
void wfmain::on_pttOffBtn_clicked()
{
// Send the PTT OFF command (more than once?)
showStatusBarText("Sending PTT OFF command");
emit setPTT(false);
// Stop the 3 min timer
pttTimer->stop();
issueDelayedCommand(cmdGetPTT);
}
void wfmain::handlePttLimit()
{
// transmission time exceeded!
showStatusBarText("Transmit timeout at 3 minutes. Sending PTT OFF command now.");
emit setPTT(false);
issueDelayedCommand(cmdGetPTT);
}
void wfmain::on_saveSettingsBtn_clicked()
{
saveSettings(); // save memory, UI, and radio settings
}
void wfmain::receiveATUStatus(unsigned char atustatus)
{
// qDebug(logSystem()) << "Received ATU status update: " << (unsigned int) atustatus;
switch(atustatus)
{
case 0x00:
// ATU not active
ui->tuneEnableChk->blockSignals(true);
ui->tuneEnableChk->setChecked(false);
ui->tuneEnableChk->blockSignals(false);
showStatusBarText("ATU not enabled.");
break;
case 0x01:
// ATU enabled
ui->tuneEnableChk->blockSignals(true);
ui->tuneEnableChk->setChecked(true);
ui->tuneEnableChk->blockSignals(false);
showStatusBarText("ATU enabled.");
break;
case 0x02:
// ATU tuning in-progress.
// Add command queue to check again and update status bar
// qDebug(logSystem()) << "Received ATU status update that *tuning* is taking place";
showStatusBarText("ATU is Tuning...");
cmdOutQue.append(cmdGetATUStatus); // Sometimes the first hit seems to be missed.
cmdOutQue.append(cmdGetATUStatus);
delayedCommand->start();
break;
default:
qDebug(logSystem()) << "Did not understand ATU status: " << (unsigned int) atustatus;
break;
}
}
void wfmain::on_pttEnableChk_clicked(bool checked)
{
prefs.enablePTT = checked;
}
void wfmain::on_serialEnableBtn_clicked(bool checked)
{
prefs.enableLAN = !checked;
ui->serialDeviceListCombo->setEnabled(checked);
ui->connectBtn->setEnabled(!checked);
ui->ipAddressTxt->setEnabled(!checked);
ui->controlPortTxt->setEnabled(!checked);
ui->usernameTxt->setEnabled(!checked);
ui->passwordTxt->setEnabled(!checked);
}
void wfmain::on_lanEnableBtn_clicked(bool checked)
{
prefs.enableLAN = checked;
ui->connectBtn->setEnabled(checked);
ui->ipAddressTxt->setEnabled(checked);
ui->controlPortTxt->setEnabled(checked);
ui->usernameTxt->setEnabled(checked);
ui->passwordTxt->setEnabled(checked);
if(checked)
{
showStatusBarText("After filling in values, press Save Settings and re-start wfview.");
}
}
void wfmain::on_ipAddressTxt_textChanged(QString text)
{
udpPrefs.ipAddress = text;
}
void wfmain::on_controlPortTxt_textChanged(QString text)
{
udpPrefs.controlLANPort = text.toUInt();
}
void wfmain::on_usernameTxt_textChanged(QString text)
{
udpPrefs.username = text;
}
void wfmain::on_passwordTxt_textChanged(QString text)
{
udpPrefs.password = text;
}
void wfmain::on_audioOutputCombo_currentIndexChanged(QString text)
{
udpPrefs.audioOutput = text;
}
void wfmain::on_audioInputCombo_currentIndexChanged(QString text)
{
udpPrefs.audioInput = text;
}
void wfmain::on_audioSampleRateCombo_currentIndexChanged(QString text)
{
udpPrefs.audioRXSampleRate = text.toInt();
udpPrefs.audioTXSampleRate = text.toInt();
}
void wfmain::on_audioRXCodecCombo_currentIndexChanged(int value)
{
udpPrefs.audioRXCodec = ui->audioRXCodecCombo->itemData(value).toInt();
}
void wfmain::on_audioTXCodecCombo_currentIndexChanged(int value)
{
udpPrefs.audioTXCodec = ui->audioTXCodecCombo->itemData(value).toInt();
}
void wfmain::on_rxLatencySlider_valueChanged(int value)
{
udpPrefs.audioRXLatency = value;
ui->rxLatencyValue->setText(QString::number(value));
emit sendChangeLatency(value);
}
void wfmain::on_txLatencySlider_valueChanged(int value)
{
udpPrefs.audioTXLatency = value;
ui->txLatencyValue->setText(QString::number(value));
}
void wfmain::on_toFixedBtn_clicked()
{
emit setScopeFixedEdge(oldLowerFreq, oldUpperFreq, ui->scopeEdgeCombo->currentIndex()+1);
emit setScopeEdge(ui->scopeEdgeCombo->currentIndex()+1);
issueDelayedCommand(cmdScopeFixedMode);
}
void wfmain::on_connectBtn_clicked()
{
this->rigStatus->setText(""); // Clear status
if (haveRigCaps) {
emit sendCloseComm();
ui->connectBtn->setText("Connect");
haveRigCaps = false;
}
else
{
emit sendCloseComm(); // Just in case there is a failed connection open.
openRig();
}
}
void wfmain::on_udpServerSetupBtn_clicked()
{
srv->show();
}
void wfmain::on_sqlSlider_valueChanged(int value)
{
emit setSql((unsigned char)value);
}
void wfmain::on_modeFilterCombo_activated(int index)
{
int filterSelection = ui->modeFilterCombo->itemData(index).toInt();
if(filterSelection == 99)
{
// TODO:
// Bump the filter selected back to F1, F2, or F3
// possibly track the filter in the class. Would make this easier.
// filterSetup.show();
//
} else {
unsigned char newMode = static_cast<unsigned char>(ui->modeSelectCombo->currentData().toUInt());
currentModeIndex = newMode; // we track this for other functions
emit setMode(newMode, (unsigned char)filterSelection);
}
}
void wfmain::on_dataModeBtn_toggled(bool checked)
{
setDataMode(checked);
usingDataMode = checked;
if(usingDataMode)
{
changeModLabelAndSlider(currentModDataSrc);
} else {
changeModLabelAndSlider(currentModSrc);
}
}
void wfmain::on_transmitBtn_clicked()
{
if(!amTransmitting)
{
// Currently receiving
if(!ui->pttEnableChk->isChecked())
{
showStatusBarText("PTT is disabled, not sending command. Change under Settings tab.");
return;
}
// Are we already PTT? Not a big deal, just send again anyway.
showStatusBarText("Sending PTT ON command. Use Control-R to receive.");
emit setPTT(true);
// send PTT
// Start 3 minute timer
pttTimer->start();
issueDelayedCommand(cmdGetPTT);
//changeTxBtn();
} else {
// Currently transmitting
emit setPTT(false);
pttTimer->stop();
issueDelayedCommand(cmdGetPTT);
}
}
void wfmain::on_adjRefBtn_clicked()
{
cal->show();
}
void wfmain::on_satOpsBtn_clicked()
{
sat->show();
}
void wfmain::changeSliderQuietly(QSlider *slider, int value)
{
slider->blockSignals(true);
slider->setValue(value);
slider->blockSignals(false);
}
void wfmain::receiveTxPower(unsigned char power)
{
changeSliderQuietly(ui->txPowerSlider, power);
}
void wfmain::receiveMicGain(unsigned char gain)
{
processModLevel(inputMic, gain);
}
void wfmain::processModLevel(rigInput source, unsigned char level)
{
rigInput currentIn;
if(usingDataMode)
{
currentIn = currentModDataSrc;
} else {
currentIn = currentModSrc;
}
switch(source)
{
case inputMic:
micGain = level;
break;
case inputACC:
accGain = level;
break;
case inputACCA:
accAGain = level;
break;
case inputACCB:
accBGain = level;
break;
case inputUSB:
usbGain = level;
break;
case inputLAN:
lanGain = level;
break;
default:
break;
}
if(currentIn == source)
{
changeSliderQuietly(ui->micGainSlider, level);
}
}
void wfmain::receiveModInput(rigInput input, bool dataOn)
{
QComboBox *box;
QString inputName;
bool found;
bool foundCurrent = false;
if(dataOn)
{
box = ui->modInputDataCombo;
currentModDataSrc = input;
if(usingDataMode)
foundCurrent = true;
} else {
box = ui->modInputCombo;
currentModSrc = input;
if(!usingDataMode)
foundCurrent = true;
}
for(int i=0; i < box->count(); i++)
{
if(box->itemData(i).toInt() == (int)input)
{
box->blockSignals(true);
box->setCurrentIndex(i);
box->blockSignals(false);
found = true;
}
}
if(foundCurrent)
{
changeModLabel(input);
}
if(!found)
qDebug(logSystem()) << "Could not find modulation input: " << (int)input;
}
void wfmain::receiveDuplexMode(duplexMode dm)
{
switch(dm)
{
case dmSimplex:
ui->rptSimplexBtn->setChecked(true);
break;
case dmDupPlus:
ui->rptDupPlusBtn->setChecked(true);
break;
case dmDupMinus:
ui->rptDupMinusBtn->setChecked(true);
break;
default:
break;
}
(void)dm;
}
void wfmain::receiveACCGain(unsigned char level, unsigned char ab)
{
if(ab==1)
{
processModLevel(inputACCB, level);
} else {
if(rigCaps.model == model7850)
{
processModLevel(inputACCA, level);
} else {
processModLevel(inputACC, level);
}
}
}
void wfmain::receiveUSBGain(unsigned char level)
{
processModLevel(inputUSB, level);
}
void wfmain::receiveLANGain(unsigned char level)
{
processModLevel(inputLAN, level);
}
void wfmain::receiveMeter(meterKind inMeter, unsigned char level)
{
unsigned int peak = 0;
unsigned int sum=0;
unsigned int average=0;
switch(inMeter)
{
case meterS:
SMeterReadings[(smeterPos++)%SMeterReadings.length()] = level;
for(int i=0; i < SMeterReadings.length(); i++)
{
if((unsigned char)SMeterReadings.at(i) > peak)
peak = (unsigned char)SMeterReadings.at(i);
sum += (unsigned char)SMeterReadings.at(i);
}
average = sum / SMeterReadings.length();
ui->meterWidget->setLevels(level, peak, average);
ui->meterWidget->repaint();
//ui->levelIndicator->setValue((int)level);
break;
case meterSWR:
//ui->levelIndicator->setValue((int)level);
break;
case meterPower:
powerMeterReadings[(powerMeterPos++)%powerMeterReadings.length()] = level;
for(int i=0; i < powerMeterReadings.length(); i++)
{
if((unsigned char)powerMeterReadings.at(i) > peak)
peak = (unsigned char)powerMeterReadings.at(i);
sum += (unsigned char)powerMeterReadings.at(i);
}
average = sum / powerMeterReadings.length();
ui->meterWidget->setLevels(level, peak, average);
ui->meterWidget->update();
//ui->levelIndicator->setValue((int)level);
break;
case meterALC:
//ui->levelIndicator->setValue((int)level);
break;
case meterComp:
//ui->levelIndicator->setValue((int)level);
break;
case meterCurrent:
//ui->levelIndicator->setValue((int)level);
break;
case meterVoltage:
//ui->levelIndicator->setValue((int)level);
break;
default:
break;
}
}
void wfmain::receiveCompLevel(unsigned char compLevel)
{
(void)compLevel;
}
void wfmain::receiveMonitorGain(unsigned char monitorGain)
{
(void)monitorGain;
}
void wfmain::receiveVoxGain(unsigned char voxGain)
{
(void)voxGain;
}
void wfmain::receiveAntiVoxGain(unsigned char antiVoxGain)
{
(void)antiVoxGain;
}
void wfmain::on_txPowerSlider_valueChanged(int value)
{
emit setTxPower(value);
}
void wfmain::on_micGainSlider_valueChanged(int value)
{
processChangingCurrentModLevel((unsigned char) value);
}
void wfmain::on_scopeRefLevelSlider_valueChanged(int value)
{
value = (value/5) * 5; // rounded to "nearest 5"
emit setSpectrumRefLevel(value);
}
void wfmain::receiveSpectrumRefLevel(int level)
{
changeSliderQuietly(ui->scopeRefLevelSlider, level);
}
// Slot to send/receive server config.
// If store is true then write to config otherwise send current config by signal
void wfmain::serverConfigRequested(SERVERCONFIG conf, bool store)
{
if (!store) {
emit sendServerConfig(serverConfig);
}
else {
// Store config in file!
qDebug(logSystem()) << "Storing server config";
serverConfig = conf;
}
}
void wfmain::on_rptDupPlusBtn_clicked()
{
// DUP+
emit setDuplexMode(dmDupAutoOff);
emit setDuplexMode(dmDupPlus);
}
void wfmain::on_rptSimplexBtn_clicked()
{
// Simplex
emit setDuplexMode(dmDupAutoOff);
emit setDuplexMode(dmSimplex);
}
void wfmain::on_rptDupMinusBtn_clicked()
{
// DUP-
emit setDuplexMode(dmDupAutoOff);
emit setDuplexMode(dmDupMinus);
}
void wfmain::on_rptAutoBtn_clicked()
{
// Auto Rptr (enable this feature)
// TODO: Hide an AutoOff button somewhere for non-US users
emit setDuplexMode(dmDupAutoOn);
}
void wfmain::on_modInputCombo_activated(int index)
{
emit setModInput( (rigInput)ui->modInputCombo->currentData().toInt(), false );
currentModSrc = (rigInput)ui->modInputCombo->currentData().toInt();
issueDelayedCommand(cmdGetCurrentModLevel);
if(!usingDataMode)
{
changeModLabel(currentModSrc);
}
(void)index;
}
void wfmain::on_modInputDataCombo_activated(int index)
{
emit setModInput( (rigInput)ui->modInputDataCombo->currentData().toInt(), true );
currentModDataSrc = (rigInput)ui->modInputDataCombo->currentData().toInt();
issueDelayedCommand(cmdGetCurrentModLevel);
if(usingDataMode)
{
changeModLabel(currentModDataSrc);
}
(void)index;
}
void wfmain::changeModLabelAndSlider(rigInput source)
{
changeModLabel(source, true);
}
void wfmain::changeModLabel(rigInput input)
{
changeModLabel(input, false);
}
void wfmain::changeModLabel(rigInput input, bool updateLevel)
{
QString inputName;
unsigned char gain = 0;
switch(input)
{
case inputMic:
inputName = "Mic";
gain = micGain;
break;
case inputACC:
inputName = "ACC";
gain = accGain;
break;
case inputACCA:
inputName = "ACCA";
gain = accAGain;
break;
case inputACCB:
inputName = "ACCB";
gain = accBGain;
break;
case inputUSB:
inputName = "USB";
gain = usbGain;
break;
case inputLAN:
inputName = "LAN";
gain = lanGain;
break;
default:
inputName = "UNK";
gain=0;
break;
}
ui->modSliderLbl->setText(inputName);
if(updateLevel)
{
changeSliderQuietly(ui->micGainSlider, gain);
}
}
void wfmain::processChangingCurrentModLevel(unsigned char level)
{
// slider moved, so find the current mod and issue the level set command.
rigInput currentIn;
if(usingDataMode)
{
currentIn = currentModDataSrc;
} else {
currentIn = currentModSrc;
}
//qDebug(logSystem()) << __func__ << ": setting current level: " << level;
emit setModLevel(currentIn, level);
}
void wfmain::on_tuneLockChk_clicked(bool checked)
{
freqLock = checked;
}
// --- DEBUG FUNCTION ---
void wfmain::on_debugBtn_clicked()
{
qDebug(logSystem()) << "Debug button pressed.";
// TODO: Why don't these commands work?!
//emit getScopeMode();
//emit getScopeEdge(); // 1,2,3 only in "fixed" mode
//emit getScopeSpan(); // in khz, only in "center" mode
// emit getLevels();
// emit getMeters(amTransmitting);
// emit getTSQL();
qDebug(logSystem()) << "Getting scope mode";
emit getScopeMode();
}