First version of DXCC entity and worked B4 status

git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@3512 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
Murray Curtis 2013-07-31 11:29:42 +00:00
parent 608a4fddd4
commit a8491db120
13 changed files with 572 additions and 14 deletions

112
logbook/adif.cpp Normal file
View File

@ -0,0 +1,112 @@
#include "adif.h"
#include <QFile>
#include <QTextStream>
/*
<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:4>0417<TIME_OFF:4>0424<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:4>0525<TIME_OFF:4>0533<TX_PWR:1>3<STATION_CALLSIGN:6>VK3ACF<MY_GRIDSQUARE:6>qf22lb<eor>
*/
void ADIF::init(QString filename)
{
_filename = filename;
_data.clear();
}
QString ADIF::_extractField(const QString line, const QString fieldName)
{
int s1 = line.indexOf(fieldName,0,Qt::CaseInsensitive);
if (s1 >=0)
{
int s2 = line.indexOf('>',s1);
if (s2 >= 0)
{
int flsi = s1+fieldName.length();
int flsl = s2-flsi;
if (flsl>0)
{
QString fieldLengthString = line.mid(flsi,flsl);
int fieldLength = fieldLengthString.toInt();
QString field = line.mid(s2+1,fieldLength);
return field;
}
}
}
return "";
}
void ADIF::load()
{
_data.clear();
QFile inputFile(_filename);
if (inputFile.open(QIODevice::ReadOnly))
{
QTextStream in(&inputFile);
while ( !in.atEnd() )
{
QString line = in.readLine();
QSO q;
q.call = _extractField(line,"CALL:");
q.band = _extractField(line,"BAND:");
q.mode = _extractField(line,"MODE:");
q.date = _extractField(line,"QSO_DATE:");
if (q.call != "")
_data << q;
}
inputFile.close();
}
}
void ADIF::add(const QString call)
{
QSO q;
q.call = call;
q.band = ""; //TODO
q.mode = "JT9"; //TODO
q.date = ""; //TODO
_data << q;
}
// return true if in the log same band and mode (where JT65 == JT9)
bool ADIF::match(const QString call, const QString band, const QString mode)
{
QSO q;
foreach(q,_data)
{
if (call.compare(q.call) == 0) //TODO handle multiple log entries from same call, should this be a hash table rather than a list?
{
if ((band.compare(q.band) == 0) || (band=="") || (q.band==""))
{
if (
(
((mode.compare("JT65",Qt::CaseInsensitive)==0) || (mode.compare("JT9",Qt::CaseInsensitive)==0))
&&
((q.mode.compare("JT65",Qt::CaseInsensitive)==0) || (q.mode.compare("JT9",Qt::CaseInsensitive)==0))
)
|| (mode.compare(q.mode)==0)
)
return true;
}
}
}
return false;
}
QList<QString> ADIF::getCallList()
{
QList<QString> p;
QSO q;
foreach(q,_data)
p << q.call;
return p;
}
int ADIF::getCount()
{
return _data.length();
}

40
logbook/adif.h Normal file
View File

@ -0,0 +1,40 @@
/*
* Reads an ADIF log file into memory
* Searches log for call, band and mode
* VK3ACF July 2013
*/
#ifndef __ADIF_H
#define __ADIF_H
#include <QtGui>
class ADIF
{
public:
void init(QString filename);
void load();
void add(const QString call);
bool match(const QString call, const QString band, const QString mode);
QList<QString> getCallList();
int getCount();
private:
struct QSO
{
QString call,band,mode,date;
};
QList<QSO> _data;
QString _filename;
QString _extractField(const QString line, const QString fieldName);
};
#endif

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)
{
if (_data.contains(countryName))
return _data.value(countryName);
return false;
}
int CountriesWorked::getWorkedCount()
{
int count = 0;
foreach (bool value,_data)
if (value)
count += 1;
return count;
}
int CountriesWorked::getSize()
{
return _data.count();
}

26
logbook/countriesworked.h Normal file
View File

@ -0,0 +1,26 @@
/*
* maintains a list of country names that have been worked
* VK3ACF July 2013
*/
#ifndef __COUNTRIESWORKDED_H
#define __COUNTRIESWORKDED_H
#include <QtGui>
class CountriesWorked
{
public:
void init(const QStringList countryNames);
void setAsWorked(const QString countryName);
bool getHasWorked(const QString countryName);
int getWorkedCount();
int getSize();
private:
QHash<QString, bool> _data;
};
#endif

