mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-11-03 13:30:52 -05:00 
			
		
		
		
	Note that reverting to v1.7 will invalidate any saved working frequencies from v1.8 but proceeding to v1.8 again will present a reset set of working frequencies i.e. any user defined entries in v1.8 will be lost of a v1.8 -> v1.7 -> v1.8 transition is made. git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@8018 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
		
			
				
	
	
		
			487 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			487 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "WSPRBandHopping.hpp"
 | 
						|
 | 
						|
#include <random>
 | 
						|
 | 
						|
#include <QPointer>
 | 
						|
#include <QSettings>
 | 
						|
#include <QBitArray>
 | 
						|
#include <QList>
 | 
						|
#include <QSet>
 | 
						|
#include <QtWidgets>
 | 
						|
 | 
						|
#include "SettingsGroup.hpp"
 | 
						|
#include "Configuration.hpp"
 | 
						|
#include "Bands.hpp"
 | 
						|
#include "FrequencyList.hpp"
 | 
						|
#include "WsprTxScheduler.h"
 | 
						|
#include "pimpl_impl.hpp"
 | 
						|
#include "moc_WSPRBandHopping.cpp"
 | 
						|
 | 
						|
extern "C"
 | 
						|
{
 | 
						|
#ifndef CMAKE_BUILD
 | 
						|
#define FC_grayline grayline_
 | 
						|
#else
 | 
						|
#include "FC.h"
 | 
						|
  void FC_grayline (int const * year, int const * month, int const * nday, float const * uth, char const * my_grid
 | 
						|
                   , int const * nduration, int * isun
 | 
						|
                   , int my_grid_len);
 | 
						|
#endif
 | 
						|
};
 | 
						|
 | 
						|
namespace
 | 
						|
{
 | 
						|
  char const * const title = "WSPR Band Hopping";
 | 
						|
  char const * const periods[] = {"Sunrise grayline", "Day", "Sunset grayline", "Night", "Tune", "Rx only"};
 | 
						|
  size_t constexpr num_periods {sizeof (periods) / sizeof (periods[0])};
 | 
						|
  // These 10 bands are globally coordinated
 | 
						|
  QList<QString> const coordinated_bands = {"160m", "80m", "60m", "40m", "30m", "20m", "17m", "15m", "12m", "10m"};
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// Dialog - maintenance of band hopping options
 | 
						|
//
 | 
						|
class Dialog
 | 
						|
  : public QDialog
 | 
						|
{
 | 
						|
public:
 | 
						|
  using BandList = QList<QString>;
 | 
						|
 | 
						|
  Dialog (QSettings *, Configuration const *, BandList const * WSPT_bands, QBitArray * bands
 | 
						|
          , int * gray_line_duration, QWidget * parent = nullptr);
 | 
						|
  ~Dialog ();
 | 
						|
 | 
						|
  Q_SLOT void frequencies_changed ();
 | 
						|
  void resize_to_maximum ();
 | 
						|
 | 
						|
private:
 | 
						|
  void closeEvent (QCloseEvent *) override;
 | 
						|
  void save_window_state ();
 | 
						|
 | 
						|
  QSettings * settings_;
 | 
						|
  Configuration const * configuration_;
 | 
						|
  BandList const * WSPR_bands_;
 | 
						|
  QBitArray * bands_;
 | 
						|
  int * gray_line_duration_;
 | 
						|
  QPointer<QTableWidget> bands_table_;
 | 
						|
  QBrush coord_background_brush_;
 | 
						|
  QPointer<QSpinBox> gray_line_width_spin_box_;
 | 
						|
  static int constexpr band_index_role {Qt::UserRole};
 | 
						|
};
 | 
						|
 | 
						|
Dialog::Dialog (QSettings * settings, Configuration const * configuration, BandList const * WSPR_bands
 | 
						|
                , QBitArray * bands, int * gray_line_duration, QWidget * parent)
 | 
						|
  : QDialog {parent, Qt::Window | Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowMinimizeButtonHint}
 | 
						|
  , settings_ {settings}
 | 
						|
  , configuration_ {configuration}
 | 
						|
  , WSPR_bands_ {WSPR_bands}
 | 
						|
  , bands_ {bands}
 | 
						|
  , gray_line_duration_ {gray_line_duration}
 | 
						|
  , bands_table_ {new QTableWidget {this}}
 | 
						|
  , coord_background_brush_ {Qt::yellow}
 | 
						|
  , gray_line_width_spin_box_ {new QSpinBox {this}}
 | 
						|
{
 | 
						|
  setWindowTitle (windowTitle () + ' ' + tr (title));
 | 
						|
  {
 | 
						|
    SettingsGroup g {settings_, title};
 | 
						|
    restoreGeometry (settings_->value ("geometry", saveGeometry ()).toByteArray ());
 | 
						|
  }
 | 
						|
 | 
						|
  QVBoxLayout * main_layout {new QVBoxLayout};
 | 
						|
 | 
						|
  bands_table_->setRowCount (num_periods);
 | 
						|
  bands_table_->setVerticalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
 | 
						|
  bands_table_->setHorizontalScrollBarPolicy (Qt::ScrollBarAlwaysOff);
 | 
						|
  bands_table_->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Expanding);
 | 
						|
  frequencies_changed ();
 | 
						|
  main_layout->addWidget (bands_table_);
 | 
						|
  // recalculate table when frequencies change
 | 
						|
  connect (configuration_->frequencies (), &QAbstractItemModel::layoutChanged
 | 
						|
           , this, &Dialog::frequencies_changed);
 | 
						|
  // handle changes by updating the underlying flags
 | 
						|
  connect (bands_table_.data (), &QTableWidget::itemChanged, [this] (QTableWidgetItem * item) {
 | 
						|
      auto band_number = item->data (band_index_role).toInt ();
 | 
						|
      bands_[item->row ()].setBit (band_number, 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);
 | 
						|
}
 | 
						|
 | 
						|
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 ());
 | 
						|
}
 | 
						|
 | 
						|
