Compare commits

...

11 Commits

9 changed files with 273 additions and 97 deletions
+1 -1
View File
@@ -2,5 +2,5 @@
set (WSJTX_VERSION_MAJOR 1) set (WSJTX_VERSION_MAJOR 1)
set (WSJTX_VERSION_MINOR 0) set (WSJTX_VERSION_MINOR 0)
set (WSJTX_VERSION_PATCH 0) set (WSJTX_VERSION_PATCH 0)
set (WSJTX_RC 1) # release candidate number, comment out or zero for development versions set (WSJTX_RC 2) # release candidate number, comment out or zero for development versions
set (WSJTX_VERSION_IS_RELEASE 0) # set to 1 for final release build set (WSJTX_VERSION_IS_RELEASE 0) # set to 1 for final release build
+15 -6
View File
@@ -101,14 +101,17 @@ void ADIF::load()
, extractField (record, "BAND") , extractField (record, "BAND")
, extractField (record, "MODE") , extractField (record, "MODE")
, extractField (record, "SUBMODE") , extractField (record, "SUBMODE")
, extractField (record, "QSO_DATE")); , extractField (record, "QSO_DATE")
, extractField (record, "NAME")
, extractField (record, "COMMENT")
);
} }
inputFile.close (); inputFile.close ();
} }
} }
void ADIF::add(QString const& call, QString const& band, QString const& mode, QString const& submode, QString const& date) void ADIF::add(QString const& call, QString const& band, QString const& mode, QString const& submode, QString const& date, QString const& name, QString const& comment)
{ {
QSO q; QSO q;
q.call = call; q.call = call;
@@ -116,6 +119,9 @@ void ADIF::add(QString const& call, QString const& band, QString const& mode, QS
q.mode = mode; q.mode = mode;
q.submode = submode; q.submode = submode;
q.date = date; q.date = date;
q.name = name;
q.comment = comment;
if (q.call.size ()) if (q.call.size ())
{ {
_data.insert(q.call,q); _data.insert(q.call,q);
@@ -135,13 +141,18 @@ bool ADIF::match(QString const& call, QString const& band) const
if ( (band.compare(q.band,Qt::CaseInsensitive) == 0) if ( (band.compare(q.band,Qt::CaseInsensitive) == 0)
|| (band=="") || (band=="")
|| (q.band=="")) || (q.band==""))
{ {
return true; return true;
} }
} }
} }
return false; return false;
} }
QList<ADIF::QSO> ADIF::find(QString const& call) const
{
return _data.values(call);
}
QList<QString> ADIF::getCallList() const QList<QString> ADIF::getCallList() const
{ {
@@ -154,8 +165,6 @@ QList<QString> ADIF::getCallList() const
} }
return p; return p;
} }
int ADIF::getCount() const int ADIF::getCount() const
{ {
+8 -3
View File
@@ -21,10 +21,14 @@ class QDateTime;
class ADIF class ADIF
{ {
public: public:
struct QSO;
void init(QString const& filename); void init(QString const& filename);
void load(); void load();
void add(QString const& call, QString const& band, QString const& mode, const QString &submode, QString const& date); void add(QString const& call, QString const& band, QString const& mode, const QString &submode, QString const& date, const QString &name, const QString &comment);
bool match(QString const& call, QString const& band) const; bool match(QString const& call, QString const& band) const;
QList<ADIF::QSO> find(QString const& call) const;
QList<QString> getCallList() const; QList<QString> getCallList() const;
int getCount() const; int getCount() const;
@@ -38,12 +42,13 @@ class ADIF
, QString const& operator_call); , QString const& operator_call);
private:
struct QSO struct QSO
{ {
QString call,band,mode,submode,date; QString call,band,mode,submode,date,name,comment;
}; };
private:
QMultiHash<QString, QSO> _data; QMultiHash<QString, QSO> _data;
QString _filename; QString _filename;
+41 -13
View File
@@ -59,25 +59,53 @@ void LogBook::match(/*in*/const QString call,
bool &callWorkedBefore, bool &callWorkedBefore,
bool &countryWorkedBefore) const bool &countryWorkedBefore) const
{ {
if (call.length() > 0) if(call.isEmpty()){
{ return;
QString currentBand = ""; // match any band }
callWorkedBefore = _log.match(call,currentBand);
countryName = _countries.find(call);
if (countryName.length() > 0) // country was found QString currentBand = ""; // match any band
callWorkedBefore = _log.match(call, currentBand);
countryName = _countries.find(call);
if (countryName.length() > 0){ // country was found
countryWorkedBefore = _worked.getHasWorked(countryName); countryWorkedBefore = _worked.getHasWorked(countryName);
else } else {
{ countryName = "where?"; //error: prefix not found
countryName = "where?"; //error: prefix not found countryWorkedBefore = false;
countryWorkedBefore = false;
}
} }
} }
void LogBook::addAsWorked(const QString call, const QString band, const QString mode, const QString submode, const QString date) bool LogBook::findCallDetails(
/*in*/
const QString call,
/*out*/
QString &date,
QString &name,
QString &comment) const
{ {
_log.add(call,band,mode,submode,date); qDebug() << "looking for call" << call;
if(call.isEmpty()){
return false;
}
auto qsos = _log.find(call);
qDebug() << "found" << qsos.length() << "qsos for call" << call;
if(qsos.isEmpty()){
return false;
}
foreach(auto qso, qsos){
if(date.isEmpty() && !qso.date.isEmpty()) date = qso.date;
if(name.isEmpty() && !qso.name.isEmpty()) name = qso.name;
if(comment.isEmpty() && !qso.comment.isEmpty()) comment = qso.comment;
}
return true;
}
void LogBook::addAsWorked(const QString call, const QString band, const QString mode, const QString submode, const QString date, const QString name, const QString comment)
{
_log.add(call,band,mode,submode,date,name,comment);
QString countryName = _countries.find(call); QString countryName = _countries.find(call);
if (countryName.length() > 0) if (countryName.length() > 0)
_worked.setAsWorked(countryName); _worked.setAsWorked(countryName);
+8 -1
View File
@@ -25,7 +25,14 @@ public:
/*out*/ QString &countryName, /*out*/ QString &countryName,
bool &callWorkedBefore, bool &callWorkedBefore,
bool &countryWorkedBefore) const; bool &countryWorkedBefore) const;
void addAsWorked(const QString call, const QString band, const QString mode, const QString submode, const QString date); bool findCallDetails(
/*in*/
const QString call,
/*out*/
QString &date,
QString &name,
QString &comment) const;
void addAsWorked(const QString call, const QString band, const QString mode, const QString submode, const QString date, const QString name, const QString comment);
private: private:
CountryDat _countries; CountryDat _countries;
+173 -58
View File
@@ -1261,7 +1261,11 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
connect(ui->tableWidgetRXAll->horizontalHeader(), &QHeaderView::customContextMenuRequested, this, [this](QPoint const &point){ connect(ui->tableWidgetRXAll->horizontalHeader(), &QHeaderView::customContextMenuRequested, this, [this](QPoint const &point){
QMenu * menu = new QMenu(ui->tableWidgetRXAll); QMenu * menu = new QMenu(ui->tableWidgetRXAll);
buildBandActivitySortByMenu(menu); QMenu * sortByMenu = menu->addMenu("Sort By...");
buildBandActivitySortByMenu(sortByMenu);
QMenu * showColumnsMenu = menu->addMenu("Show Columns...");
buildShowColumnsMenu(showColumnsMenu, "band");
menu->popup(ui->tableWidgetRXAll->horizontalHeader()->mapToGlobal(point)); menu->popup(ui->tableWidgetRXAll->horizontalHeader()->mapToGlobal(point));
}); });
@@ -1475,7 +1479,11 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
connect(ui->tableWidgetCalls->horizontalHeader(), &QHeaderView::customContextMenuRequested, this, [this](QPoint const &point){ connect(ui->tableWidgetCalls->horizontalHeader(), &QHeaderView::customContextMenuRequested, this, [this](QPoint const &point){
QMenu * menu = new QMenu(ui->tableWidgetCalls); QMenu * menu = new QMenu(ui->tableWidgetCalls);
buildCallActivitySortByMenu(menu); QMenu * sortByMenu = menu->addMenu("Sort By...");
buildCallActivitySortByMenu(sortByMenu);
QMenu * showColumnsMenu = menu->addMenu("Show Columns...");
buildShowColumnsMenu(showColumnsMenu, "call");
menu->popup(ui->tableWidgetCalls->horizontalHeader()->mapToGlobal(point)); menu->popup(ui->tableWidgetCalls->horizontalHeader()->mapToGlobal(point));
}); });
@@ -1619,7 +1627,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
if (!m_valid) throw std::runtime_error {"Fatal initialization exception"}; if (!m_valid) throw std::runtime_error {"Fatal initialization exception"};
} }
QDate eol(2019, 3, 11); QDate eol(2019, 4, 1);
void MainWindow::checkExpiryWarningMessage() void MainWindow::checkExpiryWarningMessage()
{ {
@@ -1755,6 +1763,8 @@ void MainWindow::initializeDummyData(){
displayTextForFreq("KN4CRD: JY1 ACK -10 \u2301 ", 800, now, false, true, true); displayTextForFreq("KN4CRD: JY1 ACK -10 \u2301 ", 800, now, false, true, true);
displayTextForFreq("KN4CRD: JY1 ACK -12 \u2301 ", 780, now.addSecs(120), false, true, true); displayTextForFreq("KN4CRD: JY1 ACK -12 \u2301 ", 780, now.addSecs(120), false, true, true);
displayTextForFreq("HELLO\\nBRAVE\\nNEW\\nWORLD \u2301 ", 1500, now, false, true, true);
displayActivity(true); displayActivity(true);
} }
@@ -4753,12 +4763,15 @@ void MainWindow::guiUpdate()
char ft8msgbits[75 + 12]; //packed 75 bit ft8 message plus 12-bit CRC char ft8msgbits[75 + 12]; //packed 75 bit ft8 message plus 12-bit CRC
genft8_(message, MyGrid, &bcontest, &m_i3bit, msgsent, const_cast<char *> (ft8msgbits), genft8_(message, MyGrid, &bcontest, &m_i3bit, msgsent, const_cast<char *> (ft8msgbits),
const_cast<int *> (itone), 22, 6, 22); const_cast<int *> (itone), 22, 6, 22);
msgibits = m_i3bit; msgibits = m_i3bit;
msgsent[22]=0; msgsent[22]=0;
m_currentMessage = QString::fromLatin1(msgsent); m_currentMessage = QString::fromLatin1(msgsent).trimmed();
m_currentMessageBits = msgibits; m_currentMessageBits = msgibits;
emitTones();
#if TEST_FOX_WAVE_GEN #if TEST_FOX_WAVE_GEN
if(ui->turboButton->isChecked()) { if(ui->turboButton->isChecked()) {
@@ -5717,7 +5730,7 @@ void MainWindow::displayTextForFreq(QString text, int freq, QDateTime date, bool
block = -1; block = -1;
} }
block = writeMessageTextToUI(date, text, freq, isTx, block); block = writeMessageTextToUI(date, text.replace("\\n", "\n"), freq, isTx, block);
// never cache tx or last lines // never cache tx or last lines
if(isTx || isLast) { if(isTx || isLast) {
@@ -6409,7 +6422,7 @@ void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call,
, QString const& my_call, QString const& my_grid, QByteArray const& ADIF) , QString const& my_call, QString const& my_grid, QByteArray const& ADIF)
{ {
QString date = QSO_date_on.toString("yyyyMMdd"); QString date = QSO_date_on.toString("yyyyMMdd");
m_logBook.addAsWorked (m_hisCall, m_config.bands ()->find (m_freqNominal), mode, submode, date); m_logBook.addAsWorked (m_hisCall, m_config.bands ()->find (m_freqNominal), mode, submode, date, name, comments);
sendNetworkMessage("LOG.QSO", QString(ADIF), { sendNetworkMessage("LOG.QSO", QString(ADIF), {
{"UTC.ON", QVariant(QSO_date_on.toMSecsSinceEpoch())}, {"UTC.ON", QVariant(QSO_date_on.toMSecsSinceEpoch())},
@@ -7267,11 +7280,13 @@ void MainWindow::buildShowColumnsMenu(QMenu *menu, QString tableKey){
}; };
if(tableKey == "call"){ if(tableKey == "call"){
columnKeys.prepend({"Worked Before Flag", "flag"});
columnKeys.prepend({"Callsign", "callsign"}); columnKeys.prepend({"Callsign", "callsign"});
columnKeys.append({ columnKeys.append({
{"Grid Locator", "grid"}, {"Grid Locator", "grid"},
{"Distance", "distance"} {"Distance", "distance"},
{"Worked Before", "log"},
{"Logged Name", "logName"},
{"Logged Comment", "logComment"},
}); });
} }
@@ -7730,6 +7745,20 @@ void MainWindow::buildQueryMenu(QMenu * menu, QString call){
if(m_config.transmit_directed()) toggleTx(true); if(m_config.transmit_directed()) toggleTx(true);
}); });
auto ditDitAction = menu->addAction(QString("%1 DIT DIT - End of contact / Two bits").arg(call).trimmed());
connect(ditDitAction, &QAction::triggered, this, [this](){
QString selectedCall = callsignSelected();
if(selectedCall.isEmpty()){
return;
}
addMessageText(QString("%1 DIT DIT").arg(selectedCall), true);
if(m_config.transmit_directed()) toggleTx(true);
});
} }
void MainWindow::buildRelayMenu(QMenu *menu){ void MainWindow::buildRelayMenu(QMenu *menu){
@@ -9996,7 +10025,7 @@ void MainWindow::processCommandActivity() {
} }
// HACK: if this is an autoreply cmd and relay path is populated and cmd is not MSG or MSG TO:, then swap out the relay path // HACK: if this is an autoreply cmd and relay path is populated and cmd is not MSG or MSG TO:, then swap out the relay path
if(Varicode::isCommandAutoreply(d.cmd) && !d.relayPath.isEmpty() && !d.cmd.startsWith(" MSG")){ if(Varicode::isCommandAutoreply(d.cmd) && !d.relayPath.isEmpty() && !d.cmd.startsWith(" MSG") && !d.cmd.startsWith(" QUERY")){
d.from = d.relayPath; d.from = d.relayPath;
} }
@@ -10076,7 +10105,7 @@ void MainWindow::processCommandActivity() {
} }
// PROCESS RELAY // PROCESS RELAY
else if (d.cmd == ">" && !isAllCall) { else if (d.cmd == ">" && !isAllCall && !m_config.relay_off()) {
// 1. see if there are any more hops to process // 1. see if there are any more hops to process
// 2. if so, forward // 2. if so, forward
@@ -10088,12 +10117,12 @@ void MainWindow::processCommandActivity() {
auto match = re.match(text); auto match = re.match(text);
// if the text starts with a callsign, and relay is not disabled, and this is not a group callsign, then relay. // if the text starts with a callsign, and relay is not disabled, and this is not a group callsign, then relay.
if(match.hasMatch() && !m_config.relay_off() && !isGroupCall){ if(match.hasMatch() && !isGroupCall){
// replace freetext with relayed free text // replace freetext with relayed free text
if(match.captured("type") != ">"){ if(match.captured("type") != ">"){
text = text.replace(match.capturedStart("type"), match.capturedLength("type"), ">"); text = text.replace(match.capturedStart("type"), match.capturedLength("type"), ">");
} }
reply = QString("%1 DE %2").arg(text).arg(d.from); reply = QString("%1 VIA %2").arg(text).arg(d.from);
// otherwise, as long as we're not an ACK...alert the user and either send an ACK or Message // otherwise, as long as we're not an ACK...alert the user and either send an ACK or Message
} else if(!d.text.startsWith("ACK")) { } else if(!d.text.startsWith("ACK")) {
@@ -10132,23 +10161,35 @@ void MainWindow::processCommandActivity() {
} }
// HACK: "MSG TO:" should be supported but contains a space :( // HACK: "MSG TO:" should be supported but contains a space :(
if(!relayedCmds.isEmpty() && first == " MSG"){ if(!relayedCmds.isEmpty()){
auto second = relayedCmds.first(); if(first == " MSG"){
if(second == "TO:"){ auto second = relayedCmds.first();
first = " MSG TO:"; if(second == "TO:"){
relayedCmds.removeFirst(); first = " MSG TO:";
} else if(second.startsWith("TO:")){ relayedCmds.removeFirst();
first = " MSG TO:"; } else if(second.startsWith("TO:")){
relayedCmds.replace(0, second.mid(3)); first = " MSG TO:";
relayedCmds.replace(0, second.mid(3));
}
} else if (first == " QUERY"){
auto second = relayedCmds.first();
if(second == "MSGS" || second == "MSGS?"){
first = " QUERY MSGS";
relayedCmds.removeFirst();
}
else if(second == "CALL"){
first = " QUERY CALL";
relayedCmds.removeFirst();
}
} }
} }
if(valid && Varicode::isCommandAutoreply(first)){ if(Varicode::isCommandAllowed(first) && Varicode::isCommandAutoreply(first)){
CommandDetail rd = {}; CommandDetail rd = {};
rd.bits = d.bits; rd.bits = d.bits;
rd.cmd = first; rd.cmd = first;
rd.freq = d.freq; rd.freq = d.freq;
rd.from = d.from; rd.from = d.from; // note, MSG and QUERY commands are not set with from as the relay path.
rd.relayPath = d.relayPath; rd.relayPath = d.relayPath;
rd.text = relayedCmds.join(" "); //d.text; rd.text = relayedCmds.join(" "); //d.text;
rd.to = d.to; rd.to = d.to;
@@ -10272,6 +10313,14 @@ void MainWindow::processCommandActivity() {
continue; continue;
} }
// PROCESS NACKS
else if (d.cmd == " NACK" && !isAllCall){
qDebug() << "skipping incoming nack" << d.text;
// make sure this is explicit
continue;
}
// PROCESS BUFFERED CMD // PROCESS BUFFERED CMD
else if (d.cmd == " CMD" && !isAllCall){ else if (d.cmd == " CMD" && !isAllCall){
qDebug() << "skipping incoming command" << d.text; qDebug() << "skipping incoming command" << d.text;
@@ -10282,7 +10331,14 @@ void MainWindow::processCommandActivity() {
// PROCESS BUFFERED QUERY // PROCESS BUFFERED QUERY
else if (d.cmd == " QUERY" && !isAllCall){ else if (d.cmd == " QUERY" && !isAllCall){
auto who = d.from; auto who = d.from; // keep in mind, this is the sender, not the original requestor if relayed
auto replyPath = d.from;
if(d.relayPath.contains(">")){
auto path = d.relayPath.split(">");
who = path.last();
replyPath = d.relayPath;
}
QStringList segs = d.text.split(" "); QStringList segs = d.text.split(" ");
if(segs.isEmpty()){ if(segs.isEmpty()){
@@ -10327,8 +10383,8 @@ void MainWindow::processCommandActivity() {
inbox.set(mid, msg); inbox.set(mid, msg);
// and reply // and reply
reply = QString("%1 MSG %2 DE %3"); reply = QString("%1 MSG %2 FROM %3");
reply = reply.arg(who); reply = reply.arg(replyPath);
reply = reply.arg(text); reply = reply.arg(text);
reply = reply.arg(from); reply = reply.arg(from);
} }
@@ -10336,23 +10392,35 @@ void MainWindow::processCommandActivity() {
// PROCESS BUFFERED QUERY MSGS // PROCESS BUFFERED QUERY MSGS
else if (d.cmd == " QUERY MSGS" && ui->autoReplyButton->isChecked()){ else if (d.cmd == " QUERY MSGS" && ui->autoReplyButton->isChecked()){
auto who = d.from; auto who = d.from; // keep in mind, this is the sender, not the original requestor if relayed
auto replyPath = d.from;
if(d.relayPath.contains(">")){
auto path = d.relayPath.split(">");
who = path.last();
replyPath = d.relayPath;
}
// if this is an allcall or a directed call, check to see if we have a stored message for user. // if this is an allcall or a directed call, check to see if we have a stored message for user.
// we reply yes if the user would be able to retreive a stored message // we reply yes if the user would be able to retreive a stored message
auto mid = getNextMessageIdForCallsign(who); auto mid = getNextMessageIdForCallsign(who);
if(mid != -1){ if(mid != -1){
reply = QString("%1 YES MSG ID %2").arg(who).arg(mid); reply = QString("%1 YES MSG ID %2").arg(replyPath).arg(mid);
} }
// if this is not an allcall and we have no messages, reply no. // if this is not an allcall and we have no messages, reply no.
if(!isAllCall && reply.isEmpty()){ if(!isAllCall && reply.isEmpty()){
reply = QString("%1 NO").arg(who); reply = QString("%1 NO").arg(replyPath);
} }
} }
// PROCESS BUFFERED QUERY CALL // PROCESS BUFFERED QUERY CALL
else if (d.cmd == " QUERY CALL" && ui->autoReplyButton->isChecked()){ else if (d.cmd == " QUERY CALL" && ui->autoReplyButton->isChecked()){
auto replyPath = d.from;
if(d.relayPath.contains(">")){
replyPath = d.relayPath;
}
auto who = d.text; auto who = d.text;
if(who.isEmpty()){ if(who.isEmpty()){
continue; continue;
@@ -10372,20 +10440,20 @@ void MainWindow::processCommandActivity() {
} }
if(baseCall == cd.call || baseCall == Radio::base_callsign(cd.call)){ if(baseCall == cd.call || baseCall == Radio::base_callsign(cd.call)){
auto r = QString("%1 ACK %2").arg(cd.call).arg(Varicode::formatSNR(cd.snr)); auto r = QString("%1 (%2)").arg(Varicode::formatSNR(cd.snr)).arg(since(cd.utcTimestamp)).trimmed();
replies.append(r); replies.append(r);
break;
} }
} }
if(!replies.isEmpty()){ if(!replies.isEmpty()){
replies.prepend(QString("%1 YES").arg(d.from)); replies.prepend(QString("%1 YES").arg(replyPath));
} }
reply = replies.join("\n"); reply = replies.join(" ");
if(!reply.isEmpty()){ if(!reply.isEmpty()){
if(isAllCall){ if(isAllCall){
// since all pings are technically @ALLCALL, let's bump the allcall cache here...
m_txAllcallCommandCache.insert(d.from, new QDateTime(now), 25); m_txAllcallCommandCache.insert(d.from, new QDateTime(now), 25);
} }
} }
@@ -10585,7 +10653,7 @@ int MainWindow::getNextMessageIdForCallsign(QString callsign){
QStringList MainWindow::parseRelayPathCallsigns(QString from, QString text){ QStringList MainWindow::parseRelayPathCallsigns(QString from, QString text){
QStringList calls; QStringList calls;
QString callDePattern = {R"(\sDE\s(?<callsign>\b(?<prefix>[A-Z0-9]{1,4}\/)?(?<base>([0-9A-Z])?([0-9A-Z])([0-9])([A-Z])?([A-Z])?([A-Z])?)(?<suffix>\/[A-Z0-9]{1,4})?)\b)"}; QString callDePattern = {R"(\sVIA\s(?<callsign>\b(?<prefix>[A-Z0-9]{1,4}\/)?(?<base>([0-9A-Z])?([0-9A-Z])([0-9])([A-Z])?([A-Z])?([A-Z])?)(?<suffix>\/[A-Z0-9]{1,4})?)\b)"};
QRegularExpression re(callDePattern); QRegularExpression re(callDePattern);
auto iter = re.globalMatch(text); auto iter = re.globalMatch(text);
while(iter.hasNext()){ while(iter.hasNext()){
@@ -10607,7 +10675,7 @@ void MainWindow::processAlertReplyForCommand(CommandDetail d, QString from, QStr
QString fromReplace = QString{}; QString fromReplace = QString{};
foreach(auto call, calls){ foreach(auto call, calls){
fromReplace.append(" DE "); fromReplace.append(" VIA ");
fromReplace.append(call); fromReplace.append(call);
} }
@@ -10871,6 +10939,7 @@ void MainWindow::displayBandActivity() {
QList < ActivityDetail > items = m_bandActivity[offset]; QList < ActivityDetail > items = m_bandActivity[offset];
if (items.length() > 0) { if (items.length() > 0) {
QDateTime timestamp;
QStringList text; QStringList text;
QString age; QString age;
int snr = 0; int snr = 0;
@@ -10922,6 +10991,7 @@ void MainWindow::displayBandActivity() {
text.append(item.text); text.append(item.text);
snr = item.snr; snr = item.snr;
age = since(item.utcTimestamp); age = since(item.utcTimestamp);
timestamp = item.utcTimestamp;
tdrift = item.tdrift; tdrift = item.tdrift;
} }
@@ -10938,11 +11008,13 @@ void MainWindow::displayBandActivity() {
offsetItem->setData(Qt::UserRole, QVariant(offset)); offsetItem->setData(Qt::UserRole, QVariant(offset));
ui->tableWidgetRXAll->setItem(row, col++, offsetItem); ui->tableWidgetRXAll->setItem(row, col++, offsetItem);
auto ageItem = new QTableWidgetItem(QString("%1").arg(age)); auto ageItem = new QTableWidgetItem(age);
ageItem->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter); ageItem->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter);
ageItem->setToolTip(timestamp.toString());
ui->tableWidgetRXAll->setItem(row, col++, ageItem); ui->tableWidgetRXAll->setItem(row, col++, ageItem);
auto snrItem = new QTableWidgetItem(QString("%1 dB").arg(Varicode::formatSNR(snr))); auto snrText = Varicode::formatSNR(snr);
auto snrItem = new QTableWidgetItem(snrText.isEmpty() ? "" : QString("%1 dB").arg(snrText));
snrItem->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter); snrItem->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter);
ui->tableWidgetRXAll->setItem(row, col++, snrItem); ui->tableWidgetRXAll->setItem(row, col++, snrItem);
@@ -11185,11 +11257,7 @@ void MainWindow::displayCallActivity() {
QString displayCall = d.call; QString displayCall = d.call;
#endif #endif
QString flag;
if(m_logBook.hasWorkedBefore(d.call, "")){
// unicode checkmark
flag = "\u2713";
}
auto iconItem = new QTableWidgetItem(hasMessage ? "\u2691" : hasAck ? "\u2605" : ""); auto iconItem = new QTableWidgetItem(hasMessage ? "\u2691" : hasAck ? "\u2605" : "");
iconItem->setData(Qt::UserRole, QVariant((d.call))); iconItem->setData(Qt::UserRole, QVariant((d.call)));
@@ -11202,15 +11270,16 @@ void MainWindow::displayCallActivity() {
auto displayItem = new QTableWidgetItem(displayCall); auto displayItem = new QTableWidgetItem(displayCall);
displayItem->setData(Qt::UserRole, QVariant(d.call)); displayItem->setData(Qt::UserRole, QVariant(d.call));
displayItem->setToolTip(generateCallDetail(displayCall)); displayItem->setToolTip(generateCallDetail(displayCall));
ui->tableWidgetCalls->setItem(row, col++, displayItem); ui->tableWidgetCalls->setItem(row, col++, displayItem);
auto flagItem = new QTableWidgetItem(flag); if(1){ //d.utcTimestamp.isValid()){
flagItem->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter); auto ageItem = new QTableWidgetItem(since(d.utcTimestamp));
ui->tableWidgetCalls->setItem(row, col++, flagItem); ageItem->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter);
if(d.utcTimestamp.isValid()){ ageItem->setToolTip(d.utcTimestamp.toString());
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem(QString("%1").arg(since(d.utcTimestamp)))); ui->tableWidgetCalls->setItem(row, col++, ageItem);
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem(QString("%1 dB").arg(Varicode::formatSNR(d.snr))));
auto snrText = Varicode::formatSNR(d.snr);
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem(snrText.isEmpty() ? "" : QString("%1 dB").arg(snrText)));
auto offsetItem = new QTableWidgetItem(QString("%1 Hz").arg(d.freq)); auto offsetItem = new QTableWidgetItem(QString("%1 Hz").arg(d.freq));
offsetItem->setData(Qt::UserRole, QVariant(d.freq)); offsetItem->setData(Qt::UserRole, QVariant(d.freq));
@@ -11224,7 +11293,32 @@ void MainWindow::displayCallActivity() {
auto distanceItem = new QTableWidgetItem(calculateDistance(d.grid)); auto distanceItem = new QTableWidgetItem(calculateDistance(d.grid));
distanceItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); distanceItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, col++, distanceItem); ui->tableWidgetCalls->setItem(row, col++, distanceItem);
QString flag;
if(m_logBook.hasWorkedBefore(d.call, "")){
// unicode checkmark
flag = "\u2713";
}
auto workedBeforeItem = new QTableWidgetItem(flag);
workedBeforeItem->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
ui->tableWidgetCalls->setItem(row, col++, workedBeforeItem);
QString logDetailDate;
QString logDetailName;
QString logDetailComment;
if(showColumn("call", "log") || showColumn("call", "logName") || showColumn("call", "logComment")){
m_logBook.findCallDetails(d.call, logDetailDate, logDetailName, logDetailComment);
}
auto logNameItem = new QTableWidgetItem(logDetailName);
logNameItem->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
ui->tableWidgetCalls->setItem(row, col++, logNameItem);
auto logCommentItem = new QTableWidgetItem(logDetailComment);
logCommentItem->setTextAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
ui->tableWidgetCalls->setItem(row, col++, logCommentItem);
} else { } else {
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem("")); // age ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem("")); // age
@@ -11233,8 +11327,9 @@ void MainWindow::displayCallActivity() {
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem("")); // tdrift ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem("")); // tdrift
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem("")); // grid ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem("")); // grid
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem("")); // distance ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem("")); // distance
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem("")); // worked before
//ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem("")); ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem("")); // log name
ui->tableWidgetCalls->setItem(row, col++, new QTableWidgetItem("")); // log comment
} }
if (isCallSelected) { if (isCallSelected) {
@@ -11277,13 +11372,15 @@ void MainWindow::displayCallActivity() {
// Hide columns // Hide columns
ui->tableWidgetCalls->setColumnHidden(0, !showIconColumn); ui->tableWidgetCalls->setColumnHidden(0, !showIconColumn);
ui->tableWidgetCalls->setColumnHidden(1, !showColumn("call", "callsign")); ui->tableWidgetCalls->setColumnHidden(1, !showColumn("call", "callsign"));
ui->tableWidgetCalls->setColumnHidden(2, !showColumn("call", "flag")); ui->tableWidgetCalls->setColumnHidden(2, !showColumn("call", "timestamp"));
ui->tableWidgetCalls->setColumnHidden(3, !showColumn("call", "timestamp")); ui->tableWidgetCalls->setColumnHidden(3, !showColumn("call", "snr"));
ui->tableWidgetCalls->setColumnHidden(4, !showColumn("call", "snr")); ui->tableWidgetCalls->setColumnHidden(4, !showColumn("call", "offset"));
ui->tableWidgetCalls->setColumnHidden(5, !showColumn("call", "offset")); ui->tableWidgetCalls->setColumnHidden(5, !showColumn("call", "tdrift", false));
ui->tableWidgetCalls->setColumnHidden(6, !showColumn("call", "tdrift", false)); ui->tableWidgetCalls->setColumnHidden(6, !showColumn("call", "grid", false));
ui->tableWidgetCalls->setColumnHidden(7, !showColumn("call", "grid", false)); ui->tableWidgetCalls->setColumnHidden(7, !showColumn("call", "distance", false));
ui->tableWidgetCalls->setColumnHidden(8, !showColumn("call", "distance", false)); ui->tableWidgetCalls->setColumnHidden(8, !showColumn("call", "log"));
ui->tableWidgetCalls->setColumnHidden(9, !showColumn("call", "logName"));
ui->tableWidgetCalls->setColumnHidden(10, !showColumn("call", "logComment"));
// Resize the table columns // Resize the table columns
ui->tableWidgetCalls->resizeColumnToContents(0); ui->tableWidgetCalls->resizeColumnToContents(0);
@@ -11294,6 +11391,8 @@ void MainWindow::displayCallActivity() {
ui->tableWidgetCalls->resizeColumnToContents(5); ui->tableWidgetCalls->resizeColumnToContents(5);
ui->tableWidgetCalls->resizeColumnToContents(6); ui->tableWidgetCalls->resizeColumnToContents(6);
ui->tableWidgetCalls->resizeColumnToContents(7); ui->tableWidgetCalls->resizeColumnToContents(7);
ui->tableWidgetCalls->resizeColumnToContents(8);
ui->tableWidgetCalls->resizeColumnToContents(9);
// Reset the scroll position // Reset the scroll position
ui->tableWidgetCalls->verticalScrollBar()->setValue(currentScrollPos); ui->tableWidgetCalls->verticalScrollBar()->setValue(currentScrollPos);
@@ -11326,6 +11425,22 @@ void MainWindow::emitPTT(bool on){
}); });
} }
void MainWindow::emitTones(){
if(!m_config.udpEnabled()){
return;
}
// emit tone numbers to network
QVariantList t;
for(int i = 0; i < NUM_FT8_SYMBOLS; i++){
t.append(QVariant((int)itone[i]));
}
sendNetworkMessage("TX.FRAME", "", {
{"TONES", t}
});
}
void MainWindow::networkMessage(Message const &message) void MainWindow::networkMessage(Message const &message)
{ {
if(!m_config.udpEnabled()){ if(!m_config.udpEnabled()){
+1
View File
@@ -383,6 +383,7 @@ private slots:
void on_cbFirst_toggled(bool b); void on_cbFirst_toggled(bool b);
void on_cbAutoSeq_toggled(bool b); void on_cbAutoSeq_toggled(bool b);
void emitPTT(bool on); void emitPTT(bool on);
void emitTones();
void networkMessage(Message const &message); void networkMessage(Message const &message);
void sendNetworkMessage(QString const &type, QString const &message); void sendNetworkMessage(QString const &type, QString const &message);
void sendNetworkMessage(QString const &type, QString const &message, const QMap<QString, QVariant> &params); void sendNetworkMessage(QString const &type, QString const &message, const QMap<QString, QVariant> &params);
+18 -8
View File
@@ -1242,14 +1242,6 @@ QTextEdit[transmitting=&quot;true&quot;] {
<string>Callsigns</string> <string>Callsigns</string>
</property> </property>
</column> </column>
<column>
<property name="text">
<string>✓</string>
</property>
<property name="textAlignment">
<set>AlignCenter</set>
</property>
</column>
<column> <column>
<property name="text"> <property name="text">
<string>Age</string> <string>Age</string>
@@ -1280,6 +1272,24 @@ QTextEdit[transmitting=&quot;true&quot;] {
<string>Distance</string> <string>Distance</string>
</property> </property>
</column> </column>
<column>
<property name="text">
<string>✓</string>
</property>
<property name="toolTip">
<string>Worked Before</string>
</property>
</column>
<column>
<property name="text">
<string>Name</string>
</property>
</column>
<column>
<property name="text">
<string>Comment</string>
</property>
</column>
</widget> </widget>
</widget> </widget>
</widget> </widget>
+8 -7
View File
@@ -50,8 +50,9 @@ QMap<QString, int> directed_cmds = {
{" SNR?", 0 }, // query snr {" SNR?", 0 }, // query snr
{"?", 0 }, // compat {"?", 0 }, // compat
//{" ", 1 }, // unused {" DIT DIT", 1 }, // unused
//{" ", 2 }, // unused
{" NACK", 2 }, // negative acknowledge
{" HEARING?", 3 }, // query station calls heard {" HEARING?", 3 }, // query station calls heard
@@ -104,7 +105,7 @@ QMap<QString, int> directed_cmds = {
}; };
// commands allowed to be processed // commands allowed to be processed
QSet<int> allowed_cmds = {-1, 0, /*1,*/ /*2,*/ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; QSet<int> allowed_cmds = {-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
// commands that result in an autoreply (which can be relayed) // commands that result in an autoreply (which can be relayed)
QSet<int> autoreply_cmds = {0, 3, 4, 6, 9, 10, 11, 12, 13, 16, 30}; QSet<int> autoreply_cmds = {0, 3, 4, 6, 9, 10, 11, 12, 13, 16, 30};
@@ -129,10 +130,10 @@ QMap<int, int> checksum_cmds = {
}; };
QString callsign_pattern = QString("(?<callsign>[@]?[A-Z0-9/]+)"); QString callsign_pattern = QString("(?<callsign>[@]?[A-Z0-9/]+)");
QString optional_cmd_pattern = QString("(?<cmd>\\s?(?:AGN[?]|QSL[?]|HW CPY[?]|APRS[:]|MSG TO[:]|SNR[?]|INFO[?]|GRID[?]|STATUS[?]|QUERY MSGS[?]|HEARING[?]|(?:(?:STATUS|HEARING|QUERY CALL|QUERY MSGS|QUERY|CMD|MSG|ACK|73|YES|NO|SNR|QSL|RR|SK|FB|INFO|GRID)(?=[ ]|$))|[?> ]))?"); QString optional_cmd_pattern = QString("(?<cmd>\\s?(?:AGN[?]|QSL[?]|HW CPY[?]|APRS[:]|MSG TO[:]|SNR[?]|INFO[?]|GRID[?]|STATUS[?]|QUERY MSGS[?]|HEARING[?]|(?:(?:STATUS|HEARING|QUERY CALL|QUERY MSGS|QUERY|CMD|MSG|NACK|ACK|73|YES|NO|SNR|QSL|RR|SK|FB|INFO|GRID|DIT DIT)(?=[ ]|$))|[?> ]))?");
QString optional_grid_pattern = QString("(?<grid>\\s?[A-R]{2}[0-9]{2})?"); QString optional_grid_pattern = QString("(?<grid>\\s?[A-R]{2}[0-9]{2})?");
QString optional_extended_grid_pattern = QString("^(?<grid>\\s?(?:[A-R]{2}[0-9]{2}(?:[A-X]{2}(?:[0-9]{2})?)*))?"); QString optional_extended_grid_pattern = QString("^(?<grid>\\s?(?:[A-R]{2}[0-9]{2}(?:[A-X]{2}(?:[0-9]{2})?)*))?");
QString optional_num_pattern = QString("(?<num>(?<=SNR|ACK)\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?"); QString optional_num_pattern = QString("(?<num>(?<=SNR|\\bACK)\\s?[-+]?(?:3[01]|[0-2]?[0-9]))?");
QRegularExpression directed_re("^" + QRegularExpression directed_re("^" +
callsign_pattern + callsign_pattern +
@@ -1545,8 +1546,8 @@ QStringList Varicode::unpackDirectedMessage(const QString &text, quint8 *pType){
quint32 packed_to = Varicode::bitsToInt(bits.mid(31, 28)); quint32 packed_to = Varicode::bitsToInt(bits.mid(31, 28));
quint8 packed_cmd = Varicode::bitsToInt(bits.mid(59, 5)); quint8 packed_cmd = Varicode::bitsToInt(bits.mid(59, 5));
bool portable_from = (extra >> 7) & 1 == 1; bool portable_from = ((extra >> 7) & 1) == 1;
bool portable_to = (extra >> 6) & 1 == 1; bool portable_to = ((extra >> 6) & 1) == 1;
extra = extra % 64; extra = extra % 64;
QString from = Varicode::unpackCallsign(packed_from, portable_from); QString from = Varicode::unpackCallsign(packed_from, portable_from);