Initial Commit

This commit is contained in:
Jordan Sherer
2018-02-08 21:28:33 -05:00
commit 678c1d3966
14352 changed files with 3176737 additions and 0 deletions
+216
View File
@@ -0,0 +1,216 @@
#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();
}
// 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)
{
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
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>";
out << t << endl;
f2.close();
}
return true;
}
+49
View File
@@ -0,0 +1,49 @@
/*
* Reads an ADIF log file into memory
* Searches log for call, band and mode
* VK3ACF July 2013
*/
#ifndef __ADIF_H
#define __ADIF_H
#if defined (QT5)
#include <QList>
#include <QString>
#include <QMultiHash>
#else
#include <QtGui>
#endif
class QDateTime;
class ADIF
{
public:
void init(QString const& filename);
void load();
void add(QString const& call, QString const& band, QString const& mode, QString const& date);
bool match(QString const& call, QString const& band, QString const& mode) const;
QList<QString> getCallList() const;
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);
private:
struct QSO
{
QString call,band,mode,date;
};
QMultiHash<QString, QSO> _data;
QString _filename;
QString extractField(QString const& line, QString const& fieldName) const;
};
#endif
+42
View File
@@ -0,0 +1,42 @@
#include "countriesworked.h"
void CountriesWorked::init(const QStringList countryNames)
{
_data.clear();
foreach(QString name,countryNames)
_data.insert(name,false);
}
void CountriesWorked::setAsWorked(const QString countryName)
{
if (_data.contains(countryName))
_data.insert(countryName,true);
}
bool CountriesWorked::getHasWorked(const QString countryName) const
{
if (_data.contains(countryName))
return _data.value(countryName);
return false;
}
int CountriesWorked::getWorkedCount() const
{
int count = 0;
foreach (bool value,_data)
if (value)
count += 1;
return count;
}
int CountriesWorked::getSize() const
{
return _data.count();
}
+29
View File
@@ -0,0 +1,29 @@
/*
* maintains a list of country names that have been worked
* VK3ACF July 2013
*/
#ifndef __COUNTRIESWORKDED_H
#define __COUNTRIESWORKDED_H
#include <QList>
#include <QString>
#include <QStringList>
#include <QHash>
class CountriesWorked
{
public:
void init(const QStringList countryNames);
void setAsWorked(const QString countryName);
bool getHasWorked(const QString countryName) const;
int getWorkedCount() const;
int getSize() const;
private:
QHash<QString, bool> _data;
};
#endif
+153
View File
@@ -0,0 +1,153 @@
/*
#Sov Mil Order of Malta: 15: 28: EU: 41.90: -12.43: -1.0: 1A:
#1A;
#Spratly Islands: 26: 50: AS: 9.88: -114.23: -8.0: 1S:
#1S,9M0,BV9S;
#Monaco: 14: 27: EU: 43.73: -7.40: -1.0: 3A:
#3A;
#Heard Island: 39: 68: AF: -53.08: -73.50: -5.0: VK0H:
#=VK0IR;
#Macquarie Island: 30: 60: OC: -54.60: -158.88: -10.0: VK0M:
#=VK0KEV;
#Cocos-Keeling: 29: 54: OC: -12.15: -96.82: -6.5: VK9C:
#AX9C,AX9Y,VH9C,VH9Y,VI9C,VI9Y,VJ9C,VJ9Y,VK9C,VK9Y,VL9C,VL9Y,VM9C,VM9Y,
#VN9C,VN9Y,VZ9C,VZ9Y,=VK9AA;
*/
#include "countrydat.h"
#include <QFile>
#include <QTextStream>
void CountryDat::init(const QString filename)
{
_filename = filename;
_data.clear();
}
QString CountryDat::_extractName(const QString line) const
{
int s1 = line.indexOf(':');
if (s1>=0)
{
QString name = line.mid(0,s1);
return name;
}
return "";
}
void CountryDat::_removeBrackets(QString &line, const QString a, const QString b) const
{
int s1 = line.indexOf(a);
while (s1 >= 0)
{
int s2 = line.indexOf(b);
line = line.mid(0,s1) + line.mid(s2+1,-1);
s1 = line.indexOf(a);
}
}
QStringList CountryDat::_extractPrefix(QString &line, bool &more) const
{
line = line.remove(" \n");
line = line.replace(" ","");
_removeBrackets(line,"(",")");
_removeBrackets(line,"[","]");
_removeBrackets(line,"<",">");
_removeBrackets(line,"~","~");
int s1 = line.indexOf(';');
more = true;
if (s1 >= 0)
{
line = line.mid(0,s1);
more = false;
}
QStringList r = line.split(',');
return r;
}
void CountryDat::load()
{
_data.clear();
_countryNames.clear(); //used by countriesWorked
QFile inputFile(_filename);
if (inputFile.open(QIODevice::ReadOnly))
{
QTextStream in(&inputFile);
while ( !in.atEnd() )
{
QString line1 = in.readLine();
if ( !in.atEnd() )
{
QString line2 = in.readLine();
QString name = _extractName(line1);
if (name.length()>0)
{
_countryNames << name;
bool more = true;
QStringList prefixs;
while (more)
{
QStringList p = _extractPrefix(line2,more);
prefixs += p;
if (more)
line2 = in.readLine();
}
QString p;
foreach(p,prefixs)
{
if (p.length() > 0)
_data.insert(p,name);
}
}
}
}
inputFile.close();
}
}
// return country name else ""
QString CountryDat::find(QString call) const
{
call = call.toUpper ();
// check for exact match first
if (_data.contains ("=" + call))
{
return fixup (_data.value ("=" + call), call);
}
auto prefix = call;
while (prefix.size () >= 1)
{
if (_data.contains (prefix))
{
return fixup (_data.value (prefix), call);
}
prefix = prefix.left (prefix.size () - 1);
}
return QString {};
}
QString CountryDat::fixup (QString country, QString const& call) const
{
//
// deal with special rules that cty.dat does not cope with
//
// KG4 2x1 and 2x3 calls that map to Gitmo are mainland US not Gitmo
if (call.startsWith ("KG4") && call.size () != 5)
{
country.replace ("Guantanamo Bay", "United States");
}
return country;
}
+36
View File
@@ -0,0 +1,36 @@
/*
* Reads cty.dat file
* Establishes a map between prefixes and their country names
* VK3ACF July 2013
*/
#ifndef __COUNTRYDAT_H
#define __COUNTRYDAT_H
#include <QString>
#include <QStringList>
#include <QHash>
class CountryDat
{
public:
void init(const QString filename);
void load();
QString find(QString prefix) const; // return country name or ""
QStringList getCountryNames() const { return _countryNames; };
private:
QString _extractName(const QString line) const;
void _removeBrackets(QString &line, const QString a, const QString b) const;
QStringList _extractPrefix(QString &line, bool &more) const;
QString fixup (QString country, QString const& call) const;
QString _filename;
QStringList _countryNames;
QHash<QString, QString> _data;
};
#endif
+100
View File
@@ -0,0 +1,100 @@
#include "logbook.h"
#include <QDebug>
#include <QFontMetrics>
#include <QStandardPaths>
#include <QDir>
namespace
{
auto logFileName = "wsjtx_log.adi";
auto countryFileName = "cty.dat";
}
void LogBook::init()
{
QDir dataPath {QStandardPaths::writableLocation (QStandardPaths::DataLocation)};
QString countryDataFilename;
if (dataPath.exists (countryFileName))
{
// User override
countryDataFilename = dataPath.absoluteFilePath (countryFileName);
}
else
{
countryDataFilename = QString {":/"} + countryFileName;
}
_countries.init(countryDataFilename);
_countries.load();
_worked.init(_countries.getCountryNames());
_log.init(dataPath.absoluteFilePath (logFileName));
_log.load();
_setAlreadyWorkedFromLog();
/*
int QSOcount = _log.getCount();
int count = _worked.getWorkedCount();
qDebug() << QSOcount << "QSOs and" << count << "countries worked in file" << logFilename;
*/
// QString call = "ok1ct";
// QString countryName;
// bool callWorkedBefore,countryWorkedBefore;
// match(/*in*/call, /*out*/ countryName,callWorkedBefore,countryWorkedBefore);
// qDebug() << countryName;
}
void LogBook::_setAlreadyWorkedFromLog()
{
QList<QString> calls = _log.getCallList();
QString c;
foreach(c,calls)
{
QString countryName = _countries.find(c);
if (countryName.length() > 0)
{
_worked.setAsWorked(countryName);
//qDebug() << countryName << " worked " << c;
}
}
}
void LogBook::match(/*in*/const QString call,
/*out*/ QString &countryName,
bool &callWorkedBefore,
bool &countryWorkedBefore) const
{
if (call.length() > 0)
{
QString currentMode = "JT9"; // JT65 == JT9 in ADIF::match()
QString currentBand = ""; // match any band
callWorkedBefore = _log.match(call,currentBand,currentMode);
countryName = _countries.find(call);
if (countryName.length() > 0) // country was found
countryWorkedBefore = _worked.getHasWorked(countryName);
else
{
countryName = "where?"; //error: prefix not found
countryWorkedBefore = false;
}
}
//qDebug() << "Logbook:" << call << ":" << countryName << "Cty B4:" << countryWorkedBefore << "call B4:" << callWorkedBefore;
}
void LogBook::addAsWorked(const QString call, const QString band, const QString mode, const QString date)
{
//qDebug() << "adding " << call << " as worked";
_log.add(call,band,mode,date);
QString countryName = _countries.find(call);
if (countryName.length() > 0)
_worked.setAsWorked(countryName);
}
+39
View File
@@ -0,0 +1,39 @@
/*
* From an ADIF file and cty.dat, get a call's DXCC entity and its worked before status
* VK3ACF July 2013
*/
#ifndef LOGBOOK_H
#define LOGBOOK_H
#include <QString>
#include <QFont>
#include "countrydat.h"
#include "countriesworked.h"
#include "adif.h"
class QDir;
class LogBook
{
public:
void init();
void match(/*in*/ const QString call,
/*out*/ QString &countryName,
bool &callWorkedBefore,
bool &countryWorkedBefore) const;
void addAsWorked(const QString call, const QString band, const QString mode, const QString date);
private:
CountryDat _countries;
CountriesWorked _worked;
ADIF _log;
void _setAlreadyWorkedFromLog();
};
#endif // LOGBOOK_H