128
logbook/countrydat.cpp Normal file
View File

@ -0,0 +1,128 @@
/*
#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)
{
int s1 = line.indexOf(':');
QString name = line.mid(0,s1);
return name;
}
void CountryDat::_removeBrackets(QString &line, const QString a, const QString b)
{
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)
{
line = line.remove(" \n");
line = line.replace("=","");
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(); //dictionary was = {}
_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);
_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 prefix)
{
while(prefix.length() >= 1)
{
if (_data.contains(prefix))
{
QString country = _data.value(prefix);
return country;
}
prefix = prefix.left(prefix.length()-1);
}
return "";
}

32
logbook/countrydat.h Normal file
View File

@ -0,0 +1,32 @@
/*
* Reads cty.dat file
* Establishes a map between prefixes and their country names
* VK3ACF July 2013
*/
#ifndef __COUNTRYDAT_H
#define __COUNTRYDAT_H
#include <QtGui>
class CountryDat
{
public:
void init(const QString filename);
void load();
QString find(QString prefix); // return country name or ""
QStringList getCountryNames() { return _countryNames; };
private:
QString _extractName(const QString line);
void _removeBrackets(QString &line, const QString a, const QString b);
QStringList _extractPrefix(QString &line, bool &more);
QString _filename;
QStringList _countryNames;
QHash<QString, QString> _data;
};
#endif

70
logbook/logbook.cpp Normal file
View File

@ -0,0 +1,70 @@
#include "logbook.h"
void LogBook::init()
{
const QString logFilename = "wsjtx_log.adi"; //TODO get from user
const QString countryDataFilename = "cty.dat"; //TODO get from user
_countries.init(countryDataFilename);
_countries.load();
_worked.init(_countries.getCountryNames());
_log.init(logFilename);
_log.load();
_setAlreadyWorkedFromLog();
int QSOcount = _log.getCount();
int count = _worked.getWorkedCount();
qDebug() << QSOcount << "QSOs and" << count << "countries worked in file" << logFilename;
}
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)
{
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)
{
qDebug() << "adding " << call << " as worked";
_log.add(call);
QString countryName = _countries.find(call);
if (countryName.length() > 0)
_worked.setAsWorked(countryName);
}

35
logbook/logbook.h Normal file
View File

@ -0,0 +1,35 @@
/*
* 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 <QtGui>
#include "countrydat.h"
#include "countriesworked.h"
#include "adif.h"
class LogBook
{
public:
void init();
void match(/*in*/ const QString call,
/*out*/ QString &countryName,
bool &callWorkedBefore,
bool &countryWorkedBefore);
void addAsWorked(const QString call);
private:
CountryDat _countries;
CountriesWorked _worked;
ADIF _log;
void _setAlreadyWorkedFromLog();
};
#endif // LOGBOOK_H

View File

