Restructured building frames and decoding for better support of compound callsigns
This commit is contained in:
parent
1df975f274
commit
5d64ac37c5
@ -106,14 +106,29 @@ bool DecodedText::tryUnpackBeacon(){
|
||||
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("/");
|
||||
|
||||
// BCN|CQ
|
||||
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 {
|
||||
message_ = QString("%1: ").arg(compound_);
|
||||
// :TO
|
||||
if(isAlt){
|
||||
message_ = QString("%1").arg(compound_);
|
||||
}
|
||||
// DE:
|
||||
else {
|
||||
message_ = QString("%1: ").arg(compound_);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -41,6 +41,10 @@ public:
|
||||
QString compoundCall() const { return compound_; }
|
||||
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_; }
|
||||
bool isDirectedMessage() const { return !directed_.isEmpty() && directed_.length() > 2; }
|
||||
|
||||
@ -91,7 +95,10 @@ private:
|
||||
column_mode = 19,
|
||||
column_qsoText = 22 };
|
||||
|
||||
bool isBeacon_;
|
||||
bool isAlt_;
|
||||
QString compound_;
|
||||
QString extra_;
|
||||
QStringList directed_;
|
||||
QString string_;
|
||||
int padding_;
|
||||
|
370
mainwindow.cpp
370
mainwindow.cpp
@ -3299,14 +3299,21 @@ void MainWindow::readFromStdout() //readFromStdout
|
||||
m_config.color_DXCC(), m_config.color_NewCall(),
|
||||
m_config.ppfx(),(ui->cbCQonly->isVisible() and ui->cbCQonly->isChecked()));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Parse General Activity
|
||||
if(decodedtext.messageWords().length() > 0){
|
||||
#if 1
|
||||
bool shouldParseGeneralActivity = true;
|
||||
if(shouldParseGeneralActivity && !decodedtext.messageWords().isEmpty()){
|
||||
int offset = decodedtext.frequencyOffset();
|
||||
|
||||
if(!m_bandActivity.contains(offset)){
|
||||
QList<int> offsets = {
|
||||
offset - 1, offset - 2, offset - 3, offset - 4, offset - 5,
|
||||
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 + 6};
|
||||
foreach(int prevOffset, offsets){
|
||||
if(!m_bandActivity.contains(prevOffset)){ continue; }
|
||||
m_bandActivity[offset] = m_bandActivity[prevOffset];
|
||||
@ -3321,13 +3328,14 @@ void MainWindow::readFromStdout() //readFromStdout
|
||||
d.isCompound = decodedtext.isCompoundMessage();
|
||||
d.bits = decodedtext.bits();
|
||||
d.freq = offset;
|
||||
d.text = decodedtext.message(); //decodedtext.messageWords().isEmpty() ? "" : decodedtext.messageWords().first().trimmed();
|
||||
d.text = decodedtext.message();
|
||||
d.utcTimestamp = QDateTime::currentDateTimeUtc();
|
||||
d.snr = decodedtext.snr();
|
||||
m_bandActivity[offset].append(d);
|
||||
|
||||
if(m_messageBuffer.contains(d.freq/10*10)){
|
||||
qDebug() << "buffering" << (d.freq/10*10) << d.text;
|
||||
// if we have a data frame, and a message buffer has been established, buffer it...
|
||||
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);
|
||||
}
|
||||
|
||||
@ -3335,23 +3343,123 @@ void MainWindow::readFromStdout() //readFromStdout
|
||||
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
|
||||
QString cqCall = decodedtext.CQersCall();
|
||||
if(!cqCall.isEmpty()){
|
||||
QString theircall;
|
||||
QString theirgrid;
|
||||
decodedtext.deCallAndGrid(/*out*/theircall,theirgrid);
|
||||
#if 0
|
||||
bool shouldParseCQs = false;
|
||||
if(shouldParseCQs){
|
||||
QString cqCall = decodedtext.CQersCall();
|
||||
if(!cqCall.isEmpty()){
|
||||
QString theircall;
|
||||
QString theirgrid;
|
||||
decodedtext.deCallAndGrid(/*out*/theircall,theirgrid);
|
||||
|
||||
CallDetail d;
|
||||
d.call = theircall;
|
||||
d.grid = theirgrid;
|
||||
d.snr = decodedtext.snr();
|
||||
d.freq = decodedtext.frequencyOffset();
|
||||
d.utcTimestamp = QDateTime::currentDateTimeUtc();
|
||||
m_callActivity[Radio::base_callsign(cqCall)] = d; // original call is stored in CallDetail.
|
||||
CallDetail d;
|
||||
d.call = theircall;
|
||||
d.grid = theirgrid;
|
||||
d.snr = decodedtext.snr();
|
||||
d.freq = decodedtext.frequencyOffset();
|
||||
d.utcTimestamp = QDateTime::currentDateTimeUtc();
|
||||
m_callActivity[Radio::base_callsign(cqCall)] = d; // original call is stored in CallDetail.
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Parse standard message callsigns
|
||||
// K1JT KN4CRD EM73
|
||||
@ -3359,7 +3467,8 @@ void MainWindow::readFromStdout() //readFromStdout
|
||||
// K1JT KN4CRD R-12
|
||||
// DE KN4CRD
|
||||
// KN4CRD
|
||||
bool shouldParseCallsigns = true;
|
||||
#if 0
|
||||
bool shouldParseCallsigns = false;
|
||||
if(shouldParseCallsigns){
|
||||
QStringList callsigns = Varicode::parseCallsigns(decodedtext.message());
|
||||
if(!callsigns.isEmpty()){
|
||||
@ -3390,76 +3499,7 @@ void MainWindow::readFromStdout() //readFromStdout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -3490,6 +3530,7 @@ void MainWindow::readFromStdout() //readFromStdout
|
||||
bDisplayRight=true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if(isRecentOffset(audioFreq) || isAllCallIncluded(decodedtext.message())){
|
||||
// TODO: jsherer - create a method for bumping this...
|
||||
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);
|
||||
bDisplayRight = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bDisplayRight) {
|
||||
// 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 ();
|
||||
|
||||
#if 0
|
||||
// TODO: jsherer - parse decode...
|
||||
ActivityDetail d;
|
||||
d.isFree = !decodedtext.isStandardMessage();
|
||||
@ -3524,6 +3567,7 @@ void MainWindow::readFromStdout() //readFromStdout
|
||||
d.text = decodedtext.message();
|
||||
d.utcTimestamp = QDateTime::currentDateTimeUtc();
|
||||
m_rxFrameQueue.append(d);
|
||||
#endif
|
||||
}
|
||||
|
||||
if(m_mode=="FT8" and m_config.bHound()) {
|
||||
@ -3605,6 +3649,21 @@ void MainWindow::readFromStdout() //readFromStdout
|
||||
// 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 myBaseCall = Radio::base_callsign(m_config.my_callsign());
|
||||
if(call == myBaseCall){
|
||||
@ -5575,6 +5634,7 @@ void MainWindow::clearActivity(){
|
||||
int MainWindow::logRxTxMessageText(QDateTime date, QString text, int freq, bool tx, int block){
|
||||
auto c = ui->textEditRX->textCursor();
|
||||
|
||||
#if ALIAS_COMPOUND_CALLS
|
||||
// fixup compound callsigns cache / aliases...
|
||||
// ensure our callsign is cached...
|
||||
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)));
|
||||
text = text.replace(re, m_compoundCallCache[call]);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool found = false;
|
||||
if(block != -1){
|
||||
@ -5668,13 +5729,17 @@ void MainWindow::createMessage(QString const& text){
|
||||
}
|
||||
|
||||
void MainWindow::createMessageTransmitQueue(QString const& text){
|
||||
auto pair = buildFT8MessageFrames(text);
|
||||
auto frames = pair.first;
|
||||
auto lines = pair.second;
|
||||
auto frames = buildFT8MessageFrames(text);
|
||||
m_txFrameQueue.append(frames);
|
||||
m_txFrameCount = frames.length();
|
||||
|
||||
int freq = currentFreq();
|
||||
|
||||
QStringList lines;
|
||||
foreach(auto frame, frames){
|
||||
lines.append(DecodedText(frame).message());
|
||||
}
|
||||
|
||||
logRxTxMessageText(QDateTime::currentDateTimeUtc(), lines.join(""), freq, true);
|
||||
|
||||
// keep track of the last message text sent
|
||||
@ -5766,12 +5831,11 @@ int MainWindow::currentFreq(){
|
||||
}
|
||||
|
||||
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 lines;
|
||||
|
||||
// prepare compound
|
||||
bool compoundSent = false;
|
||||
@ -5779,6 +5843,9 @@ QPair<QStringList, QStringList> MainWindow::buildFT8MessageFrames(QString const&
|
||||
QString mygrid = m_config.my_grid();
|
||||
QString mycall = 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, "");
|
||||
bool prefix = !fix.startsWith("/");
|
||||
fix = fix.replace("/", "");
|
||||
@ -5811,10 +5878,11 @@ QPair<QStringList, QStringList> MainWindow::buildFT8MessageFrames(QString const&
|
||||
|
||||
int n = 0;
|
||||
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;
|
||||
// packDataMessage can output a new line (huff escaping special characters)
|
||||
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)
|
||||
|
||||
@ -5850,7 +5918,6 @@ QPair<QStringList, QStringList> MainWindow::buildFT8MessageFrames(QString const&
|
||||
break;
|
||||
}
|
||||
frames.append(frame);
|
||||
lines.append(line.left(frame.length()) + " ");
|
||||
|
||||
if(!line.startsWith(frame)){
|
||||
line = (
|
||||
@ -5864,30 +5931,35 @@ QPair<QStringList, QStringList> MainWindow::buildFT8MessageFrames(QString const&
|
||||
|
||||
if(useBcn){
|
||||
frames.append(frame);
|
||||
lines.append(QString("%1: ").arg(mycall) + line.left(l) + " ");
|
||||
line = line.mid(l);
|
||||
}
|
||||
|
||||
if(useDir){
|
||||
if(compound && !compoundSent){
|
||||
// from compound callsign
|
||||
if(compound){
|
||||
QString compoundMessage = QString("DE %1").arg(mygrid);
|
||||
QString beaconFrame = Varicode::packBeaconMessage(compoundMessage, mycall, nullptr);
|
||||
if(!beaconFrame.isEmpty()){
|
||||
frames.append(beaconFrame);
|
||||
lines.append(QString("%1: ").arg(mycall));
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
if(!line.startsWith(basecall) && !compound){
|
||||
lines.append(QString("%1: ").arg(lookupCallInCompoundCache(basecall)));
|
||||
// to compound callsign
|
||||
if(!dirTo.isEmpty()){
|
||||
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);
|
||||
|
||||
// generate a checksum for buffered commands with line data
|
||||
if(Varicode::isCommandBuffered(dirCmd) && !line.isEmpty()){
|
||||
// strip leading whitespace after a buffered directed command
|
||||
line = lstrip(line);
|
||||
@ -5900,7 +5972,6 @@ QPair<QStringList, QStringList> MainWindow::buildFT8MessageFrames(QString const&
|
||||
|
||||
if(useDat){
|
||||
frames.append(frame);
|
||||
lines.append(line.left(m));
|
||||
line = line.mid(m);
|
||||
}
|
||||
}
|
||||
@ -5909,16 +5980,11 @@ QPair<QStringList, QStringList> MainWindow::buildFT8MessageFrames(QString const&
|
||||
#if 1
|
||||
qDebug() << "parsed frames:";
|
||||
foreach(auto frame, frames){
|
||||
qDebug() << "->" << frame << Varicode::unpackDataMessage(frame) << Varicode::unpackDirectedMessage(frame) << Varicode::unpackCompoundFrame(frame, nullptr) << Varicode::unpackBeaconMessage(frame, nullptr);
|
||||
}
|
||||
|
||||
qDebug() << "lines:";
|
||||
foreach(auto line, lines){
|
||||
qDebug() << "->" << line;
|
||||
qDebug() << "->" << frame << Varicode::unpackDataMessage(frame) << Varicode::unpackDirectedMessage(frame) << Varicode::unpackCompoundFrame(frame, nullptr, nullptr) << Varicode::unpackBeaconMessage(frame, nullptr, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
return {frames,lines};
|
||||
return frames;
|
||||
}
|
||||
|
||||
|
||||
@ -8634,6 +8700,10 @@ void MainWindow::displayActivity(bool force){
|
||||
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
|
||||
int selectedOffset = -1;
|
||||
auto selectedItems = ui->tableWidgetRXAll->selectedItems();
|
||||
@ -8826,6 +8896,64 @@ void MainWindow::displayActivity(bool force){
|
||||
|
||||
// Grouped Compound Activity
|
||||
// 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
|
||||
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;
|
||||
#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
|
||||
if(!Varicode::isCommandAllowed(d.cmd)){
|
||||
continue;
|
||||
@ -8894,9 +9027,14 @@ void MainWindow::displayActivity(bool force){
|
||||
continue;
|
||||
}
|
||||
|
||||
// construct reply
|
||||
QString reply;
|
||||
// record the spot to PSKReporter
|
||||
if (okToPost){
|
||||
pskSetLocal();
|
||||
pskLogReport("FT8Call", d.freq, d.snr, d.from, "");
|
||||
}
|
||||
|
||||
// construct a reply
|
||||
QString reply;
|
||||
|
||||
// QUERIED SNR
|
||||
if(d.cmd == "?"){
|
||||
|
@ -90,6 +90,7 @@ class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT;
|
||||
|
||||
struct CallDetail;
|
||||
public:
|
||||
using Frequency = Radio::Frequency;
|
||||
using FrequencyDelta = Radio::FrequencyDelta;
|
||||
@ -121,6 +122,7 @@ public slots:
|
||||
void msgAvgDecode2();
|
||||
void fastPick(int x0, int x1, int y);
|
||||
|
||||
void logCallActivity(CallDetail d);
|
||||
QString lookupCallInCompoundCache(QString const &call);
|
||||
void clearActivity();
|
||||
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&);
|
||||
int currentFreq();
|
||||
int countFT8MessageFrames(QString const& text);
|
||||
QPair<QStringList, QStringList> buildFT8MessageFrames(QString const& text);
|
||||
QStringList buildFT8MessageFrames(QString const& text);
|
||||
QString parseFT8Message(QString input, bool *isFree);
|
||||
bool prepareNextMessageFrame();
|
||||
bool isFreqOffsetFree(int f, int bw);
|
||||
@ -648,6 +650,7 @@ private:
|
||||
int freq;
|
||||
QDateTime utcTimestamp;
|
||||
int snr;
|
||||
int bits;
|
||||
};
|
||||
|
||||
struct CommandDetail
|
||||
@ -658,6 +661,7 @@ private:
|
||||
int freq;
|
||||
QDateTime utcTimestamp;
|
||||
int snr;
|
||||
int bits;
|
||||
QString text;
|
||||
};
|
||||
|
||||
@ -674,6 +678,7 @@ private:
|
||||
};
|
||||
|
||||
struct MessageBuffer {
|
||||
QQueue<CallDetail> compound;
|
||||
CommandDetail cmd;
|
||||
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 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(n) *n = 0;
|
||||
return frame;
|
||||
}
|
||||
|
||||
auto extra = parsedText.captured("grid");
|
||||
|
||||
/*
|
||||
if(extra.isEmpty()){
|
||||
if(n) *n = 0;
|
||||
return frame;
|
||||
}
|
||||
*/
|
||||
|
||||
auto isBeacon = parsedText.captured("type") == "BCN";
|
||||
auto isAlt = false;
|
||||
// Beacon Alt Type
|
||||
// ---------------
|
||||
// 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);
|
||||
if(!parsedCall.hasMatch()){
|
||||
@ -953,7 +966,7 @@ QStringList Varicode::unpackCompoundFrame(const QString &text, bool *isBeacon, q
|
||||
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;
|
||||
|
||||
auto match = directed_re.match(text);
|
||||
@ -962,24 +975,26 @@ QString Varicode::packDirectedMessage(const QString &text, const QString &baseCa
|
||||
return frame;
|
||||
}
|
||||
|
||||
QString from = baseCallsign;
|
||||
QString from = callsign;
|
||||
QString to = match.captured("to");
|
||||
QString cmd = match.captured("cmd");
|
||||
QString num = match.captured("num").trimmed();
|
||||
QString pwr = match.captured("pwr").trimmed().toUpper();
|
||||
|
||||
// validate callsign
|
||||
// validate "to" callsign
|
||||
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(n) *n = 0;
|
||||
return frame;
|
||||
}
|
||||
|
||||
if(parsedTo.hasMatch()){
|
||||
if(pTo) *pTo = parsedTo.captured(0);
|
||||
|
||||
auto parsedBase = parsedTo.captured("base");
|
||||
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 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 QString packDataMessage(QString const& text, QString *out, int *n);
|
||||
|
Loading…
Reference in New Issue
Block a user