mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2024-11-25 05:38:46 -05:00
Priorities for decoded message highlighting and new worked before internal database
Settings option to highlight not worked before entities/grids/calls by mode. Fix issues with highlighting decodes and generally refactor the internal workings of ADIF and QSO recording for worked before detection.
This commit is contained in:
parent
c6a3b9a170
commit
8215f3412b
@ -281,10 +281,9 @@ set (jt9_CXXSRCS
|
||||
)
|
||||
|
||||
set (wsjtx_CXXSRCS
|
||||
logbook/adif.cpp
|
||||
logbook/countrydat.cpp
|
||||
logbook/countriesworked.cpp
|
||||
logbook/logbook.cpp
|
||||
logbook/WorkedBefore.cpp
|
||||
psk_reporter.cpp
|
||||
Modulator.cpp
|
||||
Detector.cpp
|
||||
|
@ -525,6 +525,7 @@ private:
|
||||
|
||||
DecodeHighlightingModel decode_highlighing_model_;
|
||||
DecodeHighlightingModel next_decode_highlighing_model_;
|
||||
bool highlight_by_mode_;
|
||||
int LotW_days_since_upload_;
|
||||
|
||||
TransceiverFactory::ParameterPack rig_params_;
|
||||
@ -708,6 +709,7 @@ bool Configuration::pwrBandTxMemory () const {return m_->pwrBandTxMemory_;}
|
||||
bool Configuration::pwrBandTuneMemory () const {return m_->pwrBandTuneMemory_;}
|
||||
LotWUsers const& Configuration::lotw_users () const {return m_->lotw_users_;}
|
||||
DecodeHighlightingModel const& Configuration::decode_highlighting () const {return m_->decode_highlighing_model_;}
|
||||
bool Configuration::highlight_by_mode () const {return m_->highlight_by_mode_;}
|
||||
|
||||
void Configuration::set_calibration (CalibrationParams params)
|
||||
{
|
||||
@ -903,6 +905,7 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
|
||||
, station_delete_action_ {tr ("&Delete"), nullptr}
|
||||
, station_insert_action_ {tr ("&Insert ..."), nullptr}
|
||||
, station_dialog_ {new StationDialog {&next_stations_, &bands_, this}}
|
||||
, highlight_by_mode_ {false}
|
||||
, LotW_days_since_upload_ {0}
|
||||
, last_port_type_ {TransceiverFactory::Capabilities::none}
|
||||
, rig_is_dummy_ {false}
|
||||
@ -1266,6 +1269,7 @@ void Configuration::impl::initialize_models ()
|
||||
next_stations_.station_list (stations_.station_list ());
|
||||
|
||||
next_decode_highlighing_model_.items (decode_highlighing_model_.items ());
|
||||
ui_->highlight_by_mode_check_box->setChecked (highlight_by_mode_);
|
||||
ui_->LotW_days_since_upload_spin_box->setValue (LotW_days_since_upload_);
|
||||
|
||||
set_rig_invariants ();
|
||||
@ -1411,6 +1415,7 @@ void Configuration::impl::read_settings ()
|
||||
stations_.station_list (settings_->value ("stations").value<StationList::Stations> ());
|
||||
|
||||
decode_highlighing_model_.items (settings_->value ("DecodeHighlighting", QVariant::fromValue (DecodeHighlightingModel::default_items ())).value<DecodeHighlightingModel::HighlightItems> ());
|
||||
highlight_by_mode_ = settings_->value("HighlightByMode", false).toBool ();
|
||||
LotW_days_since_upload_ = settings_->value ("LotWDaysSinceLastUpload", 365).toInt ();
|
||||
lotw_users_.set_age_constraint (LotW_days_since_upload_);
|
||||
|
||||
@ -1524,6 +1529,7 @@ void Configuration::impl::write_settings ()
|
||||
settings_->setValue ("FrequenciesForRegionModes", QVariant::fromValue (frequencies_.frequency_list ()));
|
||||
settings_->setValue ("stations", QVariant::fromValue (stations_.station_list ()));
|
||||
settings_->setValue ("DecodeHighlighting", QVariant::fromValue (decode_highlighing_model_.items ()));
|
||||
settings_->setValue ("HighlightByMode", highlight_by_mode_);
|
||||
settings_->setValue ("LotWDaysSinceLastUpload", LotW_days_since_upload_);
|
||||
settings_->setValue ("toRTTY", log_as_RTTY_);
|
||||
settings_->setValue ("dBtoComments", report_in_comments_);
|
||||
@ -2026,6 +2032,7 @@ void Configuration::impl::accept ()
|
||||
decode_highlighing_model_.items (next_decode_highlighing_model_.items ());
|
||||
Q_EMIT self_->decode_highlighting_changed (decode_highlighing_model_);
|
||||
}
|
||||
highlight_by_mode_ = ui_->highlight_by_mode_check_box->isChecked ();
|
||||
LotW_days_since_upload_ = ui_->LotW_days_since_upload_spin_box->value ();
|
||||
lotw_users_.set_age_constraint (LotW_days_since_upload_);
|
||||
|
||||
|
@ -176,6 +176,7 @@ public:
|
||||
bool pwrBandTuneMemory () const;
|
||||
LotWUsers const& lotw_users () const;
|
||||
DecodeHighlightingModel const& decode_highlighting () const;
|
||||
bool highlight_by_mode () const;
|
||||
|
||||
struct CalibrationParams
|
||||
{
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>534</width>
|
||||
<height>546</height>
|
||||
<width>527</width>
|
||||
<height>540</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -2235,6 +2235,20 @@ Right click for insert and delete options.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="highlight_by_mode_check_box">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Check to indicate new DXCC entities, grid squares, and callsigns per mode.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Highlight by Mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@ -2313,19 +2327,6 @@ Right click for insert and delete options.</string>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_8">
|
||||
<property name="orientation">
|
||||
@ -2339,6 +2340,19 @@ Right click for insert and delete options.</string>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="advanced_tab">
|
||||
@ -2905,6 +2919,7 @@ Right click for insert and delete options.</string>
|
||||
<tabstop>stations_table_view</tabstop>
|
||||
<tabstop>highlighting_list_view</tabstop>
|
||||
<tabstop>reset_highlighting_to_defaults_push_button</tabstop>
|
||||
<tabstop>highlight_by_mode_check_box</tabstop>
|
||||
<tabstop>LotW_CSV_URL_line_edit</tabstop>
|
||||
<tabstop>LotW_CSV_fetch_push_button</tabstop>
|
||||
<tabstop>LotW_days_since_upload_spin_box</tabstop>
|
||||
@ -2994,12 +3009,12 @@ Right click for insert and delete options.</string>
|
||||
</connection>
|
||||
</connections>
|
||||
<buttongroups>
|
||||
<buttongroup name="TX_mode_button_group"/>
|
||||
<buttongroup name="CAT_data_bits_button_group"/>
|
||||
<buttongroup name="PTT_method_button_group"/>
|
||||
<buttongroup name="CAT_handshake_button_group"/>
|
||||
<buttongroup name="TX_audio_source_button_group"/>
|
||||
<buttongroup name="split_mode_button_group"/>
|
||||
<buttongroup name="CAT_stop_bits_button_group"/>
|
||||
<buttongroup name="CAT_data_bits_button_group"/>
|
||||
<buttongroup name="TX_mode_button_group"/>
|
||||
<buttongroup name="PTT_method_button_group"/>
|
||||
<buttongroup name="TX_audio_source_button_group"/>
|
||||
</buttongroups>
|
||||
</ui>
|
||||
|
104
displaytext.cpp
104
displaytext.cpp
@ -11,10 +11,12 @@
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QListIterator>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "Configuration.hpp"
|
||||
#include "LotWUsers.hpp"
|
||||
#include "DecodeHighlightingModel.hpp"
|
||||
#include "logbook/logbook.h"
|
||||
|
||||
#include "qt_helpers.hpp"
|
||||
#include "moc_displaytext.cpp"
|
||||
@ -95,7 +97,7 @@ namespace
|
||||
{
|
||||
auto const& item = it.previous ();
|
||||
auto const& type = std::find (types.begin (), types.end (), item.type_);
|
||||
if (type != types.end () && *type == item.type_ && item.enabled_)
|
||||
if (type != types.end () && item.enabled_)
|
||||
{
|
||||
if (item.background_.style () != Qt::NoBrush)
|
||||
{
|
||||
@ -117,40 +119,40 @@ void DisplayText::appendText(QString const& text, QColor bg, QColor fg
|
||||
auto cursor = textCursor ();
|
||||
cursor.movePosition (QTextCursor::End);
|
||||
auto block_format = cursor.blockFormat ();
|
||||
auto format = cursor.blockCharFormat ();
|
||||
format.setFont (char_font_);
|
||||
format.clearBackground ();
|
||||
if (bg.isValid ())
|
||||
{
|
||||
block_format.setBackground (bg);
|
||||
}
|
||||
else
|
||||
{
|
||||
block_format.clearBackground ();
|
||||
}
|
||||
format.clearForeground ();
|
||||
if (fg.isValid ())
|
||||
{
|
||||
block_format.setForeground (fg);
|
||||
format.setForeground (fg);
|
||||
}
|
||||
else
|
||||
if (call2.size () && m_config && m_config->lotw_users ().user (call2))
|
||||
{
|
||||
block_format.clearForeground ();
|
||||
}
|
||||
if (0 == cursor.position ())
|
||||
{
|
||||
cursor.setBlockFormat (block_format);
|
||||
auto char_format = cursor.charFormat ();
|
||||
char_format.setFont (char_font_);
|
||||
cursor.setCharFormat (char_format);
|
||||
}
|
||||
else
|
||||
{
|
||||
cursor.insertBlock (block_format);
|
||||
auto char_format = cursor.charFormat ();
|
||||
char_format.clearBackground ();
|
||||
char_format.clearForeground ();
|
||||
cursor.setCharFormat (char_format);
|
||||
QColor bg;
|
||||
QColor fg;
|
||||
highlight_types types {DecodeHighlightingModel::Highlight::LotW};
|
||||
set_colours (m_config, &bg, &fg, types);
|
||||
if (bg.isValid ()) block_format.setBackground (bg);
|
||||
if (fg.isValid ()) format.setForeground (fg);
|
||||
}
|
||||
|
||||
if (cursor.position ())
|
||||
{
|
||||
cursor.insertBlock (block_format, format);
|
||||
}
|
||||
else
|
||||
{
|
||||
cursor.setBlockFormat (block_format);
|
||||
cursor.setBlockCharFormat (format);
|
||||
}
|
||||
|
||||
QTextCharFormat format = cursor.charFormat();
|
||||
int text_index {0};
|
||||
auto temp_format = format;
|
||||
if (call1.size ())
|
||||
{
|
||||
auto call_index = text.indexOf (call1);
|
||||
@ -159,16 +161,16 @@ void DisplayText::appendText(QString const& text, QColor bg, QColor fg
|
||||
auto pos = highlighted_calls_.find (call1);
|
||||
if (pos != highlighted_calls_.end ())
|
||||
{
|
||||
cursor.insertText(text.left (call_index), format);
|
||||
cursor.insertText(text.left (call_index));
|
||||
if (pos.value ().first.isValid ())
|
||||
{
|
||||
format.setBackground (pos.value ().first);
|
||||
temp_format.setBackground (pos.value ().first);
|
||||
}
|
||||
if (pos.value ().second.isValid ())
|
||||
{
|
||||
format.setForeground (pos.value ().second);
|
||||
temp_format.setForeground (pos.value ().second);
|
||||
}
|
||||
cursor.insertText(text.mid (call_index, call1.size ()), format);
|
||||
cursor.insertText(text.mid (call_index, call1.size ()), temp_format);
|
||||
text_index = call_index + call1.size ();
|
||||
}
|
||||
}
|
||||
@ -181,31 +183,21 @@ void DisplayText::appendText(QString const& text, QColor bg, QColor fg
|
||||
auto pos = highlighted_calls_.find (call2);
|
||||
if (pos != highlighted_calls_.end ())
|
||||
{
|
||||
format.setBackground (bg);
|
||||
format.setForeground (fg);
|
||||
temp_format = format;
|
||||
cursor.insertText(text.mid (text_index, call_index - text_index), format);
|
||||
if (pos.value ().second.isValid ())
|
||||
{
|
||||
format.setBackground (pos.value ().first);
|
||||
temp_format.setBackground (pos.value ().first);
|
||||
}
|
||||
if (pos.value ().second.isValid ())
|
||||
{
|
||||
format.setForeground (pos.value ().second);
|
||||
temp_format.setForeground (pos.value ().second);
|
||||
}
|
||||
cursor.insertText(text.mid (call_index, call2.size ()), format);
|
||||
cursor.insertText(text.mid (call_index, call2.size ()), temp_format);
|
||||
text_index = call_index + call2.size ();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (call2.size () && m_config && m_config->lotw_users ().user (call2))
|
||||
{
|
||||
QColor bg;
|
||||
QColor fg;
|
||||
highlight_types types {DecodeHighlightingModel::Highlight::LotW};
|
||||
set_colours (m_config, &bg, &fg, types);
|
||||
if (bg.isValid ()) format.setBackground (bg);
|
||||
if (fg.isValid ()) format.setForeground (fg);
|
||||
}
|
||||
cursor.insertText(text.mid (text_index), format);
|
||||
|
||||
// position so viewport scrolled to left
|
||||
@ -215,8 +207,9 @@ void DisplayText::appendText(QString const& text, QColor bg, QColor fg
|
||||
document ()->setMaximumBlockCount (document ()->maximumBlockCount ());
|
||||
}
|
||||
|
||||
QString DisplayText::appendWorkedB4(QString message, QString const& callsign, QString grid,
|
||||
QColor * bg, QColor * fg, LogBook const& logBook, QString currentBand)
|
||||
QString DisplayText::appendWorkedB4 (QString message, QString const& callsign, QString const& grid,
|
||||
QColor * bg, QColor * fg, LogBook const& logBook,
|
||||
QString const& currentBand, QString const& currentMode)
|
||||
{
|
||||
// allow for seconds
|
||||
int padding {message.indexOf (" ") > 4 ? 2 : 0};
|
||||
@ -238,8 +231,8 @@ QString DisplayText::appendWorkedB4(QString message, QString const& callsign, QS
|
||||
if(call.length()<3) return message;
|
||||
if(!call.contains(QRegExp("[0-9]|[A-Z]"))) return message;
|
||||
|
||||
logBook.match(/*in*/call,grid,/*out*/countryName,callWorkedBefore,countryWorkedBefore,gridB4);
|
||||
logBook.match(/*in*/call,grid,/*out*/countryName,callB4onBand,countryB4onBand,gridB4onBand,
|
||||
logBook.match(/*in*/call,currentMode,grid,/*out*/countryName,callWorkedBefore,countryWorkedBefore,gridB4);
|
||||
logBook.match(/*in*/call,currentMode,grid,/*out*/countryName,callB4onBand,countryB4onBand,gridB4onBand,
|
||||
/*in*/ currentBand);
|
||||
if(grid=="") {
|
||||
gridB4=true;
|
||||
@ -247,7 +240,7 @@ QString DisplayText::appendWorkedB4(QString message, QString const& callsign, QS
|
||||
}
|
||||
|
||||
message = message.trimmed ();
|
||||
QString appendage{""};
|
||||
QString appendage;
|
||||
|
||||
highlight_types types;
|
||||
// no shortcuts here as some types may be disabled
|
||||
@ -313,8 +306,9 @@ QString DisplayText::appendWorkedB4(QString message, QString const& callsign, QS
|
||||
}
|
||||
|
||||
void DisplayText::displayDecodedText(DecodedText const& decodedText, QString const& myCall,
|
||||
QString const& mode,
|
||||
bool displayDXCCEntity, LogBook const& logBook,
|
||||
QString currentBand, bool ppfx, bool bCQonly)
|
||||
QString const& currentBand, bool ppfx, bool bCQonly)
|
||||
{
|
||||
m_bPrincipalPrefix=ppfx;
|
||||
QColor bg;
|
||||
@ -348,9 +342,17 @@ void DisplayText::displayDecodedText(DecodedText const& decodedText, QString con
|
||||
if(!dxGrid.contains(grid_regexp)) dxGrid="";
|
||||
message = message.left (message.indexOf (QChar::Nbsp)); // strip appended info
|
||||
if (displayDXCCEntity && CQcall)
|
||||
// if enabled add the DXCC entity and B4 status to the end of the
|
||||
// preformated text line t1
|
||||
message = appendWorkedB4 (message, decodedText.CQersCall(), dxGrid, &bg, &fg, logBook, currentBand);
|
||||
{
|
||||
// if enabled add the DXCC entity and B4 status to the end of the
|
||||
// preformated text line t1
|
||||
auto currentMode = mode;
|
||||
if ("JT9+JT65" == mode)
|
||||
{
|
||||
currentMode = decodedText.isJT65 () ? "JT65" : "JT9";
|
||||
}
|
||||
message = appendWorkedB4 (message, decodedText.CQersCall(), dxGrid, &bg, &fg
|
||||
, logBook, currentBand, currentMode);
|
||||
}
|
||||
appendText (message.trimmed (), bg, fg, decodedText.call (), dxCall);
|
||||
}
|
||||
|
||||
|
@ -8,11 +8,11 @@
|
||||
#include <QPair>
|
||||
#include <QString>
|
||||
|
||||
#include "logbook/logbook.h"
|
||||
#include "decodedtext.h"
|
||||
|
||||
class QAction;
|
||||
class Configuration;
|
||||
class LogBook;
|
||||
|
||||
class DisplayText
|
||||
: public QTextEdit
|
||||
@ -23,9 +23,9 @@ public:
|
||||
void set_configuration (Configuration const * configuration) {m_config = configuration;}
|
||||
void setContentFont (QFont const&);
|
||||
void insertLineSpacer(QString const&);
|
||||
void displayDecodedText(DecodedText const& decodedText, QString const& myCall,
|
||||
bool displayDXCCEntity, LogBook const& logBook,
|
||||
QString currentBand="", bool ppfx=false, bool bCQonly=false);
|
||||
void displayDecodedText(DecodedText const& decodedText, QString const& myCall, QString const& mode,
|
||||
bool displayDXCCEntity, LogBook const& logBook,
|
||||
QString const& currentBand=QString {}, bool ppfx=false, bool bCQonly=false);
|
||||
void displayTransmittedText(QString text, QString modeTx, qint32 txFreq, bool bFastMode);
|
||||
void displayQSY(QString text);
|
||||
void displayFoxToBeCalled(QString t, QColor bg = QColor {}, QColor fg = QColor {});
|
||||
@ -45,8 +45,9 @@ private:
|
||||
Configuration const * m_config;
|
||||
bool m_bPrincipalPrefix;
|
||||
QString appendWorkedB4(QString message, QString const& callsign
|
||||
, QString grid, QColor * bg, QColor * fg
|
||||
, LogBook const& logBook, QString currentBand);
|
||||
, QString const& grid, QColor * bg, QColor * fg
|
||||
, LogBook const& logBook, QString const& currentBand
|
||||
, QString const& currentMode);
|
||||
QFont char_font_;
|
||||
QAction * erase_action_;
|
||||
QHash<QString, QPair<QColor, QColor>> highlighted_calls_;
|
||||
|
377
logbook/WorkedBefore.cpp
Normal file
377
logbook/WorkedBefore.cpp
Normal file
@ -0,0 +1,377 @@
|
||||
#include "WorkedBefore.hpp"
|
||||
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <boost/multi_index/ordered_index.hpp>
|
||||
#include <boost/multi_index/key_extractors.hpp>
|
||||
#include <QByteArray>
|
||||
#include <QStandardPaths>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include "countrydat.h"
|
||||
|
||||
#include "pimpl_impl.hpp"
|
||||
|
||||
// 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}
|
||||
{
|
||||
}
|
||||
|
||||
std::string call;
|
||||
std::string grid;
|
||||
std::string band;
|
||||
std::string mode;
|
||||
std::string country;
|
||||
};
|
||||
|
||||
// tags
|
||||
struct call_mode_band {};
|
||||
struct call_band {};
|
||||
struct grid_mode_band {};
|
||||
struct grid_band {};
|
||||
struct entity_mode_band {};
|
||||
struct entity_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<
|
||||
worked_entry,
|
||||
boost::multi_index::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>
|
||||
>
|
||||
>,
|
||||
// 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>
|
||||
>
|
||||
>,
|
||||
// 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>
|
||||
>
|
||||
>,
|
||||
// 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>
|
||||
>
|
||||
>,
|
||||
// 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>
|
||||
>
|
||||
>,
|
||||
// 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>
|
||||
>
|
||||
>
|
||||
>
|
||||
> worked_type;
|
||||
|
||||
namespace
|
||||
{
|
||||
auto const logFileName = "wsjtx_log.adi";
|
||||
|
||||
std::string extractField (QString const& record, QString const& fieldName)
|
||||
{
|
||||
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.toStdString ();
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
class WorkedBefore::impl final
|
||||
{
|
||||
public:
|
||||
impl ()
|
||||
: path_ {QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}.absoluteFilePath (logFileName)}
|
||||
{
|
||||
}
|
||||
|
||||
QString path_;
|
||||
CountryDat countries_;
|
||||
worked_type worked_;
|
||||
};
|
||||
|
||||
WorkedBefore::WorkedBefore ()
|
||||
{
|
||||
QFile inputFile {m_->path_};
|
||||
if (inputFile.open (QFile::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 {'<'}));
|
||||
auto call = extractField (record, "CALL");
|
||||
if (call.size ())
|
||||
{
|
||||
m_->worked_.emplace (call
|
||||
, extractField (record, "GRIDSQUARE").substr (0, 4) // not interested in 6-digit grids
|
||||
, extractField (record, "BAND")
|
||||
, extractField (record, "MODE")
|
||||
, m_->countries_.find (QString::fromStdString (call)).toStdString ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WorkedBefore::~WorkedBefore ()
|
||||
{
|
||||
}
|
||||
|
||||
QString const& WorkedBefore::path () const
|
||||
{
|
||||
return m_->path_;
|
||||
}
|
||||
|
||||
CountryDat const& WorkedBefore::countries () const
|
||||
{
|
||||
return m_->countries_;
|
||||
}
|
||||
|
||||
bool WorkedBefore::add (QString const& call
|
||||
, QString const& grid
|
||||
, QString const& band
|
||||
, QString const& mode
|
||||
, QByteArray const& ADIF_record)
|
||||
{
|
||||
if (call.size ())
|
||||
{
|
||||
QFile file {m_->path_};
|
||||
if (!file.open(QIODevice::Text | QIODevice::Append))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
QTextStream out {&file};
|
||||
if (!file.size ())
|
||||
{
|
||||
out << "WSJT-X ADIF Export<eoh>" << endl; // new file
|
||||
}
|
||||
out << ADIF_record << " <eor>" << endl;
|
||||
}
|
||||
m_->worked_.emplace (call.toStdString ()
|
||||
, grid.toStdString ()
|
||||
, band.toStdString ()
|
||||
, mode.toStdString ()
|
||||
, m_->countries_.find (call).toStdString ());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WorkedBefore::country_worked (QString const& country, QString const& mode, QString const& band) const
|
||||
{
|
||||
if (mode.size ())
|
||||
{
|
||||
if (band.size ())
|
||||
{
|
||||
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 ()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// partial key lookup
|
||||
return
|
||||
country.size ()
|
||||
&& m_->worked_.get<entity_mode_band> ().end ()
|
||||
!= m_->worked_.get<entity_mode_band> ().find (std::make_tuple (country.toStdString ()
|
||||
, mode.toStdString ()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (band.size ())
|
||||
{
|
||||
return
|
||||
country.size ()
|
||||
&& m_->worked_.get<entity_band> ().end ()
|
||||
!= m_->worked_.get<entity_band> ().find (std::make_tuple (country.toStdString ()
|
||||
, band.toStdString ()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// partial key lookup
|
||||
return
|
||||
country.size ()
|
||||
&& m_->worked_.get<entity_band> ().end ()
|
||||
!= m_->worked_.get<entity_band> ().find (std::make_tuple (country.toStdString ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WorkedBefore::grid_worked (QString const& grid, QString const& mode, QString const& band) const
|
||||
{
|
||||
if (mode.size ())
|
||||
{
|
||||
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 ()));
|
||||
}
|
||||
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 ()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (band.size ())
|
||||
{
|
||||
return m_->worked_.get<grid_band> ().end ()
|
||||
!= m_->worked_.get<grid_band> ().find (std::make_tuple (grid.toStdString ()
|
||||
, band.toStdString ()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// partial key lookup
|
||||
return m_->worked_.get<grid_band> ().end ()
|
||||
!= m_->worked_.get<grid_band> ().find (std::make_tuple (grid.toStdString ()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WorkedBefore::call_worked (QString const& call, QString const& mode, QString const& band) const
|
||||
{
|
||||
if (mode.size ())
|
||||
{
|
||||
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 ()));
|
||||
}
|
||||
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 ()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (band.size ())
|
||||
{
|
||||
return m_->worked_.get<call_band> ().end ()
|
||||
!= m_->worked_.get<call_band> ().find (std::make_tuple (call.toStdString ()
|
||||
, band.toStdString ()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// partial key lookup
|
||||
return m_->worked_.get<call_band> ().end ()
|
||||
!= m_->worked_.get<call_band> ().find (std::make_tuple (call.toStdString ()));
|
||||
}
|
||||
}
|
||||
}
|
34
logbook/WorkedBefore.hpp
Normal file
34
logbook/WorkedBefore.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef WORKWED_BEFORE_HPP_
|
||||
#define WORKWED_BEFORE_HPP_
|
||||
|
||||
#include <boost/core/noncopyable.hpp>
|
||||
#include "pimpl_h.hpp"
|
||||
|
||||
class CountryDat;
|
||||
class QString;
|
||||
class QByteArray;
|
||||
|
||||
class WorkedBefore final
|
||||
: private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
explicit WorkedBefore ();
|
||||
~WorkedBefore ();
|
||||
|
||||
QString const& path () const;
|
||||
CountryDat const& countries () const;
|
||||
bool add (QString const& call
|
||||
, QString const& grid
|
||||
, QString const& band
|
||||
, QString const& mode
|
||||
, QByteArray const& ADIF_record);
|
||||
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;
|
||||
|
||||
private:
|
||||
class impl;
|
||||
pimpl<impl> m_;
|
||||
};
|
||||
|
||||
#endif
|
236
logbook/adif.cpp
236
logbook/adif.cpp
@ -1,236 +0,0 @@
|
||||
#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();
|
||||
_data2.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();
|
||||
_data2.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, "GRIDSQUARE")
|
||||
, extractField (record, "BAND")
|
||||
, extractField (record, "MODE")
|
||||
, extractField (record, "QSO_DATE"));
|
||||
}
|
||||
inputFile.close ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ADIF::add(QString const& call, QString const& grid, QString const& band,
|
||||
QString const& mode, QString const& date)
|
||||
{
|
||||
QSO q;
|
||||
q.call = call;
|
||||
q.grid = grid.left(4); //We only want to test matches to 4-character grids.
|
||||
q.band = band;
|
||||
q.mode = mode;
|
||||
q.date = date;
|
||||
if(q.call.size ()) {
|
||||
_data.insert(q.call,q);
|
||||
_data2.insert(q.grid,q);
|
||||
// qDebug() << "In the log:" << call << grid << band << mode << date;
|
||||
}
|
||||
}
|
||||
|
||||
// return true if in the log same band and mode (where JT65 == JT9 == FT8)
|
||||
bool ADIF::match(QString const& call, QString const& band, QString const& mode) const
|
||||
{
|
||||
QList<QSO> qsos;
|
||||
QRegularExpression grid_regexp {"\\A(?![Rr]{2}73)[A-Ra-r]{2}[0-9]{2}([A-Xa-x]{2}){0,1}\\z"};
|
||||
if(!call.contains(grid_regexp)) {
|
||||
qsos = _data.values(call);
|
||||
} else {
|
||||
qsos = _data2.values(call);
|
||||
}
|
||||
// qDebug() << "AA" << call << qsos.size();
|
||||
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 const& xSent, QString const& xRcvd)
|
||||
{
|
||||
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;
|
||||
if(xRcvd!="") {
|
||||
QString t1="";
|
||||
if(xRcvd.split(" ").size()==2) t1=xRcvd.split(" ").at(1);
|
||||
if(t1.toInt()>0) {
|
||||
t += " <SRX:" + QString::number(t1.length()) + ">" + t1;
|
||||
} else {
|
||||
t += " <STATE:" + QString::number(t1.length()) + ">" + t1;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/*
|
||||
* 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>
|
||||
#include <QRegularExpression>
|
||||
#else
|
||||
#include <QtGui>
|
||||
#endif
|
||||
|
||||
class QDateTime;
|
||||
|
||||
class ADIF
|
||||
{
|
||||
public:
|
||||
void init(QString const& filename);
|
||||
void load();
|
||||
void add(QString const& call, const QString &grid, 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(QByteArray const& ADIF_record);
|
||||
|
||||
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,
|
||||
QString const& name, QString const& strDialFreq, QString const& m_myCall,
|
||||
QString const& m_myGrid, QString const& m_txPower, QString const& operator_call,
|
||||
QString const& xSent, QString const& xRcvd);
|
||||
|
||||
private:
|
||||
struct QSO
|
||||
{
|
||||
QString call,grid,band,mode,date;
|
||||
};
|
||||
|
||||
QMultiHash<QString, QSO> _data;
|
||||
QMultiHash<QString, QSO> _data2;
|
||||
QString _filename;
|
||||
QString extractField(QString const& line, QString const& fieldName) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,42 +1,24 @@
|
||||
#include "countriesworked.h"
|
||||
|
||||
void CountriesWorked::init(const QStringList countryNames)
|
||||
{
|
||||
_data.clear();
|
||||
foreach(QString name,countryNames)
|
||||
_data.insert(name,false);
|
||||
}
|
||||
#include <set>
|
||||
|
||||
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);
|
||||
#include "pimpl_impl.hpp"
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int CountriesWorked::getWorkedCount() const
|
||||
class CountriesWorkedimpl final
|
||||
{
|
||||
int count = 0;
|
||||
foreach (bool value,_data)
|
||||
if (value)
|
||||
count += 1;
|
||||
return count;
|
||||
}
|
||||
|
||||
int CountriesWorked::getSize() const
|
||||
public:
|
||||
// element is country-name+band where the '+' is actual a character
|
||||
// which allows an efficient lower_bound() search for country-name+
|
||||
// to check for ATNOs
|
||||
std::set<QString> worked_;
|
||||
};
|
||||
|
||||
void CountriesWorked::add (QString const& country, QString const& band)
|
||||
{
|
||||
return _data.count();
|
||||
m_->worked_.insert (country + '+' + band);
|
||||
}
|
||||
|
||||
bool CountriesWorked::contains (QString const& country, QString const& band) const
|
||||
{
|
||||
return m_->worked_.end () != m_->worked_.lower_bound (country + '+' + band);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,29 +1,24 @@
|
||||
/*
|
||||
* maintains a list of country names that have been worked
|
||||
* VK3ACF July 2013
|
||||
*/
|
||||
#ifndef COUNTRIES_WORKDED_H_
|
||||
#define COUNTRIES_WORKDED_H_
|
||||
|
||||
#ifndef __COUNTRIESWORKDED_H
|
||||
#define __COUNTRIESWORKDED_H
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QHash>
|
||||
|
||||
#include "pimpl_h.hpp"
|
||||
|
||||
class CountriesWorked
|
||||
class QStringList;
|
||||
|
||||
class CountriesWorked final
|
||||
{
|
||||
public:
|
||||
void init(const QStringList countryNames);
|
||||
void setAsWorked(const QString countryName);
|
||||
bool getHasWorked(const QString countryName) const;
|
||||
int getWorkedCount() const;
|
||||
int getSize() const;
|
||||
explicit CountriesWorked (QStringList const& countryNames);
|
||||
~CountriesWorked ();
|
||||
|
||||
void add (QString const& country, QString const& band);
|
||||
bool contains (QString const& country, QString const& band = QString {}) const;
|
||||
|
||||
private:
|
||||
QHash<QString, bool> _data;
|
||||
class impl;
|
||||
pimpl<impl> m_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -16,18 +16,61 @@
|
||||
|
||||
|
||||
#include "countrydat.h"
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QDebug>
|
||||
#include "Radio.hpp"
|
||||
|
||||
void CountryDat::init(const QString filename)
|
||||
namespace
|
||||
{
|
||||
_filename = filename;
|
||||
_data.clear();
|
||||
auto countryFileName = "cty.dat";
|
||||
}
|
||||
|
||||
QString CountryDat::_extractName(const QString line) const
|
||||
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)
|
||||
@ -38,7 +81,7 @@ QString CountryDat::_extractName(const QString line) const
|
||||
return "";
|
||||
}
|
||||
|
||||
void CountryDat::_removeBrackets(QString &line, const QString a, const QString b) const
|
||||
void CountryDat::removeBrackets(QString& line, QString const& a, QString const& b) const
|
||||
{
|
||||
int s1 = line.indexOf(a);
|
||||
while (s1 >= 0)
|
||||
@ -49,15 +92,15 @@ void CountryDat::_removeBrackets(QString &line, const QString a, const QString b
|
||||
}
|
||||
}
|
||||
|
||||
QStringList CountryDat::_extractPrefix(QString &line, bool &more) const
|
||||
QStringList CountryDat::extractPrefix(QString& line, bool& more) const
|
||||
{
|
||||
line = line.remove(" \n");
|
||||
line = line.replace(" ","");
|
||||
|
||||
_removeBrackets(line,"(",")");
|
||||
_removeBrackets(line,"[","]");
|
||||
_removeBrackets(line,"<",">");
|
||||
_removeBrackets(line,"~","~");
|
||||
removeBrackets(line,"(",")");
|
||||
removeBrackets(line,"[","]");
|
||||
removeBrackets(line,"<",">");
|
||||
removeBrackets(line,"~","~");
|
||||
|
||||
int s1 = line.indexOf(';');
|
||||
more = true;
|
||||
@ -72,73 +115,24 @@ QStringList CountryDat::_extractPrefix(QString &line, bool &more) const
|
||||
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)
|
||||
{
|
||||
QString continent=line1.mid(36,2);
|
||||
QString principalPrefix=line1.mid(69,4);
|
||||
int i1=principalPrefix.indexOf(":");
|
||||
if(i1>0) principalPrefix=principalPrefix.mid(0,i1);
|
||||
name += "; " + principalPrefix + "; " + continent;
|
||||
_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
|
||||
QString CountryDat::find (QString const& call) const
|
||||
{
|
||||
call = call.toUpper ();
|
||||
auto const& search_string = call.toUpper ();
|
||||
|
||||
// check for exact match first
|
||||
if (_data.contains ("=" + call))
|
||||
if (data_.contains ("=" + search_string))
|
||||
{
|
||||
return fixup (_data.value ("=" + call), call);
|
||||
return fixup (data_.value ("=" + search_string), search_string);
|
||||
}
|
||||
|
||||
auto prefix = Radio::effective_prefix (call);
|
||||
auto prefix = Radio::effective_prefix (search_string);
|
||||
auto match_candidate = prefix;
|
||||
while (match_candidate.size () >= 1)
|
||||
{
|
||||
if (_data.contains (match_candidate))
|
||||
if (data_.contains (match_candidate))
|
||||
{
|
||||
return fixup (_data.value (match_candidate), prefix);
|
||||
return fixup (data_.value (match_candidate), prefix);
|
||||
}
|
||||
match_candidate = match_candidate.left (match_candidate.size () - 1);
|
||||
}
|
||||
|
@ -4,33 +4,27 @@
|
||||
* VK3ACF July 2013
|
||||
*/
|
||||
|
||||
#ifndef COUNTRY_DAT_H_
|
||||
#define COUNTRY_DAT_H_
|
||||
|
||||
#ifndef __COUNTRYDAT_H
|
||||
#define __COUNTRYDAT_H
|
||||
|
||||
|
||||
#include <boost/core/noncopyable.hpp>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QHash>
|
||||
|
||||
|
||||
class CountryDat
|
||||
class CountryDat final
|
||||
: private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
void init(const QString filename);
|
||||
void load();
|
||||
QString find(QString prefix) const; // return country name or ""
|
||||
QStringList getCountryNames() const { return _countryNames; };
|
||||
public:
|
||||
CountryDat ();
|
||||
QString find (QString const& call) const; // return country name or ""
|
||||
|
||||
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 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;
|
||||
|
||||
QString _filename;
|
||||
QStringList _countryNames;
|
||||
QHash<QString, QString> _data;
|
||||
QHash<QString, QString> data_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,105 +1,74 @@
|
||||
#include "logbook.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QFontMetrics>
|
||||
#include <QStandardPaths>
|
||||
#include <QDir>
|
||||
#include "countrydat.h"
|
||||
#include "Configuration.hpp"
|
||||
|
||||
namespace
|
||||
LogBook::LogBook (Configuration const * configuration)
|
||||
: config_ {configuration}
|
||||
{
|
||||
auto logFileName = "wsjtx_log.adi";
|
||||
auto countryFileName = "cty.dat";
|
||||
}
|
||||
|
||||
void LogBook::init()
|
||||
void LogBook::match (QString const& call, QString const& mode, QString const& grid,
|
||||
QString &countryName,
|
||||
bool &callWorkedBefore,
|
||||
bool &countryWorkedBefore,
|
||||
bool &gridWorkedBefore,
|
||||
QString const& band) const
|
||||
{
|
||||
QDir dataPath {QStandardPaths::writableLocation (QStandardPaths::DataLocation)};
|
||||
QString countryDataFilename;
|
||||
if (dataPath.exists (countryFileName))
|
||||
if (call.length() > 0)
|
||||
{
|
||||
// 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,grid, /*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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void LogBook::match(/*in*/const QString call,QString grid,
|
||||
/*out*/ QString &countryName,
|
||||
bool &callWorkedBefore,
|
||||
bool &countryWorkedBefore,
|
||||
bool &gridWorkedBefore,
|
||||
QString currentBand) const
|
||||
bool LogBook::add (QString const& call
|
||||
, QString const& grid
|
||||
, QString const& band
|
||||
, QString const& mode
|
||||
, QByteArray const& ADIF_record)
|
||||
{
|
||||
// if(currentBand=="") qDebug() << "aa" << grid;
|
||||
// if(currentBand!="") qDebug() << "bb" << grid << currentBand;
|
||||
return worked_before_.add (call, grid, band, mode, ADIF_record);
|
||||
}
|
||||
|
||||
if (call.length() > 0) {
|
||||
QString currentMode = "JT9"; // JT65 == JT9 == FT8 in ADIF::match()
|
||||
// QString currentBand = ""; // match any band
|
||||
callWorkedBefore = _log.match(call,currentBand,currentMode);
|
||||
gridWorkedBefore = _log.match(grid,currentBand,currentMode);
|
||||
countryName = _countries.find(call);
|
||||
|
||||
if (countryName.length() > 0) { // country was found
|
||||
countryWorkedBefore = _worked.getHasWorked(countryName);
|
||||
QByteArray LogBook::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& myCall,
|
||||
QString const& myGrid, QString const& txPower, QString const& operator_call,
|
||||
QString const& xSent, QString const& xRcvd)
|
||||
{
|
||||
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(myCall.length()) + ">" + myCall;
|
||||
t += " <my_gridsquare:" + QString::number(myGrid.length()) + ">" + myGrid;
|
||||
if(txPower!="") t += " <tx_pwr:" + QString::number(txPower.length()) + ">" + 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;
|
||||
if(xRcvd!="") {
|
||||
QString t1="";
|
||||
if(xRcvd.split(" ").size()==2) t1=xRcvd.split(" ").at(1);
|
||||
if(t1.toInt()>0) {
|
||||
t += " <SRX:" + QString::number(t1.length()) + ">" + t1;
|
||||
} else {
|
||||
countryName = "where?"; //error: prefix not found
|
||||
countryWorkedBefore = false;
|
||||
t += " <STATE:" + QString::number(t1.length()) + ">" + t1;
|
||||
}
|
||||
// qDebug() << "Logbook:" << call << currentBand << callWorkedBefore << countryName << countryWorkedBefore;
|
||||
}
|
||||
return t.toLatin1();
|
||||
}
|
||||
|
||||
void LogBook::addAsWorked(const QString call, const QString grid, const QString band,
|
||||
const QString mode, const QString date)
|
||||
{
|
||||
//qDebug() << "adding " << call << " as worked";
|
||||
_log.add(call,grid,band,mode,date);
|
||||
QString countryName = _countries.find(call);
|
||||
if (countryName.length() > 0)
|
||||
_worked.setAsWorked(countryName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -3,37 +3,43 @@
|
||||
* VK3ACF July 2013
|
||||
*/
|
||||
|
||||
#ifndef LOGBOOK_H
|
||||
#define LOGBOOK_H
|
||||
|
||||
#ifndef LOG_BOOK_H_
|
||||
#define LOG_BOOK_H_
|
||||
|
||||
#include <boost/core/noncopyable.hpp>
|
||||
#include <QString>
|
||||
#include <QFont>
|
||||
|
||||
#include "countrydat.h"
|
||||
#include "countriesworked.h"
|
||||
#include "adif.h"
|
||||
#include "WorkedBefore.hpp"
|
||||
|
||||
class QDir;
|
||||
class Configuration;
|
||||
class QByteArray;
|
||||
class QDateTime;
|
||||
|
||||
class LogBook
|
||||
class LogBook final
|
||||
: private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
void init();
|
||||
void match(/*in*/ const QString call, QString grid,
|
||||
/*out*/ QString &countryName, bool &callWorkedBefore, bool &countryWorkedBefore,
|
||||
bool &gridWorkedBefore, QString currentBand="") const;
|
||||
void addAsWorked(const QString call, const QString grid, const QString band,
|
||||
const QString mode, const QString date);
|
||||
|
||||
private:
|
||||
CountryDat _countries;
|
||||
CountriesWorked _worked;
|
||||
ADIF _log;
|
||||
|
||||
void _setAlreadyWorkedFromLog();
|
||||
public:
|
||||
LogBook (Configuration const *);
|
||||
QString const& path () const {return worked_before_.path ();}
|
||||
bool add (QString const& call
|
||||
, QString const& grid
|
||||
, QString const& band
|
||||
, QString const& mode
|
||||
, QByteArray const& ADIF_record);
|
||||
CountryDat 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;
|
||||
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,
|
||||
QString const& name, QString const& strDialFreq, QString const& myCall,
|
||||
QString const& m_myGrid, QString const& m_txPower, QString const& operator_call,
|
||||
QString const& xSent, QString const& xRcvd);
|
||||
|
||||
private:
|
||||
Configuration const * config_;
|
||||
WorkedBefore worked_before_;
|
||||
};
|
||||
|
||||
#endif // LOGBOOK_H
|
||||
|
||||
#endif
|
||||
|
64
logqso.cpp
64
logqso.cpp
@ -5,9 +5,8 @@
|
||||
#include <QStandardPaths>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <QUdpSocket>
|
||||
|
||||
#include "logbook/adif.h"
|
||||
#include "logbook/logbook.h"
|
||||
#include "MessageBox.hpp"
|
||||
#include "Configuration.hpp"
|
||||
#include "Bands.hpp"
|
||||
@ -120,33 +119,8 @@ void LogQSO::accept()
|
||||
m_comments=comments;
|
||||
QString strDialFreq(QString::number(m_dialFreq / 1.e6,'f',6));
|
||||
operator_call = ui->loggedOperator->text();
|
||||
//Log this QSO to ADIF file "wsjtx_log.adi"
|
||||
QString filename = "wsjtx_log.adi"; // TODO allow user to set
|
||||
ADIF adifile;
|
||||
auto adifilePath = QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}.absoluteFilePath ("wsjtx_log.adi");
|
||||
adifile.init(adifilePath);
|
||||
|
||||
QByteArray ADIF {adifile.QSOToADIF (hisCall, hisGrid, mode, rptSent, rptRcvd,
|
||||
m_dateTimeOn, m_dateTimeOff, band, comments, name, strDialFreq, m_myCall,
|
||||
m_myGrid, m_txPower, operator_call, m_xSent, m_xRcvd)};
|
||||
if (!adifile.addQSOToFile (ADIF))
|
||||
{
|
||||
MessageBox::warning_message (this, tr ("Log file error"),
|
||||
tr ("Cannot open \"%1\"").arg (adifilePath));
|
||||
}
|
||||
|
||||
// Log to N1MM Logger
|
||||
if (m_config->broadcast_to_n1mm() && m_config->valid_n1mm_info()) {
|
||||
const QHostAddress n1mmhost = QHostAddress(m_config->n1mm_server_name());
|
||||
QUdpSocket _sock;
|
||||
auto rzult = _sock.writeDatagram (ADIF + " <eor>", n1mmhost, quint16(m_config->n1mm_server_port()));
|
||||
if (rzult == -1) {
|
||||
MessageBox::warning_message (this, tr ("Error sending log to N1MM"),
|
||||
tr ("Write returned \"%1\"").arg (rzult));
|
||||
}
|
||||
}
|
||||
|
||||
//Log this QSO to file "wsjtx.log"
|
||||
//Log this QSO to file "wsjtx.log"
|
||||
static QFile f {QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}.absoluteFilePath ("wsjtx.log")};
|
||||
if(!f.open(QIODevice::Text | QIODevice::Append)) {
|
||||
MessageBox::warning_message (this, tr ("Log file error"),
|
||||
@ -165,8 +139,38 @@ void LogQSO::accept()
|
||||
f.close();
|
||||
}
|
||||
|
||||
//Clean up and finish logging
|
||||
Q_EMIT acceptQSO (m_dateTimeOff, hisCall, hisGrid, m_dialFreq, mode, rptSent, rptRcvd, m_txPower, comments, name,m_dateTimeOn, operator_call, m_myCall, m_myGrid, ADIF);
|
||||
//Clean up and finish logging
|
||||
Q_EMIT acceptQSO (m_dateTimeOff
|
||||
, hisCall
|
||||
, hisGrid
|
||||
, m_dialFreq
|
||||
, mode
|
||||
, rptSent
|
||||
, rptRcvd
|
||||
, m_txPower
|
||||
, comments
|
||||
, name
|
||||
, m_dateTimeOn
|
||||
, operator_call
|
||||
, m_myCall
|
||||
, m_myGrid
|
||||
, LogBook::QSOToADIF (hisCall
|
||||
, hisGrid
|
||||
, mode
|
||||
, rptSent
|
||||
, rptRcvd
|
||||
, m_dateTimeOn
|
||||
, m_dateTimeOff
|
||||
, band
|
||||
, comments
|
||||
, name
|
||||
, strDialFreq
|
||||
, m_myCall
|
||||
, m_myGrid
|
||||
, m_txPower
|
||||
, operator_call
|
||||
, m_xSent
|
||||
, m_xRcvd));
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
|
6
logqso.h
6
logqso.h
@ -2,11 +2,7 @@
|
||||
#ifndef LogQSO_H
|
||||
#define LogQSO_H
|
||||
|
||||
#ifdef QT5
|
||||
#include <QtWidgets>
|
||||
#else
|
||||
#include <QtGui>
|
||||
#endif
|
||||
#include <QDialog>
|
||||
|
||||
#include <QString>
|
||||
#include <QScopedPointer>
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <QAction>
|
||||
#include <QActionGroup>
|
||||
#include <QSplashScreen>
|
||||
#include <QUdpSocket>
|
||||
|
||||
#include "revision_utils.hpp"
|
||||
#include "qt_helpers.hpp"
|
||||
@ -61,6 +62,7 @@
|
||||
#include "ExchangeValidator.hpp"
|
||||
#include "EqualizationToolsDialog.hpp"
|
||||
#include "LotWUsers.hpp"
|
||||
#include "logbook/countrydat.h"
|
||||
|
||||
#include "ui_mainwindow.h"
|
||||
#include "moc_mainwindow.cpp"
|
||||
@ -353,6 +355,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
||||
},
|
||||
m_sfx {"P", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A"},
|
||||
mem_jt9 {shdmem},
|
||||
m_logBook {&m_config},
|
||||
m_msAudioOutputBuffered (0u),
|
||||
m_framesAudioInputBuffered (RX_SAMPLE_RATE / 10),
|
||||
m_downSampleFactor (downSampleFactor),
|
||||
@ -1296,7 +1299,7 @@ void MainWindow::dataSink(qint64 frames)
|
||||
freqcal_(&dec_data.d2[0],&k,&nkhz,&RxFreq,&ftol,&line[0],80);
|
||||
QString t=QString::fromLatin1(line);
|
||||
DecodedText decodedtext {t};
|
||||
ui->decodedTextBrowser->displayDecodedText (decodedtext,m_baseCall,m_config.DXCC(),
|
||||
ui->decodedTextBrowser->displayDecodedText (decodedtext,m_baseCall,m_mode,m_config.DXCC(),
|
||||
m_logBook,m_currentBand, m_config.ppfx());
|
||||
if (ui->measure_check_box->isChecked ()) {
|
||||
// Append results text to file "fmt.all".
|
||||
@ -1538,7 +1541,7 @@ void MainWindow::fastSink(qint64 frames)
|
||||
if(bmsk144 and (line[0]!=0)) {
|
||||
QString message {QString::fromLatin1 (line)};
|
||||
DecodedText decodedtext {message.replace (QChar::LineFeed, "")};
|
||||
ui->decodedTextBrowser->displayDecodedText (decodedtext,m_baseCall,m_config.DXCC(),
|
||||
ui->decodedTextBrowser->displayDecodedText (decodedtext,m_baseCall,m_mode,m_config.DXCC(),
|
||||
m_logBook,m_currentBand,m_config.ppfx());
|
||||
m_bDecoded=true;
|
||||
auto_sequence (decodedtext, ui->sbFtol->value (), std::numeric_limits<unsigned>::max ());
|
||||
@ -2821,7 +2824,7 @@ void::MainWindow::fast_decode_done()
|
||||
//Left (Band activity) window
|
||||
DecodedText decodedtext {message.replace (QChar::LineFeed, "")};
|
||||
if(!m_bFastDone) {
|
||||
ui->decodedTextBrowser->displayDecodedText (decodedtext,m_baseCall,m_config.DXCC(),
|
||||
ui->decodedTextBrowser->displayDecodedText (decodedtext,m_baseCall,m_mode,m_config.DXCC(),
|
||||
m_logBook,m_currentBand,m_config.ppfx());
|
||||
}
|
||||
|
||||
@ -2968,12 +2971,12 @@ void MainWindow::readFromStdout() //readFromStdout
|
||||
if(!m_bDisplayedOnce) {
|
||||
// This hack sets the font. Surely there's a better way!
|
||||
DecodedText dt{"."};
|
||||
ui->decodedTextBrowser->displayDecodedText(dt,m_baseCall,m_config.DXCC(),
|
||||
ui->decodedTextBrowser->displayDecodedText(dt,m_baseCall,m_mode,m_config.DXCC(),
|
||||
m_logBook,m_currentBand,m_config.ppfx());
|
||||
m_bDisplayedOnce=true;
|
||||
}
|
||||
} else {
|
||||
ui->decodedTextBrowser->displayDecodedText(decodedtext0,m_baseCall,m_config.DXCC(),
|
||||
ui->decodedTextBrowser->displayDecodedText(decodedtext0,m_baseCall,m_mode,m_config.DXCC(),
|
||||
m_logBook,m_currentBand,m_config.ppfx(),
|
||||
(ui->cbCQonly->isVisible() and ui->cbCQonly->isChecked()));
|
||||
}
|
||||
@ -3008,7 +3011,7 @@ void MainWindow::readFromStdout() //readFromStdout
|
||||
if (bDisplayRight) {
|
||||
// This msg is within 10 hertz of our tuned frequency, or a JT4 or JT65 avg,
|
||||
// or contains MyCall
|
||||
ui->decodedTextBrowser2->displayDecodedText(decodedtext0,m_baseCall,m_config.DXCC(),
|
||||
ui->decodedTextBrowser2->displayDecodedText(decodedtext0,m_baseCall,m_mode,m_config.DXCC(),
|
||||
m_logBook,m_currentBand,m_config.ppfx());
|
||||
|
||||
if(m_mode!="JT4") {
|
||||
@ -4536,7 +4539,7 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie
|
||||
QString s2 = message.string ().trimmed();
|
||||
if (s1!=s2 and !message.isTX()) {
|
||||
if (!s2.contains(m_baseCall) or m_mode=="MSK144") { // Taken care of elsewhere if for_us and slow mode
|
||||
ui->decodedTextBrowser2->displayDecodedText(message, m_baseCall,m_config.DXCC(),
|
||||
ui->decodedTextBrowser2->displayDecodedText(message, m_baseCall,m_mode,m_config.DXCC(),
|
||||
m_logBook,m_currentBand,m_config.ppfx());
|
||||
}
|
||||
m_QSOText = s2;
|
||||
@ -5294,11 +5297,30 @@ void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call,
|
||||
, QString const& my_call, QString const& my_grid, QByteArray const& ADIF)
|
||||
{
|
||||
QString date = QSO_date_on.toString("yyyyMMdd");
|
||||
m_logBook.addAsWorked (m_hisCall,grid,m_config.bands()->find(m_freqNominal),m_modeTx,date);
|
||||
if (!m_logBook.add (m_hisCall, grid, m_config.bands()->find(m_freqNominal), m_modeTx, ADIF))
|
||||
{
|
||||
MessageBox::warning_message (this, tr ("Log file error"),
|
||||
tr ("Cannot open \"%1\"").arg (m_logBook.path ()));
|
||||
}
|
||||
|
||||
m_messageClient->qso_logged (QSO_date_off, call, grid, dial_freq, mode, rpt_sent, rpt_received, tx_power, comments, name, QSO_date_on, operator_call, my_call, my_grid);
|
||||
m_messageClient->qso_logged (QSO_date_off, call, grid, dial_freq, mode, rpt_sent, rpt_received
|
||||
, tx_power, comments, name, QSO_date_on, operator_call, my_call, my_grid);
|
||||
m_messageClient->logged_ADIF (ADIF);
|
||||
if (m_config.clear_DX () and !m_config.bHound()) clearDX ();
|
||||
|
||||
// Log to N1MM Logger
|
||||
if (m_config.broadcast_to_n1mm () && m_config.valid_n1mm_info ())
|
||||
{
|
||||
QUdpSocket sock;
|
||||
if (-1 == sock.writeDatagram (ADIF + " <eor>"
|
||||
, QHostAddress {m_config.n1mm_server_name ()}
|
||||
, m_config.n1mm_server_port ()))
|
||||
{
|
||||
MessageBox::warning_message (this, tr ("Error sending log to N1MM"),
|
||||
tr ("Write returned \"%1\"").arg (sock.errorString ()));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_config.clear_DX () and !m_config.bHound ()) clearDX ();
|
||||
m_dateTimeQSOOn = QDateTime {};
|
||||
}
|
||||
|
||||
@ -6154,7 +6176,7 @@ void MainWindow::vhfWarning()
|
||||
void MainWindow::enable_DXCC_entity (bool on)
|
||||
{
|
||||
if (on and !m_mode.startsWith ("WSPR") and m_mode!="Echo") {
|
||||
m_logBook.init(); // re-read the log and cty.dat files
|
||||
//m_logBook.init(); // re-read the log and cty.dat files
|
||||
// ui->gridLayout->setColumnStretch(0,55); // adjust proportions of text displays
|
||||
// ui->gridLayout->setColumnStretch(1,45);
|
||||
} else {
|
||||
@ -7915,12 +7937,8 @@ 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
|
||||
QString countryName,continent;
|
||||
bool callWorkedBefore,countryWorkedBefore,gridWorkedBefore;
|
||||
m_logBook.match(/*in*/houndCall,"",/*out*/countryName,callWorkedBefore,countryWorkedBefore,
|
||||
gridWorkedBefore,/*in*/ m_currentBand);
|
||||
int i1=countryName.lastIndexOf(";");
|
||||
continent=countryName.mid(i1+2,-1);
|
||||
auto const& countryName = m_logBook.countries ().find (houndCall);
|
||||
auto const& continent = countryName.mid (countryName.lastIndexOf (';') + 2, -1);
|
||||
|
||||
//If we are using a directed CQ, ignore Hound calls that do not comply.
|
||||
QString CQtext=ui->comboBoxCQ->currentText();
|
||||
|
Loading…
Reference in New Issue
Block a user