Compare commits

..

7 Commits

Author SHA1 Message Date
Jordan Sherer 8f8772f1bd Version bump 2018-07-27 16:52:25 -04:00
Jordan Sherer f09132f6b4 Fix beacon for compound calls 2018-07-27 16:52:16 -04:00
Jordan Sherer bf57a67c43 Encoding of beacon messages that consist of compound call + (grid or arbitrary 15 bit number) + a flag (isCQ) 2018-07-27 16:11:11 -04:00
Jordan Sherer 091b3b3ee8 Disabled global keyboard shortcuts... 2018-07-27 14:40:37 -04:00
Jordan Sherer 17033f1044 Automatic reply to AGN queries. Restore last message in the text area menu 2018-07-27 13:53:07 -04:00
Jordan Sherer 7c656fac71 Updated compound callsign parsing in directed messages (use base callsign instead) 2018-07-27 11:28:31 -04:00
Jordan Sherer a6771b81c3 Fixed leading whitespace issue for buffered commands 2018-07-27 10:57:51 -04:00
6 changed files with 139 additions and 19 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# Version number components # Version number components
set (WSJTX_VERSION_MAJOR 0) set (WSJTX_VERSION_MAJOR 0)
set (WSJTX_VERSION_MINOR 3) set (WSJTX_VERSION_MINOR 3)
set (WSJTX_VERSION_PATCH 1) set (WSJTX_VERSION_PATCH 2)
set (WSJTX_RC 0) # release candidate number, comment out or zero for development versions set (WSJTX_RC 0) # 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
+1 -1
View File
@@ -98,7 +98,7 @@ bool DecodedText::tryUnpackCompound(){
return false; return false;
} }
QStringList parts = Varicode::unpackCompoundMessage(m); QStringList parts = Varicode::unpackCompoundMessage(m, nullptr);
if(parts.isEmpty() || parts.length() < 2){ if(parts.isEmpty() || parts.length() < 2){
return false; return false;
+65 -7
View File
@@ -258,6 +258,16 @@ namespace
} }
return ""; return "";
} }
QString lstrip(const QString& str) {
int len = str.size() - 1;
for (int n = 0; n < len; n++) {
if (!str.at(n).isSpace()) {
return str.mid(n);
}
}
return "";
}
} }
//--------------------------------------------------- MainWindow constructor //--------------------------------------------------- MainWindow constructor
@@ -456,6 +466,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
m_previousFreq {0} m_previousFreq {0}
{ {
ui->setupUi(this); ui->setupUi(this);
createStatusBar(); createStatusBar();
add_child_to_event_filter (this); add_child_to_event_filter (this);
ui->dxGridEntry->setValidator (new MaidenheadLocatorValidator {this}); ui->dxGridEntry->setValidator (new MaidenheadLocatorValidator {this});
@@ -1066,8 +1077,23 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
auto clearAction2 = new QAction(QIcon::fromTheme("edit-clear"), QString("Clear"), ui->extFreeTextMsgEdit); auto clearAction2 = new QAction(QIcon::fromTheme("edit-clear"), QString("Clear"), ui->extFreeTextMsgEdit);
connect(clearAction2, &QAction::triggered, this, [this](){ this->on_clearAction_triggered(ui->extFreeTextMsgEdit); }); connect(clearAction2, &QAction::triggered, this, [this](){ this->on_clearAction_triggered(ui->extFreeTextMsgEdit); });
ui->extFreeTextMsgEdit->setContextMenuPolicy(Qt::ActionsContextMenu); ui->extFreeTextMsgEdit->setContextMenuPolicy(Qt::ActionsContextMenu);
ui->extFreeTextMsgEdit->addAction(clearAction2);
ui->extFreeTextMsgEdit->addAction(clearActionAll); auto restoreAction = new QAction(QString("Restore Previous Message"), ui->extFreeTextMsgEdit);
connect(restoreAction, &QAction::triggered, this, [this](){ this->restoreMessage(); });
ui->extFreeTextMsgEdit->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->extFreeTextMsgEdit, &QTableWidget::customContextMenuRequested, this, [this, clearAction2, clearActionAll, restoreAction](QPoint const &point){
QMenu * menu = new QMenu(ui->extFreeTextMsgEdit);
restoreAction->setDisabled(m_lastTxMessage.isEmpty());
menu->addAction(restoreAction);
menu->addSeparator();
menu->addAction(clearAction2);
menu->addAction(clearActionAll);
menu->popup(ui->extFreeTextMsgEdit->mapToGlobal(point));
});
auto clearAction3 = new QAction(QIcon::fromTheme("edit-clear"), QString("Clear"), ui->tableWidgetRXAll); auto clearAction3 = new QAction(QIcon::fromTheme("edit-clear"), QString("Clear"), ui->tableWidgetRXAll);
connect(clearAction3, &QAction::triggered, this, [this](){ this->on_clearAction_triggered(ui->tableWidgetRXAll); }); connect(clearAction3, &QAction::triggered, this, [this](){ this->on_clearAction_triggered(ui->tableWidgetRXAll); });
@@ -1184,6 +1210,20 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
auto packed = Varicode::packCompoundMessage(basecall, fix, prefix, 99); auto packed = Varicode::packCompoundMessage(basecall, fix, prefix, 99);
qDebug() << packed << Varicode::unpackCompoundMessage(packed); qDebug() << packed << Varicode::unpackCompoundMessage(packed);
bool isCQ = false;
auto packed = Varicode::packBeaconMessage("KN4CRD/P", "EM73", true);
qDebug() << packed << Varicode::unpackBeaconMessage(packed, &isCQ) << isCQ;
packed = Varicode::packBeaconMessage("VE3/KN4CRD", "EM73", false);
qDebug() << packed << Varicode::unpackBeaconMessage(packed, &isCQ) << isCQ;
bool isCQ = false;
auto packed = Varicode::packBeaconMessage("P/KN4CRD", "", true);
qDebug() << packed << Varicode::unpackBeaconMessage(packed, &isCQ) << isCQ;
m_valid = false; m_valid = false;
#endif #endif
@@ -1917,6 +1957,7 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog
} }
displayDialFrequency (); displayDialFrequency ();
displayActivity(true);
bool vhf {m_config.enable_VHF_features()}; bool vhf {m_config.enable_VHF_features()};
m_wideGraph->setVHF(vhf); m_wideGraph->setVHF(vhf);
@@ -2088,6 +2129,7 @@ void MainWindow::keyPressEvent (QKeyEvent * e)
QMainWindow::keyPressEvent (e); QMainWindow::keyPressEvent (e);
} }
#if 0
int n; int n;
switch(e->key()) switch(e->key())
{ {
@@ -2203,6 +2245,7 @@ void MainWindow::keyPressEvent (QKeyEvent * e)
} }
break; break;
} }
#endif
QMainWindow::keyPressEvent (e); QMainWindow::keyPressEvent (e);
} }
@@ -5429,6 +5472,7 @@ void MainWindow::clearActivity(){
m_rxFrameBlockNumbers.clear(); m_rxFrameBlockNumbers.clear();
m_rxFrameQueue.clear(); m_rxFrameQueue.clear();
m_rxCommandQueue.clear(); m_rxCommandQueue.clear();
m_lastTxMessage.clear();
clearTableWidget(ui->tableWidgetCalls); clearTableWidget(ui->tableWidgetCalls);
@@ -5560,6 +5604,13 @@ void MainWindow::createMessageTransmitQueue(QString const& text){
m_lastTxMessage = text; m_lastTxMessage = text;
} }
void MainWindow::restoreMessage(){
if(m_lastTxMessage.isEmpty()){
return;
}
addMessageText(m_lastTxMessage, true);
}
void MainWindow::resetMessageTransmitQueue(){ void MainWindow::resetMessageTransmitQueue(){
m_txFrameCount = 0; m_txFrameCount = 0;
m_txFrameQueue.clear(); m_txFrameQueue.clear();
@@ -5733,6 +5784,8 @@ QPair<QStringList, QStringList> MainWindow::buildFT8MessageFrames(QString const&
line = line.mid(n); line = line.mid(n);
if(Varicode::isCommandBuffered(dirCmd) && !line.isEmpty()){ if(Varicode::isCommandBuffered(dirCmd) && !line.isEmpty()){
// strip leading whitespace after a buffered directed command
line = lstrip(line);
// TODO: jsherer - this is how we can add 16-bit checksum to the message, just encode it in the data... // TODO: jsherer - this is how we can add 16-bit checksum to the message, just encode it in the data...
qDebug() << "generating checksum for line" << line; qDebug() << "generating checksum for line" << line;
line = line + " " + Varicode::checksum16(line); line = line + " " + Varicode::checksum16(line);
@@ -5751,7 +5804,7 @@ QPair<QStringList, QStringList> MainWindow::buildFT8MessageFrames(QString const&
#if 1 #if 1
qDebug() << "parsed frames:"; qDebug() << "parsed frames:";
foreach(auto frame, frames){ foreach(auto frame, frames){
qDebug() << "->" << frame << Varicode::unpackDataMessage(frame) << Varicode::unpackDirectedMessage(frame) << Varicode::unpackCompoundMessage(frame); qDebug() << "->" << frame << Varicode::unpackDataMessage(frame) << Varicode::unpackDirectedMessage(frame) << Varicode::unpackCompoundMessage(frame, nullptr);
} }
qDebug() << "lines:"; qDebug() << "lines:";
@@ -5972,12 +6025,14 @@ void MainWindow::prepareBacon(){
QString call = m_config.my_callsign(); QString call = m_config.my_callsign();
QString grid = m_config.my_grid().left(4); QString grid = m_config.my_grid().left(4);
if(call != Radio::base_callsign(call)){ QString beacon = QString("DE %1 %2").arg(call).arg(grid);
grid = ""; QString parsed = parseFT8Message(beacon, nullptr);
if(parsed != beacon){
beacon = QString("DE %1").arg(call).arg(grid);
} }
lines.append(QString("DE %1 %2").arg(call).arg(grid)); lines.append(beacon);
lines.append(QString("DE %1 %2").arg(call).arg(grid)); lines.append(beacon);
#if 0 #if 0
if(!m_callActivity.isEmpty()){ if(!m_callActivity.isEmpty()){
@@ -7084,6 +7139,7 @@ void MainWindow::on_clearAction_triggered(QObject * sender){
if(sender == ui->extFreeTextMsgEdit){ if(sender == ui->extFreeTextMsgEdit){
resetMessage(); resetMessage();
m_lastTxMessage.clear();
} }
if(sender == ui->textEditRX){ if(sender == ui->textEditRX){
@@ -8821,6 +8877,8 @@ void MainWindow::displayActivity(bool force){
msgBox->show(); msgBox->show();
continue; continue;
} else if(d.cmd == " AGN?" && !isAllCall && !m_lastTxMessage.isEmpty()){
reply = m_lastTxMessage;
} }
if(reply.isEmpty()){ if(reply.isEmpty()){
+1
View File
@@ -127,6 +127,7 @@ public slots:
void addMessageText(QString text, bool clear=false); void addMessageText(QString text, bool clear=false);
void resetMessage(); void resetMessage();
void resetMessageUI(); void resetMessageUI();
void restoreMessage();
void createMessage(QString const& text); void createMessage(QString const& text);
void createMessageTransmitQueue(QString const& text); void createMessageTransmitQueue(QString const& text);
void resetMessageTransmitQueue(); void resetMessageTransmitQueue();
+67 -9
View File
@@ -31,7 +31,7 @@ const int nalphabet = 41;
QString alphabet = {"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?"}; QString alphabet = {"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./?"};
QString grid_pattern = {R"((?<grid>[A-R]{2}[0-9]{2})+)"}; QString grid_pattern = {R"((?<grid>[A-R]{2}[0-9]{2})+)"};
QString orig_compound_callsign_pattern = {R"((?<callsign>(\d|[A-Z])+\/?((\d|[A-Z]){2,})(\/(\d|[A-Z])+)?(\/(\d|[A-Z])+)?))"}; QString orig_compound_callsign_pattern = {R"((?<callsign>(\d|[A-Z])+\/?((\d|[A-Z]){2,})(\/(\d|[A-Z])+)?(\/(\d|[A-Z])+)?))"};
QString compound_callsign_pattern = {R"((?<callsign>\b([A-Z0-9]{1,4}\/)?([0-9A-Z])?([0-9A-Z])([0-9])([A-Z])?([A-Z])?([A-Z])?(\/[A-Z0-9]{1,4})?)\b)"}; QString compound_callsign_pattern = {R"((?<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 pack_callsign_pattern = {R"(([0-9A-Z ])([0-9A-Z])([0-9])([A-Z ])([A-Z ])([A-Z ]))"}; QString pack_callsign_pattern = {R"(([0-9A-Z ])([0-9A-Z])([0-9])([A-Z ])([A-Z ])([A-Z ]))"};
QString callsign_alphabet = {"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ "}; QString callsign_alphabet = {"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ "};
@@ -39,11 +39,11 @@ QMap<QString, int> directed_cmds = {
// any changes here need to be made also in the directed regular xpression for parsing // any changes here need to be made also in the directed regular xpression for parsing
// directed queries // directed queries
{"?", 0 }, // query ack {"?", 0 }, // query snr
{"@", 1 }, // query qth {"@", 1 }, // query qth
{"&", 2 }, // query station message {"&", 2 }, // query station message
{"$", 3 }, // query station(s) heard {"$", 3 }, // query station(s) heard
{"^", 4 }, // query snr // {"^", 4 }, // query ack
{"%", 5 }, // query pwr {"%", 5 }, // query pwr
{"|", 6 }, // relay message? {"|", 6 }, // relay message?
{"!", 7 }, // alert message? {"!", 7 }, // alert message?
@@ -63,7 +63,7 @@ QMap<QString, int> directed_cmds = {
{" ", 31 }, // send freetext {" ", 31 }, // send freetext
}; };
QSet<int> allowed_cmds = {0, 1, 2, 3, 4, 5, 6, 7, 23, 24, 25, 26, 27, 28, 29, 30, 31}; QSet<int> allowed_cmds = {0, 1, 2, 3, 4, 5, 6, 7, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
QSet<int> buffered_cmds = {6, 7}; QSet<int> buffered_cmds = {6, 7};
@@ -772,6 +772,9 @@ QPair<float, float> grid2deg(QString const &grid){
// pack a 4-digit maidenhead grid locator into a 15-bit value // pack a 4-digit maidenhead grid locator into a 15-bit value
quint16 Varicode::packGrid(QString const& grid){ quint16 Varicode::packGrid(QString const& grid){
// TODO: validate grid... // TODO: validate grid...
if(grid.length() < 4){
return (1<<15)-1;
}
// TODO: encode non-grid data... // TODO: encode non-grid data...
@@ -784,7 +787,7 @@ quint16 Varicode::packGrid(QString const& grid){
QString Varicode::unpackGrid(quint16 value){ QString Varicode::unpackGrid(quint16 value){
if(value > 180*180){ if(value > 180*180){
// TODO: decode non-grid data... // TODO: decode non-grid data... for now just return an empty string...
return ""; return "";
} }
@@ -802,6 +805,50 @@ bool Varicode::isCommandBuffered(const QString &cmd){
return directed_cmds.contains(cmd) && buffered_cmds.contains(directed_cmds[cmd]); return directed_cmds.contains(cmd) && buffered_cmds.contains(directed_cmds[cmd]);
} }
QString Varicode::packBeaconMessage(QString const &callsign, QString const &extra, bool isCQ){
QString frame;
auto parsedCall = QRegularExpression(compound_callsign_pattern).match(callsign);
if(!parsedCall.hasMatch()){
return frame;
}
QString base = parsedCall.captured("base");
bool isPrefix = false;
QString fix = parsedCall.captured("prefix");
if(!fix.isEmpty()){
isPrefix = true;
}
if(!isPrefix){
fix = parsedCall.captured("suffix");
}
quint16 packed_extra = 180*180 + 1; // maximum grid + 1 (which will display an empty string)
if(extra.length() == 4 && QRegularExpression(grid_pattern).match(extra).hasMatch()){
packed_extra = Varicode::packGrid(extra);
}
if(isCQ){
packed_extra |= (1<<15);
}
return packCompoundMessage(base, fix, isPrefix, packed_extra);
}
QStringList Varicode::unpackBeaconMessage(const QString &text, bool * isCQ){
quint16 num = 0;
QStringList unpacked = unpackCompoundMessage(text, &num);
if(isCQ) *isCQ = (num & (1<<15));
unpacked.append(Varicode::unpackGrid(num & ((1<<15)-1)));
return unpacked;
}
QString Varicode::packCompoundMessage(const QString &baseCallsign, const QString &fix, bool isPrefix, quint16 num){ QString Varicode::packCompoundMessage(const QString &baseCallsign, const QString &fix, bool isPrefix, quint16 num){
QString frame; QString frame;
@@ -834,7 +881,7 @@ QString Varicode::packCompoundMessage(const QString &baseCallsign, const QString
return Varicode::pack64bits(Varicode::bitsToInt(bits)) + Varicode::pack5bits(packed_5 % 32); return Varicode::pack64bits(Varicode::bitsToInt(bits)) + Varicode::pack5bits(packed_5 % 32);
} }
QStringList Varicode::unpackCompoundMessage(const QString &text){ QStringList Varicode::unpackCompoundMessage(const QString &text, quint16 *pNum){
QStringList unpacked; QStringList unpacked;
if(text.length() < 13 || text.contains(" ")){ if(text.length() < 13 || text.contains(" ")){
@@ -856,20 +903,23 @@ QStringList Varicode::unpackCompoundMessage(const QString &text){
quint8 is_prefix = Varicode::bitsToInt(Varicode::strToBits(bits.mid(2,1))); quint8 is_prefix = Varicode::bitsToInt(Varicode::strToBits(bits.mid(2,1)));
quint32 packed_base = Varicode::bitsToInt(Varicode::strToBits(bits.mid(3, 28))); quint32 packed_base = Varicode::bitsToInt(Varicode::strToBits(bits.mid(3, 28)));
quint32 packed_fix = Varicode::bitsToInt(Varicode::strToBits(bits.mid(31, 22))); quint32 packed_fix = Varicode::bitsToInt(Varicode::strToBits(bits.mid(31, 22)));
quint8 packed_11 = Varicode::bitsToInt(Varicode::strToBits(bits.mid(53, 11))); quint16 packed_11 = Varicode::bitsToInt(Varicode::strToBits(bits.mid(53, 11)));
QString base = Varicode::unpackCallsign(packed_base).trimmed(); QString base = Varicode::unpackCallsign(packed_base).trimmed();
QString fix = Varicode::unpackCallsignPrefixSuffix(packed_fix); QString fix = Varicode::unpackCallsignPrefixSuffix(packed_fix);
quint16 num = (packed_11 << 5) | packed_5; quint16 num = (packed_11 << 5) | packed_5;
if(pNum) *pNum = num;
if(is_prefix){ if(is_prefix){
unpacked.append(fix); unpacked.append(fix);
} }
unpacked.append(base); unpacked.append(base);
if(!is_prefix){ if(!is_prefix){
unpacked.append(fix); unpacked.append(fix);
} }
unpacked.append(QString("%1").arg(num));
return unpacked; return unpacked;
} }
@@ -890,12 +940,20 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &baseCa
QString pwr = match.captured("pwr").trimmed().toUpper(); QString pwr = match.captured("pwr").trimmed().toUpper();
// validate callsign // validate callsign
bool validToCallsign = (to != baseCallsign) && (basecalls.contains(to) || QRegularExpression(compound_callsign_pattern).match(to).hasMatch()); auto parsedTo = QRegularExpression(compound_callsign_pattern).match(to);
bool validToCallsign = (to != baseCallsign) && (basecalls.contains(to) || parsedTo.hasMatch());
if(!validToCallsign){ if(!validToCallsign){
if(n) *n = 0; if(n) *n = 0;
return frame; return frame;
} }
if(parsedTo.hasMatch()){
auto parsedBase = parsedTo.captured("base");
if(parsedBase.length() != to.length()){
to = parsedBase;
}
}
// validate command // validate command
if(!Varicode::isCommandAllowed(cmd)){ if(!Varicode::isCommandAllowed(cmd)){
if(n) *n = 0; if(n) *n = 0;
+4 -1
View File
@@ -83,8 +83,11 @@ public:
static bool isCommandAllowed(const QString &cmd); static bool isCommandAllowed(const QString &cmd);
static bool isCommandBuffered(const QString &cmd); static bool isCommandBuffered(const QString &cmd);
static QString packBeaconMessage(QString const &callsign, QString const &extra, bool isCQ);
static QStringList unpackBeaconMessage(const QString &text, bool *isCQ);
static QString packCompoundMessage(const QString &baseCallsign, const QString &fix, bool isPrefix, quint16 num); static QString packCompoundMessage(const QString &baseCallsign, const QString &fix, bool isPrefix, quint16 num);
static QStringList unpackCompoundMessage(const QString &text); static QStringList unpackCompoundMessage(const QString &text, quint16 *pNum);
static QString packDirectedMessage(QString const& text, QString const& callsign, QString * pCmd, int *n); static QString packDirectedMessage(QString const& text, QString const& callsign, QString * pCmd, int *n);
static QStringList unpackDirectedMessage(QString const& text); static QStringList unpackDirectedMessage(QString const& text);