mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2026-07-26 03:54:32 -04:00
Add WSPR decodes to UDP message protocol
The message_aggregator (MessageAggregator.cpp) has been updated to do something with WSPR decodes. git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@6101 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
+200
-24
@@ -184,17 +184,148 @@ private:
|
||||
QFont text_font_;
|
||||
};
|
||||
|
||||
//
|
||||
// Beacons Model - simple data model for all beacon spots
|
||||
//
|
||||
// The model is a basic table with uniform row format. Rows consist of
|
||||
// QStandardItem instances containing the string representation of the
|
||||
// column data and if the underlying field is not a string then the
|
||||
// UserRole+1 role contains the underlying data item.
|
||||
//
|
||||
// Two slots are provided to add a new decode and remove all spots for
|
||||
// a client.
|
||||
//
|
||||
class BeaconsModel
|
||||
: public QStandardItemModel
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
BeaconsModel (QObject * parent = nullptr)
|
||||
: QStandardItemModel {0, 9, parent}
|
||||
, text_font_ {"Courier", 10}
|
||||
{
|
||||
setHeaderData (0, Qt::Horizontal, tr ("Client"));
|
||||
setHeaderData (1, Qt::Horizontal, tr ("Time"));
|
||||
setHeaderData (2, Qt::Horizontal, tr ("Snr"));
|
||||
setHeaderData (3, Qt::Horizontal, tr ("DT"));
|
||||
setHeaderData (4, Qt::Horizontal, tr ("Frequency"));
|
||||
setHeaderData (5, Qt::Horizontal, tr ("Drift"));
|
||||
setHeaderData (6, Qt::Horizontal, tr ("Callsign"));
|
||||
setHeaderData (7, Qt::Horizontal, tr ("Grid"));
|
||||
setHeaderData (8, Qt::Horizontal, tr ("Power"));
|
||||
}
|
||||
|
||||
Q_SLOT void add_beacon_spot (bool is_new, QString const& client_id, QTime time, qint32 snr, float delta_time
|
||||
, Frequency frequency, qint32 drift, QString const& callsign, QString const& grid
|
||||
, qint32 power)
|
||||
{
|
||||
if (!is_new)
|
||||
{
|
||||
int target_row {-1};
|
||||
for (auto row = 0; row < rowCount (); ++row)
|
||||
{
|
||||
if (data (index (row, 0)).toString () == client_id)
|
||||
{
|
||||
auto row_time = item (row, 1)->data ().toTime ();
|
||||
if (row_time == time
|
||||
&& item (row, 2)->data ().toInt () == snr
|
||||
&& item (row, 3)->data ().toFloat () == delta_time
|
||||
&& item (row, 4)->data ().value<Frequency> () == frequency
|
||||
&& data (index (row, 5)).toInt () == drift
|
||||
&& data (index (row, 6)).toString () == callsign
|
||||
&& data (index (row, 7)).toString () == grid
|
||||
&& data (index (row, 8)).toInt () == power)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (time <= row_time)
|
||||
{
|
||||
target_row = row; // last row with same time
|
||||
}
|
||||
}
|
||||
}
|
||||
if (target_row >= 0)
|
||||
{
|
||||
insertRow (target_row + 1, make_row (client_id, time, snr, delta_time, frequency, drift, callsign, grid, power));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
appendRow (make_row (client_id, time, snr, delta_time, frequency, drift, callsign, grid, power));
|
||||
}
|
||||
|
||||
QList<QStandardItem *> make_row (QString const& client_id, QTime time, qint32 snr, float delta_time
|
||||
, Frequency frequency, qint32 drift, QString const& callsign
|
||||
, QString const& grid, qint32 power) const
|
||||
{
|
||||
auto time_item = new QStandardItem {time.toString ("hh:mm")};
|
||||
time_item->setData (time);
|
||||
time_item->setTextAlignment (Qt::AlignRight);
|
||||
|
||||
auto snr_item = new QStandardItem {QString::number (snr)};
|
||||
snr_item->setData (snr);
|
||||
snr_item->setTextAlignment (Qt::AlignRight);
|
||||
|
||||
auto dt = new QStandardItem {QString::number (delta_time)};
|
||||
dt->setData (delta_time);
|
||||
dt->setTextAlignment (Qt::AlignRight);
|
||||
|
||||
auto freq = new QStandardItem {Radio::pretty_frequency_MHz_string (frequency)};
|
||||
freq->setData (frequency);
|
||||
freq->setTextAlignment (Qt::AlignRight);
|
||||
|
||||
auto dri = new QStandardItem {QString::number (drift)};
|
||||
dri->setData (drift);
|
||||
dri->setTextAlignment (Qt::AlignRight);
|
||||
|
||||
auto gd = new QStandardItem {grid};
|
||||
gd->setTextAlignment (Qt::AlignRight);
|
||||
|
||||
auto pwr = new QStandardItem {QString::number (power)};
|
||||
pwr->setData (power);
|
||||
pwr->setTextAlignment (Qt::AlignRight);
|
||||
|
||||
QList<QStandardItem *> row {
|
||||
new QStandardItem {client_id}, time_item, snr_item, dt, freq, dri, new QStandardItem {callsign}, gd, pwr};
|
||||
Q_FOREACH (auto& item, row)
|
||||
{
|
||||
item->setEditable (false);
|
||||
item->setFont (text_font_);
|
||||
item->setTextAlignment (item->textAlignment () | Qt::AlignVCenter);
|
||||
}
|
||||
return row;
|
||||
}
|
||||
|
||||
Q_SLOT void clear_decodes (QString const& client_id)
|
||||
{
|
||||
for (auto row = rowCount () - 1; row >= 0; --row)
|
||||
{
|
||||
if (data (index (row, 0)).toString () == client_id)
|
||||
{
|
||||
removeRow (row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QFont text_font_;
|
||||
};
|
||||
|
||||
class ClientWidget
|
||||
: public QDockWidget
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
explicit ClientWidget (QAbstractItemModel * decodes_model, QString const& id, QWidget * parent = 0)
|
||||
explicit ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemModel * beacons_model
|
||||
, QString const& id, QWidget * parent = 0)
|
||||
: QDockWidget {id, parent}
|
||||
, id_ {id}
|
||||
, decodes_table_view_ {new QTableView}
|
||||
, beacons_table_view_ {new QTableView}
|
||||
, message_line_edit_ {new QLineEdit}
|
||||
, decodes_stack_ {new QStackedLayout}
|
||||
, auto_off_button_ {new QPushButton {tr ("&Auto Off")}}
|
||||
, halt_tx_button_ {new QPushButton {tr ("&Halt Tx")}}
|
||||
, mode_label_ {new QLabel}
|
||||
@@ -202,19 +333,14 @@ public:
|
||||
, frequency_label_ {new QLabel}
|
||||
, report_label_ {new QLabel}
|
||||
{
|
||||
auto content_layout = new QVBoxLayout;
|
||||
content_layout->setContentsMargins (QMargins {2, 2, 2, 2});
|
||||
|
||||
// set up table
|
||||
auto proxy_model = new DecodesFilterModel {id, this};
|
||||
proxy_model->setSourceModel (decodes_model);
|
||||
decodes_table_view_->setModel (proxy_model);
|
||||
// set up widgets
|
||||
auto decodes_proxy_model = new IdFilterModel {id, this};
|
||||
decodes_proxy_model->setSourceModel (decodes_model);
|
||||
decodes_table_view_->setModel (decodes_proxy_model);
|
||||
decodes_table_view_->verticalHeader ()->hide ();
|
||||
decodes_table_view_->hideColumn (0);
|
||||
content_layout->addWidget (decodes_table_view_);
|
||||
decodes_table_view_->horizontalHeader ()->setStretchLastSection (true);
|
||||
|
||||
// set up controls
|
||||
auto control_layout = new QHBoxLayout;
|
||||
auto form_layout = new QFormLayout;
|
||||
form_layout->addRow (tr ("Free text:"), message_line_edit_);
|
||||
message_line_edit_->setValidator (new QRegExpValidator {message_alphabet, this});
|
||||
@@ -224,16 +350,44 @@ public:
|
||||
connect (message_line_edit_, &QLineEdit::editingFinished, [this] () {
|
||||
Q_EMIT do_free_text (id_, message_line_edit_->text (), true);
|
||||
});
|
||||
control_layout->addLayout (form_layout);
|
||||
control_layout->addWidget (auto_off_button_);
|
||||
control_layout->addWidget (halt_tx_button_);
|
||||
|
||||
auto decodes_page = new QWidget;
|
||||
auto decodes_layout = new QVBoxLayout {decodes_page};
|
||||
decodes_layout->setContentsMargins (QMargins {2, 2, 2, 2});
|
||||
decodes_layout->addWidget (decodes_table_view_);
|
||||
decodes_layout->addLayout (form_layout);
|
||||
|
||||
auto beacons_proxy_model = new IdFilterModel {id, this};
|
||||
beacons_proxy_model->setSourceModel (beacons_model);
|
||||
beacons_table_view_->setModel (beacons_proxy_model);
|
||||
beacons_table_view_->verticalHeader ()->hide ();
|
||||
beacons_table_view_->hideColumn (0);
|
||||
beacons_table_view_->horizontalHeader ()->setStretchLastSection (true);
|
||||
|
||||
auto beacons_page = new QWidget;
|
||||
auto beacons_layout = new QVBoxLayout {beacons_page};
|
||||
beacons_layout->setContentsMargins (QMargins {2, 2, 2, 2});
|
||||
beacons_layout->addWidget (beacons_table_view_);
|
||||
|
||||
decodes_stack_->addWidget (decodes_page);
|
||||
decodes_stack_->addWidget (beacons_page);
|
||||
|
||||
// stack alternative views
|
||||
auto content_layout = new QVBoxLayout;
|
||||
content_layout->setContentsMargins (QMargins {2, 2, 2, 2});
|
||||
content_layout->addLayout (decodes_stack_);
|
||||
|
||||
// set up controls
|
||||
auto control_button_box = new QDialogButtonBox;
|
||||
control_button_box->addButton (auto_off_button_, QDialogButtonBox::ActionRole);
|
||||
control_button_box->addButton (halt_tx_button_, QDialogButtonBox::ActionRole);
|
||||
connect (auto_off_button_, &QAbstractButton::clicked, [this] (bool /* checked */) {
|
||||
Q_EMIT do_halt_tx (id_, true);
|
||||
});
|
||||
connect (halt_tx_button_, &QAbstractButton::clicked, [this] (bool /* checked */) {
|
||||
Q_EMIT do_halt_tx (id_, false);
|
||||
});
|
||||
content_layout->addLayout (control_layout);
|
||||
content_layout->addWidget (control_button_box);
|
||||
|
||||
// set up status area
|
||||
auto status_bar = new QStatusBar;
|
||||
@@ -254,8 +408,8 @@ public:
|
||||
setAllowedAreas (Qt::BottomDockWidgetArea);
|
||||
|
||||
// connect up table view signals
|
||||
connect (decodes_table_view_, &QTableView::doubleClicked, this, [this, proxy_model] (QModelIndex const& index) {
|
||||
Q_EMIT do_reply (proxy_model->mapToSource (index));
|
||||
connect (decodes_table_view_, &QTableView::doubleClicked, this, [this, decodes_proxy_model] (QModelIndex const& index) {
|
||||
Q_EMIT do_reply (decodes_proxy_model->mapToSource (index));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -265,7 +419,9 @@ public:
|
||||
{
|
||||
if (id == id_)
|
||||
{
|
||||
mode_label_->setText (QString {"Mode: %1%2"}.arg (mode).arg (tx_mode.isEmpty () ? tx_mode : '(' + tx_mode + ')'));
|
||||
mode_label_->setText (QString {"Mode: %1%2"}
|
||||
.arg (mode)
|
||||
.arg (tx_mode.isEmpty () || tx_mode == mode ? "" : '(' + tx_mode + ')'));
|
||||
dx_call_label_->setText ("DX CALL: " + dx_call);
|
||||
frequency_label_->setText ("QRG: " + Radio::pretty_frequency_MHz_string (f));
|
||||
report_label_->setText ("SNR: " + report);
|
||||
@@ -277,27 +433,39 @@ public:
|
||||
}
|
||||
|
||||
Q_SLOT void decode_added (bool /*is_new*/, QString const& client_id, QTime /*time*/, qint32 /*snr*/
|
||||
, float /*delta_time*/, quint32 /*delta_frequency*/, QString const& /*mode*/
|
||||
, QString const& /*message*/)
|
||||
, float /*delta_time*/, quint32 /*delta_frequency*/, QString const& /*mode*/
|
||||
, QString const& /*message*/)
|
||||
{
|
||||
if (client_id == id_)
|
||||
{
|
||||
decodes_stack_->setCurrentIndex (0);
|
||||
decodes_table_view_->resizeColumnsToContents ();
|
||||
decodes_table_view_->horizontalHeader ()->setStretchLastSection (true);
|
||||
decodes_table_view_->scrollToBottom ();
|
||||
}
|
||||
}
|
||||
|
||||
Q_SLOT void beacon_spot_added (bool /*is_new*/, QString const& client_id, QTime /*time*/, qint32 /*snr*/
|
||||
, float /*delta_time*/, Frequency /*delta_frequency*/, qint32 /*drift*/, QString const& /*callsign*/
|
||||
, QString const& /*grid*/, qint32 /*power*/)
|
||||
{
|
||||
if (client_id == id_)
|
||||
{
|
||||
decodes_stack_->setCurrentIndex (1);
|
||||
beacons_table_view_->resizeColumnsToContents ();
|
||||
beacons_table_view_->scrollToBottom ();
|
||||
}
|
||||
}
|
||||
|
||||
Q_SIGNAL void do_reply (QModelIndex const&);
|
||||
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);
|
||||
|
||||
private:
|
||||
class DecodesFilterModel final
|
||||
class IdFilterModel final
|
||||
: public QSortFilterProxyModel
|
||||
{
|
||||
public:
|
||||
DecodesFilterModel (QString const& id, QObject * parent = nullptr)
|
||||
IdFilterModel (QString const& id, QObject * parent = nullptr)
|
||||
: QSortFilterProxyModel {parent}
|
||||
, id_ {id}
|
||||
{}
|
||||
@@ -315,7 +483,9 @@ private:
|
||||
|
||||
QString id_;
|
||||
QTableView * decodes_table_view_;
|
||||
QTableView * beacons_table_view_;
|
||||
QLineEdit * message_line_edit_;
|
||||
QStackedLayout * decodes_stack_;
|
||||
QAbstractButton * auto_off_button_;
|
||||
QAbstractButton * halt_tx_button_;
|
||||
QLabel * mode_label_;
|
||||
@@ -333,6 +503,7 @@ public:
|
||||
MainWindow ()
|
||||
: log_ {new QStandardItemModel {0, 10, this}}
|
||||
, decodes_model_ {new DecodesModel {this}}
|
||||
, beacons_model_ {new BeaconsModel {this}}
|
||||
, server_ {new MessageServer {this}}
|
||||
, multicast_group_line_edit_ {new QLineEdit}
|
||||
, log_table_view_ {new QTableView}
|
||||
@@ -395,8 +566,11 @@ public:
|
||||
connect (server_, &MessageServer::client_opened, this, &MainWindow::add_client);
|
||||
connect (server_, &MessageServer::client_closed, this, &MainWindow::remove_client);
|
||||
connect (server_, &MessageServer::client_closed, decodes_model_, &DecodesModel::clear_decodes);
|
||||
connect (server_, &MessageServer::client_closed, beacons_model_, &BeaconsModel::clear_decodes);
|
||||
connect (server_, &MessageServer::decode, decodes_model_, &DecodesModel::add_decode);
|
||||
connect (server_, &MessageServer::WSPR_decode, beacons_model_, &BeaconsModel::add_beacon_spot);
|
||||
connect (server_, &MessageServer::clear_decodes, decodes_model_, &DecodesModel::clear_decodes);
|
||||
connect (server_, &MessageServer::clear_decodes, beacons_model_, &BeaconsModel::clear_decodes);
|
||||
connect (decodes_model_, &DecodesModel::reply, server_, &MessageServer::reply);
|
||||
|
||||
// UI behaviour
|
||||
@@ -435,7 +609,7 @@ public:
|
||||
private:
|
||||
void add_client (QString const& id)
|
||||
{
|
||||
auto dock = new ClientWidget {decodes_model_, id, this};
|
||||
auto dock = new ClientWidget {decodes_model_, beacons_model_, id, this};
|
||||
dock->setAttribute (Qt::WA_DeleteOnClose);
|
||||
auto view_action = dock->toggleViewAction ();
|
||||
view_action->setEnabled (true);
|
||||
@@ -443,6 +617,7 @@ private:
|
||||
addDockWidget (Qt::BottomDockWidgetArea, dock);
|
||||
connect (server_, &MessageServer::status_update, dock, &ClientWidget::update_status);
|
||||
connect (server_, &MessageServer::decode, dock, &ClientWidget::decode_added);
|
||||
connect (server_, &MessageServer::WSPR_decode, dock, &ClientWidget::beacon_spot_added);
|
||||
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);
|
||||
@@ -464,6 +639,7 @@ private:
|
||||
QStandardItemModel * log_;
|
||||
QMenu * view_menu_;
|
||||
DecodesModel * decodes_model_;
|
||||
BeaconsModel * beacons_model_;
|
||||
MessageServer * server_;
|
||||
QLineEdit * multicast_group_line_edit_;
|
||||
QTableView * log_table_view_;
|
||||
|
||||
Reference in New Issue
Block a user