read/write QRG files with additional information; support JSON format for human inspection

This commit is contained in:
Brian Moran 2022-09-28 08:07:12 -07:00
parent c3579831ba
commit 9520eb1309
3 changed files with 531 additions and 157 deletions

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"
@ -250,7 +255,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};
}
@ -268,8 +274,21 @@ public:
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 ("Validity Period")};
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));
setWindowTitle (QApplication::applicationName () + " - " +
tr ("Add Frequency"));
region_combo_box_.setModel (regions_model);
mode_combo_box_.setModel (modes_model);
@ -277,28 +296,71 @@ 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 ("&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 ("&Description:"), &description_line_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
};
}
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_;
QDateTimeEdit *end_date_time_edit_;
QDateTimeEdit *start_date_time_edit_;
};
@ -477,7 +539,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::FrequencyItems read_frequencies_file (QString const&);
void delete_stations ();
void insert_station ();
@ -1198,6 +1262,7 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
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->verticalHeader ()->setSectionResizeMode (QHeaderView::ResizeToContents);
ui_->frequencies_table_view->verticalHeader ()->setResizeContentsPrecision (0);
@ -2508,17 +2573,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);
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);
@ -2532,15 +2605,17 @@ 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();
}
}
@ -2550,6 +2625,62 @@ FrequencyList_v2::FrequencyItems Configuration::impl::read_frequencies_file (QSt
frequencies_file.open (QFile::ReadOnly);
QDataStream ids {&frequencies_file};
FrequencyList_v2::FrequencyItems list;
FrequencyList_v2_100::FrequencyItems list_v100;
// read file as json if ends with qrg.json
if (file_name.endsWith(".qrg.json", Qt::CaseInsensitive))
{
QJsonDocument doc = QJsonDocument::fromJson(frequencies_file.readAll());
if (doc.isNull())
{
MessageBox::critical_message (this, tr ("Error reading frequencies file"), tr ("%1 - Invalid Format").arg (file_name));
return list;
}
QJsonObject obj = doc.object();
if (obj.isEmpty())
{
MessageBox::critical_message (this, tr ("Error reading frequencies file"), tr ("%1 - Information Missing ").arg (file_name));
return list;
}
QJsonArray arr = obj["frequencies"].toArray();
if (arr.isEmpty())
{
MessageBox::critical_message (this, tr ("Error reading frequencies file"), tr ("No Frequencies were found"));
return list;
}
int valid_entry_count = 0;
int skipped_entry_count = 0;
for (auto const &item: arr)
{
QString mode_s, region_s;
QJsonObject obj = item.toObject();
FrequencyList_v2::Item freq;
region_s = obj["region"].toString();
mode_s = obj["mode"].toString();
freq.frequency_ = obj["frequency"].toString().toDouble() * 1e6;
freq.region_ = IARURegions::value(region_s);
freq.mode_ = Modes::value(mode_s);
freq.description_ = obj["description"].toString();
freq.source_ = obj["source"].toString();
freq.start_time_ = QDateTime::fromString(obj["start_time"].toString(), Qt::ISODate);
freq.end_time_ = QDateTime::fromString(obj["end_time"].toString(), Qt::ISODate);
//MessageBox::critical_message (this, tr ("Entry"), tr ("Entry: %1 ").arg(freq.toString()+"[sane:" +freq.isSane() + "] [region:" + obj["region"].toString() + "] [mode:" + obj["mode"].toString()+"] "));
if ((freq.mode_ != Modes::ALL || QString::compare("ALL", mode_s)) &&
(freq.region_ != IARURegions::ALL || QString::compare("ALL", region_s, Qt::CaseInsensitive)) &&
freq.isSane())
{
list.push_back(freq);
valid_entry_count++;
} else
skipped_entry_count++;
}
MessageBox::information_message(this, tr("Loaded Frequencies from %1").arg(file_name),
tr("Entries Valid/Skipped %1").arg(QString::number(valid_entry_count) + "/" +
QString::number(skipped_entry_count)));
return list;
}
quint32 magic;
ids >> magic;
if (qrg_magic != magic)
@ -2568,8 +2699,19 @@ 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::Item{item.frequency_, item.mode_, item.region_, QString(), QString(), QDateTime(),QDateTime()};
}
}
else
{
ids >> list;
}
if (ids.status () != QDataStream::Ok || !ids.atEnd ())
{
@ -2577,13 +2719,12 @@ 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 ())
{
QFile frequencies_file {file_name};
@ -2598,11 +2739,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 (file_name.endsWith(".qrg.json", Qt::CaseInsensitive))
{
next_frequencies_.to_json_stream(&ods, "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 (file_name.endsWith(".qrg.json", Qt::CaseInsensitive))
{
next_frequencies_.to_json_stream(&ods,
"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();
}
}
}
}
@ -2616,6 +2774,7 @@ void Configuration::impl::reset_frequencies ()
{
next_frequencies_.reset_to_defaults ();
}
size_frequency_table_columns ();
}
void Configuration::impl::insert_frequency ()
@ -2624,6 +2783,7 @@ void Configuration::impl::insert_frequency ()
{
ui_->frequencies_table_view->setCurrentIndex (next_frequencies_.add (frequency_dialog_->item ()));
ui_->frequencies_table_view->resizeColumnToContents (FrequencyList_v2::mode_column);
size_frequency_table_columns();
}
}

