Improved WSPR band hopping band selection

When  band hopping,  coordinated  slots that  are  unavailable may  be
substituted  by  non-coordinated  bands or  non-scheduled  coordinated
bands.

Rather than  randomly choosing a substitute  band, random permutations
of available receive-only and transmit allowed bands are generated and
consumed  one at  a time  when a  substitute band  is required.   This
ensures that  all available  bands get  an airing  on a  regular basis
while selections remain randomized as much as possible within the user
defined constraints of  coordinated bands, transmit ratio  and Rx only
bands.

git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@5616 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
Bill Somerville 2015-06-15 12:04:09 +00:00
parent 9375c73b1c
commit cecb01b446
3 changed files with 77 additions and 42 deletions

View File

@ -4,6 +4,7 @@
#include <QSettings> #include <QSettings>
#include <QBitArray> #include <QBitArray>
#include <QList> #include <QList>
#include <QSet>
#include <QtWidgets> #include <QtWidgets>
#include "SettingsGroup.hpp" #include "SettingsGroup.hpp"
@ -221,6 +222,8 @@ public:
Configuration const * configuration_; Configuration const * configuration_;
int tx_percent_; int tx_percent_;
BandList WSPR_bands_; BandList WSPR_bands_;
BandList rx_permutation_;
BandList tx_permutation_;
QWidget * parent_widget_; QWidget * parent_widget_;
// 5 x 10 bit flags representing each hopping band in each period // 5 x 10 bit flags representing each hopping band in each period
@ -324,7 +327,7 @@ auto WSPRBandHopping::next_hop () -> Hop
int frequencies_index {-1}; int frequencies_index {-1};
auto const& frequencies = m_->configuration_->frequencies (); auto const& frequencies = m_->configuration_->frequencies ();
auto const& bands = m_->configuration_->bands (); auto const& bands = m_->configuration_->bands ();
auto const& band_name = bands->data (bands->index (band_index + 3, 0)).toString (); auto band_name = bands->data (bands->index (band_index + 3, 0)).toString ();
if (m_->bands_[period_index].testBit (band_index + 3) // +3 for if (m_->bands_[period_index].testBit (band_index + 3) // +3 for
// coordinated bands // coordinated bands
&& m_->WSPR_bands_.contains (band_name)) && m_->WSPR_bands_.contains (band_name))
@ -334,56 +337,86 @@ auto WSPRBandHopping::next_hop () -> Hop
frequencies_index = frequencies->best_working_frequency (band_name); frequencies_index = frequencies->best_working_frequency (band_name);
} }
// if we do not have a configured working frequency we next check // if we do not have a configured working frequency on the selected
// for a random selection from the other enabled bands in the // coordinated hopping band we next pick from a random permutation
// bands matrix // of the other enabled bands in the hopping bands matrix
if (frequencies_index < 0) if (frequencies_index < 0)
{ {
Dialog::BandList target_bands {m_->WSPR_bands_}; // build sets of available rx and tx bands
// // remove all coordinated bands here since they are auto target_rx_bands = m_->WSPR_bands_.toSet ();
// // scheduled above and including them in the random choice will auto target_tx_bands = target_rx_bands;
// // give them a biased weighting
// for (auto const& band : coordinated_bands)
// {
// target_bands.removeOne (band);
// }
for (auto i = 0; i < m_->bands_[period_index].size (); ++i) for (auto i = 0; i < m_->bands_[period_index].size (); ++i)
{ {
auto const& band = bands->data (bands->index (i, 0)).toString ();
// remove bands that are not enabled for hopping in this phase // remove bands that are not enabled for hopping in this phase
if (!m_->bands_[period_index].testBit (i) if (!m_->bands_[period_index].testBit (i))
// remove Rx only bands if we are wanting to transmit
|| (tx_next && m_->bands_[5].testBit (i)))
{ {
target_bands.removeOne (bands->data (bands->index (i, 0)).toString ()); target_rx_bands.remove (band);
target_tx_bands.remove (band);
}
// remove rx only bands from transmit list and vice versa
if (m_->bands_[5].testBit (i))
{
target_tx_bands.remove (band);
}
else
{
target_rx_bands.remove (band);
} }
} }
// if we have some bands to permute
auto num_bands = target_bands.size (); if (target_rx_bands.size () + target_tx_bands.size ())
if (num_bands) // we have some extra bands available
{ {
int target_index = static_cast<int> (qrand () % num_bands); // random choice if (!(m_->rx_permutation_.size () + m_->tx_permutation_.size ()) // all used up
// here we have a random choice that is enabled in the // or rx list contains a band no longer scheduled
// hopping matrix || !target_rx_bands.contains (m_->rx_permutation_.toSet ())
frequencies_index = frequencies->best_working_frequency (target_bands[target_index]); // or tx list contains a band no longer scheduled for tx
if (frequencies_index >= 0) || !target_tx_bands.contains (m_->tx_permutation_.toSet ()))
{
// build new random permutations
m_->rx_permutation_ = target_rx_bands.toList ();
std::random_shuffle (std::begin (m_->rx_permutation_), std::end (m_->rx_permutation_));
m_->tx_permutation_ = target_tx_bands.toList ();
std::random_shuffle (std::begin (m_->tx_permutation_), std::end (m_->tx_permutation_));
// qDebug () << "New random Rx permutation:" << m_->rx_permutation_
// << "random Tx permutation:" << m_->tx_permutation_;
}
if ((tx_next && m_->tx_permutation_.size ()) || !m_->rx_permutation_.size ())
{
Q_ASSERT (m_->tx_permutation_.size ());
// use one from the current random tx permutation
band_name = m_->tx_permutation_.takeFirst ();
}
else
{
Q_ASSERT (m_->rx_permutation_.size ());
// use one from the current random rx permutation
band_name = m_->rx_permutation_.takeFirst ();
}
// find the first WSPR working frequency for the chosen band
frequencies_index = frequencies->best_working_frequency (band_name);
if (frequencies_index >= 0) // should be a redundant check,
// but to be safe
{ {
// we can use the random choice // we can use the random choice
qDebug () << "random:" << frequencies->data (frequencies->index (frequencies_index, FrequencyList::frequency_column)).toString (); // qDebug () << "random:" << frequencies->data (frequencies->index (frequencies_index, FrequencyList::frequency_column)).toString ();
band_index = bands->find (target_bands[target_index]); band_index = bands->find (band_name);
if (band_index < 0) if (band_index < 0) // this shouldn't happen
{ {
// this shouldn't happen
Q_ASSERT (band_index >= 0); Q_ASSERT (band_index >= 0);
frequencies_index = -1; frequencies_index = -1;
} }
} }
} }
} }
else else
{ {
band_index += 3; band_index += 3;
qDebug () << "scheduled:" << frequencies->data (frequencies->index (frequencies_index, FrequencyList::frequency_column)).toString (); // qDebug () << "scheduled:" << frequencies->data (frequencies->index (frequencies_index, FrequencyList::frequency_column)).toString ();
// remove from random permutations to stop the coordinated bands
// getting too high a weighting - not perfect but surely helps
m_->rx_permutation_.removeOne (band_name);
m_->tx_permutation_.removeOne (band_name);
} }
return { return {
@ -391,13 +424,13 @@ auto WSPRBandHopping::next_hop () -> Hop
, frequencies_index , frequencies_index
, frequencies_index >= 0 // new band , frequencies_index >= 0 // new band
&& !tx_next // not going to Tx anyway && !tx_next // not going to Tx anyway
&& m_->bands_[4].testBit (band_index) // tune up required && m_->bands_[4].testBit (band_index) // tune up required
&& !m_->bands_[5].testBit (band_index) // not an Rx only band && !m_->bands_[5].testBit (band_index) // not an Rx only band
, frequencies_index >= 0 // new band , frequencies_index >= 0 // new band
&& tx_next // Tx scheduled && tx_next // Tx scheduled
&& !m_->bands_[5].testBit (band_index) // not an Rx only band && !m_->bands_[5].testBit (band_index) // not an Rx only band
}; };
} }

