Merge branch 'develop' into hound-list-changes

This commit is contained in:
Brian Moran 2022-11-19 07:06:16 -08:00
commit 0820a86d37
26 changed files with 1060 additions and 337 deletions

View File

@ -190,6 +190,7 @@ set (wsjt_qt_CXXSRCS
widgets/FrequencyDeltaLineEdit.cpp
item_delegates/CandidateKeyFilter.cpp
item_delegates/ForeignKeyDelegate.cpp
item_delegates/MessageItemDelegate.cpp
validators/LiveFrequencyValidator.cpp
GetUserId.cpp
Audio/AudioDevice.cpp

View File

@ -168,6 +168,11 @@
#include <QHostAddress>
#include <QStandardItem>
#include <QDebug>
#include <QDateTimeEdit>
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>
#include "pimpl_impl.hpp"
#include "Logger.hpp"
@ -180,6 +185,7 @@
#include "item_delegates/ForeignKeyDelegate.hpp"
#include "item_delegates/FrequencyDelegate.hpp"
#include "item_delegates/FrequencyDeltaDelegate.hpp"
#include "item_delegates/MessageItemDelegate.hpp"
#include "Transceiver/TransceiverFactory.hpp"
#include "Transceiver/Transceiver.hpp"
#include "models/Bands.hpp"
@ -250,7 +256,8 @@ namespace
// Magic numbers for file validation
constexpr quint32 qrg_magic {0xadbccbdb};
constexpr quint32 qrg_version {100}; // M.mm
constexpr quint32 qrg_version {101}; // M.mm
constexpr quint32 qrg_version_100 {100};
}
@ -263,13 +270,27 @@ class FrequencyDialog final
Q_OBJECT
public:
using Item = FrequencyList_v2::Item;
using Item = FrequencyList_v2_101::Item;
explicit FrequencyDialog (IARURegions * regions_model, Modes * modes_model, QWidget * parent = nullptr)
: QDialog {parent}
{
start_date_time_edit_ = new QDateTimeEdit(QDateTime(QDate::currentDate(), QTime(0,0,0,0), Qt::UTC), parent);
end_date_time_edit_ = new QDateTimeEdit(QDateTime(QDate::currentDate().addDays(2), QTime(0,0,0,0), Qt::UTC), parent);
enable_dates_checkbox_ = new QCheckBox {tr ("")};
start_date_time_edit_->setDisplayFormat("yyyy.MM.dd hh:mm:ss 'UTC'");
start_date_time_edit_->setTimeSpec(Qt::UTC);
start_date_time_edit_->setMinimumDate(QDate::currentDate().addDays(-365));
end_date_time_edit_->setDisplayFormat("yyyy.MM.dd hh:mm:ss 'UTC'");
end_date_time_edit_->setTimeSpec(Qt::UTC);
end_date_time_edit_->setMinimumDate(QDate::currentDate().addDays(-365));
preferred_frequency_checkbox_ = new QCheckBox {tr ("")};
setWindowTitle (QApplication::applicationName () + " - " +
tr ("Add Frequency"));
region_combo_box_.setModel (regions_model);
mode_combo_box_.setModel (modes_model);
@ -277,28 +298,74 @@ public:
form_layout->addRow (tr ("IARU &Region:"), &region_combo_box_);
form_layout->addRow (tr ("&Mode:"), &mode_combo_box_);
form_layout->addRow (tr ("&Frequency (MHz):"), &frequency_line_edit_);
form_layout->addRow (tr ("&Preferred for Band/Mode:"), preferred_frequency_checkbox_);
form_layout->addRow (tr ("&Description:"), &description_line_edit_);
form_layout->addRow (tr ("&Enable Date Range:"), enable_dates_checkbox_);
form_layout->addRow (tr ("S&tart:"), start_date_time_edit_);
form_layout->addRow (tr ("&End:"), end_date_time_edit_);
form_layout->addRow (tr ("&Source:"), &source_line_edit_);
auto main_layout = new QVBoxLayout (this);
main_layout->addLayout (form_layout);
auto button_box = new QDialogButtonBox {QDialogButtonBox::Ok | QDialogButtonBox::Cancel};
button_box = new QDialogButtonBox {QDialogButtonBox::Ok | QDialogButtonBox::Cancel};
main_layout->addWidget (button_box);
connect (button_box, &QDialogButtonBox::accepted, this, &FrequencyDialog::accept);
connect (button_box, &QDialogButtonBox::rejected, this, &FrequencyDialog::reject);
connect(start_date_time_edit_, &QDateTimeEdit::dateTimeChanged, this, &FrequencyDialog::checkSaneDates);
connect(end_date_time_edit_, &QDateTimeEdit::dateTimeChanged, this, &FrequencyDialog::checkSaneDates);
connect(enable_dates_checkbox_, &QCheckBox::stateChanged, this, &FrequencyDialog::toggleValidity);
toggleValidity();
}
void toggleValidity()
{
start_date_time_edit_->setEnabled(enable_dates_checkbox_->isChecked());
end_date_time_edit_->setEnabled(enable_dates_checkbox_->isChecked());
checkSaneDates();
}
void checkSaneDates()
{
if (enable_dates_checkbox_->isChecked() && start_date_time_edit_->dateTime().isValid() && end_date_time_edit_->dateTime().isValid())
{
if (start_date_time_edit_->dateTime() > end_date_time_edit_->dateTime())
{
QMessageBox::warning(this, tr("Invalid Date Range"), tr("Start date must be before end date"));
button_box->button(QDialogButtonBox::Ok)->setEnabled(false);
return;
}
}
button_box->button(QDialogButtonBox::Ok)->setEnabled(true);
}
Item item () const
{
return {frequency_line_edit_.frequency ()
, Modes::value (mode_combo_box_.currentText ())
, IARURegions::value (region_combo_box_.currentText ())};
QDateTime start_time = enable_dates_checkbox_->isChecked() ? start_date_time_edit_->dateTime() : QDateTime();
QDateTime end_time = enable_dates_checkbox_->isChecked() ? end_date_time_edit_->dateTime() : QDateTime();
return {
frequency_line_edit_.frequency(),
Modes::value(mode_combo_box_.currentText()),
IARURegions::value(region_combo_box_.currentText()),
description_line_edit_.text(), source_line_edit_.text(),
start_time,
end_time,
preferred_frequency_checkbox_->isChecked()
};
}
private:
QComboBox region_combo_box_;
QComboBox mode_combo_box_;
FrequencyLineEdit frequency_line_edit_;
QLineEdit description_line_edit_;
QLineEdit source_line_edit_;
QDialogButtonBox * button_box;
QCheckBox *enable_dates_checkbox_;
QCheckBox *preferred_frequency_checkbox_;
QDateTimeEdit *end_date_time_edit_;
QDateTimeEdit *start_date_time_edit_;
};
@ -375,31 +442,7 @@ public:
};
//
// Class MessageItemDelegate
//
// Item delegate for message entry such as free text message macros.
//
class MessageItemDelegate final
: public QStyledItemDelegate
{
public:
explicit MessageItemDelegate (QObject * parent = nullptr)
: QStyledItemDelegate {parent}
{
}
QWidget * createEditor (QWidget * parent
, QStyleOptionViewItem const& /* option*/
, QModelIndex const& /* index */
) const override
{
auto editor = new QLineEdit {parent};
editor->setFrame (false);
editor->setValidator (new QRegularExpressionValidator {message_alphabet, editor});
return editor;
}
};
// Internal implementation of the Configuration class.
class Configuration::impl final
@ -477,7 +520,9 @@ private:
void save_frequencies ();
void reset_frequencies ();
void insert_frequency ();
FrequencyList_v2::FrequencyItems read_frequencies_file (QString const&);
void size_frequency_table_columns();
FrequencyList_v2_101::FrequencyItems read_frequencies_file (QString const&);
void delete_stations ();
void insert_station ();
@ -569,8 +614,8 @@ private:
IARURegions regions_;
IARURegions::Region region_;
Modes modes_;
FrequencyList_v2 frequencies_;
FrequencyList_v2 next_frequencies_;
FrequencyList_v2_101 frequencies_;
FrequencyList_v2_101 next_frequencies_;
StationList stations_;
StationList next_stations_;
FrequencyDelta current_offset_;
@ -776,8 +821,8 @@ Bands const * Configuration::bands () const {return &m_->bands_;}
StationList * Configuration::stations () {return &m_->stations_;}
StationList const * Configuration::stations () const {return &m_->stations_;}
IARURegions::Region Configuration::region () const {return m_->region_;}
FrequencyList_v2 * Configuration::frequencies () {return &m_->frequencies_;}
FrequencyList_v2 const * Configuration::frequencies () const {return &m_->frequencies_;}
FrequencyList_v2_101 * Configuration::frequencies () {return &m_->frequencies_;}
FrequencyList_v2_101 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_;}
@ -1045,7 +1090,7 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
throw std::runtime_error {"Failed to create save directory"};
}
// we now have a deafult save path that exists
// we now have a default save path that exists
// make sure samples directory exists
QString samples_dir {"samples"};
@ -1194,20 +1239,23 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
//
// setup working frequencies table model & view
//
frequencies_.sort (FrequencyList_v2::frequency_column);
frequencies_.sort (FrequencyList_v2_101::frequency_column);
ui_->frequencies_table_view->setModel (&next_frequencies_);
ui_->frequencies_table_view->horizontalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
ui_->frequencies_table_view->horizontalHeader ()->setResizeContentsPrecision (0);
ui_->frequencies_table_view->horizontalHeader ()->moveSection(8, 3); // swap preferred to be in front of description
ui_->frequencies_table_view->verticalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
ui_->frequencies_table_view->verticalHeader ()->setResizeContentsPrecision (0);
ui_->frequencies_table_view->sortByColumn (FrequencyList_v2::frequency_column, Qt::AscendingOrder);
ui_->frequencies_table_view->setColumnHidden (FrequencyList_v2::frequency_mhz_column, true);
ui_->frequencies_table_view->sortByColumn (FrequencyList_v2_101::frequency_column, Qt::AscendingOrder);
ui_->frequencies_table_view->setColumnHidden (FrequencyList_v2_101::frequency_mhz_column, true);
ui_->frequencies_table_view->setColumnHidden (FrequencyList_v2_101::source_column, true);
// delegates
ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2::frequency_column, new FrequencyDelegate {this});
ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2::region_column, new ForeignKeyDelegate {&regions_, 0, this});
ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2::mode_column, new ForeignKeyDelegate {&modes_, 0, this});
ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2_101::frequency_column, new FrequencyDelegate {this});
ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2_101::region_column, new ForeignKeyDelegate {&regions_, 0, this});
ui_->frequencies_table_view->setItemDelegateForColumn (FrequencyList_v2_101::mode_column, new ForeignKeyDelegate {&modes_, 0, this});
// actions
frequency_delete_action_ = new QAction {tr ("&Delete"), ui_->frequencies_table_view};
@ -1452,6 +1500,13 @@ void Configuration::impl::done (int r)
void Configuration::impl::read_settings ()
{
SettingsGroup g {settings_, "Configuration"};
LOG_INFO(QString{"Configuration Settings (%1)"}.arg(settings_->fileName()));
QStringList keys = settings_->allKeys();
//Q_FOREACH (auto const& item, keys)
//{
// LOG_INFO(QString{" %1 = %2"}.arg(item).arg(settings_->value(item).toString()));
//}
restoreGeometry (settings_->value ("window/geometry").toByteArray ());
my_callsign_ = settings_->value ("MyCall", QString {}).toString ();
@ -1505,12 +1560,20 @@ void Configuration::impl::read_settings ()
region_ = settings_->value ("Region", QVariant::fromValue (IARURegions::ALL)).value<IARURegions::Region> ();
if (settings_->contains ("FrequenciesForRegionModes"))
LOG_INFO(QString{"Reading frequencies"});
if (settings_->contains ("FrequenciesForRegionModes_v2"))
{
auto const& v = settings_->value ("FrequenciesForRegionModes");
LOG_INFO(QString{"read_settings found FrequenciesForRegionModes_v2"});
if (settings_->contains ("FrequenciesForRegionModes"))
{
LOG_INFO(QString{"read_settings ALSO found FrequenciesForRegionModes"});
}
auto const& v = settings_->value ("FrequenciesForRegionModes_v2");
if (v.isValid ())
{
frequencies_.frequency_list (v.value<FrequencyList_v2::FrequencyItems> ());
frequencies_.frequency_list (v.value<FrequencyList_v2_101::FrequencyItems> ());
}
else
{
@ -1519,7 +1582,34 @@ void Configuration::impl::read_settings ()
}
else
{
frequencies_.reset_to_defaults ();
LOG_INFO(QString{"read_settings looking for FrequenciesForRegionModes"});
if (settings_->contains ("FrequenciesForRegionModes")) // has the old ones.
{
LOG_INFO(QString{"found FrequenciesForRegionModes"});
auto const& v = settings_->value("FrequenciesForRegionModes");
LOG_INFO(QString{"v is %1"}.arg(v.typeName()));
if (v.isValid())
{
LOG_INFO(QString{"read_settings found VALID FrequenciesForRegionModes"});
FrequencyList_v2_101::FrequencyItems list;
FrequencyList_v2::FrequencyItems v100 = v.value<FrequencyList_v2::FrequencyItems>();
LOG_INFO(QString{"read_settings read %1 old_format items from FrequenciesForRegionModes"}.arg(v100.size()));
Q_FOREACH (auto const& item, v100)
{
list << FrequencyList_v2_101::Item{item.frequency_, item.mode_, item.region_, QString(), QString(), QDateTime(),
QDateTime(), false};
}
LOG_INFO(QString{"converted %1 items to FrequenciesForRegionModes_v2"}.arg(list.size()));
frequencies_.frequency_list(list);
}
else
{
LOG_INFO(QString{"read_settings INVALID FrequenciesForRegionModes"});
frequencies_.reset_to_defaults();
}
}
}
stations_.station_list (settings_->value ("stations").value<StationList::Stations> ());
@ -1661,8 +1751,8 @@ void Configuration::impl::write_settings ()
settings_->setValue ("After73", id_after_73_);
settings_->setValue ("TxQSYAllowed", tx_QSY_allowed_);
settings_->setValue ("Macros", macros_.stringList ());
settings_->setValue ("FrequenciesForRegionModes", QVariant::fromValue (frequencies_.frequency_list ()));
settings_->setValue ("stations", QVariant::fromValue (stations_.station_list ()));
settings_->setValue ("FrequenciesForRegionModes_v2", QVariant::fromValue (frequencies_.frequency_list ()));
settings_->setValue ("DecodeHighlighting", QVariant::fromValue (decode_highlighing_model_.items ()));
settings_->setValue ("HighlightByMode", highlight_by_mode_);
settings_->setValue ("OnlyFieldsSought", highlight_only_fields_);
@ -1989,7 +2079,6 @@ TransceiverFactory::ParameterPack Configuration::impl::gather_rig_data ()
void Configuration::impl::accept ()
{
// Called when OK button is clicked.
if (!validate ())
{
return; // not accepting
@ -2190,7 +2279,7 @@ void Configuration::impl::accept ()
if (frequencies_.frequency_list () != next_frequencies_.frequency_list ())
{
frequencies_.frequency_list (next_frequencies_.frequency_list ());
frequencies_.sort (FrequencyList_v2::frequency_column);
frequencies_.sort (FrequencyList_v2_101::frequency_column);
}
if (stations_.station_list () != next_stations_.station_list ())
@ -2533,17 +2622,25 @@ void Configuration::impl::check_multicast (QHostAddress const& ha)
udp_server_name_edited_ = false;
}
void Configuration::impl::size_frequency_table_columns()
{
ui_->frequencies_table_view->setVisible(false);
ui_->frequencies_table_view->resizeColumnsToContents();
ui_->frequencies_table_view->setVisible(true);
}
void Configuration::impl::delete_frequencies ()
{
auto selection_model = ui_->frequencies_table_view->selectionModel ();
selection_model->select (selection_model->selection (), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
next_frequencies_.removeDisjointRows (selection_model->selectedRows ());
ui_->frequencies_table_view->resizeColumnToContents (FrequencyList_v2::mode_column);
ui_->frequencies_table_view->resizeColumnToContents (FrequencyList_v2_101::mode_column);
size_frequency_table_columns ();
}
void Configuration::impl::load_frequencies ()
{
auto file_name = QFileDialog::getOpenFileName (this, tr ("Load Working Frequencies"), writeable_data_dir_.absolutePath (), tr ("Frequency files (*.qrg);;All files (*.*)"));
auto file_name = QFileDialog::getOpenFileName (this, tr ("Load Working Frequencies"), writeable_data_dir_.absolutePath (), tr ("Frequency files (*.qrg *.qrg.json);;All files (*.*)"));
if (!file_name.isNull ())
{
auto const list = read_frequencies_file (file_name);
@ -2557,24 +2654,42 @@ void Configuration::impl::load_frequencies ()
{
next_frequencies_.frequency_list (list); // update the model
}
size_frequency_table_columns();
}
}
void Configuration::impl::merge_frequencies ()
{
auto file_name = QFileDialog::getOpenFileName (this, tr ("Merge Working Frequencies"), writeable_data_dir_.absolutePath (), tr ("Frequency files (*.qrg);;All files (*.*)"));
auto file_name = QFileDialog::getOpenFileName (this, tr ("Merge Working Frequencies"), writeable_data_dir_.absolutePath (), tr ("Frequency files (*.qrg *.qrg.json);;All files (*.*)"));
if (!file_name.isNull ())
{
next_frequencies_.frequency_list_merge (read_frequencies_file (file_name)); // update the model
size_frequency_table_columns();
}
}
FrequencyList_v2::FrequencyItems Configuration::impl::read_frequencies_file (QString const& file_name)
FrequencyList_v2_101::FrequencyItems Configuration::impl::read_frequencies_file (QString const& file_name)
{
QFile frequencies_file {file_name};
frequencies_file.open (QFile::ReadOnly);
QDataStream ids {&frequencies_file};
FrequencyList_v2::FrequencyItems list;
FrequencyList_v2_101::FrequencyItems list;
FrequencyList_v2::FrequencyItems list_v100;
// read file as json if ends with qrg.json.
if (file_name.endsWith(".qrg.json", Qt::CaseInsensitive))
{
try
{
list = FrequencyList_v2_101::from_json_file(&frequencies_file);
}
catch (ReadFileException const &e)
{
MessageBox::critical_message(this, tr("Error reading frequency file"), e.message_);
}
return list;
}
quint32 magic;
ids >> magic;
if (qrg_magic != magic)
@ -2593,8 +2708,20 @@ FrequencyList_v2::FrequencyItems Configuration::impl::read_frequencies_file (QSt
}
// de-serialize the data using version if necessary to
// handle old schemata
ids >> list;
// handle old schema
if (version == qrg_version_100)
{
ids >> list_v100;
Q_FOREACH (auto const& item, list_v100)
{
list << FrequencyList_v2_101::Item{item.frequency_, item.mode_, item.region_, QString(), QString(), QDateTime(),
QDateTime(), false};
}
}
else
{
ids >> list;
}
if (ids.status () != QDataStream::Ok || !ids.atEnd ())
{
@ -2602,18 +2729,21 @@ FrequencyList_v2::FrequencyItems Configuration::impl::read_frequencies_file (QSt
list.clear ();
return list;
}
return list;
}
void Configuration::impl::save_frequencies ()
{
auto file_name = QFileDialog::getSaveFileName (this, tr ("Save Working Frequencies"), writeable_data_dir_.absolutePath (), tr ("Frequency files (*.qrg);;All files (*.*)"));
auto file_name = QFileDialog::getSaveFileName (this, tr ("Save Working Frequencies"), writeable_data_dir_.absolutePath (), tr ("Frequency files (*.qrg *.qrg.json);;All files (*.*)"));
if (!file_name.isNull ())
{
bool b_write_json = file_name.endsWith(".qrg.json", Qt::CaseInsensitive);
QFile frequencies_file {file_name};
frequencies_file.open (QFile::WriteOnly);
QDataStream ods {&frequencies_file};
auto selection_model = ui_->frequencies_table_view->selectionModel ();
if (selection_model->hasSelection ()
&& MessageBox::Yes == MessageBox::query_message (this
@ -2623,11 +2753,28 @@ void Configuration::impl::save_frequencies ()
"Click No to save all.")))
{
selection_model->select (selection_model->selection (), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
ods << qrg_magic << qrg_version << next_frequencies_.frequency_list (selection_model->selectedRows ());
if (b_write_json)
{
next_frequencies_.to_json_file(&frequencies_file, "0x" + QString::number(qrg_magic, 16).toUpper(),
"0x" + QString::number(qrg_version, 16).toUpper(),
next_frequencies_.frequency_list(selection_model->selectedRows()));
} else
{
ods << qrg_magic << qrg_version << next_frequencies_.frequency_list(selection_model->selectedRows());
}
}
else
{
ods << qrg_magic << qrg_version << next_frequencies_.frequency_list ();
if (b_write_json)
{
next_frequencies_.to_json_file(&frequencies_file,
"0x" + QString::number(qrg_magic, 16).toUpper(),
"0x" + QString::number(qrg_version, 16).toUpper(),
next_frequencies_.frequency_list());
} else
{
ods << qrg_magic << qrg_version << next_frequencies_.frequency_list();
}
}
}
}
@ -2641,6 +2788,7 @@ void Configuration::impl::reset_frequencies ()
{
next_frequencies_.reset_to_defaults ();
}
size_frequency_table_columns ();
}
void Configuration::impl::insert_frequency ()
@ -2648,7 +2796,8 @@ void Configuration::impl::insert_frequency ()
if (QDialog::Accepted == frequency_dialog_->exec ())
{
ui_->frequencies_table_view->setCurrentIndex (next_frequencies_.add (frequency_dialog_->item ()));
ui_->frequencies_table_view->resizeColumnToContents (FrequencyList_v2::mode_column);
ui_->frequencies_table_view->resizeColumnToContents (FrequencyList_v2_101::mode_column);
size_frequency_table_columns();
}
}

View File

@ -18,7 +18,7 @@ class QAudioDeviceInfo;
class QDir;
class QNetworkAccessManager;
class Bands;
class FrequencyList_v2;
class FrequencyList_v2_101;
class StationList;
class QStringListModel;
class LotWUsers;
@ -164,8 +164,8 @@ public:
Bands * bands ();
Bands const * bands () const;
IARURegions::Region region () const;
FrequencyList_v2 * frequencies ();
FrequencyList_v2 const * frequencies () const;
FrequencyList_v2_101 * frequencies ();
FrequencyList_v2_101 const * frequencies () const;
StationList * stations ();
StationList const * stations () const;
QStringListModel * macros ();

View File

@ -52,9 +52,13 @@ void register_types ()
item_editor_factory->registerEditor (qMetaTypeId<QDateTime> (), new QStandardItemEditorCreator<DateTimeEdit> ());
// Frequency list model
// V101 Frequency list model
qRegisterMetaTypeStreamOperators<FrequencyList_v2_101::Item> ("Item_v2_101");
QMetaType::registerConverter<FrequencyList_v2_101::Item, QString> (&FrequencyList_v2_101::Item::toString);
qRegisterMetaTypeStreamOperators<FrequencyList_v2_101::FrequencyItems> ("FrequencyItems_v2_101");
// V100 Frequency list model
qRegisterMetaTypeStreamOperators<FrequencyList_v2::Item> ("Item_v2");
QMetaType::registerConverter<FrequencyList_v2::Item, QString> (&FrequencyList_v2::Item::toString);
qRegisterMetaTypeStreamOperators<FrequencyList_v2::FrequencyItems> ("FrequencyItems_v2");
// defunct old versions

View File

@ -41,7 +41,7 @@ class QWidget;
// storage using the provided QSettings object instance.
//
// A passed in Configuration object instance is used to query the
// FrequencyList_v2 model to determine working frequencies for each
// FrequencyList_v2_101 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.

View File

@ -0,0 +1,26 @@
//
// Moved from Configuration.cpp
//
#include "MessageItemDelegate.hpp"
#include <QLineEdit>
#include <QRegExpValidator>
//
// Class MessageItemDelegate
//
// Item delegate for message entry such as free text message macros.
//
MessageItemDelegate::MessageItemDelegate(QObject *parent): QStyledItemDelegate{parent}
{
}
QWidget *MessageItemDelegate::createEditor(QWidget *parent, QStyleOptionViewItem const &, QModelIndex const &) const
{
QRegularExpression message_alphabet{"[- @A-Za-z0-9+./?#<>;$]*"};
auto editor = new QLineEdit{parent};
editor->setFrame(false);
editor->setValidator(new QRegularExpressionValidator{message_alphabet, editor});
return editor;
}

View File

@ -0,0 +1,20 @@
//
//
//
#ifndef WSJTX_MESSAGEITEMDELEGATE_H
#define WSJTX_MESSAGEITEMDELEGATE_H
#include <QStyledItemDelegate>
class MessageItemDelegate: public QStyledItemDelegate
{
Q_OBJECT
public:
explicit MessageItemDelegate(QObject *parent = nullptr);
QWidget *createEditor(QWidget *parent, QStyleOptionViewItem const & /* option*/
, QModelIndex const & /* index */
) const override;
};
#endif //WSJTX_MESSAGEITEMDELEGATE_H

View File

@ -586,13 +586,26 @@ contains
xsig=xsig+s4(itone(i),i)
enddo
base=candidates(icand,5)
arg=600.0*(xsig/base)-1.0
select case(ntrperiod)
case(15)
snr_calfac=800.0
case(30)
snr_calfac=600.0
case(60)
snr_calfac=430.0
case(120)
snr_calfac=390.0
case(300)
snr_calfac=340.0
case(900)
snr_calfac=320.0
case(1800)
snr_calfac=320.0
case default
end select
arg=snr_calfac*xsig/base - 1.0
if(arg.gt.0.0) then
xsnr=10*log10(arg)-35.5-12.5*log10(nsps/8200.0)
if(ntrperiod.eq. 15) xsnr=xsnr+2
if(ntrperiod.eq. 30) xsnr=xsnr+1
if(ntrperiod.eq. 900) xsnr=xsnr+1
if(ntrperiod.eq.1800) xsnr=xsnr+2
xsnr=10*log10(arg)+10*log10(1.46/2500)+10*log10(8200.0/nsps)
else
xsnr=-99.9
endif

View File

@ -14,6 +14,7 @@ subroutine decode0(dd,ss,savg,nstandalone)
mcall3,nkeep,ntol,nxant,nrxlog,nfsample,nxpol,nmode, &
nfast,nsave,max_drift,nhsym,mycall,mygrid,hiscall,hisgrid,datetime
common/early/nhsym1,nhsym2,ldecoded(32768)
common/decodes/ndecodes
data neme0/-99/,mcall3b/1/
save
@ -62,8 +63,8 @@ subroutine decode0(dd,ss,savg,nstandalone)
call sec0(1,tdec)
if(nhsym.eq.nhsym1) write(*,1010) nsum,nsave,nstandalone,nhsym,tdec
1010 format('<EarlyFinished>',3i4,i6,f6.2)
if(nhsym.eq.nhsym2) write(*,1012) nsum,nsave,nstandalone,nhsym,tdec
1012 format('<DecodeFinished>',3i4,i6,f6.2)
if(nhsym.eq.nhsym2) write(*,1012) nsum,nsave,nstandalone,nhsym,tdec,ndecodes
1012 format('<DecodeFinished>',3i4,i6,f6.2,i5)
flush(6)
return

View File

@ -34,12 +34,15 @@ subroutine map65a(dd,ss,savg,newdat,nutc,fcenter,ntol,idphi,nfa,nfb, &
common/c3com/ mcall3a
common/testcom/ifreq
common/early/nhsym1,nhsym2,ldecoded(32768)
common/decodes/ndecodes
data blank/' '/,cm/'#'/
data shmsg0/'ATT','RO ','RRR','73 '/
data nfile/0/,nutc0/-999/,nid/0/,ip000/1/,ip001/1/,mousefqso0/-999/
save
ndecodes=0
! Clean start for Q65 at early decode
if(nhsym.eq.nhsym1 .or. nagain.ne.0) ldecoded=.false.
if(ndiskdat.eq.1) ldecoded=.false.
@ -499,6 +502,7 @@ subroutine map65a(dd,ss,savg,newdat,nutc,fcenter,ntol,idphi,nfa,nfb, &
write(26,1014) f0,ndf,ndf0,ndf1,ndf2,dt,npol,nsync1, &
nsync2,nutc,decoded,cp,cmode
1014 format(f8.3,i5,3i3,f5.1,i4,i3,i4,i5.4,4x,a22,2x,a1,3x,a2)
ndecodes=ndecodes+1
write(21,1100) f0,ndf,dt,npol,nsync2,nutc,decoded,cp, &
cmode(1:1),cmode(2:2)
1100 format(f8.3,i5,f5.1,2i4,i5.4,2x,a22,2x,a1,3x,a1,1x,a1)

View File

@ -35,6 +35,7 @@ subroutine q65b(nutc,nqd,nxant,fcenter,nfcal,nfsample,ikhz,mousedf,ntol,xpol, &
character*1 cp,cmode*2
common/cacb/ca,cb
common/early/nhsym1,nhsym2,ldecoded(32768)
common/decodes/ndecodes
data nutc00/-1/,msg00/' '/
save
@ -179,6 +180,7 @@ subroutine q65b(nutc,nqd,nxant,fcenter,nfcal,nfsample,ikhz,mousedf,ntol,xpol, &
! to map65_rx.log
if(nutc.ne.nutc00 .or. msg0(1:28).ne.msg00 .or. freq1.ne.freq1_00) then
! Write to file map65_rx.log:
ndecodes=ndecodes+1
write(21,1110) freq1,ndf,xdt0,npol,nsnr0,nutc,msg0(1:28),cq0
1110 format(f8.3,i5,f5.1,2i4,i5.4,2x,a28,': A',2x,a3)
nutc00=nutc

View File

@ -1416,6 +1416,8 @@ void MainWindow::readFromStdout() //readFromStdout
QFile lockFile(m_appDir + "/.lock");
lockFile.open(QIODevice::ReadWrite);
if(t.indexOf("<DecodeFinished>") >= 0) {
int ndecodes=t.mid(40,5).toInt();
lab5->setText(QString::number(ndecodes));
m_map65RxLog=0;
m_startAnother=m_loopall;
}

View File

@ -194,7 +194,6 @@ private:
qint32 m_RxState;
qint32 m_dB;
double m_fAdd;
// double m_IQamp;
// double m_IQphase;

View File

@ -24,7 +24,7 @@
//
// Implements the QAbstractTableModel interface as an immutable table
// where rows are bands and columns are band name, lower frequency
// limit and, upper ferquency limit respectively.
// limit and, upper frequency limit respectively.
//
class Bands final
: public QAbstractTableModel

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,11 @@
#include <QList>
#include <QSortFilterProxyModel>
#include <QDateTime>
#include <QJsonObject>
#include <QJsonDocument>
#include <QFile>
#include <QException>
#include "Radio.hpp"
#include "IARURegions.hpp"
@ -37,7 +42,7 @@ class Bands;
// Implements the QSortFilterProxyModel interface for a list of spot
// frequencies.
//
class FrequencyList_v2 final
class FrequencyList_v2_101 final
: public QSortFilterProxyModel
{
Q_OBJECT;
@ -52,18 +57,26 @@ public:
Frequency frequency_;
Mode mode_;
Region region_;
QString description_;
QString source_;
QDateTime start_time_;
QDateTime end_time_;
bool preferred_; // preferred frequency for this band and mode
QString toString () const;
bool isSane() const;
QJsonObject toJson () const;
};
using FrequencyItems = QList<Item>;
using BandSet = QSet<QString>;
enum Column {region_column, mode_column, frequency_column, frequency_mhz_column, SENTINAL};
enum Column {region_column, mode_column, frequency_column, frequency_mhz_column, description_column, start_time_column, end_time_column, source_column, preferred_column, SENTINAL};
// an iterator that meets the requirements of the C++ for range statement
class const_iterator
{
public:
const_iterator (FrequencyList_v2 const * parent, int row)
const_iterator (FrequencyList_v2_101 const * parent, int row)
: parent_ {parent}
, row_ {row}
{
@ -76,18 +89,21 @@ public:
const_iterator& operator ++ ();
private:
FrequencyList_v2 const * parent_;
FrequencyList_v2_101 const * parent_;
int row_;
};
explicit FrequencyList_v2 (Bands const *, QObject * parent = nullptr);
~FrequencyList_v2 ();
explicit FrequencyList_v2_101 (Bands const *, QObject * parent = nullptr);
~FrequencyList_v2_101 ();
// Load and store underlying items
FrequencyItems frequency_list (FrequencyItems);
FrequencyItems const& frequency_list () const;
FrequencyItems frequency_list (QModelIndexList const&) const;
void frequency_list_merge (FrequencyItems const&);
void to_json_file(QFile *, QString, QString, FrequencyItems const&);
static FrequencyItems from_json_file(QFile *);
// Iterators for the sorted and filtered items
//
@ -116,7 +132,7 @@ public:
int best_working_frequency (QString const& band) const;
// Set filter
Q_SLOT void filter (Region, Mode);
Q_SLOT void filter (Region, Mode, bool);
// Reset
Q_SLOT void reset_to_defaults ();
@ -129,6 +145,9 @@ public:
// Proxy API
bool filterAcceptsRow (int source_row, QModelIndex const& parent) const override;
// Refresh the filter based on the current filter settings (underlying data may have changed)
void filter_refresh ();
// Custom roles.
static int constexpr SortRole = Qt::UserRole;
@ -138,25 +157,56 @@ private:
};
inline
bool operator == (FrequencyList_v2::Item const& lhs, FrequencyList_v2::Item const& rhs)
bool operator == (FrequencyList_v2_101::Item const& lhs, FrequencyList_v2_101::Item const& rhs)
{
return
lhs.frequency_ == rhs.frequency_
&& lhs.region_ == rhs.region_
&& lhs.mode_ == rhs.mode_;
&& lhs.mode_ == rhs.mode_
&& lhs.description_ == rhs.description_
&& lhs.source_ == rhs.source_
&& lhs.start_time_ == rhs.start_time_
&& lhs.end_time_ == rhs.end_time_
&& lhs.preferred_ == rhs.preferred_;
}
QDataStream& operator << (QDataStream&, FrequencyList_v2_101::Item const&);
QDataStream& operator >> (QDataStream&, FrequencyList_v2_101::Item&);
#if !defined (QT_NO_DEBUG_STREAM)
QDebug operator << (QDebug, FrequencyList_v2_101::Item const&);
#endif
Q_DECLARE_METATYPE (FrequencyList_v2_101::Item);
Q_DECLARE_METATYPE (FrequencyList_v2_101::FrequencyItems);
class FrequencyList_v2 final
{
public:
using Frequency = Radio::Frequency;
using Mode = Modes::Mode;
using Region = IARURegions::Region;
struct Item
{
Frequency frequency_;
Mode mode_;
Region region_;
QString toString () const;
};
using FrequencyItems = QList<Item>;
private:
FrequencyItems frequency_list_;
};
QDataStream& operator << (QDataStream&, FrequencyList_v2::Item const&);
QDataStream& operator >> (QDataStream&, FrequencyList_v2::Item&);
#if !defined (QT_NO_DEBUG_STREAM)
QDebug operator << (QDebug, FrequencyList_v2::Item const&);
#endif
Q_DECLARE_METATYPE (FrequencyList_v2::Item);
Q_DECLARE_METATYPE (FrequencyList_v2::FrequencyItems);
//
// Obsolete version of FrequencyList no longer used but needed to
// allow loading and saving of old settings contents without damage
@ -184,4 +234,18 @@ QDataStream& operator >> (QDataStream&, FrequencyList::Item&);
Q_DECLARE_METATYPE (FrequencyList::Item);
Q_DECLARE_METATYPE (FrequencyList::FrequencyItems);
class ReadFileException : public QException {
public:
ReadFileException (QString const& message)
: message_ {message}
{
}
void raise () const override { throw *this; }
ReadFileException * clone () const override { return new ReadFileException {*this}; }
QString filename_;
QString message_;
};
#endif

View File

@ -527,7 +527,7 @@ bool StationList::impl::dropMimeData (QMimeData const * data, Qt::DropAction act
QDataStream stream {&encoded_data, QIODevice::ReadOnly};
while (!stream.atEnd ())
{
FrequencyList_v2::Item item;
FrequencyList_v2_101::Item item;
stream >> item;
auto const& band = bands_->find (item.frequency_);
if (stations_.cend () == std::find_if (stations_.cbegin ()

View File

@ -1477,7 +1477,7 @@ Error: %2 - %3</translation>
</message>
</context>
<context>
<name>FrequencyList_v2</name>
<name>FrequencyList_v2_100</name>
<message>
<location filename="../models/FrequencyList.cpp" line="680"/>
<location filename="../models/FrequencyList.cpp" line="836"/>

View File

@ -1630,7 +1630,7 @@ Error: %2 - %3</translation>
</message>
</context>
<context>
<name>FrequencyList_v2</name>
<name>FrequencyList_v2_100</name>
<message>
<location filename="../models/FrequencyList.cpp" line="680"/>
<location filename="../models/FrequencyList.cpp" line="836"/>

View File

@ -1475,7 +1475,7 @@ Error: %2 - %3</source>
</message>
</context>
<context>
<name>FrequencyList_v2</name>
<name>FrequencyList_v2_100</name>
<message>
<location filename="../models/FrequencyList.cpp" line="680"/>
<location filename="../models/FrequencyList.cpp" line="836"/>

View File

@ -1475,7 +1475,7 @@ Error: %2 - %3</source>
</message>
</context>
<context>
<name>FrequencyList_v2</name>
<name>FrequencyList_v2_100</name>
<message>
<location filename="../models/FrequencyList.cpp" line="680"/>
<location filename="../models/FrequencyList.cpp" line="836"/>

View File

@ -12,7 +12,7 @@
LiveFrequencyValidator::LiveFrequencyValidator (QComboBox * combo_box
, Bands const * bands
, FrequencyList_v2 const * frequencies
, FrequencyList_v2_101 const * frequencies
, Frequency const * nominal_frequency
, QWidget * parent)
: QRegExpValidator {

View File

@ -7,7 +7,7 @@
#include "Radio.hpp"
class Bands;
class FrequencyList_v2;
class FrequencyList_v2_101;
class QComboBox;
class QWidget;
@ -35,7 +35,7 @@ public:
LiveFrequencyValidator (QComboBox * combo_box // associated combo box
, Bands const * bands // bands model
, FrequencyList_v2 const * frequencies // working frequencies model
, FrequencyList_v2_101 const * frequencies // working frequencies model
, Frequency const * nominal_frequency
, QWidget * parent = nullptr);
@ -46,7 +46,7 @@ public:
private:
Bands const * bands_;
FrequencyList_v2 const * frequencies_;
FrequencyList_v2_101 const * frequencies_;
Frequency const * nominal_frequency_;
QComboBox * combo_box_;
};

View File

@ -15,7 +15,7 @@ BandComboBox::BandComboBox (QWidget * parent)
// item text.
void BandComboBox::showPopup ()
{
auto minimum_width = view ()->sizeHintForColumn (FrequencyList_v2::frequency_mhz_column);
auto minimum_width = view ()->sizeHintForColumn (FrequencyList_v2_101::frequency_mhz_column);
if (count () > maxVisibleItems ())
{
// for some as yet unknown reason, in FT8 mode the scrollbar

View File

@ -769,7 +769,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
// Hook up working frequencies.
ui->bandComboBox->setModel (m_config.frequencies ());
ui->bandComboBox->setModelColumn (FrequencyList_v2::frequency_mhz_column);
ui->bandComboBox->setModelColumn (FrequencyList_v2_101::frequency_mhz_column);
// Enable live band combo box entry validation and action.
auto band_validator = new LiveFrequencyValidator {ui->bandComboBox
@ -1021,6 +1021,8 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
m_wideGraph->setMode(m_mode);
connect (&minuteTimer, &QTimer::timeout, this, &MainWindow::on_the_minute);
connect (&minuteTimer, &QTimer::timeout, this, &MainWindow::invalidate_frequencies_filter);
minuteTimer.setSingleShot (true);
minuteTimer.start (ms_minute_error () + 60 * 1000);
@ -1065,6 +1067,14 @@ void MainWindow::splash_done ()
m_splash && m_splash->close ();
}
void MainWindow::invalidate_frequencies_filter ()
{
// every interval, invalidate the frequency filter, so that if any
// working frequency goes in/out of scope, we pick it up.
m_config.frequencies ()->filter_refresh ();
ui->bandComboBox->update ();
}
void MainWindow::on_the_minute ()
{
if (minuteTimer.isSingleShot ())
@ -7208,7 +7218,7 @@ void MainWindow::on_actionFreqCal_triggered()
void MainWindow::switch_mode (Mode mode)
{
m_fastGraph->setMode(m_mode);
m_config.frequencies ()->filter (m_config.region (), mode);
m_config.frequencies ()->filter (m_config.region (), mode, true); // filter on current time
auto const& row = m_config.frequencies ()->best_working_frequency (m_freqNominal);
ui->bandComboBox->setCurrentIndex (row);
if (row >= 0) {
@ -7453,7 +7463,7 @@ void MainWindow::on_actionOpen_log_directory_triggered ()
void MainWindow::on_bandComboBox_currentIndexChanged (int index)
{
auto const& frequencies = m_config.frequencies ();
auto const& source_index = frequencies->mapToSource (frequencies->index (index, FrequencyList_v2::frequency_column));
auto const& source_index = frequencies->mapToSource (frequencies->index (index, FrequencyList_v2_101::frequency_column));
Frequency frequency {m_freqNominal};
if (source_index.isValid ())
{
@ -7481,7 +7491,7 @@ void MainWindow::on_bandComboBox_editTextChanged (QString const& text)
void MainWindow::on_bandComboBox_activated (int index)
{
auto const& frequencies = m_config.frequencies ();
auto const& source_index = frequencies->mapToSource (frequencies->index (index, FrequencyList_v2::frequency_column));
auto const& source_index = frequencies->mapToSource (frequencies->index (index, FrequencyList_v2_101::frequency_column));
Frequency frequency {m_freqNominal};
if (source_index.isValid ())
{
@ -7496,7 +7506,7 @@ void MainWindow::band_changed (Frequency f)
{
// Don't allow a7 decodes during the first period because they can be leftovers from the previous band
no_a7_decodes = true;
QTimer::singleShot ((int(1000.0*m_TRperiod)), [=] {no_a7_decodes = false;});
QTimer::singleShot ((int(1500.0*m_TRperiod)), [=] {no_a7_decodes = false;});
// Set the attenuation value if options are checked
if (m_config.pwrBandTxMemory() && !m_tune) {

View File

@ -477,7 +477,7 @@ private:
qint32 m_k0;
qint32 m_kdone;
qint32 m_nPick;
FrequencyList_v2::const_iterator m_frequency_list_fcal_iter;
FrequencyList_v2_101::const_iterator m_frequency_list_fcal_iter;
qint32 m_nTx73;
qint32 m_UTCdisk;
qint32 m_wait;
@ -824,6 +824,7 @@ private:
void subProcessError (QProcess *, QProcess::ProcessError);
void statusUpdate () const;
void update_watchdog_label ();
void invalidate_frequencies_filter ();
void on_the_minute ();
void add_child_to_event_filter (QObject *);
void remove_child_from_event_filter (QObject *);