View File

@ -5,6 +5,7 @@
#include <limits>
#include <algorithm>
#include <QMetaType>
#include <QAbstractTableModel>
#include <QString>
@ -17,10 +18,16 @@
#include <QDataStream>
#include <QByteArray>
#include <QDebugStateSaver>
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>
#include <QCoreApplication>
#include <QFile>
#include "Radio.hpp"
#include "Bands.hpp"
#include "pimpl_impl.hpp"
#include "revision_utils.hpp"
#include "moc_FrequencyList.cpp"
@ -28,40 +35,40 @@ namespace
{
FrequencyList_v2::FrequencyItems const default_frequency_list =
{
{198000, Modes::FreqCal, IARURegions::R1}, // BBC Radio 4 Droitwich
{4996000, Modes::FreqCal, IARURegions::R1}, // RWM time signal
{9996000, Modes::FreqCal, IARURegions::R1}, // RWM time signal
{14996000, Modes::FreqCal, IARURegions::R1}, // RWM time signal
{198000, Modes::FreqCal, IARURegions::R1, "","", QDateTime(), QDateTime()}, // BBC Radio 4 Droitwich
{4996000, Modes::FreqCal, IARURegions::R1, "","", QDateTime(), QDateTime()}, // RWM time signal
{9996000, Modes::FreqCal, IARURegions::R1, "","", QDateTime(), QDateTime()}, // RWM time signal
{14996000, Modes::FreqCal, IARURegions::R1, "","", QDateTime(), QDateTime()}, // RWM time signal
{660000, Modes::FreqCal, IARURegions::R2},
{880000, Modes::FreqCal, IARURegions::R2},
{1210000, Modes::FreqCal, IARURegions::R2},
{660000, Modes::FreqCal, IARURegions::R2, "","", QDateTime(), QDateTime()},
{880000, Modes::FreqCal, IARURegions::R2, "","", QDateTime(), QDateTime()},
{1210000, Modes::FreqCal, IARURegions::R2, "","", QDateTime(), QDateTime()},
{2500000, Modes::FreqCal, IARURegions::ALL},
{3330000, Modes::FreqCal, IARURegions::ALL},
{5000000, Modes::FreqCal, IARURegions::ALL},
{7850000, Modes::FreqCal, IARURegions::ALL},
{10000000, Modes::FreqCal, IARURegions::ALL},
{14670000, Modes::FreqCal, IARURegions::ALL},
{15000000, Modes::FreqCal, IARURegions::ALL},
{20000000, Modes::FreqCal, IARURegions::ALL},
{2500000, Modes::FreqCal, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{3330000, Modes::FreqCal, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{5000000, Modes::FreqCal, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{7850000, Modes::FreqCal, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{10000000, Modes::FreqCal, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{14670000, Modes::FreqCal, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{15000000, Modes::FreqCal, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{20000000, Modes::FreqCal, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{136000, Modes::WSPR, IARURegions::ALL},
{136000, Modes::FST4, IARURegions::ALL},
{136000, Modes::FST4W, IARURegions::ALL},
{136000, Modes::JT9, IARURegions::ALL},
{136000, Modes::WSPR, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{136000, Modes::FST4, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{136000, Modes::FST4W, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{136000, Modes::JT9, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{474200, Modes::JT9, IARURegions::ALL},
{474200, Modes::FST4, IARURegions::ALL},
{474200, Modes::WSPR, IARURegions::ALL},
{474200, Modes::FST4W, IARURegions::ALL},
{474200, Modes::JT9, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{474200, Modes::FST4, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{474200, Modes::WSPR, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{474200, Modes::FST4W, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{1836600, Modes::WSPR, IARURegions::ALL},
{1836800, Modes::FST4W, IARURegions::ALL},
{1838000, Modes::JT65, IARURegions::ALL}, // squeezed allocations
{1839000, Modes::JT9, IARURegions::ALL},
{1839000, Modes::FST4, IARURegions::ALL},
{1840000, Modes::FT8, IARURegions::ALL},
{1836600, Modes::WSPR, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{1836800, Modes::FST4W, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{1838000, Modes::JT65, IARURegions::ALL, "","", QDateTime(), QDateTime()}, // squeezed allocations
{1839000, Modes::JT9, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{1839000, Modes::FST4, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{1840000, Modes::FT8, IARURegions::ALL, "","", QDateTime(), QDateTime()},
// Band plans (all USB dial unless stated otherwise)
//
@ -90,12 +97,12 @@ namespace
// 3580 PSK31
// 3600 LSB EMCOMM
//
{3570000, Modes::JT65, IARURegions::ALL}, // JA compatible
{3572000, Modes::JT9, IARURegions::ALL},
{3573000, Modes::FT8, IARURegions::ALL}, // above as below JT65 is out of DM allocation
{3568600, Modes::WSPR, IARURegions::ALL}, // needs guard marker and lock out
{3575000, Modes::FT4, IARURegions::ALL}, // provisional
{3568000, Modes::FT4, IARURegions::R3}, // provisional
{3570000, Modes::JT65, IARURegions::ALL, "","", QDateTime(), QDateTime()}, // JA compatible
{3572000, Modes::JT9, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{3573000, Modes::FT8, IARURegions::ALL, "","", QDateTime(), QDateTime()}, // above as below JT65 is out of DM allocation
{3568600, Modes::WSPR, IARURegions::ALL, "","", QDateTime(), QDateTime()}, // needs guard marker and lock out
{3575000, Modes::FT4, IARURegions::ALL, "","", QDateTime(), QDateTime()}, // provisional
{3568000, Modes::FT4, IARURegions::R3, "","", QDateTime(), QDateTime()}, // provisional
// Band plans (all USB dial unless stated otherwise)
//
@ -128,11 +135,11 @@ namespace
// 7090 LSB QRP CoA
// 7110 LSB EMCOMM
//
{7038600, Modes::WSPR, IARURegions::ALL},
{7074000, Modes::FT8, IARURegions::ALL},
{7076000, Modes::JT65, IARURegions::ALL},
{7078000, Modes::JT9, IARURegions::ALL},
{7047500, Modes::FT4, IARURegions::ALL}, // provisional - moved
{7038600, Modes::WSPR, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{7074000, Modes::FT8, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{7076000, Modes::JT65, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{7078000, Modes::JT9, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{7047500, Modes::FT4, IARURegions::ALL, "","", QDateTime(), QDateTime()}, // provisional - moved
// up 500Hz to clear
// W1AW code practice QRG
@ -162,11 +169,11 @@ namespace
// 10142.25 OLIVIA, Contestia, etc.
// 10143.25 OLIVIA, Contestia, etc. (main QRG)
//
{10136000, Modes::FT8, IARURegions::ALL},
{10138000, Modes::JT65, IARURegions::ALL},
{10138700, Modes::WSPR, IARURegions::ALL},
{10140000, Modes::JT9, IARURegions::ALL},
{10140000, Modes::FT4, IARURegions::ALL}, // provisional
{10136000, Modes::FT8, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{10138000, Modes::JT65, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{10138700, Modes::WSPR, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{10140000, Modes::JT9, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{10140000, Modes::FT4, IARURegions::ALL, "","", QDateTime(), QDateTime()}, // provisional
// Band plans (all USB dial unless stated otherwise)
//
@ -205,11 +212,11 @@ namespace
// 14105.5 OLIVIA 1000
// 14106.5 OLIVIA 1000 (main QRG)
//
{14095600, Modes::WSPR, IARURegions::ALL},
{14074000, Modes::FT8, IARURegions::ALL},
{14076000, Modes::JT65, IARURegions::ALL},
{14078000, Modes::JT9, IARURegions::ALL},
{14080000, Modes::FT4, IARURegions::ALL}, // provisional
{14095600, Modes::WSPR, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{14074000, Modes::FT8, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{14076000, Modes::JT65, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{14078000, Modes::JT9, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{14080000, Modes::FT4, IARURegions::ALL, "","", QDateTime(), QDateTime()}, // provisional
// Band plans (all USB dial unless stated otherwise)
//
@ -238,111 +245,111 @@ namespace
// 18104.4 OLIVIA, Contestia, etc.
// 18110 NCDXF beacons
//
{18100000, Modes::FT8, IARURegions::ALL},
{18102000, Modes::JT65, IARURegions::ALL},
{18104000, Modes::JT9, IARURegions::ALL},
{18104000, Modes::FT4, IARURegions::ALL}, // provisional
{18104600, Modes::WSPR, IARURegions::ALL},
{18100000, Modes::FT8, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{18102000, Modes::JT65, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{18104000, Modes::JT9, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{18104000, Modes::FT4, IARURegions::ALL, "","", QDateTime(), QDateTime()}, // provisional
{18104600, Modes::WSPR, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{21074000, Modes::FT8, IARURegions::ALL},
{21076000, Modes::JT65, IARURegions::ALL},
{21078000, Modes::JT9, IARURegions::ALL},
{21094600, Modes::WSPR, IARURegions::ALL},
{21140000, Modes::FT4, IARURegions::ALL},
{21074000, Modes::FT8, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{21076000, Modes::JT65, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{21078000, Modes::JT9, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{21094600, Modes::WSPR, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{21140000, Modes::FT4, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{24915000, Modes::FT8, IARURegions::ALL},
{24917000, Modes::JT65, IARURegions::ALL},
{24919000, Modes::JT9, IARURegions::ALL},
{24919000, Modes::FT4, IARURegions::ALL}, // provisional
{24924600, Modes::WSPR, IARURegions::ALL},
{24915000, Modes::FT8, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{24917000, Modes::JT65, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{24919000, Modes::JT9, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{24919000, Modes::FT4, IARURegions::ALL, "","", QDateTime(), QDateTime()}, // provisional
{24924600, Modes::WSPR, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{28074000, Modes::FT8, IARURegions::ALL},
{28076000, Modes::JT65, IARURegions::ALL},
{28078000, Modes::JT9, IARURegions::ALL},
{28124600, Modes::WSPR, IARURegions::ALL},
{28180000, Modes::FT4, IARURegions::ALL},
{28074000, Modes::FT8, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{28076000, Modes::JT65, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{28078000, Modes::JT9, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{28124600, Modes::WSPR, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{28180000, Modes::FT4, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{50200000, Modes::Echo, IARURegions::ALL},
{50211000, Modes::Q65, IARURegions::ALL},
{50275000, Modes::Q65, IARURegions::ALL},
{50276000, Modes::JT65, IARURegions::R2},
{50276000, Modes::JT65, IARURegions::R3},
{50380000, Modes::MSK144, IARURegions::R1},
{50260000, Modes::MSK144, IARURegions::R2},
{50260000, Modes::MSK144, IARURegions::R3},
{50293000, Modes::WSPR, IARURegions::R2},
{50293000, Modes::WSPR, IARURegions::R3},
{50310000, Modes::JT65, IARURegions::ALL},
{50312000, Modes::JT9, IARURegions::ALL},
{50313000, Modes::FT8, IARURegions::ALL},
{50318000, Modes::FT4, IARURegions::ALL}, // provisional
{50323000, Modes::FT8, IARURegions::ALL},
{50200000, Modes::Echo, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{50211000, Modes::Q65, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{50275000, Modes::Q65, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{50276000, Modes::JT65, IARURegions::R2, "","", QDateTime(), QDateTime()},
{50276000, Modes::JT65, IARURegions::R3, "","", QDateTime(), QDateTime()},
{50380000, Modes::MSK144, IARURegions::R1, "","", QDateTime(), QDateTime()},
{50260000, Modes::MSK144, IARURegions::R2, "","", QDateTime(), QDateTime()},
{50260000, Modes::MSK144, IARURegions::R3, "","", QDateTime(), QDateTime()},
{50293000, Modes::WSPR, IARURegions::R2, "","", QDateTime(), QDateTime()},
{50293000, Modes::WSPR, IARURegions::R3, "","", QDateTime(), QDateTime()},
{50310000, Modes::JT65, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{50312000, Modes::JT9, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{50313000, Modes::FT8, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{50318000, Modes::FT4, IARURegions::ALL, "","", QDateTime(), QDateTime()}, // provisional
{50323000, Modes::FT8, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{70102000, Modes::JT65, IARURegions::R1},
{70104000, Modes::JT9, IARURegions::R1},
{70091000, Modes::WSPR, IARURegions::R1},
{70154000, Modes::FT8, IARURegions::R1},
{70230000, Modes::MSK144, IARURegions::R1},
{70102000, Modes::JT65, IARURegions::R1, "","", QDateTime(), QDateTime()},
{70104000, Modes::JT9, IARURegions::R1, "","", QDateTime(), QDateTime()},
{70091000, Modes::WSPR, IARURegions::R1, "","", QDateTime(), QDateTime()},
{70154000, Modes::FT8, IARURegions::R1, "","", QDateTime(), QDateTime()},
{70230000, Modes::MSK144, IARURegions::R1, "","", QDateTime(), QDateTime()},
{144116000, Modes::Q65, IARURegions::ALL},
{144120000, Modes::JT65, IARURegions::ALL},
{144120000, Modes::Echo, IARURegions::ALL},
{144170000, Modes::FT4, IARURegions::ALL},
{144174000, Modes::FT8, IARURegions::ALL},
{144360000, Modes::MSK144, IARURegions::R1},
{144150000, Modes::MSK144, IARURegions::R2},
{144489000, Modes::WSPR, IARURegions::ALL},
{144116000, Modes::Q65, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{144120000, Modes::JT65, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{144120000, Modes::Echo, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{144170000, Modes::FT4, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{144174000, Modes::FT8, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{144360000, Modes::MSK144, IARURegions::R1, "","", QDateTime(), QDateTime()},
{144150000, Modes::MSK144, IARURegions::R2, "","", QDateTime(), QDateTime()},
{144489000, Modes::WSPR, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{222065000, Modes::Echo, IARURegions::R2},
{222065000, Modes::JT65, IARURegions::R2},
{222065000, Modes::Q65, IARURegions::R2},
{222065000, Modes::Echo, IARURegions::R2, "","", QDateTime(), QDateTime()},
{222065000, Modes::JT65, IARURegions::R2, "","", QDateTime(), QDateTime()},
{222065000, Modes::Q65, IARURegions::R2, "","", QDateTime(), QDateTime()},
{432065000, Modes::Echo, IARURegions::ALL},
{432065000, Modes::JT65, IARURegions::ALL},
{432300000, Modes::WSPR, IARURegions::ALL},
{432360000, Modes::MSK144, IARURegions::ALL},
{432065000, Modes::Q65, IARURegions::ALL},
{432065000, Modes::Echo, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{432065000, Modes::JT65, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{432300000, Modes::WSPR, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{432360000, Modes::MSK144, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{432065000, Modes::Q65, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{902065000, Modes::JT65, IARURegions::R2},
{902065000, Modes::Q65, IARURegions::R2},
{902065000, Modes::JT65, IARURegions::R2, "","", QDateTime(), QDateTime()},
{902065000, Modes::Q65, IARURegions::R2, "","", QDateTime(), QDateTime()},
{1296065000, Modes::Echo, IARURegions::ALL},
{1296065000, Modes::JT65, IARURegions::ALL},
{1296500000, Modes::WSPR, IARURegions::ALL},
{1296065000, Modes::Q65, IARURegions::ALL},
{1296065000, Modes::Echo, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{1296065000, Modes::JT65, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{1296500000, Modes::WSPR, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{1296065000, Modes::Q65, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{2301000000, Modes::Echo, IARURegions::ALL},
{2301065000, Modes::JT4, IARURegions::ALL},
{2301065000, Modes::JT65, IARURegions::ALL},
{2301065000, Modes::Q65, IARURegions::ALL},
{2301000000, Modes::Echo, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{2301065000, Modes::JT4, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{2301065000, Modes::JT65, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{2301065000, Modes::Q65, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{2304065000, Modes::Echo, IARURegions::ALL},
{2304065000, Modes::JT4, IARURegions::ALL},
{2304065000, Modes::JT65, IARURegions::ALL},
{2304065000, Modes::Q65, IARURegions::ALL},
{2304065000, Modes::Echo, IARURegions::ALL, "","", QDateTime(), QDateTime()},
{2304065000, Modes::JT4, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{2304065000, Modes::JT65, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{2304065000, Modes::Q65, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{2320065000, Modes::Echo, IARURegions::ALL},
{2320065000, Modes::JT4, IARURegions::ALL},
{2320065000, Modes::JT65, IARURegions::ALL},
{2320065000, Modes::Q65, IARURegions::ALL},
{2320065000, Modes::Echo, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{2320065000, Modes::JT4, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{2320065000, Modes::JT65, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{2320065000, Modes::Q65, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{3400065000, Modes::Echo, IARURegions::ALL},
{3400065000, Modes::JT4, IARURegions::ALL},
{3400065000, Modes::JT65, IARURegions::ALL},
{3400065000, Modes::Q65, IARURegions::ALL},
{3400065000, Modes::Echo, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{3400065000, Modes::JT4, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{3400065000, Modes::JT65, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{3400065000, Modes::Q65, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{5760065000, Modes::Echo, IARURegions::ALL},
{5760065000, Modes::JT4, IARURegions::ALL},
{5760065000, Modes::JT65, IARURegions::ALL},
{5760200000, Modes::Q65, IARURegions::ALL},
{5760065000, Modes::Echo, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{5760065000, Modes::JT4, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{5760065000, Modes::JT65, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{5760200000, Modes::Q65, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{10368100000, Modes::Echo, IARURegions::ALL},
{10368200000, Modes::JT4, IARURegions::ALL},
{10368200000, Modes::Q65, IARURegions::ALL},
{10368100000, Modes::Echo, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{10368200000, Modes::JT4, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{10368200000, Modes::Q65, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{24048100000, Modes::Echo, IARURegions::ALL},
{24048200000, Modes::JT4, IARURegions::ALL},
{24048200000, Modes::Q65, IARURegions::ALL},
{24048100000, Modes::Echo, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{24048200000, Modes::JT4, IARURegions::ALL,"","", QDateTime(), QDateTime()},
{24048200000, Modes::Q65, IARURegions::ALL,"","", QDateTime(), QDateTime()},
};
}
@ -353,6 +360,11 @@ QDebug operator << (QDebug debug, FrequencyList_v2::Item const& item)
return debug.nospace () << item.toString ();
}
#endif
bool FrequencyList_v2::Item::isSane() const
{
return frequency_ > 0.0 && (!start_time_.isValid() || !end_time_.isValid() || start_time_ < end_time_)
&& (region_ == IARURegions::ALL || region_ == IARURegions::R1 || region_ == IARURegions::R2 || region_ == IARURegions::R3);
}
QString FrequencyList_v2::Item::toString () const
{
@ -361,22 +373,45 @@ QString FrequencyList_v2::Item::toString () const
qts << "FrequencyItem("
<< Radio::frequency_MHz_string (frequency_) << ", "
<< IARURegions::name (region_) << ", "
<< Modes::name (mode_) << ')';
<< Modes::name (mode_) << ", "
<< start_time_.toString(Qt::ISODate) << ", "
<< end_time_.toString(Qt::ISODate) << ", "
<< description_ << ", "
<< source_ << ')';
return string;
}
QJsonObject FrequencyList_v2::Item::toJson() const {
return {{"frequency", Radio::frequency_MHz_string (frequency_) },
{"mode", Modes::name (mode_) },
{"region", IARURegions::name (region_)},
{"description", description_},
{"source", source_},
{"start_time", start_time_.toString(Qt::ISODate) },
{"end_time", end_time_.toString(Qt::ISODate)}};
}
QDataStream& operator << (QDataStream& os, FrequencyList_v2::Item const& item)
{
return os << item.frequency_
<< item.mode_
<< item.region_;
<< item.region_
<< item.start_time_
<< item.end_time_
<< item.description_
<< item.source_;
}
QDataStream& operator >> (QDataStream& is, FrequencyList_v2::Item& item)
{
return is >> item.frequency_
>> item.mode_
>> item.region_;
>> item.region_
>> item.start_time_
>> item.end_time_
>> item.description_
>> item.source_;
}
class FrequencyList_v2::impl final
@ -453,6 +488,7 @@ void FrequencyList_v2::frequency_list_merge (FrequencyItems const& items)
m_->add (items);
}
int FrequencyList_v2::best_working_frequency (Frequency f) const
{
int result {-1};
@ -761,6 +797,93 @@ QVariant FrequencyList_v2::impl::data (QModelIndex const& index, int role) const
break;
}
break;
case description_column:
switch (role)
{
case SortRole:
case Qt::DisplayRole:
case Qt::EditRole:
case Qt::AccessibleTextRole:
item = frequency_item.description_;
break;
case Qt::ToolTipRole:
case Qt::AccessibleDescriptionRole:
item = tr ("Description");
break;
case Qt::TextAlignmentRole:
item = Qt::AlignLeft + Qt::AlignVCenter;
break;
}
break;
case source_column:
switch (role)
{
case SortRole:
case Qt::DisplayRole:
case Qt::EditRole:
case Qt::AccessibleTextRole:
item = frequency_item.start_time_ == frequency_item.end_time_
? tr ("Equal")
: tr ("NOTEQUAL");
item = frequency_item.source_;
break;
case Qt::ToolTipRole:
case Qt::AccessibleDescriptionRole:
item = tr ("Source");
break;
case Qt::TextAlignmentRole:
item = Qt::AlignLeft + Qt::AlignVCenter;
break;
}
break;
case start_time_column:
switch (role)
{
case SortRole:
case Qt::DisplayRole:
case Qt::EditRole:
case Qt::AccessibleTextRole:
item = frequency_item.start_time_.toString(Qt::ISODate);
break;
case Qt::ToolTipRole:
case Qt::AccessibleDescriptionRole:
item = tr ("Start Time");
break;
case Qt::TextAlignmentRole:
item = Qt::AlignLeft + Qt::AlignVCenter;
break;
}
break;
case end_time_column:
switch (role)
{
case SortRole:
case Qt::DisplayRole:
case Qt::EditRole:
case Qt::AccessibleTextRole:
item = frequency_item.end_time_.toString(Qt::ISODate);
break;
case Qt::ToolTipRole:
case Qt::AccessibleDescriptionRole:
item = tr ("End Time");
break;
case Qt::TextAlignmentRole:
item = Qt::AlignLeft + Qt::AlignVCenter;
break;
}
break;
}
}
return item;
@ -837,6 +960,10 @@ QVariant FrequencyList_v2::impl::headerData (int section, Qt::Orientation orient
case mode_column: header = tr ("Mode"); break;
case frequency_column: header = tr ("Frequency"); break;
case frequency_mhz_column: header = tr ("Frequency (MHz)"); break;
case description_column: header = tr ("Description"); break;
case source_column: header = tr ("Source"); break;
case start_time_column: header = tr ("Start"); break;
case end_time_column: header = tr ("End"); break;
}
}
else
@ -868,7 +995,7 @@ bool FrequencyList_v2::impl::insertRows (int row, int count, QModelIndex const&
beginInsertRows (parent, row, row + count - 1);
for (auto r = 0; r < count; ++r)
{
frequency_list_.insert (row, Item {0, Mode::ALL, IARURegions::ALL});
frequency_list_.insert (row, Item {0, Mode::ALL, IARURegions::ALL, QString(), QString(), QDateTime(), QDateTime()});
}
endInsertRows ();
return true;
@ -977,6 +1104,53 @@ auto FrequencyList_v2::all_bands (Region region, Mode mode) const -> BandSet
return result;
}
// write JSON format to a file
void FrequencyList_v2::to_json_stream(QDataStream *ods, QString magic_s, QString version_s,
FrequencyItems const &frequency_items)
{
QJsonObject jobject{
{"wsjtx_file", "qrg"},
{"wsjtx_version", QCoreApplication::applicationVersion()+" "+revision()},
{"generated_at", QDateTime::currentDateTimeUtc ().toString (Qt::ISODate)},
{"wsjtx_filetype", magic_s},
{"qrg_version", version_s},
{"frequency_count", frequency_items.count()}};
QJsonArray array;
for (auto &item: frequency_items)
array.append(item.toJson());
jobject["frequencies"] = array;
QJsonDocument d = QJsonDocument(jobject);
ods->writeRawData(d.toJson().data(), d.toJson().size());
}
QTextStream& qStdOut()
{
static QTextStream ts( stdout );
return ts;
}
FrequencyList_v2::FrequencyItems FrequencyList_v2::from_json_file(QFile *input_file)
{
// attempt to read the file as JSON
FrequencyList_v2::FrequencyItems list;
QByteArray jsonData = input_file->readAll();
QJsonDocument jsonDoc(QJsonDocument::fromJson(jsonData));
QJsonArray array = jsonDoc.object().value("frequencies").toArray();
qStdOut() << "Frequencies read";
qStdOut() << array.count();
return list;
}
// previous version 100 of the FrequencyList_v2 class
QDataStream& operator >> (QDataStream& is, FrequencyList_v2_100::Item& item)
{
return is >> item.frequency_
>> item.mode_
>> item.region_;
}
//
// Obsolete version of FrequencyList no longer used but needed to
// allow loading and saving of old settings contents without damage

View File

@ -5,6 +5,10 @@
#include <QList>
#include <QSortFilterProxyModel>
#include <QDateTime>
#include <QJsonObject>
#include <QJsonDocument>
#include <QFile>
#include "Radio.hpp"
#include "IARURegions.hpp"
@ -53,11 +57,18 @@ public:
Mode mode_;
Region region_;
QString toString () const;
bool isSane() const;
QJsonObject toJson () const;
QString description_;
QString source_;
QDateTime start_time_;
QDateTime end_time_;
};
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, SENTINAL};
// an iterator that meets the requirements of the C++ for range statement
class const_iterator
@ -78,9 +89,11 @@ public:
private:
FrequencyList_v2 const * parent_;
int row_;
//qint32 qrg_version_;
};
explicit FrequencyList_v2 (Bands const *, QObject * parent = nullptr);
~FrequencyList_v2 ();
// Load and store underlying items
@ -88,6 +101,8 @@ public:
FrequencyItems const& frequency_list () const;
FrequencyItems frequency_list (QModelIndexList const&) const;
void frequency_list_merge (FrequencyItems const&);
void to_json_stream(QDataStream *, QString, QString, FrequencyItems const&);
FrequencyList_v2::FrequencyItems from_json_file(QFile *);
// Iterators for the sorted and filtered items
//
@ -156,6 +171,31 @@ QDebug operator << (QDebug, FrequencyList_v2::Item const&);
Q_DECLARE_METATYPE (FrequencyList_v2::Item);
Q_DECLARE_METATYPE (FrequencyList_v2::FrequencyItems);
class FrequencyList_v2_100 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_100::Item const&);
QDataStream& operator >> (QDataStream&, FrequencyList_v2_100::Item&);
Q_DECLARE_METATYPE (FrequencyList_v2_100::Item);
Q_DECLARE_METATYPE (FrequencyList_v2_100::FrequencyItems);
//
// Obsolete version of FrequencyList no longer used but needed to