Polish the UI for WSPR and WSPR band hopping

Provide  a  dialog for  band  hopping  scheduling which  replaces  tab
four. Ensure that split mode is  not used for WSPR. Select the correct
tab  for WSPR  operation.  Shift WSPR  band hopping  code  out of  the
MainWindow class.

git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@5517 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
Bill Somerville 2015-06-04 01:45:40 +00:00
parent 9e2cc3f9d1
commit f3930eb5c8
9 changed files with 531 additions and 476 deletions

View File

@ -237,6 +237,7 @@ set (wsjtx_CXXSRCS
Configuration.cpp
main.cpp
wsprnet.cpp
WSPRBandHopping.cpp
)
if (WIN32)
@ -623,7 +624,9 @@ endif (NOT APPLE)
include (FortranCInterface)
FortranCInterface_VERIFY (CXX QUIET)
FortranCInterface_HEADER (FC.h MACRO_NAMESPACE "FC_" SYMBOL_NAMESPACE "FC_"
SYMBOLS )
SYMBOLS
hopping
)
#

View File

@ -623,9 +623,13 @@ bool Configuration::accept_udp_requests () const {return m_->accept_udp_requests
bool Configuration::udpWindowToFront () const {return m_->udpWindowToFront_;}
bool Configuration::udpWindowRestore () const {return m_->udpWindowRestore_;}
Bands * Configuration::bands () {return &m_->bands_;}
Bands const * Configuration::bands () const {return &m_->bands_;}
StationList * Configuration::stations () {return &m_->stations_;}
StationList const * Configuration::stations () const {return &m_->stations_;}
FrequencyList * Configuration::frequencies () {return &m_->frequencies_;}
FrequencyList const * Configuration::frequencies () const {return &m_->frequencies_;}
QStringListModel * Configuration::macros () {return &m_->macros_;}
QStringListModel const * Configuration::macros () const {return &m_->macros_;}
QDir Configuration::save_directory () const {return m_->save_directory_;}
QString Configuration::rig_name () const {return m_->rig_params_.rig_name;}
@ -2155,7 +2159,7 @@ void Configuration::impl::transceiver_frequency (Frequency f)
void Configuration::impl::transceiver_tx_frequency (Frequency f)
{
if (/* set_mode () || */ cached_rig_state_.tx_frequency () != f || cached_rig_state_.split () != !!f)
if (/* set_mode () || */ cached_rig_state_.tx_frequency () != f || !cached_rig_state_.compare_split (!!f))
{
cached_rig_state_.split (f);
cached_rig_state_.tx_frequency (f);
@ -2259,8 +2263,14 @@ void Configuration::impl::handle_transceiver_update (TransceiverState state)
setup_split_ = true;
required_tx_frequency_ = 0;
// Q_EMIT self_->transceiver_failure (tr ("Rig split mode setting not consistent with WSJT-X settings. Changing WSJT-X settings for you."));
Q_EMIT self_->transceiver_failure (tr ("Rig split mode setting not consistent with WSJT-X settings."));
// Q_EMIT self_->transceiver_failure (tr ("Rig
// split mode setting not consistent with WSJT-X
// settings. Changing WSJT-X settings for
// you."));
if (cached_rig_state_.split () != state.split ())
{
Q_EMIT self_->transceiver_failure (tr ("Rig split mode setting not consistent with WSJT-X settings."));
}
}
}
}
@ -2269,7 +2279,9 @@ void Configuration::impl::handle_transceiver_update (TransceiverState state)
// One time rig setup split
if (setup_split_ && cached_rig_state_.split () != state.split ())
{
Q_EMIT tx_frequency (TransceiverFactory::split_mode_none != split_mode_selected ? (required_tx_frequency_ ? required_tx_frequency_ : state.tx_frequency ()) : 0, true);
Q_EMIT tx_frequency (TransceiverFactory::split_mode_none != split_mode_selected && cached_rig_state_.split ()
? (required_tx_frequency_ ? required_tx_frequency_ : state.tx_frequency ())
: 0, true);
}
setup_split_ = false;
required_tx_frequency_ = 0;

View File

@ -117,9 +117,13 @@ public:
bool udpWindowToFront () const;
bool udpWindowRestore () const;
Bands * bands ();
Bands const * bands () const;
FrequencyList * frequencies ();
FrequencyList const * frequencies () const;
StationList * stations ();
StationList const * stations () const;
QStringListModel * macros ();
QStringListModel const * macros () const;
QDir save_directory () const;
QString rig_name () const;
unsigned jt9w_bw_mult () const;

View File

@ -89,6 +89,7 @@ public:
Frequency frequency () const {return frequency_[0];}
Frequency tx_frequency () const {return frequency_[1];}
bool split () const {return on == split_;}
bool compare_split (bool with) const {return split_ == (with ? on : off);}
MODE mode () const {return mode_;}
bool ptt () const {return ptt_;}

