diff --git a/CMakeLists.txt b/CMakeLists.txt index e8993f6dd..c00ec9d61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ if (APPLE) set (CMAKE_OSX_DEPLOYMENT_TARGET 10.12 CACHE STRING "Earliest version of macOS supported -Earliest version we can support with Qt 5.12, C++11 & libc++ is 10.13. +Earliest version we can support with Qt 5.12, C++11 & libc++ is 10.12. Do not override this if you intend to build an official deployable installer.") endif (APPLE) @@ -29,7 +29,7 @@ if (POLICY CMP0043) endif () if (POLICY CMP0048) - cmake_policy (SET CMP0048 NEW) # clear PROJECT_Version_* vriables if not set in project() command + cmake_policy (SET CMP0048 NEW) # clear PROJECT_Version_* variables if not set in project() command endif () if (POLICY CMP0063) @@ -40,6 +40,10 @@ if (POLICY CMP0071) cmake_policy (SET CMP0071 NEW) # run automoc and autouic on generated sources endif () +if (POLICY CMP0075) + cmake_policy (SET CMP0075 NEW) # honour CMAKE_REQUIRED_LIBRARIES in config checks +endif () + project (wsjtx VERSION 2.4.0.0 DESCRIPTION "WSJT-X: Digital Modes for Weak Signal Communications in Amateur Radio" @@ -813,6 +817,7 @@ endif (APPLE) # # find some useful tools # +include (CheckTypeSize) include (CheckCSourceCompiles) include (CheckSymbolExists) include (generate_version_info) @@ -871,7 +876,8 @@ message (STATUS "hamlib_LIBRARY_DIRS: ${hamlib_LIBRARY_DIRS}") set (CMAKE_REQUIRED_INCLUDES "${hamlib_INCLUDE_DIRS}") set (CMAKE_REQUIRED_LIBRARIES "${hamlib_LIBRARIES}") -check_symbol_exists (CACHE_ALL "hamlib/rig.h" HAVE_HAMLIB_OLD_CACHING) +set (CMAKE_EXTRA_INCLUDE_FILES "hamlib/rig.h") +check_type_size (CACHE_ALL HAMLIB_OLD_CACHING) check_symbol_exists (rig_set_cache_timeout_ms "hamlib/rig.h" HAVE_HAMLIB_CACHING) diff --git a/Configuration.cpp b/Configuration.cpp index 0466fd662..5df45c543 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -1070,18 +1070,13 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network // // validation + // ui_->callsign_line_edit->setValidator (new CallsignValidator {this}); ui_->grid_line_edit->setValidator (new MaidenheadLocatorValidator {this}); 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}); - ui_->udp_server_port_spin_box->setMinimum (1); - ui_->udp_server_port_spin_box->setMaximum (std::numeric_limits::max ()); - - ui_->n1mm_server_port_spin_box->setMinimum (1); - ui_->n1mm_server_port_spin_box->setMaximum (std::numeric_limits::max ()); - // // assign ids to radio buttons // diff --git a/Configuration.ui b/Configuration.ui index d22032d53..fbd53c422 100644 --- a/Configuration.ui +++ b/Configuration.ui @@ -1893,9 +1893,6 @@ and DX Grid fields when a 73 or free text message is sent. <html><head/><body><p>Enter the service port number of the UDP server that WSJT-X should send updates to. If this is zero no updates will be broadcast.</p></body></html> - - 0 - 65534 @@ -1988,6 +1985,9 @@ and DX Grid fields when a 73 or free text message is sent. <html><head/><body><p>Enter the port number that WSJT-X should use for UDP broadcasts of ADIF log information. For N1MM Logger+, this value should be 2333. If this is zero, no updates will be broadcast.</p></body></html> + + 65534 + @@ -3194,11 +3194,11 @@ Right click for insert and delete options. - - + - + + diff --git a/Network/wsprnet.cpp b/Network/wsprnet.cpp index 669ab1554..ba1e36c18 100644 --- a/Network/wsprnet.cpp +++ b/Network/wsprnet.cpp @@ -86,26 +86,29 @@ void WSPRNet::upload (QString const& call, QString const& grid, QString const& r m_file = fileName; // Open the wsprd.out file - QFile wsprdOutFile (fileName); - if (!wsprdOutFile.open (QIODevice::ReadOnly | QIODevice::Text) || !wsprdOutFile.size ()) + if (m_uploadType != 3) { - spot_queue_.enqueue (urlEncodeNoSpot ()); - m_uploadType = 1; - } - else - { - // Read the contents - while (!wsprdOutFile.atEnd()) + QFile wsprdOutFile (fileName); + if (!wsprdOutFile.open (QIODevice::ReadOnly | QIODevice::Text) || !wsprdOutFile.size ()) { - SpotQueue::value_type query; - if (decodeLine (wsprdOutFile.readLine(), query)) + spot_queue_.enqueue (urlEncodeNoSpot ()); + m_uploadType = 1; + } + else + { + // Read the contents + while (!wsprdOutFile.atEnd()) { - // Prevent reporting data ouside of the current frequency band - float f = fabs (m_rfreq.toFloat() - query.queryItemValue ("tqrg", QUrl::FullyDecoded).toFloat()); - if (f < 0.0002) + SpotQueue::value_type query; + if (decodeLine (wsprdOutFile.readLine(), query)) { - spot_queue_.enqueue(urlEncodeSpot (query)); - m_uploadType = 2; + // Prevent reporting data ouside of the current frequency band + float f = fabs (m_rfreq.toFloat() - query.queryItemValue ("tqrg", QUrl::FullyDecoded).toFloat()); + if (f < 0.01) // MHz + { + spot_queue_.enqueue(urlEncodeSpot (query)); + m_uploadType = 2; + } } } } @@ -133,10 +136,8 @@ void WSPRNet::post (QString const& call, QString const& grid, QString const& rfr if (!spot_queue_.size ()) { spot_queue_.enqueue (urlEncodeNoSpot ()); - m_uploadType = 1; + m_uploadType = 3; } - spots_to_send_ = spot_queue_.size (); - upload_timer_.start (200); } else { @@ -144,9 +145,11 @@ void WSPRNet::post (QString const& call, QString const& grid, QString const& rfr if (match.hasMatch ()) { SpotQueue::value_type query; - // Prevent reporting data ouside of the current frequency band + // Prevent reporting data ouside of the current frequency + // band - removed by G4WJS to accommodate FST4W spots + // outside of WSPR segments auto tqrg = match.captured ("freq").toInt (); - if (tqrg >= 1400 && tqrg <= 1600) + // if (tqrg >= 1400 && tqrg <= 1600) { query.addQueryItem ("function", "wspr"); // use time as at 3/4 of T/R period before current to diff --git a/UDPExamples/ClientWidget.cpp b/UDPExamples/ClientWidget.cpp index 984b64ad9..b2ee03609 100644 --- a/UDPExamples/ClientWidget.cpp +++ b/UDPExamples/ClientWidget.cpp @@ -184,10 +184,10 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod message_line_edit_->setValidator (&message_validator); grid_line_edit_->setValidator (&locator_validator); dx_grid_line_edit_->setValidator (&locator_validator); - tr_period_spin_box_->setRange (5, 30); + tr_period_spin_box_->setRange (5, 1800); tr_period_spin_box_->setSuffix (" s"); rx_df_spin_box_->setRange (200, 5000); - frequency_tolerance_spin_box_->setRange (10, 1000); + frequency_tolerance_spin_box_->setRange (1, 1000); frequency_tolerance_spin_box_->setPrefix ("\u00b1"); frequency_tolerance_spin_box_->setSuffix (" Hz"); diff --git a/lib/fst4_decode.f90 b/lib/fst4_decode.f90 index e97d8f096..ba49fb54d 100644 --- a/lib/fst4_decode.f90 +++ b/lib/fst4_decode.f90 @@ -227,7 +227,7 @@ contains ndropmax=1 single_decode=iand(nexp_decode,32).ne.0 npct=0 - nb=nexp_decode/256 - 2 + nb=nexp_decode/256 - 3 if(nb.ge.0) npct=nb inb1=20 inb2=5 @@ -235,6 +235,8 @@ contains inb2=5 !Try NB = 0, 5, 10, 15, 20% else if(nb.eq.-2) then inb2=2 !Try NB = 0, 2, 4,... 20% + else if(nb.eq.-3) then + inb2=1 !Try NB = 0, 1, 2,... 20% else inb1=0 !Fixed NB value, 0 to 25% ipct(0)=npct @@ -900,6 +902,6 @@ contains enddo return - end subroutine dopspread + end subroutine dopspread end module fst4_decode diff --git a/widgets/mainwindow.cpp b/widgets/mainwindow.cpp index 8d1f71306..7775cd1d1 100644 --- a/widgets/mainwindow.cpp +++ b/widgets/mainwindow.cpp @@ -3130,7 +3130,7 @@ void MainWindow::decode() //decode() dec_data.params.nexp_decode = static_cast (m_config.special_op_id()); if(m_config.single_decode()) dec_data.params.nexp_decode += 32; if(m_config.enable_VHF_features()) dec_data.params.nexp_decode += 64; - if(m_mode.startsWith("FST4")) dec_data.params.nexp_decode += 256*(ui->sbNB->value()+2); + if(m_mode.startsWith("FST4")) dec_data.params.nexp_decode += 256*(ui->sbNB->value()+3); ::memcpy(dec_data.params.datetime, m_dateTime.toLatin1()+" ", sizeof dec_data.params.datetime); ::memcpy(dec_data.params.mycall, (m_config.my_callsign()+" ").toLatin1(), sizeof dec_data.params.mycall); @@ -3258,7 +3258,14 @@ void MainWindow::decodeDone () if(m_mode=="QRA64") m_wideGraph->drawRed(0,0); if ("FST4W" == m_mode) { - uploadWSPRSpots (true); // DE station info and trigger posts + if (m_uploadWSPRSpots + && m_config.is_transceiver_online ()) { // need working rig control +#if QT_VERSION >= QT_VERSION_CHECK (5, 15, 0) + uploadTimer.start(QRandomGenerator::global ()->bounded (0, 20000)); // Upload delay +#else + uploadTimer.start(20000 * qrand()/((double)RAND_MAX + 1.0)); // Upload delay +#endif + } } auto tnow = QDateTime::currentDateTimeUtc (); double tdone = fmod(double(tnow.time().second()),m_TRperiod); @@ -3305,6 +3312,12 @@ void MainWindow::readFromStdout() //readFromStdout fSpread = text.toFloat (&haveFSpread); line_read = line_read.left (64); } + auto const& cs = m_config.my_callsign ().toLocal8Bit (); + if ("FST4W" == m_mode && ui->cbNoOwnCall->isChecked () + && (line_read.contains (" " + cs + " ") + || line_read.contains ("<" + cs + ">"))) { + continue; + } } if (m_mode!="FT8" and m_mode!="FT4" && !m_mode.startsWith ("FST4")) { @@ -4041,7 +4054,9 @@ void MainWindow::guiUpdate() } genfst4_(message,&ichk,msgsent,const_cast (fst4msgbits), const_cast(itone), &iwspr, 37, 37); - int hmod=1; //No FST4/W submodes + int hmod=1; + if(m_config.x2ToneSpacing()) hmod=2; + if(m_config.x4ToneSpacing()) hmod=4; int nsps=720; if(m_TRperiod==30) nsps=1680; if(m_TRperiod==60) nsps=3888; @@ -4054,6 +4069,7 @@ void MainWindow::guiUpdate() float fsample=48000.0; float dfreq=hmod*fsample/nsps; float f0=ui->TxFreqSpinBox->value() - m_XIT + 1.5*dfreq; + if(m_mode=="FST4W") f0=ui->WSPRfreqSpinBox->value() - m_XIT + 1.5*dfreq; int nwave=(nsym+2)*nsps; int icmplx=0; gen_fst4wave_(const_cast(itone),&nsym,&nsps,&nwave, @@ -6676,8 +6692,8 @@ void MainWindow::chk_FST4_freq_range() int diff=ui->sbF_High->value() - ui->sbF_Low->value(); if(diff<100 or diff>maxDiff) { - ui->sbF_Low->setStyleSheet("QSpinBox { background-color: red; }"); - ui->sbF_High->setStyleSheet("QSpinBox { background-color: red; }"); + ui->sbF_Low->setStyleSheet("QSpinBox { color: white; background-color: red; }"); + ui->sbF_High->setStyleSheet("QSpinBox { color: white; background-color: red; }"); } else { ui->sbF_Low->setStyleSheet(""); ui->sbF_High->setStyleSheet(""); @@ -7046,7 +7062,7 @@ void MainWindow::setFreq4(int rxFreq, int txFreq) } else { if (ui->TxFreqSpinBox->isEnabled ()) { ui->TxFreqSpinBox->setValue(txFreq); - if ("FT8" == m_mode || "FT4" == m_mode) + if ("FT8" == m_mode || "FT4" == m_mode || m_mode=="FST4") { // we need to regenerate the current transmit waveform for // GFSK modulated modes @@ -7227,11 +7243,13 @@ void MainWindow::transmit (double snr) if(m_TRperiod==300) nsps=21504; if(m_TRperiod==900) nsps=66560; if(m_TRperiod==1800) nsps=134400; - int hmod=1; //No FST4/W submodes + int hmod=1; + if(m_config.x2ToneSpacing()) hmod=2; + if(m_config.x4ToneSpacing()) hmod=4; double dfreq=hmod*12000.0/nsps; double f0=ui->WSPRfreqSpinBox->value() - m_XIT; if(m_mode=="FST4") f0=ui->TxFreqSpinBox->value() - m_XIT; - if(!m_tune) f0 += + 1.5*dfreq; + if(!m_tune) f0 += 1.5*dfreq; Q_EMIT sendMessage (m_mode, NUM_FST4_SYMBOLS,double(nsps),f0,toneSpacing, m_soundOutput,m_config.audio_output_channel(), true, false, snr, m_TRperiod); @@ -7958,35 +7976,38 @@ void MainWindow::uploadWSPRSpots (bool direct_post, QString const& decode_text) { // do not spot if disabled, replays, or if rig control not working if(!m_uploadWSPRSpots || m_diskData || !m_config.is_transceiver_online ()) return; - if(m_uploading) { + if(m_uploading && !decode_text.size ()) { qDebug() << "Previous upload has not completed, spots were lost"; wsprNet->abortOutstandingRequests (); m_uploading = false; } - QString rfreq = QString("%1").arg((m_dialFreqRxWSPR + 1500) / 1e6, 0, 'f', 6); + QString rfreq = QString("%1").arg((m_dialFreqRxWSPR + m_wideGraph->rxFreq ()) / 1e6, 0, 'f', 6); QString tfreq = QString("%1").arg((m_dialFreqRxWSPR + ui->TxFreqSpinBox->value()) / 1e6, 0, 'f', 6); auto pct = QString::number (ui->autoButton->isChecked () ? ui->sbTxPercent->value () : 0); - if (!direct_post) + if (direct_post) { + // queues one FST4W spot + wsprNet->post (m_config.my_callsign (), m_config.my_grid (), rfreq, tfreq, + m_mode, m_TRperiod, pct, + QString::number (m_dBm), version (), decode_text); + } + else + { + // queues spots for each decode in wspr_spots.txt wsprNet->upload (m_config.my_callsign (), m_config.my_grid (), rfreq, tfreq, m_mode, m_TRperiod, pct, QString::number (m_dBm), version (), m_config.writeable_data_dir ().absoluteFilePath ("wspr_spots.txt")); } - else - { - wsprNet->post (m_config.my_callsign (), m_config.my_grid (), rfreq, tfreq, - m_mode, m_TRperiod, pct, - QString::number (m_dBm), version (), decode_text); - } + // trigger upload of any queued spots if (!decode_text.size ()) { m_uploading = true; } } -void MainWindow::uploadResponse(QString response) +void MainWindow::uploadResponse(QString const& response) { if (response == "done") { m_uploading=false; @@ -8275,12 +8296,22 @@ void MainWindow::statusUpdate () const if (!ui || m_block_udp_status_updates) return; auto submode = current_submode (); auto ftol = ui->sbFtol->value (); - if (!(ui->sbFtol->isVisible () && ui->sbFtol->isEnabled ())) + if ("FST4W" == m_mode) + { + ftol = ui->sbFST4W_FTol->value (); + } + else if (!(ui->sbFtol->isVisible () && ui->sbFtol->isEnabled ())) { ftol = quint32_max; } auto tr_period = ui->sbTR->value (); - if (!(ui->sbTR->isVisible () && ui->sbTR->isEnabled ())) + auto rx_frequency = ui->RxFreqSpinBox->value (); + if ("FST4W" == m_mode) + { + tr_period = ui->sbTR_FST4W->value (); + rx_frequency = ui->sbFST4W_RxFreq->value (); + } + else if (!(ui->sbTR->isVisible () && ui->sbTR->isEnabled ())) { tr_period = quint32_max; } @@ -8288,7 +8319,7 @@ void MainWindow::statusUpdate () const QString::number (ui->rptSpinBox->value ()), m_modeTx, ui->autoButton->isChecked (), m_transmitting, m_decoderBusy, - ui->RxFreqSpinBox->value (), ui->TxFreqSpinBox->value (), + rx_frequency, ui->TxFreqSpinBox->value (), m_config.my_callsign (), m_config.my_grid (), m_hisGrid, m_tx_watchdog, submode != QChar::Null ? QString {submode} : QString {}, m_bFastMode, @@ -9194,9 +9225,19 @@ void MainWindow::remote_configure (QString const& mode, quint32 frequency_tolera { set_mode (mode); } - if (frequency_tolerance != quint32_max && ui->sbFtol->isVisible ()) + auto is_FST4W = "FST4W" == m_mode; + if (frequency_tolerance != quint32_max && (ui->sbFtol->isVisible () || is_FST4W)) { - ui->sbFtol->setValue (frequency_tolerance); + m_block_udp_status_updates = true; + if (is_FST4W) + { + ui->sbFST4W_FTol->setValue (frequency_tolerance); + } + else + { + ui->sbFtol->setValue (frequency_tolerance); + } + m_block_udp_status_updates = false; } if (submode.size () && ui->sbSubmode->isVisible ()) { @@ -9208,14 +9249,30 @@ void MainWindow::remote_configure (QString const& mode, quint32 frequency_tolera } if (tr_period != quint32_max && ui->sbTR->isVisible ()) { - ui->sbTR->setValue (tr_period); - ui->sbTR->interpretText (); + if (is_FST4W) + { + ui->sbTR_FST4W->setValue (tr_period); + ui->sbTR_FST4W->interpretText (); + } + else + { + ui->sbTR->setValue (tr_period); + ui->sbTR->interpretText (); + } } if (rx_df != quint32_max && ui->RxFreqSpinBox->isVisible ()) { m_block_udp_status_updates = true; - ui->RxFreqSpinBox->setValue (rx_df); - ui->RxFreqSpinBox->interpretText (); + if (is_FST4W) + { + ui->sbFST4W_RxFreq->setValue (rx_df); + ui->sbFST4W_RxFreq->interpretText (); + } + else + { + ui->RxFreqSpinBox->setValue (rx_df); + ui->RxFreqSpinBox->interpretText (); + } m_block_udp_status_updates = false; } if (dx_call.size () && ui->dxCallEntry->isVisible ()) diff --git a/widgets/mainwindow.h b/widgets/mainwindow.h index 66a331de2..7a8537d03 100644 --- a/widgets/mainwindow.h +++ b/widgets/mainwindow.h @@ -279,7 +279,7 @@ private slots: void WSPR_config(bool b); void uploadWSPRSpots (bool direct_post = false, QString const& decode_text = QString {}); void TxAgain(); - void uploadResponse(QString response); + void uploadResponse(QString const& response); void on_WSPRfreqSpinBox_valueChanged(int n); void on_sbFST4W_RxFreq_valueChanged(int n); void on_sbFST4W_FTol_valueChanged(int n); diff --git a/widgets/mainwindow.ui b/widgets/mainwindow.ui index d25d63d1d..6ffcf9a24 100644 --- a/widgets/mainwindow.ui +++ b/widgets/mainwindow.ui @@ -1303,7 +1303,7 @@ Yellow when too low 5000 - 100 + 50 1400 @@ -1394,7 +1394,7 @@ Yellow when too low 5000 - 100 + 50 600 diff --git a/widgets/plotter.cpp b/widgets/plotter.cpp index d612b2eb6..3b5ff22ff 100644 --- a/widgets/plotter.cpp +++ b/widgets/plotter.cpp @@ -719,16 +719,17 @@ void CPlotter::mouseMoveEvent (QMouseEvent * event) void CPlotter::mouseReleaseEvent (QMouseEvent * event) { - if (Qt::LeftButton == event->button () and m_mode!="FST4W") { + if (Qt::LeftButton == event->button()) { int x=event->x(); if(x<0) x=0; if(x>m_Size.width()) x=m_Size.width(); bool ctrl = (event->modifiers() & Qt::ControlModifier); bool shift = (event->modifiers() & Qt::ShiftModifier); + if(!shift and m_mode=="FST4W") return; int newFreq = int(FreqfromX(x)+0.5); int oldTxFreq = m_txFreq; int oldRxFreq = m_rxFreq; - if (ctrl) { + if (ctrl and m_mode!="FST4W") { emit setFreq1 (newFreq, newFreq); } else if (shift) { emit setFreq1 (oldRxFreq, newFreq);