Add UDP message to set temporary Maidenhead locator

When "Auto  Grid" is  checked in  "Settings->General" UDP  messages of
type "Location" will update a temporary  DE grid square. The intent is
to  allow  an external  application  joining  the WSJT-X  UDP  message
protocol to dynamically update the DE grid during mobile operation.

This change also tidies up  some outstanding issues around logging the
operator call.

This change  adds a new UDP  message "Logged ADIF" that  is emitted in
parallel with  "QSO Logged"  messages. The new  message is  valid ADIF
file format  and contains the  logged QSO  fields. The intent  is that
basic  UDP server  applications  might already  have  ADIF log  record
capture capabilities and could use  this message to feed existing ADIF
parsing routines to log QSOs. All that should be needed is to identify
this message type and the single field is ADIF compatible ASCII.

Thanks  to  Brian,   N9ADG,  for  the  patches  that   lead  to  these
enhancements.

git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@8454 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
Bill Somerville 2018-02-04 22:42:35 +00:00
parent 8b0d5c092d
commit 3c384f7cbb
20 changed files with 287 additions and 53 deletions

View File

@ -683,6 +683,7 @@ set (message_aggregator_CXXSRCS
UDPExamples/DecodesModel.cpp
UDPExamples/BeaconsModel.cpp
UDPExamples/ClientWidget.cpp
MaidenheadLocatorValidator.cpp
)
set (message_aggregator_STYLESHEETS

View File

@ -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
}

View File

@ -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;

View File

@ -80,11 +80,22 @@
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout_21">
<item row="0" column="0">
<widget class="QCheckBox" name="use_dynamic_grid">
<property name="toolTip">
<string>Check to allow grid changes from external programs</string>
</property>
<property name="text">
<string>AutoGrid</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="1">
<widget class="QComboBox" name="region_combo_box"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
@ -95,6 +106,9 @@
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="region_combo_box"/>
</item>
</layout>
</item>
</layout>
@ -2599,6 +2613,7 @@ soundcard changes</string>
<tabstop>configuration_tabs</tabstop>
<tabstop>callsign_line_edit</tabstop>
<tabstop>grid_line_edit</tabstop>
<tabstop>use_dynamic_grid</tabstop>
<tabstop>region_combo_box</tabstop>
<tabstop>type_2_msg_gen_combo_box</tabstop>
<tabstop>insert_blank_check_box</tabstop>

View File

@ -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 {"\n<adif_ver:5>3.0.7\n<programid:6>WSJT-X\n<EOH>\n" + ADIF_record + " <EOR>"};
out << ADIF;
m_->send_message (out, message);
}
}

View File

@ -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 <EOR> 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<impl> m_;

View File

@ -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_);
}
}

View File

@ -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;

View File

@ -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.:
*
* <magic-number><schema-number><type><id><32-bit-count> # binary encoded fields
* # the remainder is the contents of the ADIF text field
* <adif_ver:5>3.0.7
* <programid:6>WSJT-X
* <EOH>
* ADIF log data fields ...<EOR>
*
* Note that receiving applications can treat the whole message
* as a valid ADIF file with one record without special parsing.
*
*/
#include <QDataStream>
@ -353,6 +394,8 @@ namespace NetworkMessage
HaltTx,
FreeText,
WSPRDecode,
Location,
LoggedADIF,
maximum_message_type_ // ONLY add new message types
// immediately before here
};

View File

@ -3,6 +3,8 @@
#include <QRegExp>
#include <QColor>
#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)

View File

@ -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_;

View File

@ -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<QStandardItem *> 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);

View File

@ -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);

View File

@ -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 ();

View File

@ -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 = "<call:" + QString::number(hisCall.length()) + ">" + hisCall;
@ -205,14 +208,12 @@ QString ADIF::QSOToADIF(QString const& hisCall, QString const& hisGrid, QString
if (operator_call!="")
t+=" <operator:" + QString::number(operator_call.length()) +
">" + operator_call;
t += " <eor>";
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<eoh>" << 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 << " <eor>" << endl;
f2.close();
}
return true;

View File

@ -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:

View File

@ -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 + " <eor>", 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();
}

View File

@ -8,7 +8,9 @@
#include <QtGui>
#endif
#include <QString>
#include <QScopedPointer>
#include <QDateTime>
#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 *);

View File

@ -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<char *> (m_config.my_callsign ().toLatin1().constData()),
const_cast<int *> (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

View File

@ -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);