Restructured building frames and decoding for better support of compound callsigns
This commit is contained in:
parent
1df975f274
commit
5d64ac37c5
@ -106,15 +106,30 @@ bool DecodedText::tryUnpackBeacon(){
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto extra = parts.at(2);
|
// Beacon Alt Type
|
||||||
|
// ---------------
|
||||||
|
// 1 0 BCN
|
||||||
|
// 1 1 CQ
|
||||||
|
// 0 0 DE
|
||||||
|
// 0 1 TO
|
||||||
|
isBeacon_ = isBeacon;
|
||||||
|
isAlt_ = isAlt;
|
||||||
|
extra_ = parts.at(2);
|
||||||
compound_ = QStringList{ parts.at(0), parts.at(1) }.join("/");
|
compound_ = QStringList{ parts.at(0), parts.at(1) }.join("/");
|
||||||
|
|
||||||
|
// BCN|CQ
|
||||||
if(isBeacon){
|
if(isBeacon){
|
||||||
message_ = QString("%1: BCN %2 ").arg(compound_).arg(extra);
|
message_ = QString("%1: %2 %3 ").arg(compound_).arg(isAlt ? "CQ" : "BCN").arg(extra_);
|
||||||
} else {
|
} else {
|
||||||
|
// :TO
|
||||||
|
if(isAlt){
|
||||||
|
message_ = QString("%1").arg(compound_);
|
||||||
|
}
|
||||||
|
// DE:
|
||||||
|
else {
|
||||||
message_ = QString("%1: ").arg(compound_);
|
message_ = QString("%1: ").arg(compound_);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,10 @@ public:
|
|||||||
QString compoundCall() const { return compound_; }
|
QString compoundCall() const { return compound_; }
|
||||||
bool isCompoundMessage() const { return !compound_.isEmpty(); }
|
bool isCompoundMessage() const { return !compound_.isEmpty(); }
|
||||||
|
|
||||||
|
QString extra() const { return extra_; }
|
||||||
|
bool isBeacon() const { return isBeacon_; }
|
||||||
|
bool isAlt() const { return isAlt_; }
|
||||||
|
|
||||||
QStringList directedMessage() const { return directed_; }
|
QStringList directedMessage() const { return directed_; }
|
||||||
bool isDirectedMessage() const { return !directed_.isEmpty() && directed_.length() > 2; }
|
bool isDirectedMessage() const { return !directed_.isEmpty() && directed_.length() > 2; }
|
||||||
|
|
||||||
@ -91,7 +95,10 @@ private:
|
|||||||
column_mode = 19,
|
column_mode = 19,
|
||||||
column_qsoText = 22 };
|
column_qsoText = 22 };
|
||||||
|
|
||||||
|
bool isBeacon_;
|
||||||
|
bool isAlt_;
|
||||||
QString compound_;
|
QString compound_;
|
||||||
|
QString extra_;
|
||||||
QStringList directed_;
|
QStringList directed_;
|
||||||
QString string_;
|
QString string_;
|
||||||
int padding_;
|
int padding_;
|
||||||
|
346
mainwindow.cpp
346
mainwindow.cpp
@ -3299,14 +3299,21 @@ void MainWindow::readFromStdout() //readFromStdout
|
|||||||
m_config.color_DXCC(), m_config.color_NewCall(),
|
m_config.color_DXCC(), m_config.color_NewCall(),
|
||||||
m_config.ppfx(),(ui->cbCQonly->isVisible() and ui->cbCQonly->isChecked()));
|
m_config.ppfx(),(ui->cbCQonly->isVisible() and ui->cbCQonly->isChecked()));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Parse General Activity
|
// Parse General Activity
|
||||||
if(decodedtext.messageWords().length() > 0){
|
#if 1
|
||||||
|
bool shouldParseGeneralActivity = true;
|
||||||
|
if(shouldParseGeneralActivity && !decodedtext.messageWords().isEmpty()){
|
||||||
int offset = decodedtext.frequencyOffset();
|
int offset = decodedtext.frequencyOffset();
|
||||||
|
|
||||||
if(!m_bandActivity.contains(offset)){
|
if(!m_bandActivity.contains(offset)){
|
||||||
QList<int> offsets = {
|
QList<int> offsets = {
|
||||||
offset - 1, offset - 2, offset - 3, offset - 4, offset - 5,
|
offset - 1, offset - 2, offset - 3, offset - 4, offset - 5, offset - 6,
|
||||||
offset + 1, offset + 2, offset + 3, offset + 4, offset + 5};
|
offset + 1, offset + 2, offset + 3, offset + 4, offset + 5, offset + 6};
|
||||||
foreach(int prevOffset, offsets){
|
foreach(int prevOffset, offsets){
|
||||||
if(!m_bandActivity.contains(prevOffset)){ continue; }
|
if(!m_bandActivity.contains(prevOffset)){ continue; }
|
||||||
m_bandActivity[offset] = m_bandActivity[prevOffset];
|
m_bandActivity[offset] = m_bandActivity[prevOffset];
|
||||||
@ -3321,13 +3328,14 @@ void MainWindow::readFromStdout() //readFromStdout
|
|||||||
d.isCompound = decodedtext.isCompoundMessage();
|
d.isCompound = decodedtext.isCompoundMessage();
|
||||||
d.bits = decodedtext.bits();
|
d.bits = decodedtext.bits();
|
||||||
d.freq = offset;
|
d.freq = offset;
|
||||||
d.text = decodedtext.message(); //decodedtext.messageWords().isEmpty() ? "" : decodedtext.messageWords().first().trimmed();
|
d.text = decodedtext.message();
|
||||||
d.utcTimestamp = QDateTime::currentDateTimeUtc();
|
d.utcTimestamp = QDateTime::currentDateTimeUtc();
|
||||||
d.snr = decodedtext.snr();
|
d.snr = decodedtext.snr();
|
||||||
m_bandActivity[offset].append(d);
|
m_bandActivity[offset].append(d);
|
||||||
|
|
||||||
if(m_messageBuffer.contains(d.freq/10*10)){
|
// if we have a data frame, and a message buffer has been established, buffer it...
|
||||||
qDebug() << "buffering" << (d.freq/10*10) << d.text;
|
if(m_messageBuffer.contains(d.freq/10*10) && !decodedtext.isCompoundMessage() && !decodedtext.isDirectedMessage()){
|
||||||
|
qDebug() << "buffering data" << (d.freq/10*10) << d.text;
|
||||||
m_messageBuffer[d.freq/10*10].msgs.append(d);
|
m_messageBuffer[d.freq/10*10].msgs.append(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3335,8 +3343,107 @@ void MainWindow::readFromStdout() //readFromStdout
|
|||||||
m_bandActivity[offset].removeFirst();
|
m_bandActivity[offset].removeFirst();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// Process compound callsign commands (put them in cache)"
|
||||||
|
#if 1
|
||||||
|
bool shouldProcessCompound = true;
|
||||||
|
if(shouldProcessCompound && decodedtext.isCompoundMessage()){
|
||||||
|
#if ALIAS_COMPOUND_CALLS
|
||||||
|
QString compoundCall = decodedtext.compoundCall();
|
||||||
|
m_compoundCallCache[Radio::base_callsign(compoundCall)] = compoundCall;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CallDetail cd;
|
||||||
|
cd.call = decodedtext.compoundCall();
|
||||||
|
cd.grid = decodedtext.extra(); // compound calls via beacons may contain grid...
|
||||||
|
cd.snr = decodedtext.snr();
|
||||||
|
cd.freq = decodedtext.frequencyOffset();
|
||||||
|
cd.utcTimestamp = QDateTime::currentDateTimeUtc();
|
||||||
|
cd.bits = decodedtext.bits();
|
||||||
|
|
||||||
|
if(!decodedtext.isBeacon()){
|
||||||
|
qDebug() << "buffering compound call" << cd.call << cd.bits;
|
||||||
|
m_messageBuffer[cd.freq/10*10].compound.append(cd);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
// DISABLED FOR NOW...
|
||||||
|
else {
|
||||||
|
logCallActivity(cd);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
//
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Parse commands
|
||||||
|
// KN4CRD K1JT ?
|
||||||
|
#if 1
|
||||||
|
bool shouldProcessDirected = true;
|
||||||
|
if(shouldProcessDirected && decodedtext.isDirectedMessage()){
|
||||||
|
auto parts = decodedtext.directedMessage();
|
||||||
|
|
||||||
|
CommandDetail d;
|
||||||
|
#if ALIAS_COMPOUND_CALLS
|
||||||
|
d.from = lookupCallInCompoundCache(parts.at(0));
|
||||||
|
#endif
|
||||||
|
d.from = parts.at(0);
|
||||||
|
d.to = parts.at(1);
|
||||||
|
d.cmd = parts.at(2);
|
||||||
|
d.freq = decodedtext.frequencyOffset();
|
||||||
|
d.snr = decodedtext.snr();
|
||||||
|
d.utcTimestamp = QDateTime::currentDateTimeUtc();
|
||||||
|
d.bits = decodedtext.bits();
|
||||||
|
|
||||||
|
// if the command is a buffered command OR we have from or to in a separate message (compound)
|
||||||
|
if(Varicode::isCommandBuffered(d.cmd) || d.from == "<....>" || d.to == "<....>"){
|
||||||
|
qDebug() << "buffering cmd" << d.cmd << d.from << d.to;
|
||||||
|
m_messageBuffer[d.freq/10*10].cmd = d;
|
||||||
|
m_messageBuffer[d.freq/10*10].msgs.clear();
|
||||||
|
} else {
|
||||||
|
m_rxCommandQueue.append(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// DISABLED FOR NOW...
|
||||||
|
CallDetail cd;
|
||||||
|
cd.bits = d.bits;
|
||||||
|
cd.call = d.from;
|
||||||
|
cd.grid = "";
|
||||||
|
cd.snr = d.snr;
|
||||||
|
cd.freq = d.freq;
|
||||||
|
cd.utcTimestamp = d.utcTimestamp;
|
||||||
|
logCallActivity(cd);
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
bool shouldCaptureThirdPartyCallsigns = false;
|
||||||
|
// check to see if this is a station we've heard 3rd party
|
||||||
|
if(shouldCaptureThirdPartyCallsigns && Radio::base_callsign(d.to) != Radio::base_callsign(m_config.my_callsign())){
|
||||||
|
QString relayCall = QString("%1|%2").arg(Radio::base_callsign(d.from)).arg(Radio::base_callsign(d.to));
|
||||||
|
int snr = -100;
|
||||||
|
if(parts.length() == 4){
|
||||||
|
snr = QString(parts.at(3)).toInt();
|
||||||
|
}
|
||||||
|
CallDetail td;
|
||||||
|
td.through = d.from;
|
||||||
|
td.call = d.to;
|
||||||
|
td.grid = "";
|
||||||
|
td.snr = snr;
|
||||||
|
td.freq = d.freq;
|
||||||
|
td.utcTimestamp = d.utcTimestamp;
|
||||||
|
m_callActivity[relayCall] = td;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Parse CQs
|
// Parse CQs
|
||||||
|
#if 0
|
||||||
|
bool shouldParseCQs = false;
|
||||||
|
if(shouldParseCQs){
|
||||||
QString cqCall = decodedtext.CQersCall();
|
QString cqCall = decodedtext.CQersCall();
|
||||||
if(!cqCall.isEmpty()){
|
if(!cqCall.isEmpty()){
|
||||||
QString theircall;
|
QString theircall;
|
||||||
@ -3351,7 +3458,8 @@ void MainWindow::readFromStdout() //readFromStdout
|
|||||||
d.utcTimestamp = QDateTime::currentDateTimeUtc();
|
d.utcTimestamp = QDateTime::currentDateTimeUtc();
|
||||||
m_callActivity[Radio::base_callsign(cqCall)] = d; // original call is stored in CallDetail.
|
m_callActivity[Radio::base_callsign(cqCall)] = d; // original call is stored in CallDetail.
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Parse standard message callsigns
|
// Parse standard message callsigns
|
||||||
// K1JT KN4CRD EM73
|
// K1JT KN4CRD EM73
|
||||||
@ -3359,7 +3467,8 @@ void MainWindow::readFromStdout() //readFromStdout
|
|||||||
// K1JT KN4CRD R-12
|
// K1JT KN4CRD R-12
|
||||||
// DE KN4CRD
|
// DE KN4CRD
|
||||||
// KN4CRD
|
// KN4CRD
|
||||||
bool shouldParseCallsigns = true;
|
#if 0
|
||||||
|
bool shouldParseCallsigns = false;
|
||||||
if(shouldParseCallsigns){
|
if(shouldParseCallsigns){
|
||||||
QStringList callsigns = Varicode::parseCallsigns(decodedtext.message());
|
QStringList callsigns = Varicode::parseCallsigns(decodedtext.message());
|
||||||
if(!callsigns.isEmpty()){
|
if(!callsigns.isEmpty()){
|
||||||
@ -3390,76 +3499,7 @@ void MainWindow::readFromStdout() //readFromStdout
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
// Parse commands
|
|
||||||
// KN4CRD K1JT ?
|
|
||||||
bool shouldProcessDirected = true;
|
|
||||||
if(shouldProcessDirected && decodedtext.isDirectedMessage()){
|
|
||||||
auto parts = decodedtext.directedMessage();
|
|
||||||
|
|
||||||
CommandDetail d;
|
|
||||||
d.from = lookupCallInCompoundCache(parts.at(0));
|
|
||||||
d.to = parts.at(1);
|
|
||||||
d.cmd = parts.at(2);
|
|
||||||
d.freq = decodedtext.frequencyOffset();
|
|
||||||
d.snr = decodedtext.snr();
|
|
||||||
d.utcTimestamp = QDateTime::currentDateTimeUtc();
|
|
||||||
|
|
||||||
if(Varicode::isCommandBuffered(d.cmd)){
|
|
||||||
m_messageBuffer[d.freq/10*10].cmd = d;
|
|
||||||
m_messageBuffer[d.freq/10*10].msgs.clear();
|
|
||||||
} else {
|
|
||||||
m_rxCommandQueue.append(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
CallDetail cd;
|
|
||||||
cd.call = d.from;
|
|
||||||
cd.grid = "";
|
|
||||||
cd.snr = d.snr;
|
|
||||||
cd.freq = d.freq;
|
|
||||||
cd.utcTimestamp = d.utcTimestamp;
|
|
||||||
m_callActivity[Radio::base_callsign(cd.call)] = cd;
|
|
||||||
|
|
||||||
bool shouldCaptureThirdPartyCallsigns = false;
|
|
||||||
// check to see if this is a station we've heard 3rd party
|
|
||||||
if(shouldCaptureThirdPartyCallsigns && Radio::base_callsign(d.to) != Radio::base_callsign(m_config.my_callsign())){
|
|
||||||
QString relayCall = QString("%1|%2").arg(Radio::base_callsign(d.from)).arg(Radio::base_callsign(d.to));
|
|
||||||
int snr = -100;
|
|
||||||
if(parts.length() == 4){
|
|
||||||
snr = QString(parts.at(3)).toInt();
|
|
||||||
}
|
|
||||||
CallDetail td;
|
|
||||||
td.through = d.from;
|
|
||||||
td.call = d.to;
|
|
||||||
td.grid = "";
|
|
||||||
td.snr = snr;
|
|
||||||
td.freq = d.freq;
|
|
||||||
td.utcTimestamp = d.utcTimestamp;
|
|
||||||
m_callActivity[relayCall] = td;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nsec=QDateTime::currentMSecsSinceEpoch()/1000-m_secBandChanged;
|
|
||||||
bool okToPost=(nsec>(4*m_TRperiod)/5);
|
|
||||||
if (okToPost){
|
|
||||||
pskSetLocal();
|
|
||||||
pskLogReport("FT8Call", d.freq, d.snr, d.from, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process compound callsign commands (put them in cache)"
|
|
||||||
bool shouldProcessCompound = true;
|
|
||||||
if(shouldProcessCompound && decodedtext.isCompoundMessage()){
|
|
||||||
QString compoundCall = decodedtext.compoundCall();
|
|
||||||
m_compoundCallCache[Radio::base_callsign(compoundCall)] = compoundCall;
|
|
||||||
|
|
||||||
CallDetail cd;
|
|
||||||
cd.call = compoundCall;
|
|
||||||
cd.grid = ""; // compound calls via beacons contain grid...
|
|
||||||
cd.snr = decodedtext.snr();
|
|
||||||
cd.freq = decodedtext.frequencyOffset();
|
|
||||||
cd.utcTimestamp = QDateTime::currentDateTimeUtc();
|
|
||||||
m_callActivity[Radio::base_callsign(cd.call)] = cd;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3490,6 +3530,7 @@ void MainWindow::readFromStdout() //readFromStdout
|
|||||||
bDisplayRight=true;
|
bDisplayRight=true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
if(isRecentOffset(audioFreq) || isAllCallIncluded(decodedtext.message())){
|
if(isRecentOffset(audioFreq) || isAllCallIncluded(decodedtext.message())){
|
||||||
// TODO: jsherer - create a method for bumping this...
|
// TODO: jsherer - create a method for bumping this...
|
||||||
m_rxRecentCache.insert(audioFreq/10*10, new QDateTime(QDateTime::currentDateTimeUtc()), 25);
|
m_rxRecentCache.insert(audioFreq/10*10, new QDateTime(QDateTime::currentDateTimeUtc()), 25);
|
||||||
@ -3500,6 +3541,7 @@ void MainWindow::readFromStdout() //readFromStdout
|
|||||||
m_rxDirectedCache.insert(audioFreq/10*10, new QDateTime(QDateTime::currentDateTimeUtc()), 25);
|
m_rxDirectedCache.insert(audioFreq/10*10, new QDateTime(QDateTime::currentDateTimeUtc()), 25);
|
||||||
bDisplayRight = true;
|
bDisplayRight = true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (bDisplayRight) {
|
if (bDisplayRight) {
|
||||||
// This msg is within 10 hertz of our tuned frequency, or a JT4 or JT65 avg,
|
// This msg is within 10 hertz of our tuned frequency, or a JT4 or JT65 avg,
|
||||||
@ -3515,6 +3557,7 @@ void MainWindow::readFromStdout() //readFromStdout
|
|||||||
}
|
}
|
||||||
m_QSOText = decodedtext.string ().trimmed ();
|
m_QSOText = decodedtext.string ().trimmed ();
|
||||||
|
|
||||||
|
#if 0
|
||||||
// TODO: jsherer - parse decode...
|
// TODO: jsherer - parse decode...
|
||||||
ActivityDetail d;
|
ActivityDetail d;
|
||||||
d.isFree = !decodedtext.isStandardMessage();
|
d.isFree = !decodedtext.isStandardMessage();
|
||||||
@ -3524,6 +3567,7 @@ void MainWindow::readFromStdout() //readFromStdout
|
|||||||
d.text = decodedtext.message();
|
d.text = decodedtext.message();
|
||||||
d.utcTimestamp = QDateTime::currentDateTimeUtc();
|
d.utcTimestamp = QDateTime::currentDateTimeUtc();
|
||||||
m_rxFrameQueue.append(d);
|
m_rxFrameQueue.append(d);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_mode=="FT8" and m_config.bHound()) {
|
if(m_mode=="FT8" and m_config.bHound()) {
|
||||||
@ -3605,6 +3649,21 @@ void MainWindow::readFromStdout() //readFromStdout
|
|||||||
// See MainWindow::postDecode for displaying the latest decodes
|
// See MainWindow::postDecode for displaying the latest decodes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MainWindow::logCallActivity(CallDetail d){
|
||||||
|
if(m_callActivity.contains(d.call)){
|
||||||
|
// update (keep grid)
|
||||||
|
CallDetail old = m_callActivity[d.call];
|
||||||
|
if(!old.grid.isEmpty()){
|
||||||
|
d.grid = old.grid;
|
||||||
|
}
|
||||||
|
m_callActivity[d.call] = d;
|
||||||
|
} else {
|
||||||
|
// create
|
||||||
|
m_callActivity[d.call] = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QString MainWindow::lookupCallInCompoundCache(QString const &call){
|
QString MainWindow::lookupCallInCompoundCache(QString const &call){
|
||||||
QString myBaseCall = Radio::base_callsign(m_config.my_callsign());
|
QString myBaseCall = Radio::base_callsign(m_config.my_callsign());
|
||||||
if(call == myBaseCall){
|
if(call == myBaseCall){
|
||||||
@ -5575,6 +5634,7 @@ void MainWindow::clearActivity(){
|
|||||||
int MainWindow::logRxTxMessageText(QDateTime date, QString text, int freq, bool tx, int block){
|
int MainWindow::logRxTxMessageText(QDateTime date, QString text, int freq, bool tx, int block){
|
||||||
auto c = ui->textEditRX->textCursor();
|
auto c = ui->textEditRX->textCursor();
|
||||||
|
|
||||||
|
#if ALIAS_COMPOUND_CALLS
|
||||||
// fixup compound callsigns cache / aliases...
|
// fixup compound callsigns cache / aliases...
|
||||||
// ensure our callsign is cached...
|
// ensure our callsign is cached...
|
||||||
QString myCall = m_config.my_callsign();
|
QString myCall = m_config.my_callsign();
|
||||||
@ -5587,6 +5647,7 @@ int MainWindow::logRxTxMessageText(QDateTime date, QString text, int freq, bool
|
|||||||
QRegularExpression re(QString(R"((?<![\/])\b%1\b(?![\/]))").arg(QRegularExpression::escape(call)));
|
QRegularExpression re(QString(R"((?<![\/])\b%1\b(?![\/]))").arg(QRegularExpression::escape(call)));
|
||||||
text = text.replace(re, m_compoundCallCache[call]);
|
text = text.replace(re, m_compoundCallCache[call]);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
if(block != -1){
|
if(block != -1){
|
||||||
@ -5668,13 +5729,17 @@ void MainWindow::createMessage(QString const& text){
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::createMessageTransmitQueue(QString const& text){
|
void MainWindow::createMessageTransmitQueue(QString const& text){
|
||||||
auto pair = buildFT8MessageFrames(text);
|
auto frames = buildFT8MessageFrames(text);
|
||||||
auto frames = pair.first;
|
|
||||||
auto lines = pair.second;
|
|
||||||
m_txFrameQueue.append(frames);
|
m_txFrameQueue.append(frames);
|
||||||
m_txFrameCount = frames.length();
|
m_txFrameCount = frames.length();
|
||||||
|
|
||||||
int freq = currentFreq();
|
int freq = currentFreq();
|
||||||
|
|
||||||
|
QStringList lines;
|
||||||
|
foreach(auto frame, frames){
|
||||||
|
lines.append(DecodedText(frame).message());
|
||||||
|
}
|
||||||
|
|
||||||
logRxTxMessageText(QDateTime::currentDateTimeUtc(), lines.join(""), freq, true);
|
logRxTxMessageText(QDateTime::currentDateTimeUtc(), lines.join(""), freq, true);
|
||||||
|
|
||||||
// keep track of the last message text sent
|
// keep track of the last message text sent
|
||||||
@ -5766,12 +5831,11 @@ int MainWindow::currentFreq(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
int MainWindow::countFT8MessageFrames(QString const& text){
|
int MainWindow::countFT8MessageFrames(QString const& text){
|
||||||
return buildFT8MessageFrames(text).first.length();
|
return buildFT8MessageFrames(text).length();
|
||||||
}
|
}
|
||||||
|
|
||||||
QPair<QStringList, QStringList> MainWindow::buildFT8MessageFrames(QString const& text){
|
QStringList MainWindow::buildFT8MessageFrames(QString const& text){
|
||||||
QStringList frames;
|
QStringList frames;
|
||||||
QStringList lines;
|
|
||||||
|
|
||||||
// prepare compound
|
// prepare compound
|
||||||
bool compoundSent = false;
|
bool compoundSent = false;
|
||||||
@ -5779,6 +5843,9 @@ QPair<QStringList, QStringList> MainWindow::buildFT8MessageFrames(QString const&
|
|||||||
QString mygrid = m_config.my_grid();
|
QString mygrid = m_config.my_grid();
|
||||||
QString mycall = m_config.my_callsign();
|
QString mycall = m_config.my_callsign();
|
||||||
QString basecall = Radio::base_callsign(m_config.my_callsign());
|
QString basecall = Radio::base_callsign(m_config.my_callsign());
|
||||||
|
if(basecall != mycall){
|
||||||
|
basecall = "<....>";
|
||||||
|
}
|
||||||
QString fix = QString(m_config.my_callsign()).replace(basecall, "");
|
QString fix = QString(m_config.my_callsign()).replace(basecall, "");
|
||||||
bool prefix = !fix.startsWith("/");
|
bool prefix = !fix.startsWith("/");
|
||||||
fix = fix.replace("/", "");
|
fix = fix.replace("/", "");
|
||||||
@ -5811,10 +5878,11 @@ QPair<QStringList, QStringList> MainWindow::buildFT8MessageFrames(QString const&
|
|||||||
|
|
||||||
int n = 0;
|
int n = 0;
|
||||||
QString dirCmd;
|
QString dirCmd;
|
||||||
QString dirFrame = Varicode::packDirectedMessage(line, basecall, &dirCmd, &n);
|
QString dirTo;
|
||||||
|
QString dirFrame = Varicode::packDirectedMessage(line, basecall, &dirTo, &dirCmd, &n);
|
||||||
|
|
||||||
|
// packDataMessage can output a new line to datLineOut (huff escaping special characters)
|
||||||
int m = 0;
|
int m = 0;
|
||||||
// packDataMessage can output a new line (huff escaping special characters)
|
|
||||||
QString datLineOut;
|
QString datLineOut;
|
||||||
QString datFrame = Varicode::packDataMessage(line.left(21) + "\x04", &datLineOut, &m); // 63 / 3 = 21 (maximum number of 3bit chars we could possibly stuff in here)
|
QString datFrame = Varicode::packDataMessage(line.left(21) + "\x04", &datLineOut, &m); // 63 / 3 = 21 (maximum number of 3bit chars we could possibly stuff in here)
|
||||||
|
|
||||||
@ -5850,7 +5918,6 @@ QPair<QStringList, QStringList> MainWindow::buildFT8MessageFrames(QString const&
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
frames.append(frame);
|
frames.append(frame);
|
||||||
lines.append(line.left(frame.length()) + " ");
|
|
||||||
|
|
||||||
if(!line.startsWith(frame)){
|
if(!line.startsWith(frame)){
|
||||||
line = (
|
line = (
|
||||||
@ -5864,30 +5931,35 @@ QPair<QStringList, QStringList> MainWindow::buildFT8MessageFrames(QString const&
|
|||||||
|
|
||||||
if(useBcn){
|
if(useBcn){
|
||||||
frames.append(frame);
|
frames.append(frame);
|
||||||
lines.append(QString("%1: ").arg(mycall) + line.left(l) + " ");
|
|
||||||
line = line.mid(l);
|
line = line.mid(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(useDir){
|
if(useDir){
|
||||||
if(compound && !compoundSent){
|
// from compound callsign
|
||||||
|
if(compound){
|
||||||
QString compoundMessage = QString("DE %1").arg(mygrid);
|
QString compoundMessage = QString("DE %1").arg(mygrid);
|
||||||
QString beaconFrame = Varicode::packBeaconMessage(compoundMessage, mycall, nullptr);
|
QString beaconFrame = Varicode::packBeaconMessage(compoundMessage, mycall, nullptr);
|
||||||
if(!beaconFrame.isEmpty()){
|
if(!beaconFrame.isEmpty()){
|
||||||
frames.append(beaconFrame);
|
frames.append(beaconFrame);
|
||||||
lines.append(QString("%1: ").arg(mycall));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
frames.append(frame);
|
frames.append(frame);
|
||||||
|
|
||||||
// TODO: jsherer - would be nice to clean this up and have an object that can just decode the actual transmitted frames instead.
|
// to compound callsign
|
||||||
if(!line.startsWith(basecall) && !compound){
|
if(!dirTo.isEmpty()){
|
||||||
lines.append(QString("%1: ").arg(lookupCallInCompoundCache(basecall)));
|
if(dirTo.contains("/")){
|
||||||
|
QString compoundMessage = QString("TO");
|
||||||
|
QString beaconFrame = Varicode::packBeaconMessage(compoundMessage, dirTo, nullptr);
|
||||||
|
if(!beaconFrame.isEmpty()){
|
||||||
|
frames.append(beaconFrame);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lines.append(line.left(n) + " ");
|
|
||||||
line = line.mid(n);
|
line = line.mid(n);
|
||||||
|
|
||||||
|
// generate a checksum for buffered commands with line data
|
||||||
if(Varicode::isCommandBuffered(dirCmd) && !line.isEmpty()){
|
if(Varicode::isCommandBuffered(dirCmd) && !line.isEmpty()){
|
||||||
// strip leading whitespace after a buffered directed command
|
// strip leading whitespace after a buffered directed command
|
||||||
line = lstrip(line);
|
line = lstrip(line);
|
||||||
@ -5900,7 +5972,6 @@ QPair<QStringList, QStringList> MainWindow::buildFT8MessageFrames(QString const&
|
|||||||
|
|
||||||
if(useDat){
|
if(useDat){
|
||||||
frames.append(frame);
|
frames.append(frame);
|
||||||
lines.append(line.left(m));
|
|
||||||
line = line.mid(m);
|
line = line.mid(m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5909,16 +5980,11 @@ 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::unpackCompoundFrame(frame, nullptr) << Varicode::unpackBeaconMessage(frame, nullptr);
|
qDebug() << "->" << frame << Varicode::unpackDataMessage(frame) << Varicode::unpackDirectedMessage(frame) << Varicode::unpackCompoundFrame(frame, nullptr, nullptr) << Varicode::unpackBeaconMessage(frame, nullptr, nullptr);
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "lines:";
|
|
||||||
foreach(auto line, lines){
|
|
||||||
qDebug() << "->" << line;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return {frames,lines};
|
return frames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -8634,6 +8700,10 @@ void MainWindow::displayActivity(bool force){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Is it ok to post spots to PSKReporter?
|
||||||
|
int nsec=QDateTime::currentMSecsSinceEpoch()/1000-m_secBandChanged;
|
||||||
|
bool okToPost=(nsec>(4*m_TRperiod)/5);
|
||||||
|
|
||||||
// Selected Rows
|
// Selected Rows
|
||||||
int selectedOffset = -1;
|
int selectedOffset = -1;
|
||||||
auto selectedItems = ui->tableWidgetRXAll->selectedItems();
|
auto selectedItems = ui->tableWidgetRXAll->selectedItems();
|
||||||
@ -8826,6 +8896,64 @@ void MainWindow::displayActivity(bool force){
|
|||||||
|
|
||||||
// Grouped Compound Activity
|
// Grouped Compound Activity
|
||||||
// TODO: jsherer - group compound callsign and directed commands together.
|
// TODO: jsherer - group compound callsign and directed commands together.
|
||||||
|
foreach(auto freq, m_messageBuffer.keys()){
|
||||||
|
QMap<int, MessageBuffer>::iterator i = m_messageBuffer.find(freq);
|
||||||
|
|
||||||
|
MessageBuffer &buffer = i.value();
|
||||||
|
|
||||||
|
qDebug() << "-> grouping buffer for freq" << freq;
|
||||||
|
|
||||||
|
if(buffer.compound.isEmpty()){
|
||||||
|
qDebug() << "-> buffer.compound is empty...skip";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we don't have an initialized command, skip...
|
||||||
|
if(buffer.cmd.bits != Varicode::FT8Call && buffer.cmd.bits != Varicode::FT8CallLast){
|
||||||
|
qDebug() << "-> buffer.cmd bits is invalid...skip";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we need two compound calls, but less than two have arrived...skip
|
||||||
|
if(buffer.cmd.from == "<....>" && buffer.cmd.to == "<....>" && buffer.compound.length() < 2){
|
||||||
|
qDebug() << "-> buffer needs two compound, but has less...skip";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we need one compound call, but non have arrived...skip
|
||||||
|
if((buffer.cmd.from == "<....>" || buffer.cmd.to == "<....>") && buffer.compound.length() < 1){
|
||||||
|
qDebug() << "-> buffer needs one compound, but has less...skip";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(buffer.cmd.from == "<....>"){
|
||||||
|
auto d = buffer.compound.dequeue();
|
||||||
|
buffer.cmd.from = d.call;
|
||||||
|
|
||||||
|
if(d.bits == Varicode::FT8CallLast){
|
||||||
|
buffer.cmd.bits = d.bits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(buffer.cmd.to == "<....>"){
|
||||||
|
auto d = buffer.compound.dequeue();
|
||||||
|
buffer.cmd.to = d.call;
|
||||||
|
|
||||||
|
if(d.bits == Varicode::FT8CallLast){
|
||||||
|
buffer.cmd.bits = d.bits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(buffer.cmd.bits != Varicode::FT8CallLast){
|
||||||
|
qDebug() << "-> still not last message...skip";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
qDebug() << "buffered compound command ready" << buffer.cmd.from << buffer.cmd.to << buffer.cmd.cmd;
|
||||||
|
|
||||||
|
m_rxCommandQueue.append(buffer.cmd);
|
||||||
|
m_messageBuffer.remove(freq);
|
||||||
|
}
|
||||||
|
|
||||||
// Buffered Activity
|
// Buffered Activity
|
||||||
foreach(auto freq, m_messageBuffer.keys()){
|
foreach(auto freq, m_messageBuffer.keys()){
|
||||||
@ -8879,6 +9007,11 @@ void MainWindow::displayActivity(bool force){
|
|||||||
qDebug() << "processing command" << d.from << d.to << d.cmd << d.freq;
|
qDebug() << "processing command" << d.from << d.to << d.cmd << d.freq;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// if we need a compound callsign but never got one...skip
|
||||||
|
if(d.from == "<....>" || d.to == "<....>"){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// we're only processing a subset of queries at this point
|
// we're only processing a subset of queries at this point
|
||||||
if(!Varicode::isCommandAllowed(d.cmd)){
|
if(!Varicode::isCommandAllowed(d.cmd)){
|
||||||
continue;
|
continue;
|
||||||
@ -8894,9 +9027,14 @@ void MainWindow::displayActivity(bool force){
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// construct reply
|
// record the spot to PSKReporter
|
||||||
QString reply;
|
if (okToPost){
|
||||||
|
pskSetLocal();
|
||||||
|
pskLogReport("FT8Call", d.freq, d.snr, d.from, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// construct a reply
|
||||||
|
QString reply;
|
||||||
|
|
||||||
// QUERIED SNR
|
// QUERIED SNR
|
||||||
if(d.cmd == "?"){
|
if(d.cmd == "?"){
|
||||||
|
@ -90,6 +90,7 @@ class MainWindow : public QMainWindow
|
|||||||
{
|
{
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
|
|
||||||
|
struct CallDetail;
|
||||||
public:
|
public:
|
||||||
using Frequency = Radio::Frequency;
|
using Frequency = Radio::Frequency;
|
||||||
using FrequencyDelta = Radio::FrequencyDelta;
|
using FrequencyDelta = Radio::FrequencyDelta;
|
||||||
@ -121,6 +122,7 @@ public slots:
|
|||||||
void msgAvgDecode2();
|
void msgAvgDecode2();
|
||||||
void fastPick(int x0, int x1, int y);
|
void fastPick(int x0, int x1, int y);
|
||||||
|
|
||||||
|
void logCallActivity(CallDetail d);
|
||||||
QString lookupCallInCompoundCache(QString const &call);
|
QString lookupCallInCompoundCache(QString const &call);
|
||||||
void clearActivity();
|
void clearActivity();
|
||||||
int logRxTxMessageText(QDateTime date, QString text, int freq, bool tx, int block=-1);
|
int logRxTxMessageText(QDateTime date, QString text, int freq, bool tx, int block=-1);
|
||||||
@ -257,7 +259,7 @@ private slots:
|
|||||||
void on_extFreeTextMsgEdit_currentTextChanged (QString const&);
|
void on_extFreeTextMsgEdit_currentTextChanged (QString const&);
|
||||||
int currentFreq();
|
int currentFreq();
|
||||||
int countFT8MessageFrames(QString const& text);
|
int countFT8MessageFrames(QString const& text);
|
||||||
QPair<QStringList, QStringList> buildFT8MessageFrames(QString const& text);
|
QStringList buildFT8MessageFrames(QString const& text);
|
||||||
QString parseFT8Message(QString input, bool *isFree);
|
QString parseFT8Message(QString input, bool *isFree);
|
||||||
bool prepareNextMessageFrame();
|
bool prepareNextMessageFrame();
|
||||||
bool isFreqOffsetFree(int f, int bw);
|
bool isFreqOffsetFree(int f, int bw);
|
||||||
@ -648,6 +650,7 @@ private:
|
|||||||
int freq;
|
int freq;
|
||||||
QDateTime utcTimestamp;
|
QDateTime utcTimestamp;
|
||||||
int snr;
|
int snr;
|
||||||
|
int bits;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CommandDetail
|
struct CommandDetail
|
||||||
@ -658,6 +661,7 @@ private:
|
|||||||
int freq;
|
int freq;
|
||||||
QDateTime utcTimestamp;
|
QDateTime utcTimestamp;
|
||||||
int snr;
|
int snr;
|
||||||
|
int bits;
|
||||||
QString text;
|
QString text;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -674,6 +678,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct MessageBuffer {
|
struct MessageBuffer {
|
||||||
|
QQueue<CallDetail> compound;
|
||||||
CommandDetail cmd;
|
CommandDetail cmd;
|
||||||
QList<ActivityDetail> msgs;
|
QList<ActivityDetail> msgs;
|
||||||
};
|
};
|
||||||
|
31
varicode.cpp
31
varicode.cpp
@ -809,20 +809,33 @@ bool Varicode::isCommandBuffered(const QString &cmd){
|
|||||||
QString Varicode::packBeaconMessage(QString const &text, const QString &callsign, int *n){
|
QString Varicode::packBeaconMessage(QString const &text, const QString &callsign, int *n){
|
||||||
QString frame;
|
QString frame;
|
||||||
|
|
||||||
auto parsedText = QRegularExpression(R"(^(?<type>BCN|DE)\s(?<grid>[A-Z]{2}[0-9]{2})\b)").match(text);
|
auto parsedText = QRegularExpression(R"(^(?<type>BCN|CQ|DE|TO)(?:\s(?<grid>[A-Z]{2}[0-9]{2}))?\b)").match(text);
|
||||||
if(!parsedText.hasMatch()){
|
if(!parsedText.hasMatch()){
|
||||||
if(n) *n = 0;
|
if(n) *n = 0;
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto extra = parsedText.captured("grid");
|
auto extra = parsedText.captured("grid");
|
||||||
|
|
||||||
|
/*
|
||||||
if(extra.isEmpty()){
|
if(extra.isEmpty()){
|
||||||
if(n) *n = 0;
|
if(n) *n = 0;
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
auto isBeacon = parsedText.captured("type") == "BCN";
|
// Beacon Alt Type
|
||||||
auto isAlt = false;
|
// ---------------
|
||||||
|
// 1 0 BCN
|
||||||
|
// 1 1 CQ
|
||||||
|
// 0 0 DE
|
||||||
|
// 0 1 TO
|
||||||
|
|
||||||
|
auto type = parsedText.captured("type");
|
||||||
|
auto isBeacon = type == "BCN" || type == "CQ";
|
||||||
|
auto isAlt = type == "CQ" || type == "TO";
|
||||||
|
auto isDe = type == "DE";
|
||||||
|
Q_UNUSED(isDe);
|
||||||
|
|
||||||
auto parsedCall = QRegularExpression(compound_callsign_pattern).match(callsign);
|
auto parsedCall = QRegularExpression(compound_callsign_pattern).match(callsign);
|
||||||
if(!parsedCall.hasMatch()){
|
if(!parsedCall.hasMatch()){
|
||||||
@ -953,7 +966,7 @@ QStringList Varicode::unpackCompoundFrame(const QString &text, bool *isBeacon, q
|
|||||||
return unpacked;
|
return unpacked;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Varicode::packDirectedMessage(const QString &text, const QString &baseCallsign, QString * pCmd, int *n){
|
QString Varicode::packDirectedMessage(const QString &text, const QString &callsign, QString *pTo, QString * pCmd, int *n){
|
||||||
QString frame;
|
QString frame;
|
||||||
|
|
||||||
auto match = directed_re.match(text);
|
auto match = directed_re.match(text);
|
||||||
@ -962,24 +975,26 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &baseCa
|
|||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString from = baseCallsign;
|
QString from = callsign;
|
||||||
QString to = match.captured("to");
|
QString to = match.captured("to");
|
||||||
QString cmd = match.captured("cmd");
|
QString cmd = match.captured("cmd");
|
||||||
QString num = match.captured("num").trimmed();
|
QString num = match.captured("num").trimmed();
|
||||||
QString pwr = match.captured("pwr").trimmed().toUpper();
|
QString pwr = match.captured("pwr").trimmed().toUpper();
|
||||||
|
|
||||||
// validate callsign
|
// validate "to" callsign
|
||||||
auto parsedTo = QRegularExpression(compound_callsign_pattern).match(to);
|
auto parsedTo = QRegularExpression(compound_callsign_pattern).match(to);
|
||||||
bool validToCallsign = (to != baseCallsign) && (basecalls.contains(to) || parsedTo.hasMatch());
|
bool validToCallsign = (to != callsign) && (basecalls.contains(to) || parsedTo.hasMatch());
|
||||||
if(!validToCallsign){
|
if(!validToCallsign){
|
||||||
if(n) *n = 0;
|
if(n) *n = 0;
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(parsedTo.hasMatch()){
|
if(parsedTo.hasMatch()){
|
||||||
|
if(pTo) *pTo = parsedTo.captured(0);
|
||||||
|
|
||||||
auto parsedBase = parsedTo.captured("base");
|
auto parsedBase = parsedTo.captured("base");
|
||||||
if(parsedBase.length() != to.length()){
|
if(parsedBase.length() != to.length()){
|
||||||
to = parsedBase;
|
to = "<....>"; // parsedBase;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ public:
|
|||||||
static QString packCompoundFrame(const QString &baseCallsign, const QString &fix, bool isPrefix, bool isBeacon, quint16 num);
|
static QString packCompoundFrame(const QString &baseCallsign, const QString &fix, bool isPrefix, bool isBeacon, quint16 num);
|
||||||
static QStringList unpackCompoundFrame(const QString &text, bool *isBeacon, quint16 *pNum);
|
static QStringList unpackCompoundFrame(const QString &text, bool *isBeacon, 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 *pTo, QString * pCmd, int *n);
|
||||||
static QStringList unpackDirectedMessage(QString const& text);
|
static QStringList unpackDirectedMessage(QString const& text);
|
||||||
|
|
||||||
static QString packDataMessage(QString const& text, QString *out, int *n);
|
static QString packDataMessage(QString const& text, QString *out, int *n);
|
||||||
|
Loading…
Reference in New Issue
Block a user