View File

@ -22,8 +22,9 @@ class QWidget;
// //
// http://physics.princeton.edu/pulsar/K1JT/doc/wspr/wspr-main.html // http://physics.princeton.edu/pulsar/K1JT/doc/wspr/wspr-main.html
// //
// Along with selecting bands a flag indicating that a short tune up // Along with selecting bands a flag indicating that a short tune up
// signal is required for specified bands. // signal is required for specified bands before they are used for
// receive.
// //
// Provides a Qt property that holds the Tx percentage which is used // Provides a Qt property that holds the Tx percentage which is used
// to generate a semi-randomized schedule of period to transmit. This // to generate a semi-randomized schedule of period to transmit. This
@ -75,6 +76,7 @@ public:
bool next_is_tx (); bool next_is_tx ();
private: private:
// implementation hidden from public interface
class impl; class impl;
pimpl<impl> m_; pimpl<impl> m_;
}; };

View File

@ -4328,10 +4328,10 @@ void MainWindow::WSPR_scheduling ()
bool transmit {false}; bool transmit {false};
if (ui->band_hopping_group_box->isChecked ()) { if (ui->band_hopping_group_box->isChecked ()) {
auto hop_data = m_WSPR_band_hopping.next_hop (); auto hop_data = m_WSPR_band_hopping.next_hop ();
qDebug () << "hop data: period:" << hop_data.period_name_ // qDebug () << "hop data: period:" << hop_data.period_name_
<< "frequencies index:" << hop_data.frequencies_index_ // << "frequencies index:" << hop_data.frequencies_index_
<< "tune:" << hop_data.tune_required_ // << "tune:" << hop_data.tune_required_
<< "tx:" << hop_data.tx_next_; // << "tx:" << hop_data.tx_next_;
transmit = hop_data.tx_next_; transmit = hop_data.tx_next_;