Take advantage of the rest of the AD1C cty.dat info for highlighting

Highlighting for new continent, CQ  zone, and ITU zone added. Includes
a new class to manage the AD1C cty.dat database in memory.
This commit is contained in:
Bill Somerville 2018-10-26 03:24:36 +01:00
parent dc3e62d499
commit 1ff4d53f05
17 changed files with 1032 additions and 412 deletions

View File

@ -281,9 +281,9 @@ set (jt9_CXXSRCS
)
set (wsjtx_CXXSRCS
logbook/countrydat.cpp
logbook/logbook.cpp
logbook/WorkedBefore.cpp
logbook/AD1CCty.cpp
psk_reporter.cpp
Modulator.cpp
Detector.cpp

View File

@ -2199,9 +2199,6 @@ Right click for insert and delete options.</string>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enable or disable using the check boxes and right-click an item to change the foreground color, background color, or reset the item to default values. Drag and drop the items to change their priority, higher in the list is higher in priority.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>

View File

@ -30,6 +30,12 @@ public:
QList<DecodeHighlightingModel::HighlightInfo> const DecodeHighlightingModel::impl::defaults_ = {
{Highlight::MyCall, true, {}, {{0xff, 0x66, 0x66}}}
, {Highlight::Continent, true, {}, {{0xff, 0x00, 0x63}}}
, {Highlight::ContinentBand, true, {}, {{0xff, 0x99, 0xc2}}}
, {Highlight::CQZone, true, {}, {{0xff, 0xbf, 0x00}}}
, {Highlight::CQZoneBand, true, {}, {{0xff, 0xe4, 0x99}}}
, {Highlight::ITUZone, false, {}, {{0xa6, 0xff, 0x00}}}
, {Highlight::ITUZoneBand, false, {}, {{0xdd, 0xff, 0x99}}}
, {Highlight::DXCC, true, {}, {{0xff, 0x00, 0xff}}}
, {Highlight::DXCCBand, true, {}, {{0xff, 0xaa, 0xff}}}
, {Highlight::Grid, false, {}, {{0xff, 0x80, 0x00}}}
@ -103,6 +109,12 @@ QString DecodeHighlightingModel::highlight_name (Highlight h)
case Highlight::GridBand: return "New Grid on Band";
case Highlight::Call: return "New Call";
case Highlight::CallBand: return "New Call on Band";
case Highlight::Continent: return "New Continent";
case Highlight::ContinentBand: return "New Continent on Band";
case Highlight::CQZone: return "New CQ Zone";
case Highlight::CQZoneBand: return "New CQ Zone on Band";
case Highlight::ITUZone: return "New ITU Zone";
case Highlight::ITUZoneBand: return "New ITU Zone on Band";
case Highlight::LotW: return "LotW User";
}
return "Unknown";

View File

@ -18,7 +18,9 @@ class DecodeHighlightingModel final
{
Q_OBJECT
public:
enum class Highlight : char {CQ, MyCall, Tx, DXCC, DXCCBand, Grid, GridBand, Call, CallBand, LotW};
enum class Highlight : char {CQ, MyCall, Tx, DXCC, DXCCBand, Grid, GridBand, Call, CallBand
, Continent, ContinentBand, CQZone, CQZoneBand, ITUZone, ITUZoneBand
, LotW};
Q_ENUM (Highlight)
static QString highlight_name (Highlight h);

View File

@ -86,6 +86,30 @@ void ColorHighlighting::set_items (DecodeHighlightingModel const& highlighting_m
example = ui->example10_label;
label = ui->p10_label;
break;
case 10:
example = ui->example11_label;
label = ui->p11_label;
break;
case 11:
example = ui->example12_label;
label = ui->p12_label;
break;
case 12:
example = ui->example13_label;
label = ui->p13_label;
break;
case 13:
example = ui->example14_label;
label = ui->p14_label;
break;
case 14:
example = ui->example15_label;
label = ui->p15_label;
break;
case 15:
example = ui->example16_label;
label = ui->p16_label;
break;
}
auto palette = example->parentWidget ()->palette ();
if (Qt::NoBrush != item.background_.style ())

View File

@ -6,11 +6,24 @@
<rect>
<x>0</x>
<y>0</y>
<width>212</width>
<width>382</width>
<height>253</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="example2_label">
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="text">
<string>K1ABC</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="example1_label">
<property name="autoFillBackground">
@ -31,8 +44,8 @@
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="example2_label">
<item row="2" column="0">
<widget class="QLabel" name="example3_label">
<property name="autoFillBackground">
<bool>true</bool>
</property>
@ -51,19 +64,6 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="example3_label">
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="text">
<string>K1ABC</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="p3_label">
<property name="text">
@ -91,6 +91,20 @@
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="p6_label">
<property name="text">
<string>New Grid</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="p5_label">
<property name="text">
<string>New DXCC on Band</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="example5_label">
<property name="autoFillBackground">
@ -104,13 +118,6 @@
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="p5_label">
<property name="text">
<string>New DXCC on Band</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="example6_label">
<property name="autoFillBackground">
@ -124,13 +131,6 @@
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="p6_label">
<property name="text">
<string>New Grid</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="example7_label">
<property name="autoFillBackground">
@ -144,6 +144,13 @@
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="p8_label">
<property name="text">
<string>New Call</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="p7_label">
<property name="text">
@ -164,14 +171,7 @@
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="p8_label">
<property name="text">
<string>New Call</string>
</property>
</widget>
</item>
<item row="8" column="0">
<item row="0" column="2">
<widget class="QLabel" name="example9_label">
<property name="autoFillBackground">
<bool>true</bool>
@ -184,14 +184,7 @@
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLabel" name="p9_label">
<property name="text">
<string>New Call on Band</string>
</property>
</widget>
</item>
<item row="9" column="0">
<item row="1" column="2">
<widget class="QLabel" name="example10_label">
<property name="autoFillBackground">
<bool>true</bool>
@ -204,13 +197,140 @@
</property>
</widget>
</item>
<item row="9" column="1">
<item row="0" column="3">
<widget class="QLabel" name="p9_label">
<property name="text">
<string>New Call on Band</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLabel" name="p10_label">
<property name="text">
<string>Uploads to LotW</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="example11_label">
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="text">
<string>K1ABC</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="example12_label">
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="text">
<string>K1ABC</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QLabel" name="example13_label">
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="text">
<string>K1ABC</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLabel" name="example14_label">
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="text">
<string>K1ABC</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="QLabel" name="example15_label">
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="text">
<string>K1ABC</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="7" column="2">
<widget class="QLabel" name="example16_label">
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="text">
<string>K1ABC</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QLabel" name="p11_label">
<property name="text">
<string>New Continent</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QLabel" name="p12_label">
<property name="text">
<string>New Continent on Band</string>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QLabel" name="p13_label">
<property name="text">
<string>New CQ Zone</string>
</property>
</widget>
</item>
<item row="5" column="3">
<widget class="QLabel" name="p14_label">
<property name="text">
<string>New CQ Zone on Band</string>
</property>
</widget>
</item>
<item row="6" column="3">
<widget class="QLabel" name="p15_label">
<property name="text">
<string>New ITU Zone</string>
</property>
</widget>
</item>
<item row="7" column="3">
<widget class="QLabel" name="p16_label">
<property name="text">
<string>New ITU Zone on Band</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>

View File

@ -85,9 +85,11 @@ void DisplayText::insertLineSpacer(QString const& line)
namespace
{
using highlight_types = std::vector<DecodeHighlightingModel::Highlight>;
void set_colours (Configuration const * config, QColor * bg, QColor * fg, highlight_types const& types)
using Highlight = DecodeHighlightingModel::Highlight;
using highlight_types = std::vector<Highlight>;
Highlight set_colours (Configuration const * config, QColor * bg, QColor * fg, highlight_types const& types)
{
Highlight result = Highlight::CQ;
if (config)
{
QListIterator<DecodeHighlightingModel::HighlightInfo> it {config->decode_highlighting ().items ()};
@ -107,9 +109,11 @@ namespace
{
*fg = item.foreground_.color ();
}
result = item.type_;
}
}
}
return result; // highest priority enabled highlighting
}
}
@ -135,7 +139,7 @@ void DisplayText::appendText(QString const& text, QColor bg, QColor fg
{
QColor bg;
QColor fg;
highlight_types types {DecodeHighlightingModel::Highlight::LotW};
highlight_types types {Highlight::LotW};
set_colours (m_config, &bg, &fg, types);
if (bg.isValid ()) block_format.setBackground (bg);
if (fg.isValid ()) format.setForeground (fg);
@ -215,12 +219,18 @@ QString DisplayText::appendWorkedB4 (QString message, QString const& callsign, Q
int padding {message.indexOf (" ") > 4 ? 2 : 0};
QString call = callsign;
QString countryName;
bool callWorkedBefore;
bool callB4;
bool callB4onBand;
bool countryWorkedBefore;
bool countryB4;
bool countryB4onBand;
bool gridB4;
bool gridB4onBand;
bool continentB4;
bool continentB4onBand;
bool CQZoneB4;
bool CQZoneB4onBand;
bool ITUZoneB4;
bool ITUZoneB4onBand;
if(call.length()==2) {
int i0=message.indexOf("CQ "+call);
@ -231,9 +241,10 @@ QString DisplayText::appendWorkedB4 (QString message, QString const& callsign, Q
if(call.length()<3) return message;
if(!call.contains(QRegExp("[0-9]|[A-Z]"))) return message;
logBook.match(/*in*/call,currentMode,grid,/*out*/countryName,callWorkedBefore,countryWorkedBefore,gridB4);
logBook.match(/*in*/call,currentMode,grid,/*out*/countryName,callB4onBand,countryB4onBand,gridB4onBand,
/*in*/ currentBand);
auto const& looked_up = logBook.countries ().lookup (call);
logBook.match (call, currentMode, grid, looked_up, callB4, countryB4, gridB4, continentB4, CQZoneB4, ITUZoneB4);
logBook.match (call, currentMode, grid, looked_up, callB4onBand, countryB4onBand, gridB4onBand,
continentB4onBand, CQZoneB4onBand, ITUZoneB4onBand, currentBand);
if(grid=="") {
gridB4=true;
gridB4onBand=true;
@ -244,33 +255,68 @@ QString DisplayText::appendWorkedB4 (QString message, QString const& callsign, Q
highlight_types types;
// no shortcuts here as some types may be disabled
if (!countryWorkedBefore) {
types.push_back (DecodeHighlightingModel::Highlight::DXCC);
if (!countryB4) {
types.push_back (Highlight::DXCC);
}
if(!countryB4onBand) {
types.push_back (DecodeHighlightingModel::Highlight::DXCCBand);
types.push_back (Highlight::DXCCBand);
}
if(!gridB4) {
types.push_back (DecodeHighlightingModel::Highlight::Grid);
types.push_back (Highlight::Grid);
}
if(!gridB4onBand) {
types.push_back (DecodeHighlightingModel::Highlight::GridBand);
types.push_back (Highlight::GridBand);
}
if (!callWorkedBefore) {
types.push_back (DecodeHighlightingModel::Highlight::Call);
if (!callB4) {
types.push_back (Highlight::Call);
}
if(!callB4onBand) {
types.push_back (DecodeHighlightingModel::Highlight::CallBand);
types.push_back (Highlight::CallBand);
}
types.push_back (DecodeHighlightingModel::Highlight::CQ);
set_colours (m_config, bg, fg, types);
if (!continentB4) {
types.push_back (Highlight::Continent);
}
if(!continentB4onBand) {
types.push_back (Highlight::ContinentBand);
}
if (!CQZoneB4) {
types.push_back (Highlight::CQZone);
}
if(!CQZoneB4onBand) {
types.push_back (Highlight::CQZoneBand);
}
if (!ITUZoneB4) {
types.push_back (Highlight::ITUZone);
}
if(!ITUZoneB4onBand) {
types.push_back (Highlight::ITUZoneBand);
}
types.push_back (Highlight::CQ);
auto top_highlight = set_colours (m_config, bg, fg, types);
switch (top_highlight)
{
case Highlight::Continent:
case Highlight::ContinentBand:
appendage = AD1CCty::continent (looked_up.continent);
break;
case Highlight::CQZone:
case Highlight::CQZoneBand:
appendage = QString {"CQ Zone %1"}.arg (looked_up.CQ_zone);
break;
case Highlight::ITUZone:
case Highlight::ITUZoneBand:
appendage = QString {"ITU Zone %1"}.arg (looked_up.ITU_zone);
break;
default:
if (m_bPrincipalPrefix)
{
appendage = looked_up.primary_prefix;
}
else
{
auto countryName = looked_up.entity_name;
int i1=countryName.indexOf(";");
if(m_bPrincipalPrefix) {
int i2=countryName.lastIndexOf(";");
if(i1>0) countryName=countryName.mid(i1+2,i2-i1-2);
} else {
if(i1>0) countryName=countryName.mid(0,i1);
// do some obvious abbreviations
countryName.replace ("Islands", "Is.");
countryName.replace ("Island", "Is.");
@ -290,9 +336,10 @@ QString DisplayText::appendWorkedB4 (QString message, QString const& callsign, Q
countryName.replace ("Asiatic", "AS");
countryName.replace ("European", "EU");
countryName.replace ("African", "AF");
}
appendage += countryName;
}
}
// use a nbsp to save the start of appended text so we can find
// it again later, align appended data at a fixed column if
@ -319,7 +366,7 @@ void DisplayText::displayDecodedText(DecodedText const& decodedText, QString con
|| decodedText.string ().contains (" QRZ "))
{
CQcall = true;
highlight_types types {DecodeHighlightingModel::Highlight::CQ};
highlight_types types {Highlight::CQ};
set_colours (m_config, &bg, &fg, types);
}
if(bCQonly and !CQcall) return;
@ -331,7 +378,7 @@ void DisplayText::displayDecodedText(DecodedText const& decodedText, QString con
or decodedText.indexOf ("<" + myCall + " ") >= 0
or decodedText.indexOf ("<" + myCall + ">") >= 0
or decodedText.indexOf (" " + myCall + ">") >= 0)) {
highlight_types types {DecodeHighlightingModel::Highlight::MyCall};
highlight_types types {Highlight::MyCall};
set_colours (m_config, &bg, &fg, types);
}
auto message = decodedText.string();
@ -379,7 +426,7 @@ void DisplayText::displayTransmittedText(QString text, QString modeTx, qint32 tx
}
QColor bg;
QColor fg;
highlight_types types {DecodeHighlightingModel::Highlight::Tx};
highlight_types types {Highlight::Tx};
set_colours (m_config, &bg, &fg, types);
appendText (t, bg, fg);
}

404
logbook/AD1CCty.cpp Normal file
View File

@ -0,0 +1,404 @@
#include "AD1CCty.hpp"
#include <string>
#include <stdexcept>
#include <algorithm>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/key_extractors.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/lexical_cast.hpp>
#include <QString>
#include <QStandardPaths>
#include <QDir>
#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <QDebugStateSaver>
#include "Radio.hpp"
#include "pimpl_impl.hpp"
#include "moc_AD1CCty.cpp"
using namespace boost::multi_index;
namespace
{
auto const file_name = "cty.dat";
}
struct entity
{
using Continent = AD1CCty::Continent;
explicit entity (int id
, QString const& name
, bool WAE_only
, int CQ_zone
, int ITU_zone
, Continent continent
, float latitude
, float longtitude
, int UTC_offset
, QString const& primary_prefix)
: id_ {id}
, name_ {name}
, WAE_only_ {WAE_only}
, CQ_zone_ {CQ_zone}
, ITU_zone_ {ITU_zone}
, continent_ {continent}
, lat_ {latitude}
, long_ {longtitude}
, UTC_offset_ {UTC_offset}
, primary_prefix_ {primary_prefix}
{
}
int id_;
QString name_;
bool WAE_only_; // DARC WAE only, not valid for ARRL awards
int CQ_zone_;
int ITU_zone_;
Continent continent_;
float lat_; // degrees + is North
float long_; // degrees + is West
int UTC_offset_; // seconds
QString primary_prefix_;
};
#if !defined (QT_NO_DEBUG_STREAM)
QDebug operator << (QDebug dbg, entity const& e)
{
QDebugStateSaver saver {dbg};
dbg.nospace () << "entity("
<< e.id_ << ", "
<< e.name_ << ", "
<< e.WAE_only_ << ", "
<< e.CQ_zone_ << ", "
<< e.ITU_zone_ << ", "
<< e.continent_ << ", "
<< e.lat_ << ", "
<< e.long_ << ", "
<< (e.UTC_offset_ / (60. * 60.)) << ", "
<< e.primary_prefix_ << ')';
return dbg;
}
#endif
// tags
struct id {};
struct primary_prefix {};
// hash operation for QString object instances
struct hash_QString
{
std::size_t operator () (QString const& qs) const
{
return qHash (qs);
}
};
// set with hashed unique index that allow for efficient lookup of
// entity by internal id
typedef multi_index_container<
entity,
indexed_by<
hashed_unique<tag<id>, member<entity, int, &entity::id_> >,
hashed_unique<tag<primary_prefix>, member<entity, QString, &entity::primary_prefix_>, hash_QString> >
> entities_type;
struct prefix
{
explicit prefix (QString const& prefix, bool exact_match_only, int entity_id)
: prefix_ {prefix}
, exact_ {exact_match_only}
, entity_id_ {entity_id}
{
}
// extract key which is the prefix ignoring the trailing override
// information
QString prefix_key () const
{
auto const& prefix = prefix_.toStdString ();
return QString::fromStdString (prefix.substr (0, prefix.find_first_of ("({[<~")));
}
QString prefix_; // call or prefix with optional
// trailing override information
bool exact_;
int entity_id_;
};
#if !defined (QT_NO_DEBUG_STREAM)
QDebug operator << (QDebug dbg, prefix const& p)
{
QDebugStateSaver saver {dbg};
dbg.nospace () << "prefix("
<< p.prefix_ << ", "
<< p.exact_ << ", "
<< p.entity_id_ << ')';
return dbg;
}
#endif
// set with ordered unique index that allow for efficient
// determination of entity and entity overrides for a call or call
// prefix
typedef multi_index_container<
prefix,
indexed_by<
ordered_unique<const_mem_fun<prefix, QString, &prefix::prefix_key> > >
> prefixes_type;
class AD1CCty::impl final
{
public:
Record fixup (QString const& call, prefix const& p) const
{
using entity_by_id = entities_type::index<id>::type;
entity_by_id::iterator e; // iterator into entity set
//
// deal with special rules that cty.dat does not cope with
//
if (call.startsWith ("KG4") && call.size () != 5 && call.size () != 3)
{
// KG4 2x1 and 2x3 calls that map to Gitmo are mainland US not Gitmo
e = entities_.project<id> (entities_.get<primary_prefix> ().find ("K"));
}
else
{
e = entities_.get<id> ().find (p.entity_id_);
}
Record result;
result.continent = e->continent_;
result.CQ_zone = e->CQ_zone_;
result.ITU_zone = e->ITU_zone_;
result.entity_name = e->name_;
result.WAE_only = e->WAE_only_;
result.latitude = e->lat_;
result.longtitude = e->long_;
result.UTC_offset = e->UTC_offset_;
result.primary_prefix = e->primary_prefix_;
// check for overrides
bool ok1 {true}, ok2 {true}, ok3 {true}, ok4 {true}, ok5 {true};
QString value;
if (override_value (p.prefix_, '(', ')', value)) result.CQ_zone = value.toInt (&ok1);
if (override_value (p.prefix_, '[', ']', value)) result.ITU_zone = value.toInt (&ok2);
if (override_value (p.prefix_, '<', '>', value))
{
auto const& fix = value.split ('/');
result.latitude = fix[0].toFloat (&ok3);
result.longtitude = fix[1].toFloat (&ok4);
}
if (override_value (p.prefix_, '{', '}', value)) result.continent = continent (value);
if (override_value (p.prefix_, '~', '~', value)) result.UTC_offset = static_cast<int> (value.toFloat (&ok5) * 60 * 60);
if (!(ok1 && ok2 && ok3 && ok4 && ok5))
{
throw std::domain_error {"Invalid number in cty.dat for override of " + p.prefix_.toStdString ()};
}
return result;
}
static bool override_value (QString const& s, QChar lb, QChar ub, QString& v)
{
auto pos = s.indexOf (lb);
if (pos >= 0)
{
v = s.mid (pos + 1, s.indexOf (ub, pos + 1) - pos - 1);
return true;
}
return false;
}
QString path_;
entities_type entities_;
prefixes_type prefixes_;
};
AD1CCty::Record::Record ()
: continent {Continent::UN}
, CQ_zone {0}
, ITU_zone {0}
, WAE_only {false}
, latitude {NAN}
, longtitude {NAN}
, UTC_offset {0}
{
}
#if !defined (QT_NO_DEBUG_STREAM)
QDebug operator << (QDebug dbg, AD1CCty::Record const& r)
{
QDebugStateSaver saver {dbg};
dbg.nospace () << "AD1CCty::Record("
<< r.continent << ", "
<< r.CQ_zone << ", "
<< r.ITU_zone << ", "
<< r.entity_name << ", "
<< r.WAE_only << ", "
<< r.latitude << ", "
<< r.longtitude << ", "
<< (r.UTC_offset / (60. * 60.)) << ", "
<< r.primary_prefix << ')';
return dbg;
}
#endif
auto AD1CCty::continent (QString const& continent_id) -> Continent
{
Continent continent;
if ("AF" == continent_id)
{
continent = Continent::AF;
}
else if ("AN" == continent_id)
{
continent = Continent::AN;
}
else if ("AS" == continent_id)
{
continent = Continent::AS;
}
else if ("EU" == continent_id)
{
continent = Continent::EU;
}
else if ("NA" == continent_id)
{
continent = Continent::NA;
}
else if ("OC" == continent_id)
{
continent = Continent::OC;
}
else if ("SA" == continent_id)
{
continent = Continent::SA;
}
else
{
throw std::domain_error {"Invalid continent id: " + continent_id.toStdString ()};
}
return continent;
}
char const * AD1CCty::continent (Continent c)
{
switch (c)
{
case Continent::AF: return "AF";
case Continent::AN: return "AN";
case Continent::AS: return "AS";
case Continent::EU: return "EU";
case Continent::NA: return "NA";
case Continent::OC: return "OC";
case Continent::SA: return "SA";
default: return "UN";
}
}
AD1CCty::AD1CCty ()
{
QDir dataPath {QStandardPaths::writableLocation (QStandardPaths::DataLocation)};
m_->path_ = dataPath.exists (file_name)
? dataPath.absoluteFilePath (file_name) // user override
: QString {":/"} + file_name; // or original in the resources FS
QFile file {m_->path_};
if (file.open (QFile::ReadOnly))
{
int entity_id = 0;
int line_number {0};
QTextStream in {&file};
while (!in.atEnd ())
{
auto const& entity_line = in.readLine ();
++line_number;
if (!in.atEnd ())
{
auto const& entity_parts = entity_line.split (':');
if (entity_parts.size () >= 8)
{
auto primary_prefix = entity_parts[7].trimmed ();
bool WAE_only {false};
if (primary_prefix.startsWith ('*'))
{
primary_prefix = primary_prefix.mid (1);
WAE_only = true;
}
bool ok1, ok2, ok3, ok4, ok5;
m_->entities_.emplace (++entity_id
, entity_parts[0].trimmed ()
, WAE_only
, entity_parts[1].trimmed ().toInt (&ok1)
, entity_parts[2].trimmed ().toInt (&ok2)
, continent (entity_parts[3].trimmed ())
, entity_parts[4].trimmed ().toFloat (&ok3)
, entity_parts[5].trimmed ().toFloat (&ok4)
, static_cast<int> (entity_parts[6].trimmed ().toFloat (&ok5) * 60 * 60)
, primary_prefix);
if (!(ok1 && ok2 && ok3 && ok4 && ok5))
{
throw std::domain_error {"Invalid number in cty.dat line " + boost::lexical_cast<std::string> (line_number)};
}
QString line;
QString detail;
do
{
in.readLineInto (&line);
++line_number;
} while (detail += line, !detail.endsWith (';'));
for (auto prefix : detail.left (detail.size () - 1).split (','))
{
prefix = prefix.trimmed ();
bool exact {false};
if (prefix.startsWith ('='))
{
prefix = prefix.mid (1);
exact = true;
}
m_->prefixes_.emplace (prefix, exact, entity_id);
}
}
}
}
}
}
AD1CCty::~AD1CCty ()
{
}
auto AD1CCty::lookup (QString const& call) const -> Record
{
auto const& exact_search = call.toUpper ();
if (!(exact_search.endsWith ("/MM") || exact_search.endsWith ("/AM")))
{
auto search_prefix = Radio::effective_prefix (exact_search);
if (search_prefix != exact_search)
{
auto p = m_->prefixes_.find (exact_search);
if (p != m_->prefixes_.end () && p->exact_)
{
return m_->fixup (call, *p);
}
}
while (search_prefix.size ())
{
auto p = m_->prefixes_.find (search_prefix);
if (p != m_->prefixes_.end ())
{
if (!p->exact_ || call.size () == search_prefix.size ())
{
return m_->fixup (call, *p);
}
}
search_prefix = search_prefix.left (search_prefix.size () - 1);
}
}
return Record {};
}

55
logbook/AD1CCty.hpp Normal file
View File

@ -0,0 +1,55 @@
#ifndef AD1C_CTY_HPP_
#define AD1C_CTY_HPP_
#include <boost/core/noncopyable.hpp>
#include <QObject>
#include "pimpl_h.hpp"
//
// AD1CCty - Fast access database of Jim Reisert, AD1C's, cty.dat
// entity and entity override information file.
//
class AD1CCty final
: public QObject
, private boost::noncopyable
{
Q_OBJECT
public:
//
// Continent enumeration
//
enum class Continent {UN, AF, AN, AS, EU, NA, OC, SA};
static Continent continent (QString const& continent_id);
static char const * continent (Continent);
Q_ENUM (Continent)
struct Record
{
explicit Record ();
Continent continent;
int CQ_zone;
int ITU_zone;
QString entity_name;
bool WAE_only;
float latitude;
float longtitude;
int UTC_offset;
QString primary_prefix;
};
explicit AD1CCty ();
~AD1CCty ();
Record lookup (QString const& call) const;
private:
class impl;
pimpl<impl> m_;
};
#if !defined (QT_NO_DEBUG_STREAM)
QDebug operator << (QDebug, AD1CCty::Record const&);
#endif
#endif

View File

@ -8,31 +8,44 @@
#include <QDir>
#include <QFile>
#include <QTextStream>
#include "countrydat.h"
#include "pimpl_impl.hpp"
using namespace boost::multi_index;
// worked before set element
struct worked_entry
{
explicit worked_entry (std::string const& call
, std::string const& grid
, std::string const& band
, std::string const& mode
, std::string const& country)
: call {call}
, grid {grid}
, band {band}
, mode {mode}
, country {country}
explicit worked_entry (QString const& call, QString const& grid, QString const& band
, QString const& mode, QString const& country, AD1CCty::Continent continent
, int CQ_zone, int ITU_zone)
: call_ {call}
, grid_ {grid}
, band_ {band}
, mode_ {mode}
, country_ {country}
, continent_ {continent}
, CQ_zone_ {CQ_zone}
, ITU_zone_ {ITU_zone}
{
}
std::string call;
std::string grid;
std::string band;
std::string mode;
std::string country;
QString call_;
QString grid_;
QString band_;
QString mode_;
QString country_;
AD1CCty::Continent continent_;
int CQ_zone_;
int ITU_zone_;
};
// less then predidate for the Continent enum class
struct Continent_less
{
operator () (AD1CCty::Continent lhs, AD1CCty::Continent rhs) const
{
return static_cast<int> (lhs) < static_cast<int> (rhs);
}
};
// tags
@ -42,77 +55,98 @@ struct grid_mode_band {};
struct grid_band {};
struct entity_mode_band {};
struct entity_band {};
struct continent_mode_band {};
struct continent_band {};
struct CQ_zone_mode_band {};
struct CQ_zone_band {};
struct ITU_zone_mode_band {};
struct ITU_zone_band {};
// set with multiple ordered unique indexes that allow for efficient
// determination of various categories of worked before status
typedef boost::multi_index::multi_index_container<
typedef multi_index_container<
worked_entry,
boost::multi_index::indexed_by<
indexed_by<
// call+mode+band
boost::multi_index::ordered_unique<
boost::multi_index::tag<call_mode_band>,
boost::multi_index::composite_key<
worked_entry,
boost::multi_index::member<worked_entry, std::string, &worked_entry::call>,
boost::multi_index::member<worked_entry, std::string, &worked_entry::mode>,
boost::multi_index::member<worked_entry, std::string, &worked_entry::band>
>
>,
ordered_unique<tag<call_mode_band>,
composite_key<worked_entry,
member<worked_entry, QString, &worked_entry::call_>,
member<worked_entry, QString, &worked_entry::mode_>,
member<worked_entry, QString, &worked_entry::band_> > >,
// call+band
boost::multi_index::ordered_unique<
boost::multi_index::tag<call_band>,
boost::multi_index::composite_key<
worked_entry,
boost::multi_index::member<worked_entry, std::string, &worked_entry::call>,
boost::multi_index::member<worked_entry, std::string, &worked_entry::band>
>
>,
ordered_unique<tag<call_band>,
composite_key<worked_entry,
member<worked_entry, QString, &worked_entry::call_>,
member<worked_entry, QString, &worked_entry::band_> > >,
// grid+mode+band
boost::multi_index::ordered_unique<
boost::multi_index::tag<grid_mode_band>,
boost::multi_index::composite_key<
worked_entry,
boost::multi_index::member<worked_entry, std::string, &worked_entry::grid>,
boost::multi_index::member<worked_entry, std::string, &worked_entry::mode>,
boost::multi_index::member<worked_entry, std::string, &worked_entry::band>
>
>,
ordered_unique<tag<grid_mode_band>,
composite_key<worked_entry,
member<worked_entry, QString, &worked_entry::grid_>,
member<worked_entry, QString, &worked_entry::mode_>,
member<worked_entry, QString, &worked_entry::band_> > >,
// grid+band
boost::multi_index::ordered_unique<
boost::multi_index::tag<grid_band>,
boost::multi_index::composite_key<
worked_entry,
boost::multi_index::member<worked_entry, std::string, &worked_entry::grid>,
boost::multi_index::member<worked_entry, std::string, &worked_entry::band>
>
>,
ordered_unique<tag<grid_band>,
composite_key<worked_entry,
member<worked_entry, QString, &worked_entry::grid_>,
member<worked_entry, QString, &worked_entry::band_> > >,
// country+mode+band
boost::multi_index::ordered_unique<
boost::multi_index::tag<entity_mode_band>,
boost::multi_index::composite_key<
worked_entry,
boost::multi_index::member<worked_entry, std::string, &worked_entry::country>,
boost::multi_index::member<worked_entry, std::string, &worked_entry::mode>,
boost::multi_index::member<worked_entry, std::string, &worked_entry::band>
>
>,
ordered_unique<tag<entity_mode_band>,
composite_key<worked_entry,
member<worked_entry, QString, &worked_entry::country_>,
member<worked_entry, QString, &worked_entry::mode_>,
member<worked_entry, QString, &worked_entry::band_> > >,
// country+band
boost::multi_index::ordered_unique<
boost::multi_index::tag<entity_band>,
boost::multi_index::composite_key<
worked_entry,
boost::multi_index::member<worked_entry, std::string, &worked_entry::country>,
boost::multi_index::member<worked_entry, std::string, &worked_entry::band>
>
>
>
ordered_unique<tag<entity_band>,
composite_key<worked_entry,
member<worked_entry, QString, &worked_entry::country_>,
member<worked_entry, QString, &worked_entry::band_> > >,
// continent+mode+band
ordered_unique<tag<continent_mode_band>,
composite_key<worked_entry,
member<worked_entry, AD1CCty::Continent, &worked_entry::continent_>,
member<worked_entry, QString, &worked_entry::mode_>,
member<worked_entry, QString, &worked_entry::band_> >,
composite_key_compare<
Continent_less,
std::less<QString>,
std::less<QString> > >,
// continent+band
ordered_unique<tag<continent_band>,
composite_key<worked_entry,
member<worked_entry, AD1CCty::Continent, &worked_entry::continent_>,
member<worked_entry, QString, &worked_entry::band_> >,
composite_key_compare<
Continent_less,
std::less<QString> > >,
// CQ-zone+mode+band
ordered_unique<tag<CQ_zone_mode_band>,
composite_key<worked_entry,
member<worked_entry, int, &worked_entry::CQ_zone_>,
member<worked_entry, QString, &worked_entry::mode_>,
member<worked_entry, QString, &worked_entry::band_> > >,
// CQ-zone+band
ordered_unique<tag<CQ_zone_band>,
composite_key<worked_entry,
member<worked_entry, int, &worked_entry::CQ_zone_>,
member<worked_entry, QString, &worked_entry::band_> > >,
// ITU-zone+mode+band
ordered_unique<tag<ITU_zone_mode_band>,
composite_key<worked_entry,
member<worked_entry, int, &worked_entry::ITU_zone_>,
member<worked_entry, QString, &worked_entry::mode_>,
member<worked_entry, QString, &worked_entry::band_> > >,
// ITU-zone+band
ordered_unique<tag<ITU_zone_band>,
composite_key<worked_entry,
member<worked_entry, int, &worked_entry::ITU_zone_>,
member<worked_entry, QString, &worked_entry::band_> > > >
> worked_type;
namespace
{
auto const logFileName = "wsjtx_log.adi";
std::string extractField (QString const& record, QString const& fieldName)
QString extractField (QString const& record, QString const& fieldName)
{
int fieldNameIndex = record.indexOf ('<' + fieldName + ':', 0, Qt::CaseInsensitive);
if (fieldNameIndex >=0)
@ -136,8 +170,7 @@ namespace
int fieldLength = fieldLengthString.toInt();
if (fieldLength > 0)
{
QString field = record.mid(closingBracketIndex+1,fieldLength);
return field.toStdString ();
return record.mid(closingBracketIndex+1,fieldLength);
}
}
}
@ -154,7 +187,7 @@ public:
}
QString path_;
CountryDat countries_;
AD1CCty prefixes_;
worked_type worked_;
};
@ -205,11 +238,15 @@ WorkedBefore::WorkedBefore ()
auto call = extractField (record, "CALL");
if (call.size ())
{
auto const& entity = m_->prefixes_.lookup (call);
m_->worked_.emplace (call
, extractField (record, "GRIDSQUARE").substr (0, 4) // not interested in 6-digit grids
, extractField (record, "GRIDSQUARE").left (4) // not interested in 6-digit grids
, extractField (record, "BAND")
, extractField (record, "MODE")
, m_->countries_.find (QString::fromStdString (call)).toStdString ());
, entity.entity_name
, entity.continent
, entity.CQ_zone
, entity.ITU_zone);
}
}
}
@ -224,9 +261,9 @@ QString const& WorkedBefore::path () const
return m_->path_;
}
CountryDat const& WorkedBefore::countries () const
AD1CCty const& WorkedBefore::countries () const
{
return m_->countries_;
return m_->prefixes_;
}
bool WorkedBefore::add (QString const& call
@ -237,6 +274,7 @@ bool WorkedBefore::add (QString const& call
{
if (call.size ())
{
auto const& entity = m_->prefixes_.lookup (call);
QFile file {m_->path_};
if (!file.open(QIODevice::Text | QIODevice::Append))
{
@ -251,11 +289,8 @@ bool WorkedBefore::add (QString const& call
}
out << ADIF_record << " <eor>" << endl;
}
m_->worked_.emplace (call.toStdString ()
, grid.toStdString ()
, band.toStdString ()
, mode.toStdString ()
, m_->countries_.find (call).toStdString ());
m_->worked_.emplace (call, grid, band, mode, entity.entity_name
, entity.continent, entity.CQ_zone, entity.ITU_zone);
}
return true;
}
@ -269,9 +304,7 @@ bool WorkedBefore::country_worked (QString const& country, QString const& mode,
return
country.size ()
&& m_->worked_.get<entity_mode_band> ().end ()
!= m_->worked_.get<entity_mode_band> ().find (std::make_tuple (country.toStdString ()
, mode.toStdString ()
, band.toStdString ()));
!= m_->worked_.get<entity_mode_band> ().find (std::make_tuple (country, mode, band));
}
else
{
@ -279,8 +312,7 @@ bool WorkedBefore::country_worked (QString const& country, QString const& mode,
return
country.size ()
&& m_->worked_.get<entity_mode_band> ().end ()
!= m_->worked_.get<entity_mode_band> ().find (std::make_tuple (country.toStdString ()
, mode.toStdString ()));
!= m_->worked_.get<entity_mode_band> ().find (std::make_tuple (country, mode));
}
}
else
@ -290,8 +322,7 @@ bool WorkedBefore::country_worked (QString const& country, QString const& mode,
return
country.size ()
&& m_->worked_.get<entity_band> ().end ()
!= m_->worked_.get<entity_band> ().find (std::make_tuple (country.toStdString ()
, band.toStdString ()));
!= m_->worked_.get<entity_band> ().find (std::make_tuple (country, band));
}
else
{
@ -299,7 +330,7 @@ bool WorkedBefore::country_worked (QString const& country, QString const& mode,
return
country.size ()
&& m_->worked_.get<entity_band> ().end ()
!= m_->worked_.get<entity_band> ().find (std::make_tuple (country.toStdString ()));
!= m_->worked_.get<entity_band> ().find (country);
}
}
}
@ -311,16 +342,13 @@ bool WorkedBefore::grid_worked (QString const& grid, QString const& mode, QStrin
if (band.size ())
{
return m_->worked_.get<grid_mode_band> ().end ()
!= m_->worked_.get<grid_mode_band> ().find (std::make_tuple (grid.toStdString ()
, mode.toStdString ()
, band.toStdString ()));
!= m_->worked_.get<grid_mode_band> ().find (std::make_tuple (grid, mode, band));
}
else
{
// partial key lookup
return m_->worked_.get<grid_mode_band> ().end ()
!= m_->worked_.get<grid_mode_band> ().find (std::make_tuple (grid.toStdString ()
, mode.toStdString ()));
!= m_->worked_.get<grid_mode_band> ().find (std::make_tuple (grid, mode));
}
}
else
@ -328,14 +356,13 @@ bool WorkedBefore::grid_worked (QString const& grid, QString const& mode, QStrin
if (band.size ())
{
return m_->worked_.get<grid_band> ().end ()
!= m_->worked_.get<grid_band> ().find (std::make_tuple (grid.toStdString ()
, band.toStdString ()));
!= m_->worked_.get<grid_band> ().find (std::make_tuple (grid, band));
}
else
{
// partial key lookup
return m_->worked_.get<grid_band> ().end ()
!= m_->worked_.get<grid_band> ().find (std::make_tuple (grid.toStdString ()));
!= m_->worked_.get<grid_band> ().find (grid);
}
}
}
@ -347,16 +374,13 @@ bool WorkedBefore::call_worked (QString const& call, QString const& mode, QStrin
if (band.size ())
{
return m_->worked_.get<call_mode_band> ().end ()
!= m_->worked_.get<call_mode_band> ().find (std::make_tuple (call.toStdString ()
, mode.toStdString ()
, band.toStdString ()));
!= m_->worked_.get<call_mode_band> ().find (std::make_tuple (call, mode, band));
}
else
{
// partial key lookup
return m_->worked_.get<call_mode_band> ().end ()
!= m_->worked_.get<call_mode_band> ().find (std::make_tuple (call.toStdString ()
, mode.toStdString ()));
!= m_->worked_.get<call_mode_band> ().find (std::make_tuple (call, mode));
}
}
else
@ -364,14 +388,109 @@ bool WorkedBefore::call_worked (QString const& call, QString const& mode, QStrin
if (band.size ())
{
return m_->worked_.get<call_band> ().end ()
!= m_->worked_.get<call_band> ().find (std::make_tuple (call.toStdString ()
, band.toStdString ()));
!= m_->worked_.get<call_band> ().find (std::make_tuple (call, band));
}
else
{
// partial key lookup
return m_->worked_.get<call_band> ().end ()
!= m_->worked_.get<call_band> ().find (std::make_tuple (call.toStdString ()));
!= m_->worked_.get<call_band> ().find (std::make_tuple (call));
}
}
}
bool WorkedBefore::continent_worked (Continent continent, QString const& mode, QString const& band) const
{
if (mode.size ())
{
if (band.size ())
{
return m_->worked_.get<continent_mode_band> ().end ()
!= m_->worked_.get<continent_mode_band> ().find (std::make_tuple (continent, mode, band));
}
else
{
// partial key lookup
return m_->worked_.get<continent_mode_band> ().end ()
!= m_->worked_.get<continent_mode_band> ().find (std::make_tuple (continent, mode));
}
}
else
{
if (band.size ())
{
return m_->worked_.get<continent_band> ().end ()
!= m_->worked_.get<continent_band> ().find (std::make_tuple (continent, band));
}
else
{
// partial key lookup
return m_->worked_.get<continent_band> ().end ()
!= m_->worked_.get<continent_band> ().find (continent);
}
}
}
bool WorkedBefore::CQ_zone_worked (int CQ_zone, QString const& mode, QString const& band) const
{
if (mode.size ())
{
if (band.size ())
{
return m_->worked_.get<CQ_zone_mode_band> ().end ()
!= m_->worked_.get<CQ_zone_mode_band> ().find (std::make_tuple (CQ_zone, mode, band));
}
else
{
// partial key lookup
return m_->worked_.get<CQ_zone_mode_band> ().end ()
!= m_->worked_.get<CQ_zone_mode_band> ().find (std::make_tuple (CQ_zone, mode));
}
}
else
{
if (band.size ())
{
return m_->worked_.get<CQ_zone_band> ().end ()
!= m_->worked_.get<CQ_zone_band> ().find (std::make_tuple (CQ_zone, band));
}
else
{
// partial key lookup
return m_->worked_.get<CQ_zone_band> ().end ()
!= m_->worked_.get<CQ_zone_band> ().find (CQ_zone);
}
}
}
bool WorkedBefore::ITU_zone_worked (int ITU_zone, QString const& mode, QString const& band) const
{
if (mode.size ())
{
if (band.size ())
{
return m_->worked_.get<ITU_zone_mode_band> ().end ()
!= m_->worked_.get<ITU_zone_mode_band> ().find (std::make_tuple (ITU_zone, mode, band));
}
else
{
// partial key lookup
return m_->worked_.get<ITU_zone_mode_band> ().end ()
!= m_->worked_.get<ITU_zone_mode_band> ().find (std::make_tuple (ITU_zone, mode));
}
}
else
{
if (band.size ())
{
return m_->worked_.get<ITU_zone_band> ().end ()
!= m_->worked_.get<ITU_zone_band> ().find (std::make_tuple (ITU_zone, band));
}
else
{
// partial key lookup
return m_->worked_.get<ITU_zone_band> ().end ()
!= m_->worked_.get<ITU_zone_band> ().find (ITU_zone);
}
}
}

View File

@ -2,6 +2,7 @@
#define WORKWED_BEFORE_HPP_
#include <boost/core/noncopyable.hpp>
#include "AD1CCty.hpp"
#include "pimpl_h.hpp"
class CountryDat;
@ -12,11 +13,13 @@ class WorkedBefore final
: private boost::noncopyable
{
public:
using Continent = AD1CCty::Continent;
explicit WorkedBefore ();
~WorkedBefore ();
QString const& path () const;
CountryDat const& countries () const;
AD1CCty const& countries () const;
bool add (QString const& call
, QString const& grid
, QString const& band
@ -25,6 +28,9 @@ public:
bool country_worked (QString const& call, QString const& mode, QString const& band) const;
bool grid_worked (QString const& grid, QString const& mode, QString const& band) const;
bool call_worked (QString const& call, QString const& mode, QString const& band) const;
bool continent_worked (Continent continent, QString const& mode, QString const& band) const;
bool CQ_zone_worked (int CQ_zone, QString const& mode, QString const& band) const;
bool ITU_zone_worked (int ITU_zone, QString const& mode, QString const& band) const;
private:
class impl;

View File

@ -1,154 +0,0 @@
/*
#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 <QDir>
#include <QStandardPaths>
#include <QFile>
#include <QTextStream>
#include <QDebug>
#include "Radio.hpp"
namespace
{
auto countryFileName = "cty.dat";
}
CountryDat::CountryDat ()
{
QDir dataPath {QStandardPaths::writableLocation (QStandardPaths::DataLocation)};
QFile file {dataPath.exists (countryFileName)
? dataPath.absoluteFilePath (countryFileName) // user override
: QString {":/"} + countryFileName}; // or original in
// the resources FS
if (file.open (QFile::ReadOnly))
{
QTextStream in {&file};
while (!in.atEnd ())
{
QString line1 = in.readLine ();
if (!in.atEnd ())
{
QString line2 = in.readLine ();
auto name = extractName (line1);
if (name.length () > 0)
{
auto const& continent = line1.mid (36,2);
auto principalPrefix = line1.mid (69,4);
principalPrefix = principalPrefix.mid (0, principalPrefix.indexOf (":"));
name += "; " + principalPrefix + "; " + continent;
bool more {true};
QStringList prefixes;
while (more)
{
QStringList p = extractPrefix (line2, more);
prefixes += p;
if (more) line2 = in.readLine ();
}
Q_FOREACH (auto const& p, prefixes)
{
if (p.length() > 0) data_.insert (p, name);
}
}
}
}
}
}
QString CountryDat::extractName (QString const& line) const
{
int s1 = line.indexOf(':');
if (s1>=0)
{
QString name = line.mid(0,s1);
return name;
}
return "";
}
void CountryDat::removeBrackets(QString& line, QString const& a, QString const& 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;
}
// return country name else ""
QString CountryDat::find (QString const& call) const
{
auto const& search_string = call.toUpper ();
// check for exact match first
if (data_.contains ("=" + search_string))
{
return fixup (data_.value ("=" + search_string), search_string);
}
auto prefix = Radio::effective_prefix (search_string);
auto match_candidate = prefix;
while (match_candidate.size () >= 1)
{
if (data_.contains (match_candidate))
{
return fixup (data_.value (match_candidate), prefix);
}
match_candidate = match_candidate.left (match_candidate.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 && call.size () != 3)
{
country.replace ("Guantanamo Bay; KG4; NA", "United States; K; NA");
}
return country;
}

View File

@ -1,30 +0,0 @@
/*
* Reads cty.dat file
* Establishes a map between prefixes and their country names
* VK3ACF July 2013
*/
#ifndef COUNTRY_DAT_H_
#define COUNTRY_DAT_H_
#include <boost/core/noncopyable.hpp>
#include <QString>
#include <QHash>
class CountryDat final
: private boost::noncopyable
{
public:
CountryDat ();
QString find (QString const& call) const; // return country name or ""
private:
QString extractName (QString const& line) const;
void removeBrackets (QString& line, QString const& a, QString const& b) const;
QStringList extractPrefix (QString& line, bool& more) const;
QString fixup (QString country, QString const& call) const;
QHash<QString, QString> data_;
};
#endif

View File

@ -2,8 +2,8 @@
#include <QDateTime>
#include <QDebug>
#include "countrydat.h"
#include "Configuration.hpp"
#include "AD1CCty.hpp"
LogBook::LogBook (Configuration const * configuration)
: config_ {configuration}
@ -11,19 +11,35 @@ LogBook::LogBook (Configuration const * configuration)
}
void LogBook::match (QString const& call, QString const& mode, QString const& grid,
QString &countryName,
bool &callWorkedBefore,
bool &countryWorkedBefore,
bool &gridWorkedBefore,
AD1CCty::Record const& looked_up,
bool& callB4,
bool& countryB4,
bool& gridB4,
bool& continentB4,
bool& CQZoneB4,
bool& ITUZoneB4,
QString const& band) const
{
if (call.length() > 0)
{
auto const& mode_to_check = (config_ && !config_->highlight_by_mode ()) ? QString {} : mode;
callWorkedBefore = worked_before_.call_worked (call, mode_to_check, band);
gridWorkedBefore = worked_before_.grid_worked(grid, mode_to_check, band);
countryName = worked_before_.countries ().find (call);
countryWorkedBefore = worked_before_.country_worked (countryName, mode_to_check, band);
callB4 = worked_before_.call_worked (call, mode_to_check, band);
gridB4 = worked_before_.grid_worked(grid, mode_to_check, band);
auto const& countryName = looked_up.entity_name;
if (countryName.size ())
{
countryB4 = worked_before_.country_worked (countryName, mode_to_check, band);
continentB4 = worked_before_.continent_worked (looked_up.continent, mode_to_check, band);
CQZoneB4 = worked_before_.CQ_zone_worked (looked_up.CQ_zone, mode_to_check, band);
ITUZoneB4 = worked_before_.ITU_zone_worked (looked_up.ITU_zone, mode_to_check, band);
}
else
{
countryB4 = true; // we don't want to flag unknown entities
continentB4 = true;
CQZoneB4 = true;
ITUZoneB4 = true;
}
}
}

View File

@ -26,10 +26,11 @@ class LogBook final
, QString const& band
, QString const& mode
, QByteArray const& ADIF_record);
CountryDat const& countries () const {return worked_before_.countries ();}
AD1CCty const& countries () const {return worked_before_.countries ();}
void match (QString const& call, QString const& mode, QString const& grid,
QString &countryName, bool &callWorkedBefore, bool &countryWorkedBefore,
bool &gridWorkedBefore, QString const& currentBand = QString {}) const;
AD1CCty::Record const&, bool& callB4, bool& countryB4,
bool &gridB4, bool &continentB4, bool& CQZoneB4, bool& ITUZoneB4,
QString const& currentBand = QString {}) const;
static QByteArray 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,