301
WSPRBandHopping.cpp Normal file
View File

@ -0,0 +1,301 @@
#include "WSPRBandHopping.hpp"
#include <QPointer>
#include <QSettings>
#include <QBitArray>
#include <QtWidgets>
#include "SettingsGroup.hpp"
#include "Configuration.hpp"
#include "FrequencyList.hpp"
#include "pimpl_impl.hpp"
#include "moc_WSPRBandHopping.cpp"
extern "C"
{
#ifndef CMAKE_BUILD
#define FC_hopping hopping_
#else
#include "FC.h"
void FC_hopping (int const * year, int const * month, int const * nday, float const * uth, char const * my_grid
, int const * nduration, int const * npctx, int * isun, int * iband
, int * ntxnext, int my_grid_len);
#endif
};
namespace
{
// These 10 bands are the hopping candidates and are globally coordinated
char const * const hopping_bands[] = {"160m", "80m", "60m", "40m", "30m", "20m", "17m", "15m", "12m", "10m"};
size_t constexpr num_bands {sizeof (hopping_bands) / sizeof (hopping_bands[0])};
char const * const periods[] = {"Sunrise grayline", "Day", "Sunset grayline", "Night", "Tune"};
size_t constexpr num_periods {sizeof (periods) / sizeof (periods[0])};
char const * const title = "WSPR Band Hopping";
}
//
// Dialog - maintenance of band hopping options
//
class Dialog
: public QDialog
{
public:
Dialog (QSettings *, QBitArray * bands, int * gray_line_duration, QWidget * parent = nullptr);
~Dialog ();
void resize_to_maximum ();
private:
void closeEvent (QCloseEvent *) override;
void save_window_state ();
QSettings * settings_;
QBitArray * bands_;
int * gray_line_duration_;
QPointer<QTableWidget> bands_table_;
QPointer<QSpinBox> gray_line_width_spin_box_;
};
Dialog::Dialog (QSettings * settings, QBitArray * bands, int * gray_line_duration, QWidget * parent)
: QDialog {parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint}
, settings_ {settings}
, bands_ {bands}
, gray_line_duration_ {gray_line_duration}
, bands_table_ {new QTableWidget {num_periods, num_bands, this}}
, gray_line_width_spin_box_ {new QSpinBox {this}}
{
QVBoxLayout * main_layout {new QVBoxLayout};
// set up and load the table of check boxes
bands_table_->setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
bands_table_->setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
for (auto row = 0u; row < num_periods; ++row)
{
auto vertical_header = new QTableWidgetItem {periods[row]};
vertical_header->setTextAlignment (Qt::AlignRight | Qt::AlignVCenter);
bands_table_->setVerticalHeaderItem (row, vertical_header);
for (auto column = 0u; column < num_bands; ++column)
{
if (0 == row)
{
auto horizontal_header = new QTableWidgetItem {hopping_bands[column]};
bands_table_->setHorizontalHeaderItem (column, horizontal_header);
}
auto item = new QTableWidgetItem;
item->setFlags (Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
item->setCheckState (bands_[row].testBit (column) ? Qt::Checked : Qt::Unchecked);
bands_table_->setItem (row, column, item);
}
}
bands_table_->resizeColumnsToContents ();
main_layout->addWidget (bands_table_);
// handle changes by updating the underlying flags
connect (bands_table_.data (), &QTableWidget::itemChanged, [this] (QTableWidgetItem * item) {
bands_[item->row ()].setBit (item->column (), Qt::Checked == item->checkState ());
});
// set up the gray line duration spin box
gray_line_width_spin_box_->setRange (1, 60 * 2);
gray_line_width_spin_box_->setSuffix ("min");
gray_line_width_spin_box_->setValue (*gray_line_duration_);
QFormLayout * form_layout = new QFormLayout;
form_layout->addRow (tr ("Gray time:"), gray_line_width_spin_box_);
connect (gray_line_width_spin_box_.data ()
, static_cast<void (QSpinBox::*) (int)> (&QSpinBox::valueChanged)
, [this] (int new_value) {*gray_line_duration_ = new_value;});
QHBoxLayout * bottom_layout = new QHBoxLayout;
bottom_layout->addStretch ();
bottom_layout->addLayout (form_layout);
main_layout->addLayout (bottom_layout);
setLayout (main_layout);
setWindowTitle (windowTitle () + ' ' + tr (title));
{
SettingsGroup g {settings_, title};
restoreGeometry (settings_->value ("geometry", saveGeometry ()).toByteArray ());
}
}
Dialog::~Dialog ()
{
// do this here too because ESC or parent shutdown closing this
// window doesn't queue a close event
save_window_state ();
}
void Dialog::closeEvent (QCloseEvent * e)
{
save_window_state ();
QDialog::closeEvent (e);
}
void Dialog::save_window_state ()
{
SettingsGroup g {settings_, title};
settings_->setValue ("geometry", saveGeometry ());
}
// to get the dialog window exactly the right size to contain the
// widgets without needing scroll bars we need to measure the size of
// the table widget and set its minimum size to the measured size
void Dialog::resize_to_maximum ()
{
int width {bands_table_->verticalHeader ()->width ()};
int height {bands_table_->horizontalHeader ()->height ()};
for (auto i = 0; i < bands_table_->columnCount (); ++i)
{
width += bands_table_->columnWidth (i);
}
for (auto i = 0; i < bands_table_->rowCount (); ++i)
{
height += bands_table_->rowHeight (i);
}
bands_table_->setMinimumSize ({width, height});
}
class WSPRBandHopping::impl
{
public:
impl (QSettings * settings, Configuration const * configuration, QWidget * parent_widget)
: settings_ {settings}
, configuration_ {configuration}
, tx_percent_ {0}
, parent_widget_ {parent_widget}
, bands_ {QBitArray {num_bands}, QBitArray {num_bands}, QBitArray {num_bands}, QBitArray {num_bands}, QBitArray {num_bands}}
{
}
QSettings * settings_;
Configuration const * configuration_;
int tx_percent_;
QWidget * parent_widget_;
// 5 x 10 bit flags representing each hopping band in each period
// and tune
QBitArray bands_[num_periods];
int gray_line_duration_;
QPointer<Dialog> dialog_;
};
WSPRBandHopping::WSPRBandHopping (QSettings * settings, Configuration const * configuration, QWidget * parent_widget)
: m_ {settings, configuration, parent_widget}
{
// load settings
SettingsGroup g {m_->settings_, title};
auto size = m_->settings_->beginReadArray ("periods");
for (auto i = 0; i < size; ++i)
{
m_->settings_->setArrayIndex (i);
m_->bands_[i] = m_->settings_->value ("bands").toBitArray ();
}
m_->settings_->endArray ();
m_->gray_line_duration_ = m_->settings_->value ("GrayLineDuration", 60).toUInt ();
}
WSPRBandHopping::~WSPRBandHopping ()
{
// save settings
SettingsGroup g {m_->settings_, title};
m_->settings_->beginWriteArray ("periods");
for (auto i = 0u; i < num_periods; ++i)
{
m_->settings_->setArrayIndex (i);
m_->settings_->setValue ("bands", m_->bands_[i]);
}
m_->settings_->endArray ();
m_->settings_->setValue ("GrayLineDuration", m_->gray_line_duration_);
}
// pop up the maintenance dialog window
void WSPRBandHopping::show_dialog (bool /* checked */)
{
if (!m_->dialog_)
{
m_->dialog_ = new Dialog {m_->settings_, m_->bands_, &m_->gray_line_duration_, m_->parent_widget_};
}
m_->dialog_->show ();
m_->dialog_->resize_to_maximum ();
m_->dialog_->adjustSize (); // fix the size
m_->dialog_->setMinimumSize (m_->dialog_->size ());
m_->dialog_->setMaximumSize (m_->dialog_->size ());
m_->dialog_->raise ();
m_->dialog_->activateWindow ();
}
int WSPRBandHopping::tx_percent () const
{
return m_->tx_percent_;
}
void WSPRBandHopping::set_tx_percent (int new_value)
{
m_->tx_percent_ = new_value;
}
// determine the parameters of the hop, if any
auto WSPRBandHopping::next_hop () -> Hop
{
auto const& now = QDateTime::currentDateTimeUtc ();
auto const& date = now.date ();
auto year = date.year ();
auto month = date.month ();
auto day = date.day ();
auto const& time = now.time ();
float uth = time.hour () + time.minute () / 60.
+ (time.second () + .001 * time.msec ()) / 3600.;
auto my_grid = m_->configuration_->my_grid ();
int period_index;
int band_index;
int tx_next;
my_grid = (my_grid + " ").left (6); // hopping doesn't like
// short grids
// look up band for this period
FC_hopping (&year, &month, &day, &uth, my_grid.toLatin1 ().constData ()
, &m_->gray_line_duration_, &m_->tx_percent_, &period_index, &band_index
, &tx_next, my_grid.size ());
int frequencies_index {-1};
auto const& frequencies = m_->configuration_->frequencies ();
auto const& filtered_bands = frequencies->filtered_bands ();
if (m_->bands_[period_index].testBit (band_index)
&& filtered_bands.contains (hopping_bands[band_index]))
{
// here we have a band that has been enabled in the hopping
// matrix so check it it has a configured working frequency
frequencies_index = frequencies->best_working_frequency (hopping_bands[band_index]);
qDebug () << "scheduled:" << hopping_bands[band_index] << "frequency:" << frequencies->data (frequencies->index (frequencies_index, FrequencyList::frequency_column)).toString ();
}
// if we do not have a configured working frequency we next check
// for a random selection from the other enabled bands in the
// hopping matrix
if (frequencies_index < 0)
{
for (auto i = 0u; i < num_bands; ++i)
{
int new_index = static_cast<int> (qrand () % num_bands); // random choice
if (new_index != band_index && m_->bands_[period_index].testBit (new_index))
{
// here we have a random choice that is enabled in the
// hopping matrix and not the scheduled choice so we now
// check if it has a configured working frequency
frequencies_index = frequencies->best_working_frequency (hopping_bands[new_index]);
if (frequencies_index >= 0)
{
// we can use the random choice
qDebug () << "random:" << hopping_bands[new_index] << "frequency:" << frequencies->data (frequencies->index (frequencies_index, FrequencyList::frequency_column)).toString ();
band_index = new_index;
break;
}
}
}
}
return {periods[period_index]
, frequencies_index
, frequencies_index >= 0 && m_->bands_[4].testBit (band_index)
, frequencies_index >= 0 && !!tx_next};
}

80
WSPRBandHopping.hpp Normal file
View File

@ -0,0 +1,80 @@
#ifndef WSPR_BAND_HOPPING_HPP__
#define WSPR_BAND_HOPPING_HPP__
#include <QObject>
#include "pimpl_h.hpp"
class QSettings;
class Configuration;
class QWidget;
//
// WSPR Band Hopping Control
//
// WSPR specifies a globally coordinated band hopping schedule and
// this class implements that.
//
// Responsibilities
//
// Provides a maintenance dialog allowing the user to define which
// bands are allowed from the band hopping schedule as defined here:
//
// http://physics.princeton.edu/pulsar/K1JT/doc/wspr/wspr-main.html
//
// Along with selecting bands a flag indicating that a short tune up
// signal is required for specified bands.
//
// Provides a Qt property that holds the Tx percentage which is used
// to generate a semi-randomized schedule of period to transmit. This
// schedule is random but adjusted to limit the number of consecutive
// transmission periods, it also adjusts the schedule to ensure that
// the overall number of transmission periods in any two hour hopping
// schedule reflects the percentage provided.
//
// Collaborations
//
// Settings including the selected bands with periods, the tune up
// flags and the gray line duration are maintained in persistent
// storage using the provided QSettings object instance.
//
// A passed in Configuration object instance is used to query the
// FrequencyList model to determine working frequencies for each
// band. The row index of this model is returned by this classes
// hopping scheduling method so it may be conveniently used to select
// a new working frequency by a client.
//
class WSPRBandHopping
: public QObject
{
Q_OBJECT;
Q_PROPERTY (int tx_percent READ tx_percent WRITE set_tx_percent);
public:
WSPRBandHopping (QSettings *, Configuration const *, QWidget * parent = nullptr);
~WSPRBandHopping ();
// display the band hopping maintenance dialog
Q_SLOT void show_dialog (bool);
// Property tx_percent implementation
int tx_percent () const;
Q_SLOT void set_tx_percent (int);
// structure that defines the results of the next_hop() method
struct Hop
{
QString period_name_;
int frequencies_index_; // may be -1 indicating no change
bool tune_required_;
bool tx_next_;
};
// return the next band parameters
Hop next_hop ();
private:
class impl;
pimpl<impl> m_;
};
#endif

View File

@ -52,9 +52,6 @@ namespace
Radio::Frequency constexpr default_frequency {14076000};
QRegExp message_alphabet {"[- @A-Za-z0-9+./?#]*"};
// These 10 bands are the hopping candidates and are globally coordinated
QStringList const hopping_bands = {"160m","80m","60m","40m","30m","20m","17m","15m","12m","10m"};
bool message_is_73 (int type, QStringList const& msg_parts)
{
return type >= 0
@ -70,19 +67,22 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme
m_dataDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)},
m_revision {revision ()},
m_multiple {multiple},
m_settings (settings),
m_settings {settings},
ui(new Ui::MainWindow),
m_config (settings, this),
m_config {settings, this},
m_WSPR_band_hopping {settings, &m_config, this},
m_wideGraph (new WideGraph (settings)),
m_logDlg (new LogQSO (program_title (), settings, this)),
m_dialFreq {std::numeric_limits<Radio::Frequency>::max ()},
m_detector (RX_SAMPLE_RATE, NTMAX, 6912 / 2, downSampleFactor),
m_modulator (TX_SAMPLE_RATE, NTMAX),
m_audioThread {new QThread},
m_pctx {0},
m_diskData {false},
m_sentFirst73 {false},
m_currentMessageType {-1},
m_lastMessageType {-1},
m_nonWSPRTab {-1},
m_appDir {QApplication::applicationDirPath ()},
mem_jt9 {shdmem},
m_msAudioOutputBuffered (0u),
@ -202,6 +202,12 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme
}
});
// Hook up WSPR band hopping
connect (ui->band_hopping_schedule_push_button, &QPushButton::clicked
, &m_WSPR_band_hopping, &WSPRBandHopping::show_dialog);
connect (ui->sbTxPercent, static_cast<void (QSpinBox::*) (int)> (&QSpinBox::valueChanged)
, &m_WSPR_band_hopping, &WSPRBandHopping::set_tx_percent);
on_EraseButton_clicked ();
QActionGroup* modeGroup = new QActionGroup(this);
@ -393,7 +399,6 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme
m_bShMsgs=false;
m_bDopplerTracking0=false;
m_uploading=false;
m_hopTest=false;
m_bTxTime=false;
m_rxDone=false;
m_bHaveTransmitted=false;
@ -617,13 +622,7 @@ void MainWindow::writeSettings()
m_settings->setValue("PctTx",m_pctx);
m_settings->setValue("dBm",m_dBm);
m_settings->setValue("UploadSpots",m_uploadSpots);
m_settings->setValue("BandHopping",m_bandHopping);
m_settings->setValue("SunriseBands",ui->sunriseBands->text());
m_settings->setValue("DayBands",ui->dayBands->text());
m_settings->setValue("SunsetBands",ui->sunsetBands->text());
m_settings->setValue("NightBands",ui->nightBands->text());
m_settings->setValue("TuneBands",ui->tuneBands->text());
m_settings->setValue("GrayLineDuration",ui->graylineDuration->text());
m_settings->setValue ("BandHopping", ui->band_hopping_group_box->isChecked ());
m_settings->endGroup();
}
@ -691,8 +690,7 @@ void MainWindow::readSettings()
m_uploadSpots=m_settings->value("UploadSpots",false).toBool();
ui->cbUploadWSPR_Spots->setChecked(m_uploadSpots);
if(!m_uploadSpots) ui->cbUploadWSPR_Spots->setStyleSheet("QCheckBox{background-color: yellow}");
m_bandHopping=m_settings->value("BandHopping",false).toBool();
ui->cbBandHop->setChecked(m_bandHopping);
ui->band_hopping_group_box->setChecked (m_settings->value ("BandHopping", false).toBool());
// setup initial value of tx attenuator
ui->outAttenuation->setValue (m_settings->value ("OutAttenuation", 0).toInt ());
on_outAttenuation_valueChanged (ui->outAttenuation->value ());
@ -703,18 +701,6 @@ void MainWindow::readSettings()
outBufSize=m_settings->value("OutBufSize",4096).toInt();
m_lockTxFreq=m_settings->value("LockTxFreq",false).toBool();
ui->cbTxLock->setChecked(m_lockTxFreq);
ui->sunriseBands->setText(m_settings->value("SunriseBands","").toString());
on_sunriseBands_editingFinished();
ui->dayBands->setText(m_settings->value("DayBands","").toString());
on_dayBands_editingFinished();
ui->sunsetBands->setText(m_settings->value("SunsetBands","").toString());
on_sunsetBands_editingFinished();
ui->nightBands->setText(m_settings->value("NightBands","").toString());
on_nightBands_editingFinished();
ui->tuneBands->setText(m_settings->value("TuneBands","").toString());
on_tuneBands_editingFinished();
ui->graylineDuration->setText(m_settings->value("GrayLineDuration","").toString());
on_graylineDuration_editingFinished();
m_settings->endGroup();
// use these initialisation settings to tune the audio o/p buffer
@ -1896,7 +1882,7 @@ void MainWindow::guiUpdate()
m_btxok=false;
}
if(m_ntr==1) {
// if(m_bandHopping) {
// if(ui->band_hopping_group_box->isChecked ()) {
// qDebug() << "Call bandHopping after Rx" << m_nseq << m_ntr << m_nrx << m_rxDone;
bandHopping();
// }
@ -2264,7 +2250,7 @@ void MainWindow::stopTx2()
}
if(m_mode.mid(0,4)=="WSPR" and m_ntr==-1 and !m_tuneup) {
m_wideGraph->setWSPRtransmitted();
// if(m_bandHopping) {
// if(ui->band_hopping_group_box->isChecked ()) {
// qDebug () << "Call bandHopping after Tx" << m_tuneup;
bandHopping();
// }
@ -3212,9 +3198,12 @@ void MainWindow::WSPR_config(bool b)
ui->decodedTextLabel->setText(
"UTC dB DT Freq Drift Call Grid dBm Dist");
auto_tx_label->setText("");
ui->tabWidget->setCurrentIndex (2);
Q_EMIT m_config.transceiver_tx_frequency (0); // turn off split
} else {
ui->decodedTextLabel->setText("UTC dB DT Freq Message");
auto_tx_label->setText (m_config.quick_call () ? "Tx-Enable Armed" : "Tx-Enable Disarmed");
ui->tabWidget->setCurrentIndex (m_nonWSPRTab >= 0 ? m_nonWSPRTab : 1);
}
}
@ -3572,18 +3561,21 @@ void MainWindow::on_pbTxMode_clicked()
void MainWindow::setXIT(int n)
{
m_XIT = 0;
if (m_config.split_mode () and (m_mode != "JT4")) //Don't use XIT in JT4 mode
if (m_mode != "WSPR-2" && m_mode != "WSPR-15") // Don't use split in WSPR
{
m_XIT=(n/500)*500 - 1500;
}
if (m_monitoring or m_transmitting)
{
if (m_config.transceiver_online ())
if (m_config.split_mode () && m_mode != "JT4") // Don't use XIT in JT4
{
if (m_config.split_mode ())
m_XIT=(n/500)*500 - 1500;
}
if (m_monitoring || m_transmitting)
{
if (m_config.transceiver_online ())
{
Q_EMIT m_config.transceiver_tx_frequency (m_dialFreq + m_XIT);
if (m_config.split_mode ())
{
Q_EMIT m_config.transceiver_tx_frequency (m_dialFreq + m_XIT);
}
}
}
}
@ -4258,118 +4250,39 @@ void MainWindow::on_pbTxNext_clicked(bool b)
m_txNext=b;
}
void MainWindow::on_cbBandHop_toggled(bool b)
{
m_bandHopping=b;
}
void MainWindow::bandHopping()
{
QDateTime t = QDateTime::currentDateTimeUtc();
QString date = t.date().toString("yyyy MMM dd").trimmed();
QString utc = t.time().toString().trimmed();
int nyear=t.date().year();
int month=t.date().month();
int nday=t.date().day();
int nhr=t.time().hour();
int nmin=t.time().minute();
float sec=t.time().second() + 0.001*t.time().msec();
float uth=nhr + nmin/60.0 + sec/3600.0;
int isun;
int iband0;
int ntxnext;
auto hop_data = m_WSPR_band_hopping.next_hop ();
static int icall=0;
if(m_hopTest) uth+= 2.0*icall/60.0;
icall++;
// Find grayline status, isun: 0=Sunrise, 1=Day, 2=Sunset, 3=Night
hopping_(&nyear, &month, &nday, &uth,
m_config.my_grid ().toLatin1().constData(),
&m_grayDuration, &m_pctx, &isun, &iband0, &ntxnext, 6);
if(m_auto and ntxnext==1) {
m_nrx=0;
if (m_auto &&hop_data.tx_next_) {
m_nrx = 0;
} else {
m_nrx=1;
m_nrx = 1;
}
if( m_bandHopping ) {
QStringList s;
if(isun==0) s=m_sunriseBands;
if(isun==1) s=m_dayBands;
if(isun==2) s=m_sunsetBands;
if(isun==3) s=m_nightBands;
if (ui->band_hopping_group_box->isChecked ()) {
QThread::msleep(500); //### Is this OK to do ??? ###
// allow numeric only band names
for (auto& item : s) {
if (!item.endsWith ('m')) {
item += 'm';
}
}
if (hop_data.frequencies_index_ >= 0) { // new band
ui->bandComboBox->setCurrentIndex (hop_data.frequencies_index_);
on_bandComboBox_activated (hop_data.frequencies_index_);
QString new_band;
if (s.contains (hopping_bands[iband0])) { //See if designated band is active
new_band = hopping_bands[iband0];
}
else {
// If designated band is not active, choose one that is active
// and in the hopping list
for (auto i = 0; i < s.size (); ++i) { // arbitrary number of iterations
auto const& bname = s[qrand() % s.size ()]; // pick a random band
if (bname != m_band00 && hopping_bands.contains (bname)) {
new_band = bname;
break;
auto const& band_name = m_config.bands ()->find (m_dialFreq).remove ('m');
m_cmnd.clear ();
QStringList prefixes {".bat", ".cmd", ".exe", ""};
for (auto const& prefix : prefixes)
{
auto const& path = m_appDir + "/user_hardware" + prefix;
QFile f {path};
if (f.exists ()) {
m_cmnd = QDir::toNativeSeparators (f.fileName ()) + ' ' + band_name;
}
}
}
}
qDebug () << "bandHopping: m_band00:" << m_band00 << "new candidate band:" << new_band;
QThread::msleep(500); //### Is this OK to do ??? ###
// qDebug() << nhr << nmin << int(sec) << m_band00 << f0 << 0.000001*f0;
auto const& row = m_config.frequencies ()->best_working_frequency (new_band);
if (row >= 0) { // band is configured
m_band00 = new_band;
ui->bandComboBox->setCurrentIndex (row);
on_bandComboBox_activated (row);
m_cmnd="";
QFile f1 {m_appDir + "/user_hardware.bat"};
if(f1.exists()) {
m_cmnd=QDir::toNativeSeparators (m_appDir + "/user_hardware.bat ") + m_band00;
}
QFile f2 {m_appDir + "/user_hardware.cmd"};
if(f2.exists()) {
m_cmnd=QDir::toNativeSeparators (m_appDir + "/user_hardware.cmd ") + m_band00;
}
QFile f3 {m_appDir + "/user_hardware.exe"};
if(f3.exists()) {
m_cmnd=QDir::toNativeSeparators (m_appDir + "/user_hardware.exe ") + m_band00;
}
QFile f4 {m_appDir + "/user_hardware"};
if(f4.exists()) {
m_cmnd=QDir::toNativeSeparators (m_appDir + "/user_hardware ") + m_band00;
}
int n=m_cmnd.length();
if(m_cmnd.mid(n-1,1)=="m") {
m_cmnd=m_cmnd.mid(0,n-1); //### Temporary? ### Strip trailimg "m"
}
if(m_cmnd!="") p3.start(m_cmnd); // Execute user's hardware controller
// Produce a short tuneup signal
m_tuneup = false;
auto tu_bands = m_tuneBands;
// allow numeric only band names
for (auto& item : tu_bands) {
if (!item.endsWith ('m')) {
item += 'm';
}
}
if (tu_bands.contains (m_band00)) {
if (hop_data.tune_required_) {
m_tuneup = true;
on_tuneButton_clicked (true);
tuneATU_Timer->start (2500);
@ -4377,49 +4290,13 @@ void MainWindow::bandHopping()
}
// Display grayline status
QString dailySequence[4]={"Sunrise grayline","Day","Sunset grayline","Night"};
auto_tx_label->setText(dailySequence[isun]);
auto_tx_label->setText (hop_data.period_name_);
}
}
void MainWindow::on_pushButton_clicked()
void MainWindow::on_tabWidget_currentChanged (int new_value)
{
qDebug() << "A" << m_config.data_dir();
qDebug() << "B" << m_config.data_dir().absolutePath();
qDebug() << "C" << m_config.data_dir().absoluteFilePath("JPLEPH");
/*
m_hopTest=true;
bandHopping();
m_hopTest=false;
*/
}
void MainWindow::on_sunriseBands_editingFinished()
{
m_sunriseBands=ui->sunriseBands->text().split(" ", QString::SkipEmptyParts);
}
void MainWindow::on_dayBands_editingFinished()
{
m_dayBands=ui->dayBands->text().split(" ", QString::SkipEmptyParts);
}
void MainWindow::on_sunsetBands_editingFinished()
{
m_sunsetBands=ui->sunsetBands->text().split(" ", QString::SkipEmptyParts);
}
void MainWindow::on_nightBands_editingFinished()
{
m_nightBands=ui->nightBands->text().split(" ", QString::SkipEmptyParts);
}
void MainWindow::on_tuneBands_editingFinished()
{
m_tuneBands=ui->tuneBands->text().split(" ", QString::SkipEmptyParts);
}
void MainWindow::on_graylineDuration_editingFinished()
{
m_grayDuration=ui->graylineDuration->text().toInt();
if (2 != new_value) { // WSPR
m_nonWSPRTab = new_value;
}
}

View File

@ -16,6 +16,7 @@
#include <QProgressDialog>
#include <QAbstractSocket>
#include <QHostAddress>
#include <QPointer>
#include "soundin.h"
#include "AudioDevice.hpp"
@ -24,6 +25,7 @@
#include "Radio.hpp"
#include "Modes.hpp"
#include "Configuration.hpp"
#include "WSPRBandHopping.hpp"
#include "Transceiver.hpp"
#include "psk_reporter.h"
#include "signalmeter.h"
@ -59,6 +61,7 @@ class Astro;
class MessageAveraging;
class MessageClient;
class QTime;
class WSPRBandHopping;
class MainWindow : public QMainWindow
{
@ -225,14 +228,7 @@ private slots:
void p3Error(QProcess::ProcessError e);
void on_WSPRfreqSpinBox_valueChanged(int n);
void on_pbTxNext_clicked(bool b);
void on_cbBandHop_toggled(bool b);
void on_sunriseBands_editingFinished();
void on_pushButton_clicked();
void on_dayBands_editingFinished();
void on_sunsetBands_editingFinished();
void on_nightBands_editingFinished();
void on_tuneBands_editingFinished();
void on_graylineDuration_editingFinished();
void on_tabWidget_currentChanged (int);
private:
Q_SIGNAL void initializeAudioOutputStream (QAudioDeviceInfo,
@ -266,6 +262,7 @@ private:
// other windows
Configuration m_config;
WSPRBandHopping m_WSPR_band_hopping;
QMessageBox m_rigErrorMessageBox;
QScopedPointer<WideGraph> m_wideGraph;
@ -327,8 +324,6 @@ private:
qint32 m_dBm;
qint32 m_pctx;
qint32 m_nseq;
qint32 m_grayDuration;
QString m_band00;
bool m_btxok; //True if OK to transmit
bool m_diskData;
@ -379,12 +374,11 @@ private:
bool m_uploading;
bool m_txNext;
bool m_grid6;
bool m_bandHopping;
bool m_hopTest;
bool m_tuneup;
bool m_bTxTime;
bool m_rxDone;
bool m_bHaveTransmitted; //Can be used to prohibit consecutive WSPR transmittions
bool m_bHaveTransmitted; //Can be used to prohibit consecutive WSPR transmissions
int m_nonWSPRTab;
float m_pctZap;
@ -549,10 +543,6 @@ extern "C" {
void wspr_downsample_(short int d2[], int* k);
void savec2_(char* fname, int* m_TRseconds, double* m_dialFreq, int len1);
void hopping_(int* nyear, int* month, int* nday, float* uth, char const * MyGrid,
int* nduration, int* npctx, int* isun, int* iband,
int* ntxnext, int len);
}
#endif // MAINWINDOW_H

View File

@ -2237,11 +2237,37 @@ list. The list can be maintained in Settings (F2).</string>
<rect>
<x>30</x>
<y>22</y>
<width>200</width>
<height>171</height>
<width>221</width>
<height>161</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="3" column="0">
<widget class="QGroupBox" name="band_hopping_group_box">
<property name="title">
<string>Band Hopping</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QPushButton" name="band_hopping_schedule_push_button">
<property name="text">
<string>Schedule ...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="cbUploadWSPR_Spots">
<property name="text">
<string>Upload spots</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QLabel" name="label_11">
<property name="maximumSize">
@ -2263,6 +2289,27 @@ list. The list can be maintained in Settings (F2).</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="pbTxNext">
<property name="styleSheet">
<string notr="true">QPushButton:checked {
background-color: red;
border-style: outset;
border-width: 1px;
border-radius: 5px;
border-color: black;
min-width: 5em;
padding: 3px;
}</string>
</property>
<property name="text">
<string>Tx Next</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QSpinBox" name="WSPRfreqSpinBox">
<property name="alignment">
@ -2285,12 +2332,21 @@ list. The list can be maintained in Settings (F2).</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="cbUploadWSPR_Spots">
<property name="text">
<string>Upload spots</string>
<item row="3" column="2">
<widget class="QComboBox" name="TxPowerComboBox"/>
</item>
<item row="2" column="1">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>17</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QSpinBox" name="sbTxPercent">
@ -2311,275 +2367,6 @@ list. The list can be maintained in Settings (F2).</string>
</property>
</widget>
</item>
<item row="2" column="1">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>17</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="2">
<widget class="QCheckBox" name="cbBandHop">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Band hopping</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QPushButton" name="pbTxNext">
<property name="styleSheet">
<string notr="true">QPushButton:checked {
background-color: red;
border-style: outset;
border-width: 1px;
border-radius: 5px;
border-color: black;
min-width: 5em;
padding: 3px;
}</string>
</property>
<property name="text">
<string>Tx Next</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QComboBox" name="TxPowerComboBox"/>
</item>
</layout>
</widget>
</widget>
<widget class="QWidget" name="tab_4">
<attribute name="title">
<string>4</string>
</attribute>
<widget class="QPushButton" name="pushButton">
<property name="geometry">
<rect>
<x>100</x>
<y>190</y>
<width>75</width>
<height>23</height>
</rect>
</property>
<property name="text">
<string>Test</string>
</property>
</widget>
<widget class="QWidget" name="layoutWidget1">
<property name="geometry">
<rect>
<x>30</x>
<y>30</y>
<width>211</width>
<height>152</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_7">
<item row="0" column="0">
<widget class="QLabel" name="label_12">
<property name="minimumSize">
<size>
<width>55</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Sunrise:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="sunriseBands">
<property name="maximumSize">
<size>
<width>160</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>160 80 40 30 20</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_13">
<property name="minimumSize">
<size>
<width>55</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Day:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="dayBands">
<property name="maximumSize">
<size>
<width>160</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>30 20 17 15 12 10</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_14">
<property name="minimumSize">
<size>
<width>55</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Sunset:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="sunsetBands">
<property name="maximumSize">
<size>
<width>160</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>160 80 40 30 20</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_15">
<property name="minimumSize">
<size>
<width>55</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Night:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="nightBands">
<property name="maximumSize">
<size>
<width>160</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>160 80 40 30 20</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_16">
<property name="minimumSize">
<size>
<width>55</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Tune:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="tuneBands">
<property name="maximumSize">
<size>
<width>160</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>80 40 30 20 17 15 12 10</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_17">
<property name="minimumSize">
<size>
<width>55</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Gray time:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="graylineDuration">
<property name="maximumSize">
<size>
<width>160</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>60</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>