mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2024-11-16 00:51:56 -05:00
3c384f7cbb
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
232 lines
7.9 KiB
C++
232 lines
7.9 KiB
C++
#include "adif.h"
|
|
|
|
#include <QFile>
|
|
#include <QTextStream>
|
|
#include <QDateTime>
|
|
#include <QDebug>
|
|
|
|
/*
|
|
<CALL:4>W1XT<BAND:3>20m<FREQ:6>14.076<GRIDSQUARE:4>DM33<MODE:4>JT65<RST_RCVD:3>-21<RST_SENT:3>-14<QSO_DATE:8>20110422<TIME_ON:6>041712<TIME_OFF:6>042435<TX_PWR:1>4<COMMENT:34>1st JT65A QSO. Him: mag loop 20W<STATION_CALLSIGN:6>VK3ACF<MY_GRIDSQUARE:6>qf22lb<eor>
|
|
<CALL:6>IK1SOW<BAND:3>20m<FREQ:6>14.076<GRIDSQUARE:4>JN35<MODE:4>JT65<RST_RCVD:3>-19<RST_SENT:3>-11<QSO_DATE:8>20110422<TIME_ON:6>052501<TIME_OFF:6>053359<TX_PWR:1>3<STATION_CALLSIGN:6>VK3ACF<MY_GRIDSQUARE:6>qf22lb<eor>
|
|
<CALL:6:S>W4ABC> ...
|
|
*/
|
|
|
|
void ADIF::init(QString const& filename)
|
|
{
|
|
_filename = filename;
|
|
_data.clear();
|
|
}
|
|
|
|
|
|
QString ADIF::extractField(QString const& record, QString const& fieldName) const
|
|
{
|
|
int fieldNameIndex = record.indexOf (fieldName + ':', 0, Qt::CaseInsensitive);
|
|
if (fieldNameIndex >=0)
|
|
{
|
|
int closingBracketIndex = record.indexOf('>',fieldNameIndex);
|
|
int fieldLengthIndex = record.indexOf(':',fieldNameIndex); // find the size delimiter
|
|
int dataTypeIndex = -1;
|
|
if (fieldLengthIndex >= 0)
|
|
{
|
|
dataTypeIndex = record.indexOf(':',fieldLengthIndex+1); // check for a second : indicating there is a data type
|
|
if (dataTypeIndex > closingBracketIndex)
|
|
dataTypeIndex = -1; // second : was found but it was beyond the closing >
|
|
}
|
|
|
|
if ((closingBracketIndex > fieldNameIndex) && (fieldLengthIndex > fieldNameIndex) && (fieldLengthIndex< closingBracketIndex))
|
|
{
|
|
int fieldLengthCharCount = closingBracketIndex - fieldLengthIndex -1;
|
|
if (dataTypeIndex >= 0)
|
|
fieldLengthCharCount -= 2; // data type indicator is always a colon followed by a single character
|
|
QString fieldLengthString = record.mid(fieldLengthIndex+1,fieldLengthCharCount);
|
|
int fieldLength = fieldLengthString.toInt();
|
|
if (fieldLength > 0)
|
|
{
|
|
QString field = record.mid(closingBracketIndex+1,fieldLength);
|
|
return field;
|
|
}
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
|
|
|
|
void ADIF::load()
|
|
{
|
|
_data.clear();
|
|
QFile inputFile(_filename);
|
|
if (inputFile.open(QIODevice::ReadOnly))
|
|
{
|
|
QTextStream in(&inputFile);
|
|
QString buffer;
|
|
bool pre_read {false};
|
|
int end_position {-1};
|
|
|
|
// skip optional header record
|
|
do
|
|
{
|
|
buffer += in.readLine () + '\n';
|
|
if (buffer.startsWith (QChar {'<'})) // denotes no header
|
|
{
|
|
pre_read = true;
|
|
}
|
|
else
|
|
{
|
|
end_position = buffer.indexOf ("<EOH>", 0, Qt::CaseInsensitive);
|
|
}
|
|
}
|
|
while (!in.atEnd () && !pre_read && end_position < 0);
|
|
if (!pre_read) // found header
|
|
{
|
|
buffer.remove (0, end_position + 5);
|
|
}
|
|
while (buffer.size () || !in.atEnd ())
|
|
{
|
|
do
|
|
{
|
|
end_position = buffer.indexOf ("<EOR>", 0, Qt::CaseInsensitive);
|
|
if (!in.atEnd () && end_position < 0)
|
|
{
|
|
buffer += in.readLine () + '\n';
|
|
}
|
|
}
|
|
while (!in.atEnd () && end_position < 0);
|
|
int record_length {end_position >= 0 ? end_position + 5 : -1};
|
|
auto record = buffer.left (record_length).trimmed ();
|
|
auto next_record = buffer.indexOf (QChar {'<'}, record_length);
|
|
buffer.remove (0, next_record >=0 ? next_record : buffer.size ());
|
|
record = record.mid (record.indexOf (QChar {'<'}));
|
|
add (extractField (record, "CALL")
|
|
, extractField (record, "BAND")
|
|
, extractField (record, "MODE")
|
|
, extractField (record, "QSO_DATE"));
|
|
}
|
|
inputFile.close ();
|
|
}
|
|
}
|
|
|
|
|
|
void ADIF::add(QString const& call, QString const& band, QString const& mode, QString const& date)
|
|
{
|
|
QSO q;
|
|
q.call = call;
|
|
q.band = band;
|
|
q.mode = mode;
|
|
q.date = date;
|
|
if (q.call.size ())
|
|
{
|
|
_data.insert(q.call,q);
|
|
// qDebug() << "Added as worked:" << call << band << mode << date;
|
|
}
|
|
}
|
|
|
|
// return true if in the log same band and mode (where JT65 == JT9)
|
|
bool ADIF::match(QString const& call, QString const& band, QString const& mode) const
|
|
{
|
|
QList<QSO> qsos = _data.values(call);
|
|
if (qsos.size()>0)
|
|
{
|
|
QSO q;
|
|
foreach(q,qsos)
|
|
{
|
|
if ( (band.compare(q.band,Qt::CaseInsensitive) == 0)
|
|
|| (band=="")
|
|
|| (q.band==""))
|
|
{
|
|
if (
|
|
(
|
|
((mode.compare("JT65",Qt::CaseInsensitive)==0) ||
|
|
(mode.compare("JT9",Qt::CaseInsensitive)==0) ||
|
|
(mode.compare("FT8",Qt::CaseInsensitive)==0))
|
|
&&
|
|
((q.mode.compare("JT65",Qt::CaseInsensitive)==0) ||
|
|
(q.mode.compare("JT9",Qt::CaseInsensitive)==0) ||
|
|
(q.mode.compare("FT8",Qt::CaseInsensitive)==0))
|
|
)
|
|
|| (mode.compare(q.mode,Qt::CaseInsensitive)==0)
|
|
|| (mode=="")
|
|
|| (q.mode=="")
|
|
)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
QList<QString> ADIF::getCallList() const
|
|
{
|
|
QList<QString> p;
|
|
QMultiHash<QString,QSO>::const_iterator i = _data.constBegin();
|
|
while (i != _data.constEnd())
|
|
{
|
|
p << i.key();
|
|
++i;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
|
|
|
|
int ADIF::getCount() const
|
|
{
|
|
return _data.size();
|
|
}
|
|
|
|
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;
|
|
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;
|
|
return t.toLatin1 ();
|
|
}
|
|
|
|
|
|
// open ADIF file and append the QSO details. Return true on success
|
|
bool ADIF::addQSOToFile(QByteArray const& ADIF_record)
|
|
{
|
|
QFile f2(_filename);
|
|
if (!f2.open(QIODevice::Text | QIODevice::Append))
|
|
return false;
|
|
else
|
|
{
|
|
QTextStream out(&f2);
|
|
if (f2.size()==0)
|
|
out << "WSJT-X ADIF Export<eoh>" << endl; // new file
|
|
|
|
out << ADIF_record << " <eor>" << endl;
|
|
f2.close();
|
|
}
|
|
return true;
|
|
}
|