@ -182,6 +182,7 @@ MainWindow::MainWindow(QSharedMemory *shdmem, QString *thekey, \
m_promptToLog=false;
m_blankLine=false;
m_insertBlank=false;
m_displayDXCCEntity=false;
m_clearCallGrid=false;
m_bMiles=false;
m_decodedText2=false;
@ -353,6 +354,8 @@ MainWindow::MainWindow(QSharedMemory *shdmem, QString *thekey, \
psk_Reporter->setLocalStation(m_myCall,m_myGrid, m_antDescription[m_band], "WSJT-X r" + rev.mid(6,4) );
#endif
m_logBook.init();
ui->label_9->setStyleSheet("QLabel{background-color: #aabec8}");
ui->label_10->setStyleSheet("QLabel{background-color: #aabec8}");
ui->labUTC->setStyleSheet( \
@ -464,6 +467,7 @@ void MainWindow::writeSettings()
settings.setValue("BandIndex",m_band);
settings.setValue("PromptToLog",m_promptToLog);
settings.setValue("InsertBlank",m_insertBlank);
settings.setValue("DXCCEntity",m_displayDXCCEntity);
settings.setValue("ClearCallGrid",m_clearCallGrid);
settings.setValue("Miles",m_bMiles);
settings.setValue("GUItab",ui->tabWidget->currentIndex());
@ -607,6 +611,8 @@ void MainWindow::readSettings()
ui->actionPrompt_to_log_QSO->setChecked(m_promptToLog);
m_insertBlank=settings.value("InsertBlank",false).toBool();
ui->actionBlank_line_between_decoding_periods->setChecked(m_insertBlank);
m_displayDXCCEntity=settings.value("DXCCEntity",false).toBool();
ui->actionEnable_DXCC_entity->setChecked(m_displayDXCCEntity);
m_clearCallGrid=settings.value("ClearCallGrid",false).toBool();
ui->actionClear_DX_Call_and_Grid_after_logging->setChecked(m_clearCallGrid);
m_bMiles=settings.value("Miles",false).toBool();
@ -1473,6 +1479,44 @@ void MainWindow::readFromStdout() //readFromStdout
if(m_myCall!="" and t.indexOf(" "+m_myCall+" ")>0) bg="#ff6666"; //red
bool bQSO=abs(t.mid(14,4).toInt() - g_pWideGraph->rxFreq()) <= 10;
QString t1=t.replace("\n","").mid(0,t.length()-4);
// if enabled add the DXCC entity and B4 status to the end of the preformated text line t1
int cqi = t.indexOf(" CQ ");
if (m_displayDXCCEntity && (cqi >= 0))
{
// extract the CQer's call TODO: does this work with all call formats? What about 'CQ DX'?
int s1 = 4 + t.indexOf(" CQ ");
int s2 = t.indexOf(" ",s1);
QString call = t.mid(s1,s2-s1);
QString countryName;
bool callWorkedBefore;
bool countryWorkedBefore;
m_logBook.match(/*in*/call,/*out*/countryName,callWorkedBefore,countryWorkedBefore);
t1 = t1.left(36); // reduce trailing white space TODO: hardcoded char count
if (!countryWorkedBefore) // therefore not worked call either
{
t1 += "!";
bg = "#66ff66"; // strong green
}
else
if (!callWorkedBefore) // but have worked the country
{
t1 += "~";
bg = "#76cd76"; // mid green
}
else
{
t1 += " "; // have worked this call before
bg="#9cc79c"; // pale green
}
if (countryName.length()>10) //TODO: hardcoded width. Depends on font and window size/layout
countryName = countryName.left(1)+"."+countryName.right(8); //abreviate the first word to the first letter, show remaining right most chars
t1 += countryName;
}
QString s = "<table border=0 cellspacing=0 width=100%><tr><td bgcolor=\"" +
bg + "\"><pre>" + t1 + "</pre></td></tr></table>";
bool b65=t1.indexOf("#")==19;
@ -1489,10 +1533,9 @@ void MainWindow::readFromStdout() //readFromStdout
}
if(jt9com_.nagain==0) {
if(t.indexOf(" CQ ")>0) bg="#66ff66"; //green
if(m_myCall!="" and t.indexOf(" "+m_myCall+" ")>0) bg="#ff6666"; //red
QString s = "<table border=0 cellspacing=0 width=100%><tr><td bgcolor=\"" +
bg + "\"><pre>" + t1 + "</pre></td></tr></table>";
bg + "\"><pre>" + t1 + "</pre></td></tr></table>";
cursor = ui->decodedTextBrowser->textCursor();
cursor.movePosition(QTextCursor::End);
bf = cursor.blockFormat();
@ -2506,6 +2549,7 @@ void MainWindow::acceptQSO2(bool accepted)
m_saveComments=logDlg->m_saveComments;
m_txPower=logDlg->m_txPower;
m_logComments=logDlg->m_comments;
m_logBook.addAsWorked(m_hisCall);
if(m_clearCallGrid) {
m_hisCall="";
ui->dxCallEntry->setText("");
@ -2775,6 +2819,13 @@ void MainWindow::on_actionBlank_line_between_decoding_periods_triggered(bool che
m_insertBlank=checked;
}
void MainWindow::on_actionEnable_DXCC_entity_triggered(bool checked)
{
m_displayDXCCEntity=checked;
if (checked)
m_logBook.init(); // re-read the log and cty.dat files
}
void MainWindow::on_actionClear_DX_Call_and_Grid_after_logging_triggered(bool checked)
{
m_clearCallGrid=checked;

View File

@ -13,6 +13,7 @@
#include "psk_reporter.h"
#include "rigclass.h"
#include "signalmeter.h"
#include "logbook/logbook.h"
#ifdef WIN32
#include "PSKReporter.h"
@ -134,6 +135,7 @@ private slots:
void stopTx2();
void on_actionPrompt_to_log_QSO_triggered(bool checked);
void on_actionBlank_line_between_decoding_periods_triggered(bool checked);
void on_actionEnable_DXCC_entity_triggered(bool checked);
void on_actionClear_DX_Call_and_Grid_after_logging_triggered(bool checked);
void on_actionDisplay_distance_in_miles_triggered(bool checked);
void on_pbCallCQ_clicked();
@ -243,6 +245,7 @@ private:
bool m_promptToLog;
bool m_blankLine;
bool m_insertBlank;
bool m_displayDXCCEntity;
bool m_clearCallGrid;
bool m_bMiles;
bool m_decodedText2;
@ -344,6 +347,8 @@ private:
QString *mykey_jt9;
PSK_Reporter *psk_Reporter;
SignalMeter *signalMeter;
LogBook m_logBook;
//---------------------------------------------------- private functions
void readSettings();

View File

@ -1314,7 +1314,7 @@ p, li { white-space: pre-wrap; }
<string/>
</property>
<attribute name="buttonGroup">
<string>buttonGroup</string>
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
@ -1348,7 +1348,7 @@ p, li { white-space: pre-wrap; }
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string>buttonGroup</string>
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
@ -1379,7 +1379,7 @@ p, li { white-space: pre-wrap; }
<string/>
</property>
<attribute name="buttonGroup">
<string>buttonGroup</string>
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
@ -1540,7 +1540,7 @@ p, li { white-space: pre-wrap; }
<string/>
</property>
<attribute name="buttonGroup">
<string>buttonGroup</string>
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
@ -1688,7 +1688,7 @@ p, li { white-space: pre-wrap; }
<string/>
</property>
<attribute name="buttonGroup">
<string>buttonGroup</string>
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
@ -1719,7 +1719,7 @@ p, li { white-space: pre-wrap; }
<string/>
</property>
<attribute name="buttonGroup">
<string>buttonGroup</string>
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
@ -2202,7 +2202,7 @@ p, li { white-space: pre-wrap; }
<x>0</x>
<y>0</y>
<width>760</width>
<height>21</height>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@ -2245,6 +2245,7 @@ p, li { white-space: pre-wrap; }
<addaction name="action_73TxDisable"/>
<addaction name="actionRunaway_Tx_watchdog"/>
<addaction name="actionTx2QSO"/>
<addaction name="actionEnable_DXCC_entity"/>
<addaction name="separator"/>
<addaction name="menuAdvanced"/>
</widget>
@ -2855,6 +2856,14 @@ p, li { white-space: pre-wrap; }
<string>Gray1</string>
</property>
</action>
<action name="actionEnable_DXCC_entity">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Show DXCC entity and B4 status</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

View File

@ -95,10 +95,10 @@ void PSK_Reporter::sendReport()
txInfoData_h += "0000";
txInfoData_h.replace("50E3llll", "50E3" + QString("%1").arg(txInfoData_h.length()/2,4,16,QChar('0')));
report_h = header_h + m_rxInfoDescriptor_h + m_txInfoDescriptor_h + rxInfoData_h + txInfoData_h;
qDebug() << "Sending Report TX: ";
//qDebug() << "Sending Report TX: ";
} else {
report_h = header_h + m_rxInfoDescriptor_h + rxInfoData_h;
qDebug() << "Sending Report RX: ";
//qDebug() << "Sending Report RX: ";
}
report_h.replace("000Allll", "000A" + QString("%1").arg(report_h.length()/2,4,16,QChar('0')));

View File

@ -16,7 +16,7 @@ VERSION = 1.1
TEMPLATE = app
#DEFINES = QT4
DEFINES = QT5
DEFINES += QAUDIO_INPUT
#DEFINES += QAUDIO_INPUT
win32 {
DEFINES += WIN32
@ -41,7 +41,11 @@ SOURCES += main.cpp mainwindow.cpp plotter.cpp about.cpp \
getfile.cpp displaytext.cpp getdev.cpp logqso.cpp \
psk_reporter.cpp rigclass.cpp \
signalmeter.cpp \
meterwidget.cpp
meterwidget.cpp \
logbook/logbook.cpp \
logbook/countrydat.cpp \
logbook/countriesworked.cpp \
logbook/adif.cpp
win32 {
SOURCES += killbyname.cpp
@ -52,7 +56,11 @@ HEADERS += mainwindow.h plotter.h soundin.h soundout.h \
commons.h sleep.h displaytext.h logqso.h \
psk_reporter.h rigclass.h \
signalmeter.h \
meterwidget.h
meterwidget.h \
logbook/logbook.h \
logbook/countrydat.h \
logbook/countriesworked.h \
logbook/adif.h
# (Is the following really needed???)
#DEFINES += __cplusplus