diff --git a/Configuration.cpp b/Configuration.cpp
index d11584ce6..27538f734 100644
--- a/Configuration.cpp
+++ b/Configuration.cpp
@@ -193,6 +193,7 @@
#include "models/FrequencyList.hpp"
#include "models/StationList.hpp"
#include "Network/NetworkServerLookup.hpp"
+#include "Network/FoxVerifier.hpp"
#include "widgets/MessageBox.hpp"
#include "validators/MaidenheadLocatorValidator.hpp"
#include "validators/CallsignValidator.hpp"
@@ -596,6 +597,8 @@ private:
Q_SLOT void on_rbARRL_Digi_clicked (bool);
Q_SLOT void on_cbSuperFox_clicked (bool);
Q_SLOT void on_cbContestName_clicked (bool);
+ Q_SLOT void on_cbOTP_clicked (bool);
+
void error_during_hamlib_download (QString const& reason);
void after_hamlib_downloaded();
void display_file_information();
@@ -608,6 +611,8 @@ private:
Q_SLOT void on_Field_Day_Exchange_textEdited (QString const&);
Q_SLOT void on_RTTY_Exchange_textEdited (QString const&);
Q_SLOT void on_FoxKey_textEdited (QString const&);
+ Q_SLOT void on_OTPUrl_textEdited (QString const&);
+ Q_SLOT void on_OTPSeed_textEdited (QString const&);
Q_SLOT void on_Contest_Name_textEdited (QString const&);
// typenames used as arguments must match registered type names :(
@@ -708,6 +713,11 @@ private:
QString hamlib_backed_up_;
QString FoxKey_;
+ QString OTPUrl_;
+ QString OTPSeed_;
+ bool OTPEnabled_;
+ qint32 OTPinterval_;
+
qint32 id_interval_;
qint32 ntrials_;
qint32 aggressive_;
@@ -978,6 +988,26 @@ void Configuration::invalidate_audio_output_device (QString /* error */)
m_->audio_output_device_ = QAudioDeviceInfo {};
}
+// OTP seed can be empty, in which case it is not used, or a valid 16 character base32 string.
+bool Configuration::validate_otp_seed(QString seed)
+{
+ if (seed.isEmpty())
+ {
+ return true;
+ }
+ if (seed.size() != 16)
+ {
+ return false;
+ }
+ for (QChar c: seed)
+ {
+ if (!QString(BASE32_CHARSET).contains(c))
+ {
+ return false;
+ }
+ }
+ return true;
+}
bool Configuration::valid_n1mm_info () const
{
// do very rudimentary checking on the n1mm server name and port number.
@@ -1084,6 +1114,26 @@ void Configuration::toggle_SF()
m_->write_settings();
}
+QString Configuration::OTPSeed() const
+{
+ return m_->OTPSeed_;
+}
+
+QString Configuration::OTPUrl() const
+{
+ return m_->OTPUrl_;
+}
+
+u_int Configuration::OTPinterval() const
+{
+ return m_->OTPinterval_;
+}
+
+bool Configuration::OTPEnabled() const
+{
+ return m_->OTPSeed_.size() == 16 && m_->OTPEnabled_;
+}
+
namespace
{
#if defined (Q_OS_MAC)
@@ -1254,6 +1304,8 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
ui_->add_macro_line_edit->setValidator (new QRegularExpressionValidator {message_alphabet, this});
ui_->Field_Day_Exchange->setValidator (new QRegularExpressionValidator {field_day_exchange_re, this});
ui_->RTTY_Exchange->setValidator (new QRegularExpressionValidator {RTTY_roundup_exchange_re, this});
+ QRegularExpression b32(QString("(^[") + QString(BASE32_CHARSET)+QString(BASE32_CHARSET).toLower() + QString("]{16}$)|(^$)"));
+ ui_->OTPSeed->setValidator(new QRegularExpressionValidator(b32, this));
//
// assign ids to radio buttons
@@ -1619,6 +1671,16 @@ void Configuration::impl::read_settings ()
ui_->Contest_Name->setText(Contest_Name_);
hamlib_backed_up_ = settings_->value ("HamlibBackedUp",QString {}).toString ();
+ OTPinterval_ = settings_->value ("OTPinterval", 3).toUInt ();
+ OTPUrl_ = settings_->value ("OTPUrl", FoxVerifier::default_url()).toString ();
+ OTPSeed_ = settings_->value ("OTPSeed", QString {}).toString ();
+ OTPEnabled_ = settings_->value ("OTPEnabled", false).toBool ();
+
+ ui_->sbOTPinterval->setValue(OTPinterval_);
+ ui_->OTPUrl->setText(OTPUrl_);
+ ui_->OTPSeed->setText(OTPSeed_);
+ ui_->cbOTP->setChecked(OTPEnabled_);
+
if (next_font_.fromString (settings_->value ("Font", QGuiApplication::font ().toString ()).toString ())
&& next_font_ != font_)
{
@@ -1945,6 +2007,10 @@ void Configuration::impl::write_settings ()
settings_->setValue ("AutoGrid", use_dynamic_grid_);
settings_->setValue ("highlight_DXcall", highlight_DXcall_);
settings_->setValue ("highlight_DXgrid", highlight_DXgrid_);
+ settings_->setValue ("OTPinterval", OTPinterval_);
+ settings_->setValue ("OTPUrl", OTPUrl_);
+ settings_->setValue ("OTPSeed", OTPSeed_);
+ settings_->setValue ("OTPEnabled", OTPEnabled_);
settings_->sync ();
}
@@ -2362,6 +2428,10 @@ void Configuration::impl::accept ()
pwrBandTxMemory_ = ui_->checkBoxPwrBandTxMemory->isChecked ();
pwrBandTuneMemory_ = ui_->checkBoxPwrBandTuneMemory->isChecked ();
opCall_=ui_->opCallEntry->text();
+ OTPinterval_=ui_->sbOTPinterval->value();
+ OTPSeed_=ui_->OTPSeed->text();
+ OTPUrl_=ui_->OTPUrl->text();
+ OTPEnabled_=ui_->cbOTP->isChecked();
auto new_server = ui_->udp_server_line_edit->text ().trimmed ();
auto new_interfaces = get_selected_network_interfaces (ui_->udp_interfaces_combo_box);
@@ -3211,6 +3281,11 @@ void Configuration::impl::on_cbContestName_clicked (bool)
check_visibility ();
}
+void Configuration::impl::on_cbOTP_clicked(bool)
+{
+ check_visibility();
+}
+
void Configuration::impl::check_visibility ()
{
if (ui_->rbFox->isChecked() and ui_->cbSuperFox->isChecked() and ui_->gbSpecialOpActivity->isChecked()) {
@@ -3253,6 +3328,53 @@ void Configuration::impl::check_visibility ()
} else {
ui_->cbContestName->setEnabled (false);
}
+ if (!ui_->cbOTP->isChecked() or !ui_->gbSpecialOpActivity->isChecked())
+ {
+ ui_->OTPSeed->setEnabled(false);
+ ui_->OTPUrl->setEnabled(false);
+ ui_->sbOTPinterval->setEnabled(false);
+ ui_->lblOTPSeed->setEnabled(false);
+ ui_->lblOTPUrl->setEnabled(false);
+ ui_->lblOTPEvery->setEnabled(false);
+ } else
+ {
+ if (ui_->rbHound->isChecked())
+ {
+ if (ui_->OTPUrl->text().isEmpty())
+ {
+ ui_->OTPUrl->setText(FoxVerifier::default_url());
+ }
+ ui_->OTPUrl->setEnabled(true);
+ ui_->lblOTPUrl->setEnabled(true);
+ } else
+ {
+ ui_->OTPUrl->setEnabled(false);
+ ui_->lblOTPUrl->setEnabled(false);
+ }
+ if (ui_->rbFox->isChecked())
+ {
+ ui_->sbOTPinterval->setEnabled(true);
+ ui_->OTPSeed->setEnabled(true);
+ ui_->lblOTPSeed->setEnabled(true);
+ ui_->lblOTPEvery->setEnabled(true);
+ } else
+ {
+ ui_->OTPSeed->setEnabled(false);
+ ui_->lblOTPSeed->setEnabled(false);
+ ui_->lblOTPEvery->setEnabled(false);
+ ui_->sbOTPinterval->setEnabled(false);
+ }
+ }
+}
+void Configuration::impl::on_OTPUrl_textEdited (QString const& url){
+ auto text = url;
+ if (text.size() == 0)
+ {
+ ui_->OTPUrl->setText(FoxVerifier::default_url());
+ }
+}
+void Configuration::impl::on_OTPSeed_textEdited (QString const& url){
+ ui_->OTPSeed->setText(url.toUpper());
}
void Configuration::impl::on_Field_Day_Exchange_textEdited (QString const& exchange)
diff --git a/Configuration.hpp b/Configuration.hpp
index c965d5e12..87281b310 100644
--- a/Configuration.hpp
+++ b/Configuration.hpp
@@ -9,6 +9,7 @@
#include "models/IARURegions.hpp"
#include "Audio/AudioDevice.hpp"
#include "Transceiver/Transceiver.hpp"
+#include "foxotpcode.h"
#include "pimpl_h.hpp"
@@ -193,7 +194,11 @@ public:
bool highlight_DXcall () const;
bool highlight_DXgrid () const;
bool Individual_Contest_Name() const;
-
+ bool validate_otp_seed(QString);
+ QString OTPSeed() const;
+ QString OTPUrl() const;
+ bool OTPEnabled() const;
+ u_int OTPinterval() const;
// 0 1 2 3 4 5 6 7 8 9
enum class SpecialOperatingActivity {NONE, NA_VHF, EU_VHF, FIELD_DAY, RTTY, WW_DIGI, FOX, HOUND, ARRL_DIGI, Q65_PILEUP};
SpecialOperatingActivity special_op_id () const;
diff --git a/Configuration.ui b/Configuration.ui
index 307d843ea..dfb598288 100644
--- a/Configuration.ui
+++ b/Configuration.ui
@@ -2907,30 +2907,8 @@ Right click for insert and delete options.
false
-
- -
-
-
-
- 0
- 18
-
-
-
- <html><head/><body><p>World-Wide Digi-mode contest</p><p><br/></p></body></html>
-
-
- WW Digital Contest
-
-
- WW Digi Contest
-
-
- special_op_activity_button_group
-
-
-
- -
+
+
-
-
@@ -2999,7 +2977,293 @@ Right click for insert and delete options.
- -
+
-
+
+
+ <html><head/><body><p>URL used to verify OTP codes.</p></body></html>
+
+
+
+
+
+
+ -
+
+
+ <html><head/><body><p>Click to enable OTP method of Fox verification. Requires internet.</p></body></html>
+
+
+
+
+
+ OTP
+
+
+
+ -
+
+
+ <html><head/><body><p>FT8 DXpedition mode: Fox (DXpedition) operator.</p></body></html>
+
+
+ Fox
+
+
+ Fox
+
+
+ false
+
+
+ special_op_activity_button_group
+
+
+
+ -
+
+
-
+
+
+ OTP every
+
+
+
+ -
+
+
+ <html><head/><body><p>How many cycles between sends of the OTP.</p></body></html>
+
+
+
+
+
+ 1
+
+
+ 20
+
+
+ 3
+
+
+
+ -
+
+
+ OTP Key
+
+
+
+ -
+
+
+ <html><head/><body><p>Fox's key to generate OTP Codes.</p></body></html>
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 18
+
+
+
+ <html><head/><body><p>Exchange 4-character locator instead of signal report. Provides q3-level sensitivities for the DX operator. Especially useful for 6m EME DXpeditions.</p></body></html>
+
+
+ Q65 Pileup
+
+
+ special_op_activity_button_group
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 18
+
+
+
+ <html><head/><body><p>World-Wide Digi-mode contest</p><p><br/></p></body></html>
+
+
+ WW Digital Contest
+
+
+ WW Digi Contest
+
+
+ special_op_activity_button_group
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ <html><head/><body><p>North American VHF/UHF/Microwave contests and others in which a 4-character grid locator is the required exchange.</p></body></html>
+
+
+ NA VHF Contest
+
+
+ NA VHF
+
+
+ special_op_activity_button_group
+
+
+
+ -
+
+
-
+
+
+ <html><head/><body><p>Call CQ with an individual contest name instead of TEST, RU, or WW. </p></body></html>
+
+
+ CQ with individual contest name
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
-
+
+
+ Contest name:
+
+
+
+ -
+
+
+
+ 70
+ 16777215
+
+
+
+
+
+
+ 4
+
+
+ 0
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ <html><head/><body><p>European VHF+ contests requiring a signal report, serial number, and 6-character locator.</p></body></html>
+
+
+ EU VHF Contest
+
+
+ EU VHF Contest
+
+
+ special_op_activity_button_group
+
+
+
+ -
+
+
+ OTP URL
+
+
+
+ -
+
+
+ <html><head/><body><p>FT8 DXpedition mode: Hound operator calling the DX.</p></body></html>
+
+
+ Hound
+
+
+ Hound
+
+
+ true
+
+
+ special_op_activity_button_group
+
+
+
+ -
+
+
+ <html><head/><body><p>ARRL International Digital Contest</p></body></html>
+
+
+ ARRL Digi Contest
+
+
+ special_op_activity_button_group
+
+
+
+ -
-
@@ -3064,124 +3328,7 @@ Right click for insert and delete options.
- -
-
-
-
-
-
- <html><head/><body><p>Call CQ with an individual contest name instead of TEST, RU, or WW. </p></body></html>
-
-
- CQ with individual contest name
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
-
-
-
- Contest name:
-
-
-
- -
-
-
-
- 70
- 16777215
-
-
-
-
-
-
- 4
-
-
- 0
-
-
- Qt::AlignCenter
-
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- <html><head/><body><p>North American VHF/UHF/Microwave contests and others in which a 4-character grid locator is the required exchange.</p></body></html>
-
-
- NA VHF Contest
-
-
- NA VHF
-
-
- special_op_activity_button_group
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- <html><head/><body><p>European VHF+ contests requiring a signal report, serial number, and 6-character locator.</p></body></html>
-
-
- EU VHF Contest
-
-
- EU VHF Contest
-
-
- special_op_activity_button_group
-
-
-
- -
-
-
- <html><head/><body><p>ARRL International Digital Contest</p></body></html>
-
-
- ARRL Digi Contest
-
-
- special_op_activity_button_group
-
-
-
- -
+
-
-
@@ -3250,76 +3397,6 @@ Right click for insert and delete options.
- -
-
-
-
- 0
- 18
-
-
-
- <html><head/><body><p>Exchange 4-character locator instead of signal report. Provides q3-level sensitivities for the DX operator. Especially useful for 6m EME DXpeditions.</p></body></html>
-
-
- Q65 Pileup
-
-
- special_op_activity_button_group
-
-
-
- -
-
-
- <html><head/><body><p>FT8 DXpedition mode: Hound operator calling the DX.</p></body></html>
-
-
- Hound
-
-
- Hound
-
-
- true
-
-
- special_op_activity_button_group
-
-
-
- -
-
-
- <html><head/><body><p>FT8 DXpedition mode: Fox (DXpedition) operator.</p></body></html>
-
-
- Fox
-
-
- Fox
-
-
- false
-
-
- special_op_activity_button_group
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
@@ -3558,13 +3635,13 @@ Right click for insert and delete options.
-
-
-
-
-
+
+
+
+
+
diff --git a/Network/FoxVerifier.cpp b/Network/FoxVerifier.cpp
index 1c7683469..87d0715e8 100644
--- a/Network/FoxVerifier.cpp
+++ b/Network/FoxVerifier.cpp
@@ -51,6 +51,7 @@ bool FoxVerifier::finished() {
return finished_;
}
+
void FoxVerifier::errorOccurred(QNetworkReply::NetworkError code)
{
int status = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
@@ -109,3 +110,7 @@ QString FoxVerifier::formatDecodeMessage(QDateTime ts, QString callsign, QString
else
return QString{};
}
+
+QString FoxVerifier::default_url() {
+ return QString(FOXVERIFIER_DEFAULT_BASE_URL);
+}
\ No newline at end of file
diff --git a/Network/FoxVerifier.hpp b/Network/FoxVerifier.hpp
index 5a6a93702..920fbf0bf 100644
--- a/Network/FoxVerifier.hpp
+++ b/Network/FoxVerifier.hpp
@@ -9,7 +9,7 @@
#include
#define FOXVERIFIER_DEFAULT_TIMEOUT_MSEC 5000
-#define FOXVERIFIER_DEFAULT_BASE_URL "https://www.9dx.ccm"
+#define FOXVERIFIER_DEFAULT_BASE_URL "https://www.9dx.cc"
class FoxVerifier : public QObject {
Q_OBJECT
@@ -22,6 +22,7 @@ public:
QString return_value;
bool finished();
static QString formatDecodeMessage(QDateTime ts, QString callsign, QString const& verify_message);
+ static QString default_url();
private:
QNetworkAccessManager* manager_;
@@ -57,4 +58,6 @@ signals:
void verifyError(int status, QDateTime ts, QString callsign, QString code, QString const& response);
};
+
+
#endif //WSJTX2_FOXVERIFIER_HPP
diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp
index 36254fdd4..cd1503011 100644
--- a/widgets/mainwindow.cpp
+++ b/widgets/mainwindow.cpp
@@ -4306,12 +4306,29 @@ void MainWindow::readFromStdout() //readFromStdout
ARRL_Digi_Update(decodedtext1);
}
- if (ui->labDXped->text() == "Super Hound" && (decodedtext0.mid(24, 8) == "$VERIFY$")) {
+ if ((SpecOp::HOUND == m_specOp) &&
+ ((m_config.superFox() && (decodedtext0.mid(24, 8) == "$VERIFY$")) || // $VERIFY$ K8R 920749
+ (decodedtext0.mid(24, 8).contains(QRegularExpression{"^[A-Z0-9]{2,5}\\.V[0-9]{6}$"})))) // K8R.V920749
+ {
+ // two cases:
+ // QString test_return = QString{"203630 -12 0.1 775 ~ K8R.V920749"};
// $VERIFY$ foxcall otp
// QString test_return = QString{"203630 -12 0.1 775 ~ $VERIFY$ K8R 920749"};
QStringList lineparts;
+ QStringList otp_parts;
+ QString callsign, otp;
lineparts = decodedtext0.string().split(' ', SkipEmptyParts);
-
+ if (lineparts.length() <= 6) {
+ // split K8R.V920749 into K8R and 920749
+ otp_parts = lineparts[5].split('.', SkipEmptyParts);
+ callsign = otp_parts[0];
+ otp = otp_parts[1].mid(1); // remove the V
+ } else
+ {
+ // split $VERIFY$ K8R 920749 into K8R and 920749
+ callsign = lineparts[6];
+ otp = lineparts[7];
+ }
QDateTime verifyDateTime;
if (m_diskData) {
verifyDateTime = m_UTCdiskDateTime; // get the date set from reading the wav file
@@ -4322,9 +4339,9 @@ void MainWindow::readFromStdout() //readFromStdout
FoxVerifier *fv = new FoxVerifier(MainWindow::userAgent(),
&m_network_manager,
FOXVERIFIER_DEFAULT_BASE_URL,
- lineparts[6], // foxcall
+ callsign, // foxcall
verifyDateTime,
- lineparts[7]); // otp
+ otp); // otp
connect(fv, &FoxVerifier::verifyComplete, this, &MainWindow::handleVerifyMsg);
m_verifications << fv;
} else {
@@ -10185,6 +10202,33 @@ void MainWindow::on_comboBoxHoundSort_activated(int index)
if(index!=-99) houndCallers(); //Silence compiler warning
}
+QString MainWindow::foxOTPcode()
+{
+ QString code;
+ if (!m_config.OTPSeed().isEmpty())
+ {
+ char output[7];
+ QDateTime dateTime = dateTime.currentDateTime();
+ QByteArray ba = m_config.OTPSeed().toLocal8Bit();
+ char *c_str = ba.data();
+ int return_length;
+ if (6 == (return_length = create_totp(c_str, output, dateTime.toTime_t(), 30, 0)))
+ {
+ code = QString(output);
+ } else
+ {
+ code = "000000";
+ LOG_INFO(QString("foxOTPcode: Incorrect return length %1").arg(return_length));
+ }
+ } else
+ {
+ code = "000000";
+ showStatusMessage(tr("TOTP: No seed entered in fox configuration to generate verification code."));
+ LOG_INFO(QString("foxOTPcode: No seed entered in fox configuration to generate verification code."));
+ }
+ return code;
+}
+
//------------------------------------------------------------------------------
QString MainWindow::sortHoundCalls(QString t, int isort, int max_dB)
{
@@ -10505,6 +10549,7 @@ void MainWindow::foxRxSequencer(QString msg, QString houndCall, QString rptRcvd)
}
}
}
+
void MainWindow::updateFoxQSOsInProgressDisplay()
{
@@ -10535,8 +10580,12 @@ void MainWindow::foxTxSequencer()
QString hc,hc1,hc2; //Hound calls
QString t,rpt;
qint32 islot=0;
+ qint32 ncalls_sent=0;
qint32 n1,n2,n3;
int nMaxRemainingSlots=0;
+ static u_int m_tFoxTxSinceOTP=99;
+
+ m_tFoxTxSinceOTP++;
m_tFoxTx++; //Increment Fox Tx cycle counter
//Is it time for a stand-alone CQ?
@@ -10552,15 +10601,27 @@ void MainWindow::foxTxSequencer()
foxGenWaveform(islot-1,fm);
goto Transmit;
}
+
+ // Send OTP message maybe for regular fox mode
+ if (!m_config.superFox() && m_config.OTPEnabled() && (islot < m_Nslots) && (m_tFoxTxSinceOTP >= m_config.OTPinterval()))
+ {
+ // truncated callsign + OTP code (to be under 13 character limit of free text)
+ QString trunc_call=m_config.my_callsign().left(5).split("/").at(0);
+ fm = trunc_call + ".V" + foxOTPcode(); // N5J-> N5J.V123456, W1AW/7 -> W1AW.V123456, 4U1IARU -> 4U1IA.V123456
+ m_tFoxTxSinceOTP = 0; //Remember when we sent a Tx5
+ islot++;
+ foxGenWaveform(islot - 1, fm);
+ }
+
//Compile list1: up to NSLots Hound calls to be sent RR73
for(QString hc: m_foxQSO.keys()) { //Check all Hound calls: First priority
if(m_foxQSO[hc].tFoxRrpt<0) continue;
if(m_foxQSO[hc].tFoxRrpt - m_foxQSO[hc].tFoxTxRR73 > 3) {
//Has been a long time since we sent RR73
+ if(list1.size()>=(m_Nslots - islot)) goto list1Done;
list1 << hc; //Add to list1
m_foxQSO[hc].tFoxTxRR73 = m_tFoxTx; //Time RR73 is sent
m_foxQSO[hc].nRR73++; //Increment RR73 counter
- if(list1.size()==m_Nslots) goto list1Done;
}
}
@@ -10568,10 +10629,10 @@ void MainWindow::foxTxSequencer()
if(m_foxQSO[hc].tFoxRrpt<0) continue;
if(m_foxQSO[hc].tFoxTxRR73 < 0) {
//Have not yet sent RR73
+ if(list1.size()>=(m_Nslots - islot)) goto list1Done;
list1 << hc; //Add to list1
m_foxQSO[hc].tFoxTxRR73 = m_tFoxTx; //Time RR73 is sent
m_foxQSO[hc].nRR73++; //Increment RR73 counter
- if(list1.size()==m_Nslots) goto list1Done;
}
}
@@ -10579,10 +10640,10 @@ void MainWindow::foxTxSequencer()
if(m_foxQSO[hc].tFoxRrpt<0) continue;
if(m_foxQSO[hc].tFoxTxRR73 <= m_foxQSO[hc].tFoxRrpt) {
//We received R+rpt more recently than we sent RR73
+ if(list1.size()>=(m_Nslots - islot)) goto list1Done;
list1 << hc; //Add to list1
m_foxQSO[hc].tFoxTxRR73 = m_tFoxTx; //Time RR73 is sent
m_foxQSO[hc].nRR73++; //Increment RR73 counter
- if(list1.size()==m_Nslots) goto list1Done;
}
}
@@ -10595,13 +10656,18 @@ list1Done:
hc=m_foxQSOinProgress.at(i);
if((m_foxQSO[hc].tFoxRrpt < 0) and (m_foxQSO[hc].ncall < m_maxStrikes)) {
//Sent him a report and have not received R+rpt: call him again
+ if(list2.size()>=(nMaxRemainingSlots - islot)) goto list2Done;
list2 << hc; //Add to list2
- if(list2.size()==nMaxRemainingSlots) goto list2Done;
+ if(list2.size() == nMaxRemainingSlots) goto list2Done;
}
}
while(!m_houndQueue.isEmpty()) {
//Start QSO with a new Hound
+ if (list2.size() == (nMaxRemainingSlots - islot))
+ {
+ break;
+ }
t=m_houndQueue.dequeue(); //Fetch new hound from queue
int i0=t.indexOf(" ");
hc=t.mid(0,i0); //hound call
@@ -10616,11 +10682,6 @@ list1Done:
m_foxQSO[hc].tFoxRrpt = -1; //Have not received R+rpt
m_foxQSO[hc].tFoxTxRR73 = -1; //Have not sent RR73
refreshHoundQueueDisplay();
-
- if(list2.size()==nMaxRemainingSlots) {
- break;
- }
-
}
list2Done:
@@ -10680,12 +10741,13 @@ list2Done:
fm = Radio::base_callsign(hc2) + " " + m_baseCall + " " + m_foxQSO[hc2].sent; //Standard FT8 message
}
islot++;
+ ncalls_sent++;
foxGenWaveform(islot-1,fm); //Generate tx waveform
}
if(islot < m_Nslots) {
//At least one slot is still open
- if(islot==0 or ((m_tFoxTx-m_tFoxTx0>=4) and ui->cbMoreCQs->isChecked())) {
+ if(ncalls_sent==0 or ((m_tFoxTx-m_tFoxTx0>=4) and ui->cbMoreCQs->isChecked())) {
//Roughly every 4th Tx sequence, put a CQ message in an otherwise empty slot
fm=ui->comboBoxCQ->currentText() + " " + m_config.my_callsign();
if(!fm.contains("/")) {
@@ -11321,28 +11383,24 @@ void MainWindow::sfox_tx() {
qint32 otp_key = 0;
args.append(m_config.my_callsign());
#ifdef FOX_OTP
- if (m_config.FoxKey().startsWith("OTP:", Qt::CaseInsensitive))
+ if (m_config.OTPEnabled())
{
- LOG_INFO("OTP: Generating OTP key");
- if (m_config.FoxKey().length() > 19) {
- QString foxCodeSeed = m_config.FoxKey().mid(4);
- char output[7];
- QDateTime dateTime = dateTime.currentDateTime();
- QByteArray ba = foxCodeSeed.toLocal8Bit();
- char *c_str = ba.data();
- int return_length;
- if (6 == (return_length = create_totp(c_str, output, dateTime.toTime_t(), 30, 0)))
+ LOG_INFO("TOTP: Generating OTP key");
+ if (m_config.OTPSeed().length() == 16) {
+ QString output=foxOTPcode();
+ if (6 == output.length())
{
otp_key = QString(output).toInt();
- LOG_INFO(QString("TOTP: %1 [%2]").arg(output).arg(otp_key).toStdString());
+ LOG_INFO(QString("TOTP SF: %1 [%2]").arg(output).arg(otp_key).toStdString());
} else
{
- LOG_INFO(QString("TOTP: Incorrect return length %1").arg(return_length));
+ otp_key = 0;
+ LOG_INFO(QString("TOTP SF: Incorrect length"));
}
} else
{
- showStatusMessage (tr ("TOTP: seed not long enough."));
- LOG_INFO(QString("TOTP: seed not long enough"));
+ showStatusMessage (tr ("TOTP SF: seed not long enough."));
+ LOG_INFO(QString("TOTP SF: seed not long enough"));
}
}
args.append(QString("OTP:%1").arg(otp_key));
diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h
index 8c1c35b38..1e078721f 100644
--- a/widgets/mainwindow.h
+++ b/widgets/mainwindow.h
@@ -898,6 +898,7 @@ private:
QString userAgent();
void handleVerifyMsg(int status, QDateTime ts, QString callsign, QString code, QString const &response);
void writeFoxTxMsgs();
+ QString foxOTPcode();
};
extern int killbyname(const char* progName);