void Dialog::frequencies_changed ()
 | 
						|
{
 | 
						|
  bands_table_->setColumnCount (WSPR_bands_->size ());
 | 
						|
  // set up and load the table of check boxes
 | 
						|
  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);
 | 
						|
      int column {0};
 | 
						|
      int band_number {0};
 | 
						|
      for (auto const& band : *configuration_->bands ())
 | 
						|
        {
 | 
						|
          if (WSPR_bands_->contains (band))
 | 
						|
            {
 | 
						|
              if (0 == row)
 | 
						|
                {
 | 
						|
                  auto horizontal_header = new QTableWidgetItem {band};
 | 
						|
                  bands_table_->setHorizontalHeaderItem (column, horizontal_header);
 | 
						|
                }
 | 
						|
              auto item = new QTableWidgetItem;
 | 
						|
              item->setFlags (Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
 | 
						|
              item->setCheckState (bands_[row].testBit (band_number) ? Qt::Checked : Qt::Unchecked);
 | 
						|
              item->setData (band_index_role, band_number);
 | 
						|
              if (coordinated_bands.contains (band))
 | 
						|
                {
 | 
						|
                  item->setBackground (coord_background_brush_);
 | 
						|
                }
 | 
						|
              bands_table_->setItem (row, column, item);
 | 
						|
              ++column;
 | 
						|
            }
 | 
						|
          ++band_number;
 | 
						|
        }
 | 
						|
    }
 | 
						|
  bands_table_->resizeColumnsToContents ();
 | 
						|
  auto is_visible = isVisible ();
 | 
						|
  show ();
 | 
						|
  resize_to_maximum ();
 | 
						|
  adjustSize ();   // fix the size
 | 
						|
  if (!is_visible)
 | 
						|
    {
 | 
						|
      hide ();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// 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 ()
 | 
						|
{
 | 
						|
  bands_table_->setMinimumSize ({
 | 
						|
      bands_table_->horizontalHeader ()->length ()
 | 
						|
        + bands_table_->verticalHeader ()->width ()
 | 
						|
        + 2 * bands_table_->frameWidth ()
 | 
						|
      , bands_table_->verticalHeader ()->length ()
 | 
						|
        + bands_table_->horizontalHeader ()->height ()
 | 
						|
        + 2 * bands_table_->frameWidth ()
 | 
						|
    });
 | 
						|
  bands_table_->setMaximumSize (bands_table_->minimumSize ());
 | 
						|
}
 | 
						|
 | 
						|
class WSPRBandHopping::impl
 | 
						|
{
 | 
						|
public:
 | 
						|
  using BandList = Dialog::BandList;
 | 
						|
 | 
						|
  impl (QSettings * settings, Configuration const * configuration, QWidget * parent_widget)
 | 
						|
    : settings_ {settings}
 | 
						|
    , configuration_ {configuration}
 | 
						|
    , tx_percent_ {0}
 | 
						|
    , parent_widget_ {parent_widget}
 | 
						|
    , last_was_tx_ {false}
 | 
						|
    , carry_ {false}
 | 
						|
    , seed_ {{rand (), rand (), rand (), rand (), rand (), rand (), rand (), rand ()}}
 | 
						|
    , gen_ {seed_}
 | 
						|
    , dist_ {1, 100}
 | 
						|
  {
 | 
						|
    auto num_bands = configuration_->bands ()->rowCount ();
 | 
						|
    for (auto& flags : bands_)
 | 
						|
      {
 | 
						|
        flags.resize (num_bands);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  bool simple_scheduler ();
 | 
						|
 | 
						|
  QSettings * settings_;
 | 
						|
  Configuration const * configuration_;
 | 
						|
  int tx_percent_;
 | 
						|
  BandList WSPR_bands_;
 | 
						|
  BandList rx_permutation_;
 | 
						|
  BandList tx_permutation_;
 | 
						|
  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_;
 | 
						|
  bool last_was_tx_;
 | 
						|
  bool carry_;
 | 
						|
  std::seed_seq seed_;
 | 
						|
  std::mt19937 gen_;
 | 
						|
  std::uniform_int_distribution<int> dist_;
 | 
						|
};
 | 
						|
 | 
						|
bool WSPRBandHopping::impl::simple_scheduler ()
 | 
						|
{
 | 
						|
  auto tx = carry_ || tx_percent_ > dist_ (gen_);
 | 
						|
  if (carry_)
 | 
						|
    {
 | 
						|
      carry_ = false;
 | 
						|
    }
 | 
						|
  else if (tx_percent_ < 40 && last_was_tx_ && tx)
 | 
						|
    {
 | 
						|
      // if percentage is less than 40 then avoid consecutive tx but
 | 
						|
      // always catch up on the next round
 | 
						|
      tx = false;
 | 
						|
      carry_ = true;
 | 
						|
    }
 | 
						|
  last_was_tx_ = tx;
 | 
						|
  return tx;
 | 
						|
}
 | 
						|
 | 
						|
WSPRBandHopping::WSPRBandHopping (QSettings * settings, Configuration const * configuration, QWidget * parent_widget)
 | 
						|
  : m_ {settings, configuration, parent_widget}
 | 
						|
{
 | 
						|
  // detect changes to the working frequencies model
 | 
						|
  m_->WSPR_bands_ = m_->configuration_->frequencies ()->all_bands (m_->configuration_->region (), Modes::WSPR).toList ();
 | 
						|
  connect (m_->configuration_->frequencies (), &QAbstractItemModel::layoutChanged
 | 
						|
           , [this] () {
 | 
						|
             m_->WSPR_bands_ = m_->configuration_->frequencies ()->all_bands (m_->configuration_->region (), Modes::WSPR).toList ();
 | 
						|
           });
 | 
						|
 | 
						|
  // load settings
 | 
						|
  SettingsGroup g {m_->settings_, title};
 | 
						|
  size_t size = m_->settings_->beginReadArray ("phases");
 | 
						|
  for (auto i = 0u; i < size; ++i)
 | 
						|
    {
 | 
						|
      if (i < num_periods)
 | 
						|
        {
 | 
						|
          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 ("phases");
 | 
						|
  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_->configuration_, &m_->WSPR_bands_, m_->bands_
 | 
						|
                                , &m_->gray_line_duration_, m_->parent_widget_};
 | 
						|
    }
 | 
						|
  m_->dialog_->show ();
 | 
						|
  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 (bool tx_enabled) -> 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 the period for this time
 | 
						|
  FC_grayline (&year, &month, &day, &uth, my_grid.toLatin1 ().constData ()
 | 
						|
               , &m_->gray_line_duration_, &period_index
 | 
						|
               , my_grid.size ());
 | 
						|
 | 
						|
  band_index = next_hopping_band();
 | 
						|
 | 
						|
  tx_next = next_is_tx () && tx_enabled;
 | 
						|
 | 
						|
  int frequencies_index {-1};
 | 
						|
  auto const& frequencies = m_->configuration_->frequencies ();
 | 
						|
  auto const& bands = m_->configuration_->bands ();
 | 
						|
  auto band_name = bands->data (bands->index (band_index + 3, 0)).toString ();
 | 
						|
  if (m_->bands_[period_index].testBit (band_index + 3) // +3 for
 | 
						|
                                                        // coordinated bands
 | 
						|
      && m_->WSPR_bands_.contains (band_name))
 | 
						|
    {
 | 
						|
      // 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 (band_name);
 | 
						|
    }
 | 
						|
 | 
						|
  // if we do not have a configured working frequency on the selected
 | 
						|
  // coordinated hopping band we next pick from a random permutation
 | 
						|
  // of the other enabled bands in the hopping bands matrix
 | 
						|
  if (frequencies_index < 0)
 | 
						|
    {
 | 
						|
      // build sets of available rx and tx bands
 | 
						|
      auto target_rx_bands = m_->WSPR_bands_.toSet ();
 | 
						|
      auto target_tx_bands = target_rx_bands;
 | 
						|
      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
 | 
						|
          if (!m_->bands_[period_index].testBit (i))
 | 
						|
            {
 | 
						|
              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
 | 
						|
      if (target_rx_bands.size () + target_tx_bands.size ())
 | 
						|
        {
 | 
						|
          if (!(m_->rx_permutation_.size () + m_->tx_permutation_.size ()) // all used up
 | 
						|
              // or rx list contains a band no longer scheduled
 | 
						|
              || !target_rx_bands.contains (m_->rx_permutation_.toSet ())
 | 
						|
              // or tx list contains a band no longer scheduled for tx
 | 
						|
              || !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
 | 
						|
              // qDebug () << "random:" << frequencies->data (frequencies->index (frequencies_index, FrequencyList_v2::frequency_column)).toString ();
 | 
						|
              band_index = bands->find (band_name);
 | 
						|
              if (band_index < 0) // this shouldn't happen
 | 
						|
                {
 | 
						|
                  Q_ASSERT (band_index >= 0);
 | 
						|
                  frequencies_index = -1;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
     }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      band_index += 3;
 | 
						|
      // qDebug () << "scheduled:" << frequencies->data (frequencies->index (frequencies_index, FrequencyList_v2::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 {
 | 
						|
    periods[period_index]
 | 
						|
 | 
						|
      , frequencies_index
 | 
						|
 | 
						|
      , frequencies_index >= 0               // new band
 | 
						|
      && tx_enabled                          // transmit is allowed
 | 
						|
      && !tx_next                            // not going to Tx anyway
 | 
						|
      && m_->bands_[4].testBit (band_index)  // tune up required
 | 
						|
      && !m_->bands_[5].testBit (band_index) // not an Rx only band
 | 
						|
 | 
						|
      , frequencies_index >= 0               // new band
 | 
						|
      && tx_next                             // Tx scheduled
 | 
						|
      && !m_->bands_[5].testBit (band_index) // not an Rx only band
 | 
						|
   };
 | 
						|
}
 | 
						|
 | 
						|
bool WSPRBandHopping::next_is_tx (bool simple_schedule)
 | 
						|
{
 | 
						|
  if (simple_schedule)
 | 
						|
    {
 | 
						|
      return m_->simple_scheduler ();
 | 
						|
    }
 | 
						|
  if (100 == m_->tx_percent_)
 | 
						|
    {
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
  else
 | 
						|
    {
 | 
						|
      // consult scheduler to determine if next period should be a tx interval
 | 
						|
      return next_tx_state(m_->tx_percent_);
 | 
						|
    }
 | 
						|
}
 |