From 655ea0ac0e2734b7dbde493f8dbd7e2d06ca986e Mon Sep 17 00:00:00 2001 From: Elliott Liggett Date: Sun, 24 Jan 2021 16:16:38 -0800 Subject: [PATCH] Added support for various spectrum sizes and rig capability support. wfview has been tested with the following Icom radios over USB port: IC-7300, IC-7610, IC-7851 (and IC-7850), and IC-9700. It likely works fine with the IC-705 as well. At this time, the rig's CIV address must be changed in the preference file to indicate the rig you are using, and this must be in integer, not hex. --- rigcommander.cpp | 90 ++++++++++++++++++++++++++++-- rigcommander.h | 9 +++ rigidentities.cpp | 14 ++++- rigidentities.h | 17 ++++++ wfmain.cpp | 139 ++++++++++++++++++++++++++++++++++++++-------- wfmain.h | 12 +++- 6 files changed, 251 insertions(+), 30 deletions(-) diff --git a/rigcommander.cpp b/rigcommander.cpp index 295373a..d5f270e 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -37,6 +37,8 @@ rigCommander::rigCommander(unsigned char rigCivAddr, QString rigSerialPort, quin civAddr = rigCivAddr; // address of the radio. Decimal is 148. setCIVAddr(civAddr); + usingNativeLAN = false; // TODO: set to true if we are connected over ethernet to the rig + spectSeqMax = 0; // this is now set after rig ID determined //compCivAddr = 0xE1; //payloadPrefix = QByteArray("\xFE\xFE\x94\xE0"); payloadPrefix = QByteArray("\xFE\xFE"); @@ -533,6 +535,10 @@ void rigCommander::parseCommand() // qDebug() << "Have rig ID: " << (unsigned int)payloadIn[2]; // printHex(payloadIn, false, true); model = determineRadioModel(payloadIn[2]); + determineRigCaps(); + qDebug() << "Have rig ID: decimal: " << (unsigned int)model; + + break; case '\x26': if((int)payloadIn[1] == 0) @@ -794,7 +800,6 @@ void rigCommander::parseDetailedRegisters1A05() void rigCommander::parseWFData() { float freqSpan = 0.0; - switch(payloadIn[1]) { case 0: @@ -846,8 +851,85 @@ void rigCommander::parseWFData() } } +void rigCommander::determineRigCaps() +{ + //TODO: Add if(usingNativeLAN) condition + //TODO: Determine available bands (low priority, rig will reject out of band requests anyway) + + switch(model){ + case model7300: + rigCaps.hasSpectrum = true; + rigCaps.spectSeqMax = 11; + rigCaps.spectAmpMax = 160; + rigCaps.spectLenMax = 475; + rigCaps.hasLan = false; + rigCaps.hasEthernet = false; + rigCaps.hasWiFi = false; + break; + case model9700: + rigCaps.hasSpectrum = true; + rigCaps.spectSeqMax = 11; + rigCaps.spectAmpMax = 160; + rigCaps.spectLenMax = 475; + rigCaps.hasLan = true; + rigCaps.hasEthernet = true; + rigCaps.hasWiFi = false; + break; + case model7610: + rigCaps.hasSpectrum = true; + rigCaps.spectSeqMax = 15; + rigCaps.spectAmpMax = 200; + rigCaps.spectLenMax = 689; + rigCaps.hasLan = true; + rigCaps.hasEthernet = true; + rigCaps.hasWiFi = false; + break; + case model7850: + rigCaps.hasSpectrum = true; + rigCaps.spectSeqMax = 15; + rigCaps.spectAmpMax = 136; + rigCaps.spectLenMax = 689; + rigCaps.hasLan = true; + rigCaps.hasEthernet = true; + rigCaps.hasWiFi = false; + break; + case model705: + rigCaps.hasSpectrum = true; + rigCaps.spectSeqMax = 11; + rigCaps.spectAmpMax = 160; + rigCaps.spectLenMax = 475; + rigCaps.hasLan = true; + rigCaps.hasEthernet = false; + rigCaps.hasWiFi = true; + break; + default: + rigCaps.hasSpectrum = false; + rigCaps.spectSeqMax = 0; + rigCaps.spectAmpMax = 0; + rigCaps.spectLenMax = 0; + rigCaps.hasLan = false; + rigCaps.hasEthernet = false; + rigCaps.hasWiFi = false; + break; + + } + haveRigCaps = true; + emit haveRigID(rigCaps); +} + void rigCommander::parseSpectrum() { + if(!haveRigCaps) + { + qDebug() << "Spectrum received in rigCommander, but rigID is incomplete."; + return; + } + if(rigCaps.spectSeqMax == 0) + { + // there is a chance this will happen with rigs that support spectrum. Once our RigID query returns, we will parse correctly. + qDebug() << "Warning: Spectrum sequence max was zero, yet spectrum was received."; + return; + } // Here is what to expect: // payloadIn[00] = '\x27'; // payloadIn[01] = '\x00'; @@ -906,16 +988,16 @@ void rigCommander::parseSpectrum() spectrumStartFreq -= spectrumEndFreq; spectrumEndFreq = spectrumStartFreq + 2*(spectrumEndFreq); } - } else if ((sequence > 1) && (sequence < 11)) + } else if ((sequence > 1) && (sequence < rigCaps.spectSeqMax)) { // spectrum from index 05 to index 54, length is 55 per segment. Length is 56 total. Pixel data is 50 pixels. // sequence numbers 2 through 10, 50 pixels each. Total after sequence 10 is 450 pixels. payloadIn.chop(1); spectrumLine.insert(spectrumLine.length(), payloadIn.right(payloadIn.length() - 5)); // write over the FD, last one doesn't, oh well. //qDebug() << "sequence: " << sequence << "spec index: " << (sequence-2)*55 << " payloadPosition: " << payloadIn.length() - 5 << " payload length: " << payloadIn.length(); - } else if (sequence == 11) + } else if (sequence == rigCaps.spectSeqMax) { - // last spectrum, a little bit different (last 25 pixels). Total at end is 475 pixels. + // last spectrum, a little bit different (last 25 pixels). Total at end is 475 pixels (7300). payloadIn.chop(1); spectrumLine.insert(spectrumLine.length(), payloadIn.right(payloadIn.length() - 5)); //qDebug() << "sequence: " << sequence << " spec index: " << (sequence-2)*55 << " payloadPosition: " << payloadIn.length() - 5 << " payload length: " << payloadIn.length(); diff --git a/rigcommander.h b/rigcommander.h index c5377ed..9d339d3 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -64,6 +64,7 @@ public slots: signals: void haveSpectrumData(QByteArray spectrum, double startFreq, double endFreq); // pass along data to UI + void haveRigID(rigCapabilities rigCaps); void haveFrequency(double frequencyMhz); void haveMode(QString mode); void haveDataMode(bool dataModeEnabled); @@ -106,6 +107,7 @@ private: void prepDataAndSend(QByteArray data); void debugMe(); void printHex(const QByteArray &pdata, bool printVert, bool printHoriz); + void determineRigCaps(); commHandler * comm; QByteArray payloadIn; QByteArray echoPerfix; @@ -121,7 +123,14 @@ private: double spectrumStartFreq; double spectrumEndFreq; + struct rigCapabilities rigCaps; + bool haveRigCaps; model_kind model; + quint8 spectSeqMax; + quint16 spectAmpMax; + quint16 spectLenMax; + + bool usingNativeLAN; // indicates using OEM LAN connection (705,7610,9700,7850) double frequencyMhz; unsigned char civAddr; // 0x94 is default = 148decimal diff --git a/rigidentities.cpp b/rigidentities.cpp index 3ba02a5..6981eff 100644 --- a/rigidentities.cpp +++ b/rigidentities.cpp @@ -1,5 +1,6 @@ #include "rigidentities.h" -// Copytight 2017-2020 Elliott H. Liggett + +// Copytight 2017-2021 Elliott H. Liggett model_kind determineRadioModel(unsigned char rigID) { @@ -32,6 +33,12 @@ model_kind determineRadioModel(unsigned char rigID) case model7850: rig = model7850; break; + case model9700: + rig = model9700; + break; + case model705: + rig = model705; + break; default: rig = modelUnknown; break; @@ -39,3 +46,8 @@ model_kind determineRadioModel(unsigned char rigID) return rig; } + + + + + diff --git a/rigidentities.h b/rigidentities.h index c67c7d1..ffe9043 100644 --- a/rigidentities.h +++ b/rigidentities.h @@ -1,6 +1,8 @@ #ifndef RIGIDENTITIES_H #define RIGIDENTITIES_H +#include + // Credit: // http://www.docksideradio.com/Icom%20Radio%20Hex%20Addresses.htm @@ -15,12 +17,27 @@ enum model_kind { model7700 = 0x74, model7800 = 0x6A, model7850 = 0x8E, + model9700 = 0xA2, + model705 = 0xA4, modelUnknown = 0xFF }; model_kind determineRadioModel(unsigned char rigID); +struct rigCapabilities { + //model_kind model; + + bool hasLan; // OEM ethernet or wifi connection + bool hasEthernet; + bool hasWiFi; + + bool hasSpectrum; + quint8 spectSeqMax; + quint16 spectAmpMax; + quint16 spectLenMax; + +}; #endif // RIGIDENTITIES_H diff --git a/wfmain.cpp b/wfmain.cpp index 5b15b89..c38ace5 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -20,6 +20,8 @@ wfmain::wfmain(QWidget *parent) : setWindowTitle(QString("wfview")); + haveRigCaps = false; + ui->bandStkLastUsedBtn->setVisible(false); ui->bandStkVoiceBtn->setVisible(false); ui->bandStkDataBtn->setVisible(false); @@ -180,17 +182,17 @@ wfmain::wfmain(QWidget *parent) : tracer->setBrush(Qt::green); tracer->setSize(30); - spectWidth = 475; // fixed for now - wfLength = 160; // fixed for now +// spectWidth = 475; // fixed for now +// wfLength = 160; // fixed for now, time-length of waterfall - // Initialize before use! +// // Initialize before use! - QByteArray empty((int)spectWidth, '\x01'); - spectrumPeaks = QByteArray( (int)spectWidth, '\x01' ); - for(quint16 i=0; istart(); + qRegisterMetaType(); connect(rig, SIGNAL(haveFrequency(double)), this, SLOT(receiveFreq(double))); connect(this, SIGNAL(getFrequency()), rig, SLOT(getFrequency())); @@ -266,8 +269,9 @@ wfmain::wfmain(QWidget *parent) : 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 IC-7300 speaker) + // 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())); @@ -295,16 +299,20 @@ wfmain::wfmain(QWidget *parent) : wf->addPlottable(colorMap); #endif + //TRY moving to prepareWf(): + colorScale = new QCPColorScale(wf); - colorMap->data()->setValueRange(QCPRange(0, wfLength-1)); - colorMap->data()->setKeyRange(QCPRange(0, spectWidth-1)); - colorMap->setDataRange(QCPRange(0, 160)); - 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); +// colorMap->data()->setValueRange(QCPRange(0, wfLength-1)); +// colorMap->data()->setKeyRange(QCPRange(0, spectWidth-1)); +// colorMap->setDataRange(QCPRange(0, 160)); +// 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); + + // end TRY ui->tabWidget->setCurrentIndex(0); @@ -547,6 +555,43 @@ void wfmain::saveSettings() 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; idata()->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); + + } else { + qDebug() << "Cannot prepare WF view without rigCaps. Waiting on this."; + return; + } + +} + // Key shortcuts (hotkeys) @@ -742,7 +787,7 @@ void wfmain::getInitialRigState() // the polling interval is set at 100ms. Faster is possible but slower // computers will glitch occassionally. - cmdOutQue.append(cmdGetRigID); // This may be used in the future. + cmdOutQue.append(cmdGetRigID); cmdOutQue.append(cmdGetFreq); cmdOutQue.append(cmdGetMode); @@ -759,6 +804,11 @@ void wfmain::getInitialRigState() // get TX level // get Scope reference Level + cmdOutQue.append(cmdNone); + cmdOutQue.append(cmdGetRigID); + cmdOutQue.append(cmdNone); + cmdOutQue.append(cmdGetRigID); + cmdOutQue.append(cmdDispEnable); cmdOutQue.append(cmdSpecOn); @@ -962,9 +1012,35 @@ void wfmain::runDelayedCommand() } } + +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 { + qDebug() << "Rig ID received into wfmain: spectLenMax: " << rigCaps.spectLenMax; + qDebug() << "Rig ID received into wfmain: spectAmpMax: " << rigCaps.spectAmpMax; + qDebug() << "Rig ID received into wfmain: spectSeqMax: " << rigCaps.spectSeqMax; + qDebug() << "Rig ID received into wfmain: hasSpectrum: " << rigCaps.hasSpectrum; + + this->rigCaps = rigCaps; + this->spectWidth = rigCaps.spectLenMax; // used once haveRigCaps is true. + haveRigCaps = true; + 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); + } +} + void wfmain::receiveFreq(double freqMhz) { - //qDebug() << "Frequency: " << freqMhz; + //qDebug() << "HEY WE GOT A Frequency: " << freqMhz; ui->freqLabel->setText(QString("%1").arg(freqMhz, 0, 'f')); this->freqMhz = freqMhz; this->knobFreqMhz = freqMhz; @@ -979,6 +1055,12 @@ void wfmain::receivePTTstatus(bool pttOn) void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double endFreq) { + if(!haveRigCaps) + { + qDebug() << "Spectrum received, but RigID incomplete."; + return; + } + if((startFreq != oldLowerFreq) || (endFreq != oldUpperFreq)) { // If the frequency changed and we were drawing peaks, now is the time to clearn them @@ -996,9 +1078,14 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e //qDebug() << "start: " << startFreq << " end: " << endFreq; quint16 specLen = spectrum.length(); //qDebug() << "Spectrum data received at UI! Length: " << specLen; - if(specLen != 475) + //if( (specLen != 475) || (specLen!=689) ) + + if( specLen != rigCaps.spectLenMax ) { - //qDebug () << "Unusual spectrum: length: " << specLen; + qDebug() << "-------------------------------------------"; + qDebug() << "------ Unusual spectrum received, length: " << specLen; + qDebug() << "------ Expected spectrum length: " << rigCaps.spectLenMax; + qDebug() << "------ This should happen once at most. "; return; // safe. Using these unusual length things is a problem. } @@ -1208,7 +1295,11 @@ void wfmain::receiveDataModeStatus(bool dataEnabled) void wfmain::on_clearPeakBtn_clicked() { - spectrumPeaks = QByteArray( (int)spectWidth, '\x01' ); + if(haveRigCaps) + { + spectrumPeaks = QByteArray( (int)spectWidth, '\x01' ); + } + return; } void wfmain::on_drawPeakChk_clicked(bool checked) diff --git a/wfmain.h b/wfmain.h index c93b975..7365788 100644 --- a/wfmain.h +++ b/wfmain.h @@ -8,11 +8,14 @@ #include #include #include +#include #include "commhandler.h" #include "rigcommander.h" #include "freqmemory.h" +#include "rigidentities.h" + #include #include @@ -111,7 +114,7 @@ private slots: void receiveAfGain(unsigned char level); void receiveSql(unsigned char level); void receiveATUStatus(unsigned char atustatus); - + void receiveRigID(rigCapabilities rigCaps); void handlePlotClick(QMouseEvent *); void handlePlotDoubleClick(QMouseEvent *); void handleWFClick(QMouseEvent *); @@ -240,6 +243,7 @@ private: //commHandler *comm; void setAppTheme(bool isDark); void setPlotTheme(QCustomPlot *plot, bool isDark); + void prepareWf(); void getInitialRigState(); QWidget * theParent; QStringList portList; @@ -366,10 +370,16 @@ private: int oldFreqDialVal; + rigCapabilities rigCaps; + bool haveRigCaps; + void bandStackBtnClick(); bool waitingForBandStackRtn; char bandStkBand; char bandStkRegCode; }; +Q_DECLARE_METATYPE(struct rigCapabilities) ; + + #endif // WFMAIN_H