Broadcast logged QSOs to N1MM Logger+. Mostly thanks to Brian, N9ADG. Needs testing!

git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@8269 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
Joe Taylor 2017-11-29 21:05:27 +00:00
parent f62cadfa2d
commit bc31f581bd
6 changed files with 163 additions and 32 deletions

View File

@ -567,6 +567,13 @@ private:
QString opCall_;
QString udp_server_name_;
port_type udp_server_port_;
// QString n1mm_server_name () const;
QString n1mm_server_name_;
port_type n1mm_server_port_;
bool broadcast_to_n1mm_;
// port_type n1mm_server_port () const;
// bool valid_n1mm_info () const;
// bool broadcast_to_n1mm() const;
bool accept_udp_requests_;
bool udpWindowToFront_;
bool udpWindowRestore_;
@ -662,6 +669,9 @@ QString Configuration::opCall() const {return m_->opCall_;}
QString Configuration::udp_server_name () const {return m_->udp_server_name_;}
auto Configuration::udp_server_port () const -> port_type {return m_->udp_server_port_;}
bool Configuration::accept_udp_requests () const {return m_->accept_udp_requests_;}
QString Configuration::n1mm_server_name () const {return m_->n1mm_server_name_;}
auto Configuration::n1mm_server_port () const -> port_type {return m_->n1mm_server_port_;}
bool Configuration::broadcast_to_n1mm () const {return m_->broadcast_to_n1mm_;}
bool Configuration::udpWindowToFront () const {return m_->udpWindowToFront_;}
bool Configuration::udpWindowRestore () const {return m_->udpWindowRestore_;}
Bands * Configuration::bands () {return &m_->bands_;}
@ -772,6 +782,15 @@ void Configuration::sync_transceiver (bool force_signal, bool enforce_mode_and_s
}
}
bool Configuration::valid_n1mm_info () const
{
// do very rudimentary checking on the n1mm server name and port number.
//
auto server_name = m_->n1mm_server_name_;
auto port_number = m_->n1mm_server_port_;
return(!(server_name.trimmed().isEmpty() || port_number == 0));
}
namespace
{
#if defined (Q_OS_MAC)
@ -907,6 +926,9 @@ Configuration::impl::impl (Configuration * self, QDir const& temp_directory,
ui_->udp_server_port_spin_box->setMinimum (1);
ui_->udp_server_port_spin_box->setMaximum (std::numeric_limits<port_type>::max ());
ui_->n1mm_server_port_spin_box->setMinimum (1);
ui_->n1mm_server_port_spin_box->setMaximum (std::numeric_limits<port_type>::max ());
//
// assign ids to radio buttons
//
@ -1145,6 +1167,9 @@ void Configuration::impl::initialize_models ()
ui_->udp_server_line_edit->setText (udp_server_name_);
ui_->udp_server_port_spin_box->setValue (udp_server_port_);
ui_->accept_udp_requests_check_box->setChecked (accept_udp_requests_);
ui_->n1mm_server_name_line_edit->setText (n1mm_server_name_);
ui_->n1mm_server_port_spin_box->setValue (n1mm_server_port_);
ui_->enable_n1mm_broadcast_check_box->setChecked (broadcast_to_n1mm_);
ui_->udpWindowToFront->setChecked(udpWindowToFront_);
ui_->udpWindowRestore->setChecked(udpWindowRestore_);
ui_->calibration_intercept_spin_box->setValue (calibration_.intercept);
@ -1350,6 +1375,9 @@ void Configuration::impl::read_settings ()
opCall_ = settings_->value ("OpCall", "").toString ();
udp_server_name_ = settings_->value ("UDPServer", "127.0.0.1").toString ();
udp_server_port_ = settings_->value ("UDPServerPort", 2237).toUInt ();
n1mm_server_name_ = settings_->value ("N1MMServer", "127.0.0.1").toString ();
n1mm_server_port_ = settings_->value ("N1MMServerPort", 2333).toUInt ();
broadcast_to_n1mm_ = settings_->value ("BroadcastToN1MM", false).toBool ();
accept_udp_requests_ = settings_->value ("AcceptUDPRequests", false).toBool ();
udpWindowToFront_ = settings_->value ("udpWindowToFront",false).toBool ();
udpWindowRestore_ = settings_->value ("udpWindowRestore",false).toBool ();
@ -1449,6 +1477,9 @@ void Configuration::impl::write_settings ()
settings_->setValue ("OpCall", opCall_);
settings_->setValue ("UDPServer", udp_server_name_);
settings_->setValue ("UDPServerPort", udp_server_port_);
settings_->setValue ("N1MMServer", n1mm_server_name_);
settings_->setValue ("N1MMServerPort", n1mm_server_port_);
settings_->setValue ("BroadcastToN1MM", broadcast_to_n1mm_);
settings_->setValue ("AcceptUDPRequests", accept_udp_requests_);
settings_->setValue ("udpWindowToFront", udpWindowToFront_);
settings_->setValue ("udpWindowRestore", udpWindowRestore_);
@ -1864,6 +1895,12 @@ void Configuration::impl::accept ()
}
accept_udp_requests_ = ui_->accept_udp_requests_check_box->isChecked ();
auto new_n1mm_server = ui_->n1mm_server_name_line_edit->text ();
n1mm_server_name_ = new_n1mm_server;
auto new_n1mm_port = ui_->n1mm_server_port_spin_box->value ();
n1mm_server_port_ = new_n1mm_port;
broadcast_to_n1mm_ = ui_->enable_n1mm_broadcast_check_box->isChecked ();
udpWindowToFront_ = ui_->udpWindowToFront->isChecked ();
udpWindowRestore_ = ui_->udpWindowRestore->isChecked ();

View File

@ -139,6 +139,10 @@ public:
QString opCall() const;
QString udp_server_name () const;
port_type udp_server_port () const;
QString n1mm_server_name () const;
port_type n1mm_server_port () const;
bool valid_n1mm_info () const;
bool broadcast_to_n1mm() const;
bool accept_udp_requests () const;
bool udpWindowToFront () const;
bool udpWindowRestore () const;

View File

@ -1652,7 +1652,11 @@ QListView::item:hover {
</widget>
</item>
<item row="0" column="3">
<widget class="QLineEdit" name="opCallEntry"/>
<widget class="QLineEdit" name="opCallEntry">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The callsign of the operator, if different from the station callsign.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="log_as_RTTY_check_box">
@ -1821,6 +1825,60 @@ for assessing propagation and system performance.</string>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="n1mm_group_box">
<property name="title">
<string>N1MM Logger+ Broadcasts</string>
</property>
<layout class="QFormLayout" name="formLayout_15">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QCheckBox" name="enable_n1mm_broadcast_check_box">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When checked, WSJT-X will broadcast a logged contact in ADIF format to the configured hostname and port. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Enable logged contact ADIF broadcast</string>
</property>
</widget>
</item>
<item row="1" column="0">
<layout class="QFormLayout" name="formLayout_20">
<item row="0" column="0">
<widget class="QLabel" name="n1mm_server_name_label">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;N1MM Server name or IP address:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="n1mm_server_name_line_edit">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Optional host name of N1MM Logger+ program to receive ADIF UDP broadcasts. This is usually 'localhost' or ip address 127.0.0.1&lt;/p&gt;&lt;p&gt;Formats:&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;hostname&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;IPv4 address&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;IPv6 address&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;IPv4 multicast group address&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;IPv6 multicast group address&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Clearing this field will disable broadcasting of ADIF information via UDP.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="n1mm_server_port_label">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;N1MM Server port number:&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="n1mm_server_port_spin_box">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enter the port number that WSJT-X should use for UDP broadcasts of ADIF log information. For N1MM Logger+, this value should be 2333. If this is zero, no updates will be broadcast.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
@ -2688,12 +2746,12 @@ soundcard changes</string>
</connection>
</connections>
<buttongroups>
<buttongroup name="CAT_handshake_button_group"/>
<buttongroup name="PTT_method_button_group"/>
<buttongroup name="CAT_data_bits_button_group"/>
<buttongroup name="CAT_stop_bits_button_group"/>
<buttongroup name="TX_audio_source_button_group"/>
<buttongroup name="CAT_data_bits_button_group"/>
<buttongroup name="TX_mode_button_group"/>
<buttongroup name="PTT_method_button_group"/>
<buttongroup name="CAT_handshake_button_group"/>
<buttongroup name="split_mode_button_group"/>
<buttongroup name="CAT_stop_bits_button_group"/>
</buttongroups>
</ui>

View File

@ -14,7 +14,7 @@
void ADIF::init(QString const& filename)
{
_filename = filename;
_data.clear();
_data.clear();
}
@ -165,13 +165,47 @@ QList<QString> ADIF::getCallList() const
++i;
}
return p;
}
}
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 t;
t = "<call:" + QString::number(hisCall.length()) + ">" + hisCall;
t += " <gridsquare:" + QString::number(hisGrid.length()) + ">" + hisGrid;
t += " <mode:" + QString::number(mode.length()) + ">" + mode;
t += " <rst_sent:" + QString::number(rptSent.length()) + ">" + rptSent;
t += " <rst_rcvd:" + QString::number(rptRcvd.length()) + ">" + rptRcvd;
t += " <qso_date:8>" + dateTimeOn.date().toString("yyyyMMdd");
t += " <time_on:6>" + dateTimeOn.time().toString("hhmmss");
t += " <qso_date_off:8>" + dateTimeOff.date().toString("yyyyMMdd");
t += " <time_off:6>" + dateTimeOff.time().toString("hhmmss");
t += " <band:" + QString::number(band.length()) + ">" + band;
t += " <freq:" + QString::number(strDialFreq.length()) + ">" + strDialFreq;
t += " <station_callsign:" + QString::number(m_myCall.length()) + ">" +
m_myCall;
t += " <my_gridsquare:" + QString::number(m_myGrid.length()) + ">" +
m_myGrid;
if (m_txPower != "")
t += " <tx_pwr:" + QString::number(m_txPower.length()) +
">" + m_txPower;
if (comments != "")
t += " <comment:" + QString::number(comments.length()) +
">" + comments;
if (name != "")
t += " <name:" + QString::number(name.length()) +
">" + name;
t += " <eor>";
return t;
}
// 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,
@ -187,30 +221,8 @@ bool ADIF::addQSOToFile(QString const& hisCall, QString const& hisGrid, QString
out << "WSJT-X ADIF Export<eoh>" << endl; // new file
QString t;
t="<call:" + QString::number(hisCall.length()) + ">" + hisCall;
t+=" <gridsquare:" + QString::number(hisGrid.length()) + ">" + hisGrid;
t+=" <mode:" + QString::number(mode.length()) + ">" + mode;
t+=" <rst_sent:" + QString::number(rptSent.length()) + ">" + rptSent;
t+=" <rst_rcvd:" + QString::number(rptRcvd.length()) + ">" + rptRcvd;
t+=" <qso_date:8>" + dateTimeOn.date ().toString ("yyyyMMdd");
t+=" <time_on:6>" + dateTimeOn.time ().toString ("hhmmss");
t+=" <qso_date_off:8>" + dateTimeOff.date ().toString ("yyyyMMdd");
t+=" <time_off:6>" + dateTimeOff.time ().toString ("hhmmss");
t+=" <band:" + QString::number(band.length()) + ">" + band;
t+=" <freq:" + QString::number(strDialFreq.length()) + ">" + strDialFreq;
t+=" <station_callsign:" + QString::number(m_myCall.length()) + ">" +
m_myCall;
t+=" <my_gridsquare:" + QString::number(m_myGrid.length()) + ">" +
m_myGrid;
if(m_txPower!="") t+= " <tx_pwr:" + QString::number(m_txPower.length()) +
">" + m_txPower;
if(comments!="") t+=" <comment:" + QString::number(comments.length()) +
">" + comments;
if(name!="") t+=" <name:" + QString::number(name.length()) +
">" + name;
if(operator_call!="") t+=" <operator:" + QString::number(operator_call.length()) +
">" + operator_call;
t+=" <eor>";
t = QSOToADIF(hisCall,hisGrid,mode,rptSent,rptRcvd,dateTimeOn,dateTimeOff,
band,comments,name,strDialFreq,m_myCall,m_myGrid,m_txPower);
out << t << endl;
f2.close();
}

View File

@ -32,7 +32,11 @@ class ADIF
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);
private:
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);
private:
struct QSO
{
QString call,band,mode,date;

View File

@ -5,6 +5,7 @@
#include <QStandardPaths>
#include <QDir>
#include <QDebug>
#include <QUdpSocket>
#include "logbook/adif.h"
#include "MessageBox.hpp"
@ -85,6 +86,7 @@ void LogQSO::initLogQSO(QString const& hisCall, QString const& hisGrid, QString
ui->band->setText (m_config->bands ()->find (dialFreq));
ui->loggedOperator->setText(opCall);
show ();
QTimer::singleShot(700, this, SLOT(accept()));
}
void LogQSO::accept()
@ -118,6 +120,20 @@ void LogQSO::accept()
tr ("Cannot open \"%1\"").arg (adifilePath));
}
// 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);
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()));
if (rzult == -1) {
MessageBox::warning_message (this, tr ("Error sending log to N1MM"),
tr ("Write returned \"%1\"").arg (errno));
}
}
//Log this QSO to file "wsjtx.log"
static QFile f {QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}.absoluteFilePath ("wsjtx.log")};
if(!f.open(QIODevice::Text | QIODevice::Append)) {