diff --git a/cwsender.cpp b/cwsender.cpp new file mode 100644 index 0000000..74540c7 --- /dev/null +++ b/cwsender.cpp @@ -0,0 +1,281 @@ +#include "cwsender.h" +#include "ui_cwsender.h" + +cwSender::cwSender(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::cwSender) +{ + ui->setupUi(this); + this->setWindowTitle("CW Sender"); + ui->textToSendEdit->setFocus(); + QFont f = QFont("Monospace"); + f.setStyleHint(QFont::TypeWriter); + ui->textToSendEdit->setFont(f); + ui->transcriptText->setFont(f); + ui->textToSendEdit->setFocus(); + ui->statusbar->setToolTipDuration(3000); + this->setToolTipDuration(3000); +} + +cwSender::~cwSender() +{ + qDebug(logCW()) << "Running CW Sender destructor."; + delete ui; +} + +void cwSender::showEvent(QShowEvent *event) +{ + emit getCWSettings(); + (void)event; +} + +void cwSender::handleKeySpeed(unsigned char wpm) +{ + //qDebug(logCW()) << "Told that current WPM is" << wpm; + if((wpm >= 6) && (wpm <=48)) + { + //qDebug(logCW()) << "Setting WPM UI control to" << wpm; + ui->wpmSpin->blockSignals(true); + ui->wpmSpin->setValue(wpm); + ui->wpmSpin->blockSignals(false); + } +} + +void cwSender::handleBreakInMode(unsigned char b) +{ + if(b < 3) + { + ui->breakinCombo->blockSignals(true); + ui->breakinCombo->setCurrentIndex(b); + ui->breakinCombo->blockSignals(false); + } +} + +void cwSender::handleCurrentModeUpdate(mode_kind mode) +{ + this->currentMode = mode; + if( (currentMode==modeCW) || (currentMode==modeCW_R) ) + { + } else { + ui->statusbar->showMessage("Note: Mode needs to be set to CW or CW-R to send CW.", 3000); + } +} + +void cwSender::on_sendBtn_clicked() +{ + if( (ui->textToSendEdit->text().length() > 0) && + (ui->textToSendEdit->text().length() <= 30) ) + { + emit sendCW(ui->textToSendEdit->text()); + ui->transcriptText->appendPlainText(ui->textToSendEdit->text()); + ui->textToSendEdit->clear(); + ui->textToSendEdit->setFocus(); + ui->statusbar->showMessage("Sending CW", 3000); + } + if( (currentMode==modeCW) || (currentMode==modeCW_R) ) + { + } else { + ui->statusbar->showMessage("Note: Mode needs to be set to CW or CW-R to send CW.", 3000); + } +} + +void cwSender::on_stopBtn_clicked() +{ + emit stopCW(); + ui->textToSendEdit->setFocus(); + ui->statusbar->showMessage("Stopping CW transmission.", 3000); +} + +void cwSender::on_textToSendEdit_returnPressed() +{ + on_sendBtn_clicked(); +} + +void cwSender::on_breakinCombo_activated(int brkmode) +{ + // 0 = off, 1 = semi, 2 = full + emit setBreakInMode((unsigned char)brkmode); + ui->textToSendEdit->setFocus(); +} + +void cwSender::on_wpmSpin_valueChanged(int wpm) +{ + emit setKeySpeed((unsigned char)wpm); +} + +void cwSender::on_macro1btn_clicked() +{ + processMacroButton(1, ui->macro1btn); +} + +void cwSender::on_macro2btn_clicked() +{ + processMacroButton(2, ui->macro2btn); +} + +void cwSender::on_macro3btn_clicked() +{ + processMacroButton(3, ui->macro3btn); +} + +void cwSender::on_macro4btn_clicked() +{ + processMacroButton(4, ui->macro4btn); +} + +void cwSender::on_macro5btn_clicked() +{ + processMacroButton(5, ui->macro5btn); +} + +void cwSender::on_macro6btn_clicked() +{ + processMacroButton(6, ui->macro6btn); +} + +void cwSender::on_macro7btn_clicked() +{ + processMacroButton(7, ui->macro7btn); +} + +void cwSender::on_macro8btn_clicked() +{ + processMacroButton(8, ui->macro8btn); +} + +void cwSender::on_macro9btn_clicked() +{ + processMacroButton(9, ui->macro9btn); +} + +void cwSender::on_macro10btn_clicked() +{ + processMacroButton(10, ui->macro10btn); +} + +void cwSender::processMacroButton(int buttonNumber, QPushButton *btn) +{ + if(ui->macroEditChk->isChecked()) + { + editMacroButton(buttonNumber, btn); + } else { + runMacroButton(buttonNumber); + } + ui->textToSendEdit->setFocus(); +} + +void cwSender::runMacroButton(int buttonNumber) +{ + if(macroText[buttonNumber].isEmpty()) + return; + QString outText; + if(macroText[buttonNumber].contains("\%1")) + { + outText = macroText[buttonNumber].arg(sequenceNumber, 3, 10, QChar('0')); + sequenceNumber++; + ui->sequenceSpin->blockSignals(true); + ui->sequenceSpin->setValue(sequenceNumber); + ui->sequenceSpin->blockSignals(false); + } else { + outText = macroText[buttonNumber]; + } + emit sendCW(outText); + ui->transcriptText->appendPlainText(outText); + ui->textToSendEdit->setFocus(); + + if( (currentMode==modeCW) || (currentMode==modeCW_R) ) + { + ui->statusbar->showMessage(QString("Sending CW macro %1").arg(buttonNumber)); + } else { + ui->statusbar->showMessage("Note: Mode needs to be set to CW or CW-R to send CW."); + } +} + +void cwSender::editMacroButton(int buttonNumber, QPushButton* btn) +{ + bool ok; + QString promptFirst = QString("Please enter the text for macro %1,\n" + "up to 30 characters.\n").arg(buttonNumber); + QString promptSecond = QString("You may use \"\%1\" to insert a sequence number."); + QString prompt = promptFirst+promptSecond; + + QString newMacroText = QInputDialog::getText(this, "Macro Edit", + prompt, + QLineEdit::Normal, macroText[buttonNumber], &ok); + if(!ok) + return; + + if(newMacroText.length() > 30) + { + QMessageBox msgBox; + msgBox.setText(QString("The text entered was too long \n" + "(max length is 30 characters).\n" + "Your input was %1 characters.").arg(newMacroText.length())); + msgBox.exec(); + this->raise(); + return; + } + + macroText[buttonNumber] = newMacroText; + setMacroButtonText(newMacroText, btn); +} + +void cwSender::setMacroButtonText(QString btnText, QPushButton *btn) +{ + if(btn==Q_NULLPTR) + return; + if(btnText.isEmpty()) + return; + + QString shortBtnName; + if(btnText.length() <= 8) + { + shortBtnName = btnText; + } else { + shortBtnName = btnText.left(7); + shortBtnName.append("…"); + } + btn->setText(shortBtnName); +} + +void cwSender::on_sequenceSpin_valueChanged(int newSeq) +{ + sequenceNumber = newSeq; + ui->textToSendEdit->setFocus(); +} + +QStringList cwSender::getMacroText() +{ + // This is for preference saving: + QStringList mlist; + for(int i=1; i < 11; i++) + { + mlist << macroText[i]; + } + return mlist; +} + +void cwSender::setMacroText(QStringList macros) +{ + if(macros.length() != 10) + { + qCritical(logCW()) << "Macro list must be exactly 10. Rejecting macro text load."; + return; + } + + for(int i=0; i < 10; i++) + { + macroText[i+1] = macros.at(i); + } + + setMacroButtonText(macroText[1], ui->macro1btn); + setMacroButtonText(macroText[2], ui->macro2btn); + setMacroButtonText(macroText[3], ui->macro3btn); + setMacroButtonText(macroText[4], ui->macro4btn); + setMacroButtonText(macroText[5], ui->macro5btn); + setMacroButtonText(macroText[6], ui->macro6btn); + setMacroButtonText(macroText[7], ui->macro7btn); + setMacroButtonText(macroText[8], ui->macro8btn); + setMacroButtonText(macroText[9], ui->macro9btn); + setMacroButtonText(macroText[10], ui->macro10btn); +} diff --git a/cwsender.h b/cwsender.h new file mode 100644 index 0000000..2f77f9d --- /dev/null +++ b/cwsender.h @@ -0,0 +1,83 @@ +#ifndef CWSENDER_H +#define CWSENDER_H + +#include +#include +#include +#include +#include +#include "wfviewtypes.h" +#include "logcategories.h" + + +namespace Ui { +class cwSender; +} + +class cwSender : public QMainWindow +{ + Q_OBJECT + +public: + explicit cwSender(QWidget *parent = 0); + ~cwSender(); + QStringList getMacroText(); + void setMacroText(QStringList macros); +signals: + void sendCW(QString cwMessage); + void stopCW(); + void setKeySpeed(unsigned char wpm); + void setBreakInMode(unsigned char b); + void getCWSettings(); + +public slots: + void handleKeySpeed(unsigned char wpm); + void handleBreakInMode(unsigned char b); + void handleCurrentModeUpdate(mode_kind mode); + +private slots: + void on_sendBtn_clicked(); + void showEvent(QShowEvent* event); + + void on_stopBtn_clicked(); + + void on_textToSendEdit_returnPressed(); + + void on_breakinCombo_activated(int index); + + void on_wpmSpin_valueChanged(int arg1); + + void on_macro1btn_clicked(); + + void on_macro2btn_clicked(); + + void on_macro3btn_clicked(); + + void on_macro4btn_clicked(); + + void on_macro5btn_clicked(); + + void on_macro6btn_clicked(); + + void on_macro7btn_clicked(); + + void on_macro8btn_clicked(); + + void on_macro9btn_clicked(); + + void on_macro10btn_clicked(); + + void on_sequenceSpin_valueChanged(int arg1); + +private: + Ui::cwSender *ui; + QString macroText[11]; + int sequenceNumber = 1; + mode_kind currentMode; + void processMacroButton(int buttonNumber, QPushButton *btn); + void runMacroButton(int buttonNumber); + void editMacroButton(int buttonNumber, QPushButton *btn); + void setMacroButtonText(QString btnText, QPushButton *btn); +}; + +#endif // CWSENDER_H diff --git a/cwsender.ui b/cwsender.ui new file mode 100644 index 0000000..8e8b531 --- /dev/null +++ b/cwsender.ui @@ -0,0 +1,283 @@ + + + cwSender + + + + 0 + 0 + 681 + 451 + + + + MainWindow + + + Send the text in the edit box + + + + + + + WPM: + + + + + + + Qt::NoFocus + + + CW Transmission Transcript + + + false + + + true + + + + + + + Macros + + + + + + Macro Access Button + + + Macro 5 + + + + + + + Macro Access Button + + + Macro 2 + + + + + + + Macro Access Button + + + Macro 4 + + + + + + + Check this box to enter edit mode, where you can then press the macro buttons to edit the macros. + + + Edit Mode + + + + + + + Macro Access Button + + + Macro 7 + + + + + + + Macro Access Button + + + Macro 3 + + + + + + + Macro Access Button + + + Macro 6 + + + + + + + Macro Access Button + + + Macro 10 + + + + + + + Macro Access Button + + + Macro 9 + + + + + + + Macro Access Button + + + Macro 8 + + + + + + + Macro Access Button + + + Macro 1 + + + + + + + + + Seq + + + + + + + <html><head/><body><p>Sequence number, for contests. </p><p>Substitute &quot;%1&quot; in your macro text to use it. </p></body></html> + + + 1 + + + 99999 + + + + + + + + + + + + Stop sending CW + + + Stop + + + + + + + Break In + + + + + + + + 25 + 0 + + + + + 400 + 16777215 + + + + Type the text you wish to send as CW here. + + + 30 + + + Type here... + + + + + + + Set the Words Per Minute + + + 6 + + + 48 + + + 20 + + + + + + + Send + + + true + + + + + + + <html><head/><body><p>Set the desired break-in mode:</p><p>1. None: You must manually key and unkey the radio.</p><p>2. Semi: Transmit is automatic and switches to receive at the end of the text.</p><p>3. Full: Same as semi, but with breaks between characters when possible.</p></body></html> + + + + Off + + + + + Semi + + + + + Full + + + + + + + + + + + diff --git a/logcategories.cpp b/logcategories.cpp index 527f619..8ba2677 100644 --- a/logcategories.cpp +++ b/logcategories.cpp @@ -6,6 +6,8 @@ Q_LOGGING_CATEGORY(logGui, "gui") Q_LOGGING_CATEGORY(logLogger, "log") Q_LOGGING_CATEGORY(logUser, "user") Q_LOGGING_CATEGORY(logRig, "rig") +Q_LOGGING_CATEGORY(logRigTraffic, "rigTraffic") +Q_LOGGING_CATEGORY(logCW, "cw") Q_LOGGING_CATEGORY(logAudio, "audio") Q_LOGGING_CATEGORY(logUdp, "udp") Q_LOGGING_CATEGORY(logUdpServer, "udp.server") diff --git a/logcategories.h b/logcategories.h index 4aa6cfb..36da605 100644 --- a/logcategories.h +++ b/logcategories.h @@ -9,6 +9,8 @@ Q_DECLARE_LOGGING_CATEGORY(logGui) Q_DECLARE_LOGGING_CATEGORY(logLogger) Q_DECLARE_LOGGING_CATEGORY(logUser) Q_DECLARE_LOGGING_CATEGORY(logRig) +Q_DECLARE_LOGGING_CATEGORY(logRigTraffic) +Q_DECLARE_LOGGING_CATEGORY(logCW) Q_DECLARE_LOGGING_CATEGORY(logAudio) Q_DECLARE_LOGGING_CATEGORY(logUdp) Q_DECLARE_LOGGING_CATEGORY(logUdpServer) diff --git a/printhex.h b/printhex.h new file mode 100644 index 0000000..e39c1cb --- /dev/null +++ b/printhex.h @@ -0,0 +1,42 @@ +#ifndef PRINTHEX_H +#define PRINTHEX_H +#include +#include + +#include "logcategories.h" + +QString inline getHex(const QByteArray &pdata) +{ + QString head = "---- Begin hex dump -----:\n"; + QString sdata("DATA: "); + QString index("INDEX: "); + + for(int i=0; i < pdata.length(); i++) + { + sdata.append(QString("%1 ").arg((unsigned char)pdata[i], 2, 16, QChar('0')) ); + index.append(QString("%1 ").arg(i, 2, 10, QChar('0'))); + } + + sdata.append("\n"); + index.append("\n"); + + QString tail = "---- End hex dump -----\n"; + return head + sdata + index + tail; +} + +void inline printHexNow(const QByteArray &pdata, const QLoggingCategory &cat) +{ + QString d = getHex(pdata); + // These lines are needed to keep the formatting as expected in the log file + if(d.endsWith("\n")) + { + d.chop(1); + } + QStringList s = d.split("\n"); + for(int i=0; i < s.length(); i++) + { + qDebug(cat) << s.at(i); + } +} + +#endif // PRINTHEX_H diff --git a/rigcommander.cpp b/rigcommander.cpp index d1d0eb5..0f441e0 100644 --- a/rigcommander.cpp +++ b/rigcommander.cpp @@ -3,6 +3,7 @@ #include "rigidentities.h" #include "logcategories.h" +#include "printhex.h" // Copyright 2017-2020 Elliott H. Liggett @@ -307,8 +308,10 @@ void rigCommander::prepDataAndSend(QByteArray data) if(data[4] != '\x15') { // We don't print out requests for meter levels - qDebug(logRig()) << "Final payload in rig commander to be sent to rig: "; - printHex(data); + qDebug(logRigTraffic()) << "Final payload in rig commander to be sent to rig: "; + //printHex(data); + //printHex(data, logRigTraffic()); + printHexNow(data, logRigTraffic()); } emit dataForComm(data); @@ -1164,6 +1167,52 @@ void rigCommander::setPTT(bool pttOn) } } +void rigCommander::sendCW(QString textToSend) +{ + if(textToSend.length() >30) + { + qCritical(logRig()).nospace() << "Cannot send CW message, length > 30 characters (" << textToSend.length() << ")"; + return; + } + + QByteArray textData = textToSend.toLocal8Bit(); + unsigned char p=0; + for(int c=0; c < textData.length(); c++) + { + p = textData.at(c); + if( ( (p >= 0x30) && (p <= 0x39) ) || + ( (p >= 0x41) && (p <= 0x5A) ) || + ( (p >= 0x61) && (p <= 0x7A) ) || + (p==0x2F) || (p==0x3F) || (p==0x2E) || + (p==0x2D) || (p==0x2C) || (p==0x3A) || + (p==0x27) || (p==0x28) || (p==0x29) || + (p==0x3D) || (p==0x2B) || (p==0x22) || + (p==0x40) || (p==0x20) ) + { + // Allowed character, continue + } else { + qWarning(logRig()) << "Invalid character detected in CW message at position " << c << ", the character is " << textToSend.at(c); + printHex(textData); + textData[c] = 0x3F; // "?" + } + } + + if(pttAllowed) + { + QByteArray payload("\x17", 1); + payload.append(textData); + prepDataAndSend(payload); + } + // Does it need to end in "FF" or is that implicit at the end of a message? +} + +void rigCommander::sendStopCW() +{ + QByteArray payload("\x17", 1); + payload.append("\xFF"); + prepDataAndSend(payload); +} + void rigCommander::setCIVAddr(unsigned char civAddr) { // Note: This sets the radio's CIV address @@ -1344,7 +1393,8 @@ void rigCommander::parseCommand() { // We do not log spectrum and meter data, // as they tend to clog up any useful logging. - printHex(payloadIn); + qDebug(logRigTraffic()) << "Received from radio:"; + printHexNow(payloadIn, logRigTraffic()); } switch(payloadIn[00]) @@ -1539,8 +1589,9 @@ void rigCommander::parseLevels() state.set(MICGAIN, level, false); break; case '\x0C': - // CW Keying Speed - ignore for now state.set(KEYSPD, level, false); + //qInfo(logRig()) << "Have received key speed in RC, raw level: " << level << ", WPM: " << (level/6.071)+6 << ", rounded: " << round((level/6.071)+6); + emit haveKeySpeed(round((level/6.071)+6)); break; case '\x0D': // Notch filder setting - ignore for now @@ -2740,6 +2791,7 @@ void rigCommander::parseRegister16() state.set(FBKINFUNC, true, false); state.set(SBKINFUNC, false, false); } + emit haveCWBreakMode(payloadIn.at(2)); break; case '\x48': // Manual Notch state.set(MNFUNC, payloadIn.at(2) != 0, false); @@ -3070,20 +3122,20 @@ void rigCommander::parseWFData() isSub = payloadIn.at(2)==0x01; freqSpan = parseFrequency(payloadIn, 6); emit haveScopeSpan(freqSpan, isSub); - qInfo(logRig()) << "Received 0x15 center span data: for frequency " << freqSpan.Hz; + //qInfo(logRig()) << "Received 0x15 center span data: for frequency " << freqSpan.Hz; //printHex(payloadIn, false, true); break; case 0x16: // read edge mode center in edge mode emit haveScopeEdge((char)payloadIn[2]); - qInfo(logRig()) << "Received 0x16 edge in center mode:"; + //qInfo(logRig()) << "Received 0x16 edge in center mode:"; printHex(payloadIn, false, true); // [1] 0x16 // [2] 0x01, 0x02, 0x03: Edge 1,2,3 break; case 0x17: // Hold status (only 9700?) - qInfo(logRig()) << "Received 0x17 hold status - need to deal with this!"; + qDebug(logRig()) << "Received 0x17 hold status - need to deal with this!"; printHex(payloadIn, false, true); break; case 0x19: @@ -4558,6 +4610,26 @@ void rigCommander::getBreakIn() prepDataAndSend(payload); } +void rigCommander::setKeySpeed(unsigned char wpm) +{ + // 0 = 6 WPM + // 255 = 48 WPM + + unsigned char wpmRadioSend = round((wpm-6) * (6.071)); + //qInfo(logRig()) << "Setting keyspeed to " << wpm << "WPM, via command value" << wpmRadioSend; + QByteArray payload; + payload.setRawData("\x14\x0C", 2); + payload.append(bcdEncodeInt(wpmRadioSend)); + prepDataAndSend(payload); +} + +void rigCommander::getKeySpeed() +{ + QByteArray payload; + payload.setRawData("\x14\x0C", 2); + prepDataAndSend(payload); +} + void rigCommander::setManualNotch(bool enabled) { QByteArray payload("\x16\x48"); @@ -4572,7 +4644,6 @@ void rigCommander::getManualNotch() prepDataAndSend(payload); } - void rigCommander::getRigID() { QByteArray payload; diff --git a/rigcommander.h b/rigcommander.h index 5edc132..47d563a 100644 --- a/rigcommander.h +++ b/rigcommander.h @@ -86,6 +86,8 @@ public slots: // PTT, ATU, ATT, Antenna, and Preamp: void getPTT(); void setPTT(bool pttOn); + void sendCW(QString textToSend); + void sendStopCW(); void startATU(); void setATU(bool enabled); void getATUStatus(); @@ -113,6 +115,8 @@ public slots: void getVox(); void setBreakIn(unsigned char type); void getBreakIn(); + void setKeySpeed(unsigned char wpm); + void getKeySpeed(); void setManualNotch(bool enabled); void getManualNotch(); @@ -339,6 +343,11 @@ signals: void havePreamp(unsigned char pre); void haveAntenna(unsigned char ant,bool rx); + // CW: + void haveKeySpeed(unsigned char wpm); + void haveCWBreakMode(unsigned char bmode); + + // Rig State void stateInfo(rigstate* state); diff --git a/wfmain.cpp b/wfmain.cpp index 966e014..c2e9873 100644 --- a/wfmain.cpp +++ b/wfmain.cpp @@ -48,6 +48,7 @@ wfmain::wfmain(const QString settingsFile, const QString logFile, bool debugMode rpt = new repeaterSetup(); sat = new satelliteSetup(); trxadj = new transceiverAdjustments(); + cw = new cwSender(); shut = new controllerSetup(); abtBox = new aboutbox(); selRad = new selectRadio(); @@ -341,6 +342,18 @@ void wfmain::rigConnections() 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(this, SIGNAL(sendCW(QString)), rig, SLOT(sendCW(QString))); + connect(this, SIGNAL(stopCW()), rig, SLOT(sendStopCW())); + connect(this, SIGNAL(setKeySpeed(unsigned char)), rig, SLOT(setKeySpeed(unsigned char))); + connect(this, SIGNAL(getKeySpeed()), rig, SLOT(getKeySpeed())); + connect(this, SIGNAL(setCWBreakMode(unsigned char)), rig, SLOT(setBreakIn(unsigned char))); + connect(this, SIGNAL(getCWBreakMode()), rig, SLOT(getBreakIn())); + connect(this->rig, &rigCommander::haveKeySpeed, + [=](const unsigned char &wpm) { cw->handleKeySpeed(wpm);}); + connect(this->rig, &rigCommander::haveCWBreakMode, + [=](const unsigned char &bm) { cw->handleBreakInMode(bm);}); + connect(rig, SIGNAL(haveBandStackReg(freqt,char,char,bool)), this, SLOT(receiveBandStackReg(freqt,char,char,bool))); connect(this, SIGNAL(setRitEnable(bool)), rig, SLOT(setRitEnable(bool))); connect(this, SIGNAL(setRitValue(int)), rig, SLOT(setRitValue(int))); @@ -1058,6 +1071,19 @@ void wfmain::setupMainUI() connect(this->trxadj, &transceiverAdjustments::setPassband, [=](const quint16 &passbandHz) { issueCmdUniquePriority(cmdSetPassband, passbandHz);} ); + + connect(this->cw, &cwSender::sendCW, + [=](const QString &cwMessage) { issueCmd(cmdSendCW, cwMessage);}); + connect(this->cw, &cwSender::stopCW, + [=]() { issueDelayedCommand(cmdStopCW);}); + connect(this->cw, &cwSender::setBreakInMode, + [=](const unsigned char &bmode) { issueCmd(cmdSetBreakMode, bmode);}); + connect(this->cw, &cwSender::setKeySpeed, + [=](const unsigned char &wpm) { issueCmd(cmdSetKeySpeed, wpm);}); + connect(this->cw, &cwSender::getCWSettings, + [=]() { issueDelayedCommand(cmdGetKeySpeed); + issueDelayedCommand(cmdGetBreakMode);}); + } void wfmain::prepareSettingsWindow() @@ -1460,12 +1486,97 @@ void wfmain::setupKeyShortcuts() keyM->setKey(Qt::Key_M); connect(keyM, SIGNAL(activated()), this, SLOT(shortcutM())); + // Alternate for plus: + keyK = new QShortcut(this); + keyK->setKey(Qt::Key_K); + connect(keyK, &QShortcut::activated, + [=]() { + if (freqLock) return; + this->shortcutPlus(); + }); + + // Alternate for minus: + keyJ = new QShortcut(this); + keyJ->setKey(Qt::Key_J); + connect(keyJ, &QShortcut::activated, + [=]() { + if (freqLock) return; + this->shortcutMinus(); + }); + + keyShiftK = new QShortcut(this); + keyShiftK->setKey(Qt::SHIFT + Qt::Key_K); + connect(keyShiftK, &QShortcut::activated, + [=]() { + if (freqLock) return; + this->shortcutShiftPlus(); + }); + + + keyShiftJ = new QShortcut(this); + keyShiftJ->setKey(Qt::SHIFT + Qt::Key_J); + connect(keyShiftJ, &QShortcut::activated, + [=]() { + if (freqLock) return; + this->shortcutShiftMinus(); + }); + + keyControlK = new QShortcut(this); + keyControlK->setKey(Qt::CTRL + Qt::Key_K); + connect(keyControlK, &QShortcut::activated, + [=]() { + if (freqLock) return; + this->shortcutControlPlus(); + }); + + + keyControlJ = new QShortcut(this); + keyControlJ->setKey(Qt::CTRL + Qt::Key_J); + connect(keyControlJ, &QShortcut::activated, + [=]() { + if (freqLock) return; + this->shortcutControlMinus(); + }); + // Move the tuning knob by the tuning step selected: + // H = Down + keyH = new QShortcut(this); + keyH->setKey(Qt::Key_H); + connect(keyH, &QShortcut::activated, + [=]() { + if (freqLock) return; + + freqt f; + f.Hz = roundFrequencyWithStep(freq.Hz, -1, tsKnobHz); + f.MHzDouble = f.Hz / (double)1E6; + freq.Hz = f.Hz; + freq.MHzDouble = f.MHzDouble; + setUIFreq(); + issueCmd(cmdSetFreq, f); + }); + + // L = Up + keyL = new QShortcut(this); + keyL->setKey(Qt::Key_L); + connect(keyL, &QShortcut::activated, + [=]() { + if (freqLock) return; + + freqt f; + f.Hz = roundFrequencyWithStep(freq.Hz, 1, tsKnobHz); + f.MHzDouble = f.Hz / (double)1E6; + ui->freqLabel->setText(QString("%1").arg(f.MHzDouble, 0, 'f')); + freq.Hz = f.Hz; + freq.MHzDouble = f.MHzDouble; + setUIFreq(); + issueCmd(cmdSetFreq, f); + }); + keyDebug = new QShortcut(this); #if (QT_VERSION < QT_VERSION_CHECK(6,0,0)) keyDebug->setKey(Qt::CTRL + Qt::SHIFT + Qt::Key_D); #else keyDebug->setKey(Qt::CTRL + Qt::Key_D); - #endif +#endif connect(keyDebug, SIGNAL(activated()), this, SLOT(on_debugBtn_clicked())); } @@ -2124,8 +2235,24 @@ void wfmain::loadSettings() ui->clusterTimeoutLineEdit->setEnabled(false); } settings->endArray(); - settings->endGroup(); + + // CW Memory Load: + settings->beginGroup("Keyer"); + int numMemories = settings->beginReadArray("macros"); + if(numMemories==10) + { + QStringList macroList; + for(int m=0; m < 10; m++) + { + settings->setArrayIndex(m); + macroList << settings->value("macroText", "").toString(); + } + cw->setMacroText(macroList); + } + settings->endArray(); + settings->endGroup(); + #if defined (USB_CONTROLLER) /* Load USB buttons*/ settings->beginGroup("USB"); @@ -2679,7 +2806,22 @@ void wfmain::saveSettings() } settings->endArray(); + settings->endGroup(); + settings->beginGroup("Keyer"); + QStringList macroList = cw->getMacroText(); + if(macroList.length() == 10) + { + settings->beginWriteArray("macros"); + for(int m=0; m < 10; m++) + { + settings->setArrayIndex(m); + settings->setValue("macroText", macroList.at(m)); + } + settings->endArray(); + } else { + qDebug(logSystem()) << "Error, CW macro list is wrong length: " << macroList.length(); + } settings->endGroup(); #if defined(USB_CONTROLLER) @@ -3021,12 +3163,13 @@ void wfmain::shortcutMinus() freqt f; f.Hz = roundFrequencyWithStep(freq.Hz, -1, tsPlusHz); - f.MHzDouble = f.Hz / (double)1E6; + freq.Hz = f.Hz; + freq.MHzDouble = f.MHzDouble; setUIFreq(); //issueCmd(cmdSetFreq, f); issueCmdUniquePriority(cmdSetFreq, f); - issueDelayedCommandUnique(cmdGetFreq); + //issueDelayedCommandUnique(cmdGetFreq); } void wfmain::shortcutPlus() @@ -3035,12 +3178,13 @@ void wfmain::shortcutPlus() freqt f; f.Hz = roundFrequencyWithStep(freq.Hz, 1, tsPlusHz); - f.MHzDouble = f.Hz / (double)1E6; + freq.Hz = f.Hz; + freq.MHzDouble = f.MHzDouble; setUIFreq(); //issueCmd(cmdSetFreq, f); issueCmdUniquePriority(cmdSetFreq, f); - issueDelayedCommandUnique(cmdGetFreq); + //issueDelayedCommandUnique(cmdGetFreq); } void wfmain::shortcutStepMinus() @@ -3075,12 +3219,13 @@ void wfmain::shortcutShiftMinus() freqt f; f.Hz = roundFrequencyWithStep(freq.Hz, -1, tsPlusShiftHz); - f.MHzDouble = f.Hz / (double)1E6; + freq.Hz = f.Hz; + freq.MHzDouble = f.MHzDouble; setUIFreq(); //emit setFrequency(0,f); issueCmd(cmdSetFreq, f); - issueDelayedCommandUnique(cmdGetFreq); + //issueDelayedCommandUnique(cmdGetFreq); } void wfmain::shortcutShiftPlus() @@ -3089,12 +3234,13 @@ void wfmain::shortcutShiftPlus() freqt f; f.Hz = roundFrequencyWithStep(freq.Hz, 1, tsPlusShiftHz); - f.MHzDouble = f.Hz / (double)1E6; + freq.Hz = f.Hz; + freq.MHzDouble = f.MHzDouble; setUIFreq(); //emit setFrequency(0,f); issueCmd(cmdSetFreq, f); - issueDelayedCommandUnique(cmdGetFreq); + //issueDelayedCommandUnique(cmdGetFreq); } void wfmain::shortcutControlMinus() @@ -3103,12 +3249,12 @@ void wfmain::shortcutControlMinus() freqt f; f.Hz = roundFrequencyWithStep(freq.Hz, -1, tsPlusControlHz); - f.MHzDouble = f.Hz / (double)1E6; + freq.Hz = f.Hz; + freq.MHzDouble = f.MHzDouble; setUIFreq(); - //emit setFrequency(0,f); issueCmd(cmdSetFreq, f); - issueDelayedCommandUnique(cmdGetFreq); + //issueDelayedCommandUnique(cmdGetFreq); } void wfmain::shortcutControlPlus() @@ -3117,12 +3263,12 @@ void wfmain::shortcutControlPlus() freqt f; f.Hz = roundFrequencyWithStep(freq.Hz, 1, tsPlusControlHz); - f.MHzDouble = f.Hz / (double)1E6; + freq.Hz = f.Hz; + freq.MHzDouble = f.MHzDouble; setUIFreq(); - //emit setFrequency(0,f); issueCmd(cmdSetFreq, f); - issueDelayedCommandUnique(cmdGetFreq); + //issueDelayedCommandUnique(cmdGetFreq); } void wfmain::shortcutPageUp() @@ -3131,12 +3277,12 @@ void wfmain::shortcutPageUp() freqt f; f.Hz = freq.Hz + tsPageHz; - f.MHzDouble = f.Hz / (double)1E6; + freq.Hz = f.Hz; + freq.MHzDouble = f.MHzDouble; setUIFreq(); - //emit setFrequency(0,f); issueCmd(cmdSetFreq, f); - issueDelayedCommandUnique(cmdGetFreq); + //issueDelayedCommandUnique(cmdGetFreq); } void wfmain::shortcutPageDown() @@ -3145,12 +3291,12 @@ void wfmain::shortcutPageDown() freqt f; f.Hz = freq.Hz - tsPageHz; - f.MHzDouble = f.Hz / (double)1E6; + freq.Hz = f.Hz; + freq.MHzDouble = f.MHzDouble; setUIFreq(); - //emit setFrequency(0,f); issueCmd(cmdSetFreq, f); - issueDelayedCommandUnique(cmdGetFreq); + //issueDelayedCommandUnique(cmdGetFreq); } void wfmain::shortcutF() @@ -3544,6 +3690,24 @@ void wfmain::doCmd(commandtype cmddata) } break; } + case cmdSendCW: + { + QString messageText = (*std::static_pointer_cast(data)); + emit sendCW(messageText); + break; + } + case cmdSetBreakMode: + { + unsigned char bmode = (*std::static_pointer_cast(data)); + emit setCWBreakMode(bmode); + break; + } + case cmdSetKeySpeed: + { + unsigned char wpm = (*std::static_pointer_cast(data)); + emit setKeySpeed(wpm); + break; + } case cmdSetATU: { bool atuOn = (*std::static_pointer_cast(data)); @@ -3775,12 +3939,21 @@ void wfmain::doCmd(cmds cmd) break; case cmdGetALCMeter: if(amTransmitting) - emit getMeters(meterALC); + emit getMeters(meterALC); break; case cmdGetCompMeter: if(amTransmitting) emit getMeters(meterComp); break; + case cmdGetKeySpeed: + emit getKeySpeed(); + break; + case cmdGetBreakMode: + emit getCWBreakMode(); + break; + case cmdStopCW: + emit stopCW(); + break; case cmdStartRegularPolling: runPeriodicCommands = true; break; @@ -3972,6 +4145,14 @@ void wfmain::issueCmd(cmds cmd, qint16 c) delayedCmdQue.push_back(cmddata); } +void wfmain::issueCmd(cmds cmd, QString s) +{ + commandtype cmddata; + cmddata.cmd = cmd; + cmddata.data = std::shared_ptr(new QString(s)); + delayedCmdQue.push_back(cmddata); +} + void wfmain::issueCmdUniquePriority(cmds cmd, bool b) { commandtype cmddata; @@ -4314,7 +4495,7 @@ void wfmain::initPeriodicCommands() } } -void wfmain::insertPeriodicCommand(cmds cmd, unsigned char priority) +void wfmain::insertPeriodicCommand(cmds cmd, unsigned char priority=100) { // TODO: meaningful priority // These commands get run at the fastest pace possible @@ -4352,7 +4533,7 @@ void wfmain::removePeriodicCommand(cmds cmd) } -void wfmain::insertSlowPeriodicCommand(cmds cmd, unsigned char priority) +void wfmain::insertSlowPeriodicCommand(cmds cmd, unsigned char priority=100) { // TODO: meaningful priority // These commands are run every 20 "ticks" of the primary radio command loop @@ -4579,7 +4760,7 @@ void wfmain::receiveSpectrumData(QByteArray spectrum, double startFreq, double e wfRow = wfimage.at(row); for(int col = 0; col < spectWidth; col++) { - colorMap->data()->setCell( col, row, wfRow.at(col)); + colorMap->data()->setCell( col, row, (unsigned char)wfRow.at(col)); } } if(updateRange) @@ -5023,6 +5204,7 @@ void wfmain::receiveMode(unsigned char mode, unsigned char filter) } currentModeIndex = mode; currentModeInfo.mk = (mode_kind)mode; + cw->handleCurrentModeUpdate((mode_kind)mode); currentModeInfo.filter = filter; switch (currentModeInfo.mk) { @@ -7338,7 +7520,7 @@ void wfmain::on_underlayAverageBuffer_toggled(bool checked) void wfmain::on_debugBtn_clicked() { qInfo(logSystem()) << "Debug button pressed."; - emit getRigID(); + cw->show(); } // ---------- color helper functions: ---------- // @@ -8114,6 +8296,7 @@ void wfmain::messageHandler(QtMsgType type, const QMessageLogContext& context, c logTextMutex.lock(); logStringBuffer.push_front(text); logTextMutex.unlock(); + } void wfmain::on_customEdgeBtn_clicked() @@ -8507,3 +8690,16 @@ void wfmain::on_autoSSBchk_clicked(bool checked) { prefs.automaticSidebandSwitching = checked; } + +void wfmain::on_cwButton_clicked() +{ + if(cw->isMinimized()) + { + cw->raise(); + cw->activateWindow(); + return; + } + cw->show(); + cw->raise(); + cw->activateWindow(); +} diff --git a/wfmain.h b/wfmain.h index 9148094..45048c6 100644 --- a/wfmain.h +++ b/wfmain.h @@ -35,6 +35,7 @@ #include "repeatersetup.h" #include "satellitesetup.h" #include "transceiveradjustments.h" +#include "cwsender.h" #include "udpserver.h" #include "qledlabel.h" #include "rigctld.h" @@ -158,7 +159,7 @@ signals: // PTT, ATU, ATT, Antenna, Preamp: void getPTT(); - void setPTT(bool pttOn); + void setPTT(bool pttOn); void getAttenuator(); void getPreamp(); void getAntenna(); @@ -169,6 +170,14 @@ signals: void setATU(bool atuEnabled); void getATUStatus(); + // CW Keying: + void sendCW(QString message); + void stopCW(); + void getKeySpeed(); + void setKeySpeed(unsigned char wpm); + void setCWBreakMode(unsigned char breakMode); + void getCWBreakMode(); + // Time and date: void setTime(timekind t); void setDate(datekind d); @@ -720,6 +729,8 @@ private slots: void on_autoSSBchk_clicked(bool checked); + void on_cwButton_clicked(); + private: Ui::wfmain *ui; void closeEvent(QCloseEvent *event); @@ -796,6 +807,17 @@ private: QShortcut *keyF; QShortcut *keyM; + QShortcut *keyH; + QShortcut *keyK; // alternate + + QShortcut *keyJ; // alternate - + QShortcut *keyL; + + QShortcut *keyShiftK; + QShortcut *keyShiftJ; + + QShortcut *keyControlK; + QShortcut *keyControlJ; + QShortcut *keyDebug; @@ -889,7 +911,7 @@ private: cmdGetVdMeter, cmdGetIdMeter, cmdGetSMeter, cmdGetCenterMeter, cmdGetPowerMeter, cmdGetSWRMeter, cmdGetALCMeter, cmdGetCompMeter, cmdGetTxRxMeter, cmdGetTone, cmdGetTSQL, cmdGetDTCS, cmdGetRptAccessMode, cmdGetPreamp, cmdGetAttenuator, cmdGetAntenna, - cmdGetBandStackReg, + cmdGetBandStackReg, cmdGetKeySpeed, cmdSetKeySpeed, cmdGetBreakMode, cmdSetBreakMode, cmdSendCW, cmdStopCW, cmdSetTime, cmdSetDate, cmdSetUTCOffset}; struct commandtype { @@ -913,6 +935,7 @@ private: void issueCmd(cmds cmd, bool b); void issueCmd(cmds cmd, quint16 c); void issueCmd(cmds cmd, qint16 c); + void issueCmd(cmds cmd, QString s); // These commands pop_front and remove similar commands: void issueCmdUniquePriority(cmds cmd, bool b); @@ -1045,10 +1068,12 @@ private: unsigned char usbGain=0; unsigned char lanGain=0; + // Widgets and Special Windows: calibrationWindow *cal; repeaterSetup *rpt; satelliteSetup *sat; transceiverAdjustments *trxadj; + cwSender *cw; controllerSetup* shut; aboutbox *abtBox; selectRadio *selRad; diff --git a/wfmain.ui b/wfmain.ui index 6578ae3..cb896a4 100644 --- a/wfmain.ui +++ b/wfmain.ui @@ -18,7 +18,7 @@ - 3 + 0 @@ -1041,6 +1041,16 @@ + + + + Press to bring up the CW Sender + + + CW + + + diff --git a/wfview.pro b/wfview.pro index fd51296..416674c 100644 --- a/wfview.pro +++ b/wfview.pro @@ -197,6 +197,7 @@ win32:INCLUDEPATH += ../qcustomplot INCLUDEPATH += resampler SOURCES += main.cpp\ + cwsender.cpp \ loggingwindow.cpp \ wfmain.cpp \ commhandler.cpp \ @@ -234,8 +235,10 @@ SOURCES += main.cpp\ HEADERS += wfmain.h \ colorprefs.h \ commhandler.h \ + cwsender.h \ loggingwindow.h \ prefs.h \ + printhex.h \ rigcommander.h \ freqmemory.h \ rigidentities.h \ @@ -278,6 +281,7 @@ HEADERS += wfmain.h \ FORMS += wfmain.ui \ calibrationwindow.ui \ + cwsender.ui \ loggingwindow.ui \ satellitesetup.ui \ selectradio.ui \ diff --git a/wfviewtypes.h b/wfviewtypes.h index 9997aa9..d94b1a9 100644 --- a/wfviewtypes.h +++ b/wfviewtypes.h @@ -59,6 +59,12 @@ enum mode_kind { modeDCR }; +enum breakIn_t { + brkinOff = 0x00, + brkinSemi = 0x01, + brkinFull = 0x02 +}; + struct freqt { quint64 Hz; double MHzDouble;