View File

@ -62,7 +62,7 @@
#include "ExchangeValidator.hpp"
#include "EqualizationToolsDialog.hpp"
#include "LotWUsers.hpp"
#include "logbook/countrydat.h"
#include "logbook/AD1CCty.hpp"
#include "ui_mainwindow.h"
#include "moc_mainwindow.cpp"
@ -7937,8 +7937,9 @@ void MainWindow::houndCallers()
if(!ui->textBrowser4->toPlainText().contains(paddedHoundCall)) {
if(m_loggedByFox[houndCall].contains(m_lastBand)) continue; //already logged on this band
if(m_foxQSO.contains(houndCall)) continue; //still in the QSO map
auto const& countryName = m_logBook.countries ().find (houndCall);
auto const& continent = countryName.mid (countryName.lastIndexOf (';') + 2, -1);
auto const& entity = m_logBook.countries ().lookup (houndCall);
auto const& countryName = entity.entity_name;
auto const& continent = AD1CCty::continent (entity.continent);
//If we are using a directed CQ, ignore Hound calls that do not comply.
QString CQtext=ui->comboBoxCQ->currentText();

View File

@ -49,9 +49,9 @@ DEFINES += UNIX
#
SOURCES += \
logbook/adif.cpp \
logbook/countrydat.cpp \
logbook/countriesworked.cpp \
logbook/logbook.cpp \
logbook/AD1CCty.cpp \
astro.cpp Radio.cpp NetworkServerLookup.cpp revision_utils.cpp \
Transceiver.cpp TransceiverBase.cpp TransceiverFactory.cpp \
PollingTransceiver.cpp EmulateSplitTransceiver.cpp LettersSpinBox.cpp \
@ -80,7 +80,7 @@ HEADERS += qt_helpers.hpp \
Transceiver.hpp TransceiverBase.hpp TransceiverFactory.hpp PollingTransceiver.hpp \
EmulateSplitTransceiver.hpp DXLabSuiteCommanderTransceiver.hpp HamlibTransceiver.hpp \
Configuration.hpp wsprnet.h signalmeter.h meterwidget.h \
logbook/logbook.h logbook/countrydat.h logbook/countriesworked.h logbook/adif.h \
logbook/logbook.h logbook/countriesworked.h logbook/adif.h logbook/AD1CCty.h \
messageaveraging.h echoplot.h echograph.h fastgraph.h fastplot.h Modes.hpp WSPRBandHopping.hpp \
WsprTxScheduler.h SampleDownloader.hpp MultiSettings.hpp PhaseEqualizationDialog.hpp \
IARURegions.hpp MessageBox.hpp EqualizationToolsDialog.hpp CallsignValidator.hpp \