diff --git a/CMakeLists.txt b/CMakeLists.txt index a91121d7d..2dd2e4e13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -683,6 +683,7 @@ set (message_aggregator_CXXSRCS UDPExamples/DecodesModel.cpp UDPExamples/BeaconsModel.cpp UDPExamples/ClientWidget.cpp + MaidenheadLocatorValidator.cpp ) set (message_aggregator_STYLESHEETS diff --git a/Configuration.cpp b/Configuration.cpp index 3d19c0878..b1cab5b65 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -524,6 +524,7 @@ private: CalibrationParams calibration_; bool frequency_calibration_disabled_; // not persistent unsigned transceiver_command_number_; + QString dynamic_grid_; // configuration fields that we publish QString my_callsign_; @@ -569,6 +570,7 @@ private: bool bHound_; bool x2ToneSpacing_; bool x4ToneSpacing_; + bool use_dynamic_grid_; QString opCall_; QString udp_server_name_; port_type udp_server_port_; @@ -627,7 +629,6 @@ bool Configuration::restart_audio_input () const {return m_->restart_sound_input bool Configuration::restart_audio_output () const {return m_->restart_sound_output_device_;} auto Configuration::type_2_msg_gen () const -> Type2MsgGen {return m_->type_2_msg_gen_;} QString Configuration::my_callsign () const {return m_->my_callsign_;} -QString Configuration::my_grid () const {return m_->my_grid_;} QColor Configuration::color_CQ () const {return m_->color_CQ_;} QColor Configuration::color_MyCall () const {return m_->color_MyCall_;} QColor Configuration::color_TxMsg () const {return m_->color_TxMsg_;} @@ -797,6 +798,22 @@ bool Configuration::valid_n1mm_info () const return(!(server_name.trimmed().isEmpty() || port_number == 0)); } +QString Configuration::my_grid() const +{ + auto the_grid = m_->my_grid_; + if (m_->use_dynamic_grid_ && m_->dynamic_grid_.size () >= 4) { + the_grid = m_->dynamic_grid_; + } + return the_grid; +} + +void Configuration::set_location (QString const& grid_descriptor) +{ + // change the dynamic grid + qDebug () << "Configuration::set_location - location:" << grid_descriptor; + m_->dynamic_grid_ = grid_descriptor.trimmed (); +} + namespace { #if defined (Q_OS_MAC) @@ -1102,6 +1119,7 @@ void Configuration::impl::initialize_models () ui_->grid_line_edit->setPalette (pal); ui_->callsign_line_edit->setText (my_callsign_); ui_->grid_line_edit->setText (my_grid_); + ui_->use_dynamic_grid->setChecked(use_dynamic_grid_); ui_->labCQ->setStyleSheet(QString("background: %1").arg(color_CQ_.name())); ui_->labMyCall->setStyleSheet(QString("background: %1").arg(color_MyCall_.name())); ui_->labTx->setStyleSheet(QString("background: %1").arg(color_TxMsg_.name())); @@ -1316,6 +1334,7 @@ void Configuration::impl::read_settings () spot_to_psk_reporter_ = settings_->value ("PSKReporter", false).toBool (); id_after_73_ = settings_->value ("After73", false).toBool (); tx_QSY_allowed_ = settings_->value ("TxQSYAllowed", false).toBool (); + use_dynamic_grid_ = settings_->value ("AutoGrid", false).toBool (); macros_.setStringList (settings_->value ("Macros", QStringList {"TNX 73 GL"}).toStringList ()); @@ -1496,6 +1515,7 @@ void Configuration::impl::write_settings () settings_->setValue ("pwrBandTxMemory", pwrBandTxMemory_); settings_->setValue ("pwrBandTuneMemory", pwrBandTuneMemory_); settings_->setValue ("Region", QVariant::fromValue (region_)); + settings_->setValue ("AutoGrid", use_dynamic_grid_); } void Configuration::impl::set_rig_invariants () @@ -1931,7 +1951,14 @@ void Configuration::impl::accept () stations_.station_list(next_stations_.station_list ()); stations_.sort (StationList::band_column); } - + + if (ui_->use_dynamic_grid->isChecked() && !use_dynamic_grid_ ) + { + // turning on so clear it so only the next location update gets used + dynamic_grid_.clear (); + } + use_dynamic_grid_ = ui_->use_dynamic_grid->isChecked(); + write_settings (); // make visible to all } diff --git a/Configuration.hpp b/Configuration.hpp index a87e33814..886bed085 100644 --- a/Configuration.hpp +++ b/Configuration.hpp @@ -192,6 +192,9 @@ public: // Set the calibration parameters and enable calibration corrections. void set_calibration (CalibrationParams); + // Set the dynamic grid which is only used if configuration setting is enabled. + void set_location (QString const&); + // This method queries if a CAT and PTT connection is operational. bool is_transceiver_online () const; diff --git a/Configuration.ui b/Configuration.ui index c31fe3f22..2c1f9b173 100644 --- a/Configuration.ui +++ b/Configuration.ui @@ -80,11 +80,22 @@ + + + + + + Check to allow grid changes from external programs + + + AutoGrid + + + + + - - - @@ -95,6 +106,9 @@ + + + @@ -2599,6 +2613,7 @@ soundcard changes configuration_tabs callsign_line_edit grid_line_edit + use_dynamic_grid region_combo_box type_2_msg_gen_combo_box insert_blank_check_box diff --git a/MessageClient.cpp b/MessageClient.cpp index 799179ab7..2b6d9e308 100644 --- a/MessageClient.cpp +++ b/MessageClient.cpp @@ -207,6 +207,17 @@ void MessageClient::impl::parse_message (QByteArray const& msg) } break; + case NetworkMessage::Location: + { + QByteArray location; + in >> location; + if (check_status (in) != Fail) + { + Q_EMIT self_->location (QString::fromUtf8 (location)); + } + } + break; + default: // Ignore // @@ -433,7 +444,9 @@ void MessageClient::clear_decodes () void MessageClient::qso_logged (QDateTime time_off, QString const& dx_call, QString const& dx_grid , Frequency dial_frequency, QString const& mode, QString const& report_sent , QString const& report_received, QString const& tx_power - , QString const& comments, QString const& name, QDateTime time_on, QString const& operator_call) + , QString const& comments, QString const& name, QDateTime time_on + , QString const& operator_call, QString const& my_call + , QString const& my_grid) { if (m_->server_port_ && !m_->server_string_.isEmpty ()) { @@ -441,7 +454,19 @@ void MessageClient::qso_logged (QDateTime time_off, QString const& dx_call, QStr NetworkMessage::Builder out {&message, NetworkMessage::QSOLogged, m_->id_, m_->schema_}; out << time_off << dx_call.toUtf8 () << dx_grid.toUtf8 () << dial_frequency << mode.toUtf8 () << report_sent.toUtf8 () << report_received.toUtf8 () << tx_power.toUtf8 () << comments.toUtf8 () - << name.toUtf8 () << time_on << operator_call.toUtf8 (); + << name.toUtf8 () << time_on << operator_call.toUtf8 () << my_call.toUtf8 () << my_grid.toUtf8 (); + m_->send_message (out, message); + } +} + +void MessageClient::logged_ADIF (QByteArray const& ADIF_record) +{ + if (m_->server_port_ && !m_->server_string_.isEmpty ()) + { + QByteArray message; + NetworkMessage::Builder out {&message, NetworkMessage::LoggedADIF, m_->id_, m_->schema_}; + QByteArray ADIF {"\n3.0.7\nWSJT-X\n\n" + ADIF_record + " "}; + out << ADIF; m_->send_message (out, message); } } diff --git a/MessageClient.hpp b/MessageClient.hpp index 6cb3ea87f..f0719ac55 100644 --- a/MessageClient.hpp +++ b/MessageClient.hpp @@ -62,7 +62,12 @@ public: Q_SLOT void qso_logged (QDateTime time_off, QString const& dx_call, QString const& dx_grid , Frequency dial_frequency, QString const& mode, QString const& report_sent , QString const& report_received, QString const& tx_power, QString const& comments - , QString const& name, QDateTime time_on, QString const& operator_call); + , QString const& name, QDateTime time_on, QString const& operator_call + , QString const& my_call, QString const& my_grid); + + // ADIF_record argument should be valid ADIF excluding any end + // of record marker + Q_SLOT void logged_ADIF (QByteArray const& ADIF_record); // this slot may be used to send arbitrary UDP datagrams to and // destination allowing the underlying socket to be used for general @@ -94,6 +99,11 @@ public: // lookup fails Q_SIGNAL void error (QString const&) const; + // this signal is emitted if the message obtains a location from a + // server. (It doesn't have to be new, could be a periodic location + // update) + Q_SIGNAL void location (QString const&); + private: class impl; pimpl m_; diff --git a/MessageServer.cpp b/MessageServer.cpp index e6e100762..724da4afc 100644 --- a/MessageServer.cpp +++ b/MessageServer.cpp @@ -292,14 +292,18 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s QByteArray name; QDateTime time_on; // Note: LOTW uses TIME_ON for their +/- 30-minute time window QByteArray operator_call; + QByteArray my_call; + QByteArray my_grid; in >> time_off >> dx_call >> dx_grid >> dial_frequency >> mode >> report_sent >> report_received - >> tx_power >> comments >> name >> time_on >> operator_call; + >> tx_power >> comments >> name >> time_on >> operator_call >> my_call >> my_grid; if (check_status (in) != Fail) { Q_EMIT self_->qso_logged (id, time_off, QString::fromUtf8 (dx_call), QString::fromUtf8 (dx_grid) , dial_frequency, QString::fromUtf8 (mode), QString::fromUtf8 (report_sent) , QString::fromUtf8 (report_received), QString::fromUtf8 (tx_power) - , QString::fromUtf8 (comments), QString::fromUtf8 (name), time_on, QString::fromUtf8 (operator_call)); + , QString::fromUtf8 (comments), QString::fromUtf8 (name), time_on + , QString::fromUtf8 (operator_call), QString::fromUtf8 (my_call) + , QString::fromUtf8 (my_grid)); } } break; @@ -309,6 +313,17 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s clients_.remove (id); break; + case NetworkMessage::LoggedADIF: + { + QByteArray ADIF; + in >> ADIF; + if (check_status (in) != Fail) + { + Q_EMIT self_->logged_ADIF (id, ADIF); + } + } + break; + default: // Ignore break; @@ -453,3 +468,15 @@ void MessageServer::free_text (QString const& id, QString const& text, bool send m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_); } } + +void MessageServer::location (QString const& id, QString const& loc) +{ + auto iter = m_->clients_.find (id); + if (iter != std::end (m_->clients_)) + { + QByteArray message; + NetworkMessage::Builder out {&message, NetworkMessage::Location, id, (*iter).negotiated_schema_number_}; + out << loc.toUtf8 (); + m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_); + } +} diff --git a/MessageServer.hpp b/MessageServer.hpp index 83fb4ff83..77c976fa4 100644 --- a/MessageServer.hpp +++ b/MessageServer.hpp @@ -59,6 +59,9 @@ public: // message and optionally send it ASAP Q_SLOT void free_text (QString const& id, QString const& text, bool send); + // ask the client with identification 'id' to set the location provided + Q_SLOT void location (QString const& id, QString const& location); + // the following signals are emitted when a client broadcasts the // matching message Q_SIGNAL void client_opened (QString const& id, QString const& version, QString const& revision); @@ -77,8 +80,10 @@ public: Q_SIGNAL void qso_logged (QString const& id, QDateTime time_off, QString const& dx_call, QString const& dx_grid , Frequency dial_frequency, QString const& mode, QString const& report_sent , QString const& report_received, QString const& tx_power, QString const& comments - , QString const& name, QDateTime time_on, QString const& operator_call); + , QString const& name, QDateTime time_on, QString const& operator_call + , QString const& my_call, QString const& my_grid); Q_SIGNAL void clear_decodes (QString const& id); + Q_SIGNAL void logged_ADIF (QString const& id, QByteArray const& ADIF); // this signal is emitted when a network error occurs Q_SIGNAL void error (QString const&) const; diff --git a/NetworkMessage.hpp b/NetworkMessage.hpp index a14d6d1b8..479575a32 100644 --- a/NetworkMessage.hpp +++ b/NetworkMessage.hpp @@ -237,6 +237,9 @@ * Comments utf8 * Name utf8 * Date & Time On QDateTime + * Operator call utf8 + * My call utf8 + * My grid utf8 * * The QSO logged message is sent to the server(s) when the * WSJT-X user accepts the "Log QSO" dialog by clicking the "OK" @@ -304,6 +307,7 @@ * command to determine the contents of the current free text * message. * + * * WSPRDecode Out 10 quint32 * Id (unique key) utf8 * New bool @@ -327,6 +331,43 @@ * from a played back recording. * * + * Location In 11 + * Id (unique key) utf8 + * Location utf8 + * + * This message allows the server to set the current current + * geographical location of operation. The supplied location is + * not persistent but is used as a session lifetime replacement + * loction that overrides the Maidenhead grid locater set in the + * application settings. The intent is to allow an external + * application to update the operating location dynamically + * during a mobile period of operation. + * + * Currently only Maidenhead grid squares or sub-squares are + * accepted, i.e. 4- or 6-digit locators. Other formats may be + * accepted in future. + * + * + * Logged ADIF Out 12 quint32 + * Id (unique key) utf8 + * ADIF text ASCII (serialized like utf8) + * + * The logged ADIF message is sent to the server(s) when the + * WSJT-X user accepts the "Log QSO" dialog by clicking the "OK" + * button. The "ADIF text" field consists of a valid ADIF file + * such that the WSJT-X UDP header information is encapsulated + * into a valid ADIF header. E.g.: + * + * <32-bit-count> # binary encoded fields + * # the remainder is the contents of the ADIF text field + * 3.0.7 + * WSJT-X + * + * ADIF log data fields ... + * + * Note that receiving applications can treat the whole message + * as a valid ADIF file with one record without special parsing. + * */ #include @@ -353,6 +394,8 @@ namespace NetworkMessage HaltTx, FreeText, WSPRDecode, + Location, + LoggedADIF, maximum_message_type_ // ONLY add new message types // immediately before here }; diff --git a/UDPExamples/ClientWidget.cpp b/UDPExamples/ClientWidget.cpp index de77286b8..001041cae 100644 --- a/UDPExamples/ClientWidget.cpp +++ b/UDPExamples/ClientWidget.cpp @@ -3,6 +3,8 @@ #include #include +#include "MaidenheadLocatorValidator.hpp" + namespace { //QRegExp message_alphabet {"[- A-Za-z0-9+./?]*"}; @@ -120,9 +122,11 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod , decodes_table_view_ {new QTableView} , beacons_table_view_ {new QTableView} , message_line_edit_ {new QLineEdit} + , grid_line_edit_ {new QLineEdit} , decodes_stack_ {new QStackedLayout} , auto_off_button_ {new QPushButton {tr ("&Auto Off")}} , halt_tx_button_ {new QPushButton {tr ("&Halt Tx")}} + , de_label_ {new QLabel} , mode_label_ {new QLabel} , fast_mode_ {false} , frequency_label_ {new QLabel} @@ -141,13 +145,18 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod auto form_layout = new QFormLayout; form_layout->addRow (tr ("Free text:"), message_line_edit_); + form_layout->addRow (tr ("Temporary grid:"), grid_line_edit_); message_line_edit_->setValidator (new QRegExpValidator {message_alphabet, this}); + grid_line_edit_->setValidator (new MaidenheadLocatorValidator {this}); connect (message_line_edit_, &QLineEdit::textEdited, [this] (QString const& text) { Q_EMIT do_free_text (id_, text, false); }); connect (message_line_edit_, &QLineEdit::editingFinished, [this] () { Q_EMIT do_free_text (id_, message_line_edit_->text (), true); }); + connect (grid_line_edit_, &QLineEdit::editingFinished, [this] () { + Q_EMIT location (id_, grid_line_edit_->text ()); + }); auto decodes_page = new QWidget; auto decodes_layout = new QVBoxLayout {decodes_page}; @@ -189,6 +198,7 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod // set up status area auto status_bar = new QStatusBar; + status_bar->addPermanentWidget (de_label_); status_bar->addPermanentWidget (mode_label_); status_bar->addPermanentWidget (frequency_label_); status_bar->addPermanentWidget (dx_label_); @@ -216,7 +226,7 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod void ClientWidget::update_status (QString const& id, Frequency f, QString const& mode, QString const& dx_call , QString const& report, QString const& tx_mode, bool tx_enabled , bool transmitting, bool decoding, qint32 rx_df, qint32 tx_df - , QString const& de_call, QString const& /*de_grid*/, QString const& dx_grid + , QString const& de_call, QString const& de_grid, QString const& dx_grid , bool watchdog_timeout, QString const& sub_mode, bool fast_mode) { if (id == id_) @@ -224,6 +234,8 @@ void ClientWidget::update_status (QString const& id, Frequency f, QString const& fast_mode_ = fast_mode; decodes_proxy_model_.de_call (de_call); decodes_proxy_model_.rx_df (rx_df); + de_label_->setText (de_call.size () >= 0 ? QString {"DE: %1%2"}.arg (de_call) + .arg (de_grid.size () ? '(' + de_grid + ')' : QString {}) : QString {}); mode_label_->setText (QString {"Mode: %1%2%3%4"} .arg (mode) .arg (sub_mode) diff --git a/UDPExamples/ClientWidget.hpp b/UDPExamples/ClientWidget.hpp index 1f5ae6012..0cec23e7a 100644 --- a/UDPExamples/ClientWidget.hpp +++ b/UDPExamples/ClientWidget.hpp @@ -43,6 +43,7 @@ public: Q_SIGNAL void do_reply (QModelIndex const&, quint8 modifier); Q_SIGNAL void do_halt_tx (QString const& id, bool auto_only); Q_SIGNAL void do_free_text (QString const& id, QString const& text, bool); + Q_SIGNAL void location (QString const &id, QString const &text); private: QString id_; @@ -69,9 +70,11 @@ private: QTableView * decodes_table_view_; QTableView * beacons_table_view_; QLineEdit * message_line_edit_; + QLineEdit * grid_line_edit_; QStackedLayout * decodes_stack_; QAbstractButton * auto_off_button_; QAbstractButton * halt_tx_button_; + QLabel * de_label_; QLabel * mode_label_; bool fast_mode_; QLabel * frequency_label_; diff --git a/UDPExamples/MessageAggregatorMainWindow.cpp b/UDPExamples/MessageAggregatorMainWindow.cpp index 196282d61..324c7d0e5 100644 --- a/UDPExamples/MessageAggregatorMainWindow.cpp +++ b/UDPExamples/MessageAggregatorMainWindow.cpp @@ -22,12 +22,15 @@ namespace QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Sent"), QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Rec'd"), QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Power"), + QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Operator"), + QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "My Call"), + QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "My Grid"), QT_TRANSLATE_NOOP ("MessageAggregatorMainWindow", "Comments"), }; } MessageAggregatorMainWindow::MessageAggregatorMainWindow () - : log_ {new QStandardItemModel {0, 11, this}} + : log_ {new QStandardItemModel {0, 14, this}} , decodes_model_ {new DecodesModel {this}} , beacons_model_ {new BeaconsModel {this}} , server_ {new MessageServer {this}} @@ -111,10 +114,12 @@ MessageAggregatorMainWindow::MessageAggregatorMainWindow () show (); } -void MessageAggregatorMainWindow::log_qso (QString const& /*id*/, QDateTime time_off, QString const& dx_call, QString const& dx_grid - , Frequency dial_frequency, QString const& mode, QString const& report_sent - , QString const& report_received, QString const& tx_power, QString const& comments - , QString const& name, QDateTime time_on) +void MessageAggregatorMainWindow::log_qso (QString const& /*id*/, QDateTime time_off, QString const& dx_call + , QString const& dx_grid, Frequency dial_frequency, QString const& mode + , QString const& report_sent, QString const& report_received + , QString const& tx_power, QString const& comments + , QString const& name, QDateTime time_on, QString const& operator_call + , QString const& my_call, QString const& my_grid) { QList row; row << new QStandardItem {time_on.toString ("dd-MMM-yyyy hh:mm:ss")} @@ -127,6 +132,9 @@ void MessageAggregatorMainWindow::log_qso (QString const& /*id*/, QDateTime time << new QStandardItem {report_sent} << new QStandardItem {report_received} << new QStandardItem {tx_power} + << new QStandardItem {operator_call} + << new QStandardItem {my_call} + << new QStandardItem {my_grid} << new QStandardItem {comments}; log_->appendRow (row); log_table_view_->resizeColumnsToContents (); @@ -149,6 +157,7 @@ void MessageAggregatorMainWindow::add_client (QString const& id, QString const& connect (dock, &ClientWidget::do_reply, decodes_model_, &DecodesModel::do_reply); connect (dock, &ClientWidget::do_halt_tx, server_, &MessageServer::halt_tx); connect (dock, &ClientWidget::do_free_text, server_, &MessageServer::free_text); + connect (dock, &ClientWidget::location, server_, &MessageServer::location); connect (view_action, &QAction::toggled, dock, &ClientWidget::setVisible); dock_widgets_[id] = dock; server_->replay (id); diff --git a/UDPExamples/MessageAggregatorMainWindow.hpp b/UDPExamples/MessageAggregatorMainWindow.hpp index 4039c60ab..4ee5bface 100644 --- a/UDPExamples/MessageAggregatorMainWindow.hpp +++ b/UDPExamples/MessageAggregatorMainWindow.hpp @@ -29,7 +29,8 @@ public: Q_SLOT void log_qso (QString const& /*id*/, QDateTime time_off, QString const& dx_call, QString const& dx_grid , Frequency dial_frequency, QString const& mode, QString const& report_sent , QString const& report_received, QString const& tx_power, QString const& comments - , QString const& name, QDateTime time_on); + , QString const& name, QDateTime time_on, QString const& operator_call + , QString const& my_call, QString const& my_grid); private: void add_client (QString const& id, QString const& version, QString const& revision); diff --git a/UDPExamples/UDPDaemon.cpp b/UDPExamples/UDPDaemon.cpp index 731f31661..b75a8adec 100644 --- a/UDPExamples/UDPDaemon.cpp +++ b/UDPExamples/UDPDaemon.cpp @@ -96,23 +96,37 @@ public: } Q_SLOT void qso_logged (QString const&client_id, QDateTime time_off, QString const& dx_call, QString const& dx_grid - , Frequency dial_frequency, QString const& mode, QString const& report_sent - , QString const& report_received, QString const& tx_power - , QString const& comments, QString const& name, QDateTime time_on, QString const& operator_call) + , Frequency dial_frequency, QString const& mode, QString const& report_sent + , QString const& report_received, QString const& tx_power + , QString const& comments, QString const& name, QDateTime time_on + , QString const& operator_call, QString const& my_call, QString const& my_grid) { if (client_id == id_) { qDebug () << "time_on:" << time_on << "time_off:" << time_off << "dx_call:" << dx_call << "grid:" << dx_grid << "freq:" << dial_frequency << "mode:" << mode << "rpt_sent:" << report_sent << "rpt_rcvd:" << report_received << "Tx_pwr:" << tx_power << "comments:" << comments - << "name:" << name << "operator_call:" << operator_call; - std::cout << tr ("%1: Logged %2 grid: %3 power: %4 sent: %5 recd: %6 freq: %7 op: %8").arg (id_) - .arg (dx_call).arg (dx_grid).arg (tx_power).arg (report_sent).arg (report_received).arg (dial_frequency).arg (operator_call).toStdString () - << tr (" @ %1").arg (time_off.toString("yyyy-MM-dd hh:mm:ss.z")).toStdString() + << "name:" << name << "operator_call:" << operator_call << "my_call:" << my_call + << "my_grid:" << my_grid; + std::cout << QByteArray {80, '-'}.data () << '\n'; + std::cout << tr ("%1: Logged %2 grid: %3 power: %4 sent: %5 recd: %6 freq: %7 time_off: %8 op: %9 my_call: %10 my_grid: %11") + .arg (id_).arg (dx_call).arg (dx_grid).arg (tx_power).arg (report_sent).arg (report_received) + .arg (dial_frequency).arg (time_off.toString("yyyy-MM-dd hh:mm:ss.z")).arg (operator_call) + .arg (my_call).arg (my_grid).toStdString () << std::endl; } } + Q_SLOT void logged_ADIF (QString const&client_id, QByteArray const& ADIF) + { + if (client_id == id_) + { + qDebug () << "ADIF:" << ADIF; + std::cout << QByteArray {80, '-'}.data () << '\n'; + std::cout << ADIF.data () << std::endl; + } + } + private: QString id_; Frequency dial_frequency_; @@ -146,6 +160,7 @@ private: connect (server_, &MessageServer::decode, client, &Client::decode_added); connect (server_, &MessageServer::WSPR_decode, client, &Client::beacon_spot_added); connect (server_, &MessageServer::qso_logged, client, &Client::qso_logged); + connect (server_, &MessageServer::logged_ADIF, client, &Client::logged_ADIF); clients_[id] = client; server_->replay (id); std::cout << "Discovered WSJT-X instance: " << id.toStdString (); diff --git a/logbook/adif.cpp b/logbook/adif.cpp index 31cd39a9d..568a2eb1c 100644 --- a/logbook/adif.cpp +++ b/logbook/adif.cpp @@ -174,8 +174,11 @@ int ADIF::getCount() const return _data.size(); } -QString ADIF::QSOToADIF(QString const& hisCall, QString const& hisGrid, QString const& mode, QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn, QDateTime const& dateTimeOff, QString const& band, - QString const& comments, QString const& name, QString const& strDialFreq, QString const& m_myCall, QString const& m_myGrid, QString const& m_txPower, QString const& operator_call) +QByteArray ADIF::QSOToADIF(QString const& hisCall, QString const& hisGrid, QString const& mode + , QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn + , QDateTime const& dateTimeOff, QString const& band, QString const& comments + , QString const& name, QString const& strDialFreq, QString const& m_myCall + , QString const& m_myGrid, QString const& m_txPower, QString const& operator_call) { QString t; t = "" + hisCall; @@ -205,14 +208,12 @@ QString ADIF::QSOToADIF(QString const& hisCall, QString const& hisGrid, QString if (operator_call!="") t+=" " + operator_call; - t += " "; - return t; + return t.toLatin1 (); } // open ADIF file and append the QSO details. Return true on success -bool ADIF::addQSOToFile(QString const& hisCall, QString const& hisGrid, QString const& mode, QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn, QDateTime const& dateTimeOff, QString const& band, - QString const& comments, QString const& name, QString const& strDialFreq, QString const& m_myCall, QString const& m_myGrid, QString const& m_txPower, QString const& operator_call) +bool ADIF::addQSOToFile(QByteArray const& ADIF_record) { QFile f2(_filename); if (!f2.open(QIODevice::Text | QIODevice::Append)) @@ -223,10 +224,7 @@ bool ADIF::addQSOToFile(QString const& hisCall, QString const& hisGrid, QString if (f2.size()==0) out << "WSJT-X ADIF Export" << endl; // new file - QString t; - t = QSOToADIF(hisCall,hisGrid,mode,rptSent,rptRcvd,dateTimeOn,dateTimeOff, - band,comments,name,strDialFreq,m_myCall,m_myGrid,m_txPower,operator_call); - out << t << endl; + out << ADIF_record << " " << endl; f2.close(); } return true; diff --git a/logbook/adif.h b/logbook/adif.h index 2c82c2144..727ca9952 100644 --- a/logbook/adif.h +++ b/logbook/adif.h @@ -29,11 +29,13 @@ class ADIF int getCount() const; // open ADIF file and append the QSO details. Return true on success - bool addQSOToFile(QString const& hisCall, QString const& hisGrid, QString const& mode, QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn, QDateTime const& dateTimeOff, QString const& band, - QString const& comments, QString const& name, QString const& strDialFreq, QString const& m_myCall, QString const& m_myGrid, QString const& m_txPower, QString const& operator_call); + bool addQSOToFile(QByteArray const& ADIF_record); - QString QSOToADIF(QString const& hisCall, QString const& hisGrid, QString const& mode, QString const& rptSent, QString const& rptRcvd, QDateTime const& dateTimeOn, QDateTime const& dateTimeOff, QString const& band, - QString const& comments, QString const& name, QString const& strDialFreq, QString const& m_myCall, QString const& m_myGrid, QString const& m_txPower, QString const& operator_call); + QByteArray QSOToADIF(QString const& hisCall, QString const& hisGrid, QString const& mode, QString const& rptSent + , QString const& rptRcvd, QDateTime const& dateTimeOn, QDateTime const& dateTimeOff + , QString const& band, QString const& comments, QString const& name + , QString const& strDialFreq, QString const& m_myCall, QString const& m_myGrid + , QString const& m_txPower, QString const& operator_call); private: diff --git a/logqso.cpp b/logqso.cpp index c0c7572d6..3fa454c6a 100644 --- a/logqso.cpp +++ b/logqso.cpp @@ -117,7 +117,9 @@ void LogQSO::accept() auto adifilePath = QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}.absoluteFilePath ("wsjtx_log.adi"); adifile.init(adifilePath); - if (!adifile.addQSOToFile(hisCall,hisGrid,mode,rptSent,rptRcvd,m_dateTimeOn,m_dateTimeOff,band,comments,name,strDialFreq,m_myCall,m_myGrid,m_txPower, operator_call)) + QByteArray ADIF {adifile.QSOToADIF (hisCall, hisGrid, mode, rptSent, rptRcvd, m_dateTimeOn, m_dateTimeOff, band + , comments, name, strDialFreq, m_myCall, m_myGrid, m_txPower, operator_call)}; + if (!adifile.addQSOToFile (ADIF)) { MessageBox::warning_message (this, tr ("Log file error"), tr ("Cannot open \"%1\"").arg (adifilePath)); @@ -125,12 +127,9 @@ void LogQSO::accept() // Log to N1MM Logger if (m_config->broadcast_to_n1mm() && m_config->valid_n1mm_info()) { - QString adif = adifile.QSOToADIF(hisCall,hisGrid,mode,rptSent,rptRcvd,m_dateTimeOn,m_dateTimeOff,band,comments,name,strDialFreq,m_myCall,m_myGrid,m_txPower, operator_call); const QHostAddress n1mmhost = QHostAddress(m_config->n1mm_server_name()); - QByteArray qba_adif = adif.toLatin1(); QUdpSocket _sock; - - auto rzult = _sock.writeDatagram ( qba_adif, n1mmhost, quint16(m_config->n1mm_server_port())); + auto rzult = _sock.writeDatagram (ADIF + " ", n1mmhost, quint16(m_config->n1mm_server_port())); if (rzult == -1) { MessageBox::warning_message (this, tr ("Error sending log to N1MM"), tr ("Write returned \"%1\"").arg (rzult)); @@ -157,7 +156,7 @@ void LogQSO::accept() } //Clean up and finish logging - Q_EMIT acceptQSO (m_dateTimeOff, hisCall, hisGrid, m_dialFreq, mode, rptSent, rptRcvd, m_txPower, comments, name,m_dateTimeOn, operator_call); + Q_EMIT acceptQSO (m_dateTimeOff, hisCall, hisGrid, m_dialFreq, mode, rptSent, rptRcvd, m_txPower, comments, name,m_dateTimeOn, operator_call, m_myCall, m_myGrid, ADIF); QDialog::accept(); } diff --git a/logqso.h b/logqso.h index 4f22649b8..fb92201e1 100644 --- a/logqso.h +++ b/logqso.h @@ -8,7 +8,9 @@ #include #endif +#include #include +#include #include "Radio.hpp" @@ -18,6 +20,7 @@ namespace Ui { class QSettings; class Configuration; +class QByteArray; class LogQSO : public QDialog { @@ -40,7 +43,8 @@ signals: , Radio::Frequency dial_freq, QString const& mode , QString const& rpt_sent, QString const& rpt_received , QString const& tx_power, QString const& comments - , QString const& name, QDateTime const& QSO_date_on, QString const& operator_call); + , QString const& name, QDateTime const& QSO_date_on, QString const& operator_call + , QString const& my_call, QString const& my_grid, QByteArray const& ADIF); protected: void hideEvent (QHideEvent *); diff --git a/mainwindow.cpp b/mainwindow.cpp index f2182b3af..556bc8be5 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -446,12 +446,13 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, connect (this, &MainWindow::finished, m_fastGraph.data (), &FastGraph::close); // setup the log QSO dialog - connect (m_logDlg.data (), &LogQSO::acceptQSO, this, &MainWindow::acceptQSO2); + connect (m_logDlg.data (), &LogQSO::acceptQSO, this, &MainWindow::acceptQSO); connect (this, &MainWindow::finished, m_logDlg.data (), &LogQSO::close); // Network message handlers connect (m_messageClient, &MessageClient::reply, this, &MainWindow::replyToCQ); connect (m_messageClient, &MessageClient::replay, this, &MainWindow::replayDecodes); + connect (m_messageClient, &MessageClient::location, this, &MainWindow::locationChange); connect (m_messageClient, &MessageClient::halt_tx, [this] (bool auto_only) { if (m_config.accept_udp_requests ()) { if (auto_only) { @@ -1581,13 +1582,16 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog { // things that might change that we need know about auto callsign = m_config.my_callsign (); + auto my_grid = m_config.my_grid (); if (QDialog::Accepted == m_config.exec ()) { if (m_config.my_callsign () != callsign) { m_baseCall = Radio::base_callsign (m_config.my_callsign ()); morse_(const_cast (m_config.my_callsign ().toLatin1().constData()), const_cast (icw), &m_ncw, m_config.my_callsign ().length()); } - + if (m_config.my_callsign () != callsign || m_config.my_grid () != my_grid) { + statusUpdate (); + } on_dxGridEntry_textChanged (m_hisGrid); // recalculate distances in case of units change enable_DXCC_entity (m_config.DXCC ()); // sets text window proportions and (re)inits the logbook @@ -4811,16 +4815,18 @@ void MainWindow::on_logQSOButton_clicked() //Log QSO button m_config.bFox(), m_opCall); } -void MainWindow::acceptQSO2(QDateTime const& QSO_date_off, QString const& call, QString const& grid +void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call, QString const& grid , Frequency dial_freq, QString const& mode , QString const& rpt_sent, QString const& rpt_received , QString const& tx_power, QString const& comments - , QString const& name, QDateTime const& QSO_date_on, QString const& operator_call) + , QString const& name, QDateTime const& QSO_date_on, QString const& operator_call + , QString const& my_call, QString const& my_grid, QByteArray const& ADIF) { QString date = QSO_date_on.toString("yyyyMMdd"); m_logBook.addAsWorked (m_hisCall, m_config.bands ()->find (m_freqNominal), m_modeTx, date); - m_messageClient->qso_logged (QSO_date_off, call, grid, dial_freq, mode, rpt_sent, rpt_received, tx_power, comments, name, QSO_date_on, operator_call); + m_messageClient->qso_logged (QSO_date_off, call, grid, dial_freq, mode, rpt_sent, rpt_received, tx_power, comments, name, QSO_date_on, operator_call, my_call, my_grid); + m_messageClient->logged_ADIF (ADIF); if (m_config.clear_DX ()) { @@ -6522,6 +6528,33 @@ void MainWindow::replyToCQ (QTime time, qint32 snr, float delta_time, quint32 de } } +void MainWindow::locationChange (QString const& location) +{ + QString grid {location.trimmed ()}; + int len; + + // string 6 chars or fewer, interpret as a grid, or use with a 'GRID:' prefix + if (grid.size () > 6) { + if (grid.toUpper ().startsWith ("GRID:")) { + grid = grid.mid (5).trimmed (); + } + else { + // TODO - support any other formats, e.g. latlong? Or have that conversion done external to wsjtx + return; + } + } + if (MaidenheadLocatorValidator::Acceptable == MaidenheadLocatorValidator ().validate (grid, len)) { + qDebug() << "locationChange: Grid supplied is " << grid; + if (m_config.my_grid () != grid) { + m_config.set_location (grid); + genStdMsgs (m_rpt, false); + statusUpdate (); + } + } else { + qDebug() << "locationChange: Invalid grid " << grid; + } +} + void MainWindow::replayDecodes () { // we accept this request even if the setting to accept UDP requests diff --git a/mainwindow.h b/mainwindow.h index 5f48e9891..5e912a097 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -218,11 +218,12 @@ private slots: void on_tuneButton_clicked (bool); void on_pbR2T_clicked(); void on_pbT2R_clicked(); - void acceptQSO2(QDateTime const&, QString const& call, QString const& grid + void acceptQSO (QDateTime const&, QString const& call, QString const& grid , Frequency dial_freq, QString const& mode , QString const& rpt_sent, QString const& rpt_received , QString const& tx_power, QString const& comments - , QString const& name, QDateTime const& QSO_date_on, QString const& operator_call); + , QString const& name, QDateTime const& QSO_date_on, QString const& operator_call + , QString const& my_call, QString const& my_grid, QByteArray const& ADIF); void on_bandComboBox_currentIndexChanged (int index); void on_bandComboBox_activated (int index); void on_readFreq_clicked(); @@ -645,6 +646,7 @@ private: void transmitDisplay (bool); void processMessage(DecodedText const&, Qt::KeyboardModifiers = 0); void replyToCQ (QTime, qint32 snr, float delta_time, quint32 delta_frequency, QString const& mode, QString const& message_text, bool low_confidence, quint8 modifiers); + void locationChange(QString const& location); void replayDecodes (); void postDecode (bool is_new, QString const& message); void postWSPRDecode (bool is_new, QStringList message_parts);