diff --git a/Configuration.cpp b/Configuration.cpp index e45bc99af..1e7f83e93 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -168,6 +168,11 @@ #include #include #include +#include +#include +#include +#include + #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:"), ®ion_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(); } } diff --git a/models/FrequencyList.cpp b/models/FrequencyList.cpp index cac85ea5a..e04a7a184 100644 --- a/models/FrequencyList.cpp +++ b/models/FrequencyList.cpp @@ -5,6 +5,7 @@ #include #include + #include #include #include @@ -17,10 +18,16 @@ #include #include #include +#include +#include +#include +#include +#include #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 diff --git a/models/FrequencyList.hpp b/models/FrequencyList.hpp index a3b9c117a..40002762c 100644 --- a/models/FrequencyList.hpp +++ b/models/FrequencyList.hpp @@ -5,6 +5,10 @@ #include #include +#include +#include +#include +#include #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; using BandSet = QSet; - 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; + +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