Continued refactoring of command processing
This commit is contained in:
parent
0a7c4a68de
commit
f0de2f2ba1
772
mainwindow.cpp
772
mainwindow.cpp
@ -222,6 +222,7 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
int round(int numToRound, int multiple)
|
int round(int numToRound, int multiple)
|
||||||
{
|
{
|
||||||
if(multiple == 0)
|
if(multiple == 0)
|
||||||
@ -237,6 +238,7 @@ namespace
|
|||||||
|
|
||||||
return roundDown;
|
return roundDown;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int roundUp(int numToRound, int multiple)
|
int roundUp(int numToRound, int multiple)
|
||||||
{
|
{
|
||||||
@ -1121,6 +1123,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
|||||||
QMenu * menu = new QMenu(ui->tableWidgetCalls);
|
QMenu * menu = new QMenu(ui->tableWidgetCalls);
|
||||||
|
|
||||||
QString selectedCall = callsignSelected();
|
QString selectedCall = callsignSelected();
|
||||||
|
bool isAllCall = isAllCallIncluded(selectedCall);
|
||||||
bool missingCallsign = selectedCall.isEmpty();
|
bool missingCallsign = selectedCall.isEmpty();
|
||||||
if(!missingCallsign && m_callActivity.contains(selectedCall)){
|
if(!missingCallsign && m_callActivity.contains(selectedCall)){
|
||||||
setFreqForRestore(m_callActivity[selectedCall].freq, true);
|
setFreqForRestore(m_callActivity[selectedCall].freq, true);
|
||||||
@ -1132,10 +1135,9 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
|
|||||||
|
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
|
|
||||||
removeStation->setDisabled(missingCallsign || callsignSelected() == "ALLCALL");
|
removeStation->setDisabled(missingCallsign || isAllCall);
|
||||||
menu->addAction(removeStation);
|
menu->addAction(removeStation);
|
||||||
|
|
||||||
|
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
menu->addAction(clearAction4);
|
menu->addAction(clearAction4);
|
||||||
menu->addAction(clearActionAll);
|
menu->addAction(clearActionAll);
|
||||||
@ -4413,15 +4415,19 @@ void MainWindow::guiUpdate()
|
|||||||
|
|
||||||
m_sec0=nsec;
|
m_sec0=nsec;
|
||||||
|
|
||||||
|
|
||||||
// once per period
|
// once per period
|
||||||
if(m_sec0 % m_TRperiod == 0){
|
if(m_sec0 % m_TRperiod == 0){
|
||||||
|
// force rx dirty at least once pre period
|
||||||
m_rxDirty = true;
|
m_rxDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// once per second
|
// update the dial frequency once per second..
|
||||||
displayDialFrequency ();
|
displayDialFrequency();
|
||||||
|
|
||||||
|
// process all received activity...
|
||||||
|
processActivity();
|
||||||
|
|
||||||
|
// once processed, lets update the display...
|
||||||
displayActivity();
|
displayActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5855,6 +5861,9 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){
|
|||||||
// once we find a directed call, data encode the rest of the line.
|
// once we find a directed call, data encode the rest of the line.
|
||||||
bool hasDirected = false;
|
bool hasDirected = false;
|
||||||
|
|
||||||
|
// do the same for when we have sent data...
|
||||||
|
bool hasData = false;
|
||||||
|
|
||||||
// remove our callsign from the start of the line...
|
// remove our callsign from the start of the line...
|
||||||
if(line.startsWith(mycall + ":")){
|
if(line.startsWith(mycall + ":")){
|
||||||
line = lstrip(line.mid(mycall.length() + 1));
|
line = lstrip(line.mid(mycall.length() + 1));
|
||||||
@ -5889,21 +5898,22 @@ QStringList MainWindow::buildFT8MessageFrames(QString const& text){
|
|||||||
// if this parses to a standard FT8 free text message
|
// if this parses to a standard FT8 free text message
|
||||||
// but it can be parsed as a directed message, then we
|
// but it can be parsed as a directed message, then we
|
||||||
// should send the directed version. if we've already sent
|
// should send the directed version. if we've already sent
|
||||||
// a directed message, we won't send any more but instead
|
// a directed message or a data frame, we will only follow it
|
||||||
// send it as a data message
|
// with more data frames.
|
||||||
|
|
||||||
if(isFree && !hasDirected && l > 0){
|
if(isFree && !hasDirected && !hasData && l > 0){
|
||||||
useBcn = true;
|
useBcn = true;
|
||||||
hasDirected = false;
|
hasDirected = false;
|
||||||
frame = bcnFrame;
|
frame = bcnFrame;
|
||||||
}
|
}
|
||||||
else if(isFree && !hasDirected && n > 0){
|
else if(isFree && !hasDirected && !hasData && n > 0){
|
||||||
useDir = true;
|
useDir = true;
|
||||||
hasDirected = true;
|
hasDirected = true;
|
||||||
frame = dirFrame;
|
frame = dirFrame;
|
||||||
}
|
}
|
||||||
else if ((isFree || hasDirected) && m > 0) {
|
else if ((isFree || hasDirected) && m > 0) {
|
||||||
useDat = true;
|
useDat = true;
|
||||||
|
hasData = true;
|
||||||
frame = datFrame;
|
frame = datFrame;
|
||||||
if(!datLineOut.isEmpty()){
|
if(!datLineOut.isEmpty()){
|
||||||
line = datLineOut;
|
line = datLineOut;
|
||||||
@ -7363,7 +7373,7 @@ void MainWindow::buildQueryMenu(QMenu * menu){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isAllCall = call == "ALLCALL";
|
bool isAllCall = isAllCallIncluded(call);
|
||||||
|
|
||||||
auto sendReplyAction = menu->addAction("CALL - Send a message to selected callsign");
|
auto sendReplyAction = menu->addAction("CALL - Send a message to selected callsign");
|
||||||
|
|
||||||
@ -8690,70 +8700,423 @@ bool MainWindow::isMyCallIncluded(const QString &text){
|
|||||||
return text.contains(myCall);
|
return text.contains(myCall);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool MainWindow::isAllCallIncluded(const QString &text){
|
bool MainWindow::isAllCallIncluded(const QString &text){
|
||||||
return text.contains("ALLCALL");
|
return text.contains("ALLCALL");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::displayActivity(bool force){
|
void MainWindow::processActivity(bool force) {
|
||||||
if(!m_rxDirty && !force){
|
if (!m_rxDirty && !force) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is it ok to post spots to PSKReporter?
|
// Recent Rx Activity
|
||||||
int nsec=QDateTime::currentMSecsSinceEpoch()/1000-m_secBandChanged;
|
processRxActivity();
|
||||||
bool okToPost=(nsec>(4*m_TRperiod)/5);
|
|
||||||
|
|
||||||
// Selected Rows
|
// Grouped Compound Activity
|
||||||
|
processCompoundActivity();
|
||||||
|
|
||||||
|
// Buffered Activity
|
||||||
|
processBufferedActivity();
|
||||||
|
|
||||||
|
// Command Activity
|
||||||
|
processCommandActivity();
|
||||||
|
|
||||||
|
// Process PSKReporter Spots
|
||||||
|
processSpots();
|
||||||
|
|
||||||
|
m_rxDirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::processRxActivity() {
|
||||||
|
if(m_rxFrameQueue.isEmpty()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!m_rxFrameQueue.isEmpty()) {
|
||||||
|
ActivityDetail d = m_rxFrameQueue.dequeue();
|
||||||
|
|
||||||
|
// TODO: jsherer - is it safe to just ignore printing these?
|
||||||
|
if (d.isCompound) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isLast = d.bits == Varicode::FT8CallLast;
|
||||||
|
|
||||||
|
if (isLast) {
|
||||||
|
// can also use \u0004 \u2666 \u2404
|
||||||
|
d.text = QString("%1 \u2301 ").arg(d.text);
|
||||||
|
}
|
||||||
|
|
||||||
|
int freq = d.freq / 10 * 10;
|
||||||
|
int block = m_rxFrameBlockNumbers.contains(freq) ? m_rxFrameBlockNumbers[freq] : -1;
|
||||||
|
block = logRxTxMessageText(d.utcTimestamp, d.text, d.freq, false, block);
|
||||||
|
m_rxFrameBlockNumbers[freq] = block;
|
||||||
|
|
||||||
|
if (isLast) {
|
||||||
|
m_rxFrameBlockNumbers.remove(freq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::processCompoundActivity() {
|
||||||
|
if(m_messageBuffer.isEmpty()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::processBufferedActivity() {
|
||||||
|
if(m_messageBuffer.isEmpty()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(auto freq, m_messageBuffer.keys()) {
|
||||||
|
auto buffer = m_messageBuffer[freq];
|
||||||
|
|
||||||
|
if (buffer.msgs.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.msgs.last().bits == Varicode::FT8Call) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString message;
|
||||||
|
foreach(auto part, buffer.msgs) {
|
||||||
|
message.append(part.text);
|
||||||
|
}
|
||||||
|
message = rstrip(message);
|
||||||
|
|
||||||
|
QString checksum = message.right(3);
|
||||||
|
message = message.left(message.length() - 4);
|
||||||
|
|
||||||
|
if (Varicode::checksum16Valid(checksum, message)) {
|
||||||
|
buffer.cmd.text = message;
|
||||||
|
m_rxCommandQueue.append(buffer.cmd);
|
||||||
|
} else {
|
||||||
|
qDebug() << "Buffered message failed checksum...discarding";
|
||||||
|
qDebug() << "Checksum:" << checksum;
|
||||||
|
qDebug() << "Message:" << message;
|
||||||
|
}
|
||||||
|
|
||||||
|
// regardless of valid or not, remove the "complete" buffered message from the buffer cache
|
||||||
|
m_messageBuffer.remove(freq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::processCommandActivity() {
|
||||||
|
#if 0
|
||||||
|
if (!m_txFrameQueue.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (m_rxCommandQueue.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
bool processed = false;
|
||||||
|
|
||||||
|
int f = currentFreq();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TODO: jsherer - should we, if we have _any_ directed messages, pause the beacon??
|
||||||
|
// pauseBacon();
|
||||||
|
|
||||||
|
while (!m_rxCommandQueue.isEmpty()) {
|
||||||
|
auto d = m_rxCommandQueue.dequeue();
|
||||||
|
|
||||||
|
bool isAllCall = isAllCallIncluded(d.to);
|
||||||
|
|
||||||
|
qDebug() << "try processing command" << d.from << d.to << d.cmd << d.freq;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we're only responding to allcall and our callsign at this point, but we'll log callsigns we've heard
|
||||||
|
if (!isAllCall && d.to != m_config.my_callsign().trimmed() && d.to != Radio::base_callsign(m_config.my_callsign()).trimmed()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// TODO: jsherer - check to make sure we haven't replied to their allcall recently (in the past beacon interval)
|
||||||
|
if (isAllCall && m_txAllcallCommandCache.contains(Radio::base_callsign(d.from)) && m_txAllcallCommandCache[Radio::base_callsign(d.from)]->secsTo(QDateTime::currentDateTimeUtc()) / 60 < m_config.beacon()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: jsherer - we need to queue these for later processing
|
||||||
|
// record the spot to PSKReporter
|
||||||
|
if (okToPost) {
|
||||||
|
pskSetLocal();
|
||||||
|
pskLogReport("FT8Call", d.freq, d.snr, d.from, "");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// construct a reply
|
||||||
|
QString reply;
|
||||||
|
|
||||||
|
// QUERIED SNR
|
||||||
|
if (d.cmd == "?") {
|
||||||
|
reply = QString("%1 SNR %2").arg(d.from).arg(Varicode::formatSNR(d.snr));
|
||||||
|
}
|
||||||
|
// QUERIED ACK
|
||||||
|
//else if(d.cmd == "#"){
|
||||||
|
// reply = QString("%1 ACK").arg(Radio::base_callsign(d.from));
|
||||||
|
//}
|
||||||
|
// QUERIED PWR
|
||||||
|
else if (d.cmd == "%" && !isAllCall && m_config.my_dBm() >= 0) {
|
||||||
|
reply = QString("%1 PWR %2").arg(d.from).arg(Varicode::formatPWR(m_config.my_dBm()));
|
||||||
|
}
|
||||||
|
// QUERIED QTH
|
||||||
|
else if (d.cmd == "@" && !isAllCall) {
|
||||||
|
QString qth = m_config.my_qth();
|
||||||
|
if (qth.isEmpty()) {
|
||||||
|
QString grid = m_config.my_grid();
|
||||||
|
if (grid.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
qth = grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
reply = QString("%1 %2").arg(d.from).arg(qth);
|
||||||
|
}
|
||||||
|
// QUERIED STATION MESSAGE
|
||||||
|
else if (d.cmd == "&" && !isAllCall) {
|
||||||
|
reply = QString("%1 %2").arg(d.from).arg(m_config.my_station());
|
||||||
|
}
|
||||||
|
// QUERIED STATIONS HEARD
|
||||||
|
else if (d.cmd == "$" && !isAllCall) {
|
||||||
|
int i = 0;
|
||||||
|
int maxStations = 4;
|
||||||
|
auto calls = m_callActivity.keys();
|
||||||
|
qSort(calls.begin(), calls.end(), [this](QString
|
||||||
|
const & a, QString
|
||||||
|
const & b) {
|
||||||
|
auto left = m_callActivity[a];
|
||||||
|
auto right = m_callActivity[b];
|
||||||
|
return right.snr < left.snr;
|
||||||
|
});
|
||||||
|
|
||||||
|
QStringList lines;
|
||||||
|
foreach(auto call, calls) {
|
||||||
|
if (i >= maxStations) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto d = m_callActivity[call];
|
||||||
|
lines.append(QString("%1 SNR %2").arg(d.call).arg(Varicode::formatSNR(d.snr)));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
reply = lines.join('\n');
|
||||||
|
}
|
||||||
|
// PROCESS RETRANSMIT
|
||||||
|
else if (d.cmd == "|" && !isAllCall) {
|
||||||
|
// TODO: jsherer - perhaps parse d.text and ensure it is a valid message as well as prefix it with our call...
|
||||||
|
reply = QString("%1 ACK\n%2 DE %1").arg(d.from).arg(d.text);
|
||||||
|
}
|
||||||
|
// PROCESS ALERT
|
||||||
|
else if (d.cmd == "!" && !isAllCall) {
|
||||||
|
|
||||||
|
QMessageBox * msgBox = new QMessageBox(this);
|
||||||
|
msgBox->setIcon(QMessageBox::Information);
|
||||||
|
|
||||||
|
auto header = QString("Message from %3 at %1 (%2):");
|
||||||
|
header = header.arg(d.utcTimestamp.time().toString());
|
||||||
|
header = header.arg(d.freq);
|
||||||
|
header = header.arg(d.from);
|
||||||
|
msgBox->setText(header);
|
||||||
|
msgBox->setInformativeText(d.text);
|
||||||
|
|
||||||
|
auto ab = msgBox->addButton("ACK", QMessageBox::AcceptRole);
|
||||||
|
auto db = msgBox->addButton("Discard", QMessageBox::NoRole);
|
||||||
|
|
||||||
|
connect(msgBox, & QMessageBox::buttonClicked, this, [this, d, db, ab](QAbstractButton * btn) {
|
||||||
|
if (btn != ab) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
addMessageText(QString("%1 ACK").arg(d.from));
|
||||||
|
toggleTx(true);
|
||||||
|
#endif
|
||||||
|
});
|
||||||
|
|
||||||
|
msgBox->show();
|
||||||
|
|
||||||
|
continue;
|
||||||
|
} else if (d.cmd == " AGN?" && !isAllCall && !m_lastTxMessage.isEmpty()) {
|
||||||
|
reply = m_lastTxMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reply.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
addMessageText(reply);
|
||||||
|
|
||||||
|
// use the last frequency
|
||||||
|
f = d.freq;
|
||||||
|
|
||||||
|
// if we're responding via allcall, pick a different frequency and mark it in the cache.
|
||||||
|
if (isAllCallIncluded(d.to)) {
|
||||||
|
f = findFreeFreqOffset(qMax(0, f - 100), qMin(f + 100, 2500), 50);
|
||||||
|
m_txAllcallCommandCache.insert(Radio::base_callsign(d.from), new QDateTime(QDateTime::currentDateTimeUtc()), 25);
|
||||||
|
}
|
||||||
|
|
||||||
|
processed = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TODO: jsherer - queue the reply here to be sent when a free interval is available
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (processed && ui->autoReplyButton->isChecked()) {
|
||||||
|
toggleTx(true);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::processSpots() {
|
||||||
|
// Is it ok to post spots to PSKReporter?
|
||||||
|
int nsec = QDateTime::currentMSecsSinceEpoch() / 1000 - m_secBandChanged;
|
||||||
|
bool okToPost = (nsec > (4 * m_TRperiod) / 5);
|
||||||
|
if (!okToPost) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process spots to be sent...
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::displayActivity(bool force) {
|
||||||
|
if (!m_rxDirty && !force) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Band Activity
|
||||||
|
displayBandActivity();
|
||||||
|
|
||||||
|
// Call Activity
|
||||||
|
displayCallActivity();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::displayBandActivity() {
|
||||||
|
auto now = QDateTime::currentDateTimeUtc();
|
||||||
|
|
||||||
|
// Selected Offset
|
||||||
int selectedOffset = -1;
|
int selectedOffset = -1;
|
||||||
auto selectedItems = ui->tableWidgetRXAll->selectedItems();
|
auto selectedItems = ui->tableWidgetRXAll->selectedItems();
|
||||||
if(!selectedItems.isEmpty()){
|
if (!selectedItems.isEmpty()) {
|
||||||
selectedOffset = selectedItems.first()->text().toInt();
|
selectedOffset = selectedItems.first()->text().toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Selected callsign
|
// Clear the table
|
||||||
QString selectedCall = callsignSelected();
|
|
||||||
|
|
||||||
// Band Activity
|
|
||||||
auto now = QDateTime::currentDateTimeUtc();
|
|
||||||
clearTableWidget(ui->tableWidgetRXAll);
|
clearTableWidget(ui->tableWidgetRXAll);
|
||||||
QList<int> keys = m_bandActivity.keys();
|
|
||||||
|
|
||||||
// sort directed & recent messages first
|
// Sort directed & recent messages first
|
||||||
qSort(keys.begin(), keys.end(), [this](const int left, int right){
|
QList < int > keys = m_bandActivity.keys();
|
||||||
if(m_rxDirectedCache.contains(left/10*10)){
|
qSort(keys.begin(), keys.end(), [this](const int left, int right) {
|
||||||
|
if (m_rxDirectedCache.contains(left / 10 * 10)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(m_rxDirectedCache.contains(right/10*10)){
|
if (m_rxDirectedCache.contains(right / 10 * 10)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(m_rxRecentCache.contains(left/10*10)){
|
if (m_rxRecentCache.contains(left / 10 * 10)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(m_rxRecentCache.contains(right/10*10)){
|
if (m_rxRecentCache.contains(right / 10 * 10)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return left < right;
|
return left < right;
|
||||||
});
|
});
|
||||||
|
|
||||||
foreach (int offset, keys) {
|
// Build the table
|
||||||
QList<ActivityDetail> items = m_bandActivity[offset];
|
foreach(int offset, keys) {
|
||||||
if(items.length() > 0){
|
QList < ActivityDetail > items = m_bandActivity[offset];
|
||||||
|
if (items.length() > 0) {
|
||||||
QStringList text;
|
QStringList text;
|
||||||
QString age;
|
QString age;
|
||||||
int snr = 0;
|
int snr = 0;
|
||||||
int activityAging = m_config.activity_aging();
|
int activityAging = m_config.activity_aging();
|
||||||
foreach(ActivityDetail item, items){
|
foreach(ActivityDetail item, items) {
|
||||||
if(activityAging && item.utcTimestamp.secsTo(now)/60 >= activityAging){
|
if (activityAging && item.utcTimestamp.secsTo(now) / 60 >= activityAging) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if(item.text.isEmpty()){
|
if (item.text.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if(item.isLowConfidence){
|
if (item.isLowConfidence) {
|
||||||
item.text = QString("[%1]").arg(item.text);
|
item.text = QString("[%1]").arg(item.text);
|
||||||
}
|
}
|
||||||
if(item.bits == Varicode::FT8CallLast){
|
if (item.bits == Varicode::FT8CallLast) {
|
||||||
// can also use \u0004 \u2666 \u2404
|
// can also use \u0004 \u2666 \u2404
|
||||||
item.text = QString("%1 \u2301 ").arg(item.text);
|
item.text = QString("%1 \u2301 ").arg(item.text);
|
||||||
}
|
}
|
||||||
@ -8763,7 +9126,7 @@ void MainWindow::displayActivity(bool force){
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto joined = text.join(" ");
|
auto joined = text.join(" ");
|
||||||
if(joined.isEmpty()){
|
if (joined.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8774,35 +9137,35 @@ void MainWindow::displayActivity(bool force){
|
|||||||
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 0, offsetItem);
|
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 0, offsetItem);
|
||||||
|
|
||||||
auto ageItem = new QTableWidgetItem(QString("(%1)").arg(age));
|
auto ageItem = new QTableWidgetItem(QString("(%1)").arg(age));
|
||||||
ageItem->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
|
ageItem->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter);
|
||||||
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 1, ageItem);
|
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 1, ageItem);
|
||||||
|
|
||||||
auto snrItem = new QTableWidgetItem(QString("%1").arg(Varicode::formatSNR(snr)));
|
auto snrItem = new QTableWidgetItem(QString("%1").arg(Varicode::formatSNR(snr)));
|
||||||
snrItem->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
|
snrItem->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter);
|
||||||
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 2, snrItem);
|
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 2, snrItem);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// align right if eliding...
|
// align right if eliding...
|
||||||
int colWidth = ui->tableWidgetRXAll->columnWidth(3);
|
int colWidth = ui->tableWidgetRXAll->columnWidth(3);
|
||||||
auto textItem = new QTableWidgetItem(joined);
|
auto textItem = new QTableWidgetItem(joined);
|
||||||
QFontMetrics fm(textItem->font());
|
QFontMetrics fm(textItem->font());
|
||||||
auto elidedText = fm.elidedText(joined, Qt::ElideLeft, colWidth);
|
auto elidedText = fm.elidedText(joined, Qt::ElideLeft, colWidth);
|
||||||
auto flag = Qt::AlignLeft|Qt::AlignVCenter;
|
auto flag = Qt::AlignLeft | Qt::AlignVCenter;
|
||||||
if(elidedText != joined){
|
if (elidedText != joined) {
|
||||||
flag = Qt::AlignRight|Qt::AlignVCenter;
|
flag = Qt::AlignRight | Qt::AlignVCenter;
|
||||||
textItem->setText(joined);
|
textItem->setText(joined);
|
||||||
}
|
}
|
||||||
textItem->setTextAlignment(flag);
|
textItem->setTextAlignment(flag);
|
||||||
|
|
||||||
if (text.last().contains(QRegularExpression {"^(CQ|QRZ|DE)\\s"})){
|
if (text.last().contains(QRegularExpression {
|
||||||
|
"^(CQ|QRZ|DE)\\s"
|
||||||
|
})) {
|
||||||
offsetItem->setBackground(QBrush(m_config.color_CQ()));
|
offsetItem->setBackground(QBrush(m_config.color_CQ()));
|
||||||
ageItem->setBackground(QBrush(m_config.color_CQ()));
|
ageItem->setBackground(QBrush(m_config.color_CQ()));
|
||||||
snrItem->setBackground(QBrush(m_config.color_CQ()));
|
snrItem->setBackground(QBrush(m_config.color_CQ()));
|
||||||
textItem->setBackground(QBrush(m_config.color_CQ()));
|
textItem->setBackground(QBrush(m_config.color_CQ()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_rxDirectedCache.contains(offset/10*10)){
|
if (m_rxDirectedCache.contains(offset / 10 * 10)) {
|
||||||
offsetItem->setBackground(QBrush(m_config.color_MyCall()));
|
offsetItem->setBackground(QBrush(m_config.color_MyCall()));
|
||||||
ageItem->setBackground(QBrush(m_config.color_MyCall()));
|
ageItem->setBackground(QBrush(m_config.color_MyCall()));
|
||||||
snrItem->setBackground(QBrush(m_config.color_MyCall()));
|
snrItem->setBackground(QBrush(m_config.color_MyCall()));
|
||||||
@ -8811,37 +9174,45 @@ void MainWindow::displayActivity(bool force){
|
|||||||
|
|
||||||
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 3, textItem);
|
ui->tableWidgetRXAll->setItem(ui->tableWidgetRXAll->rowCount() - 1, 3, textItem);
|
||||||
|
|
||||||
if(offset == selectedOffset){
|
if (offset == selectedOffset) {
|
||||||
ui->tableWidgetRXAll->selectRow(ui->tableWidgetRXAll->rowCount() - 1);
|
ui->tableWidgetRXAll->selectRow(ui->tableWidgetRXAll->rowCount() - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resize the table columns
|
||||||
ui->tableWidgetRXAll->resizeColumnToContents(0);
|
ui->tableWidgetRXAll->resizeColumnToContents(0);
|
||||||
ui->tableWidgetRXAll->resizeColumnToContents(1);
|
ui->tableWidgetRXAll->resizeColumnToContents(1);
|
||||||
ui->tableWidgetRXAll->resizeColumnToContents(2);
|
ui->tableWidgetRXAll->resizeColumnToContents(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::displayCallActivity() {
|
||||||
|
auto now = QDateTime::currentDateTimeUtc();
|
||||||
|
|
||||||
|
// Selected callsign
|
||||||
|
QString selectedCall = callsignSelected();
|
||||||
|
|
||||||
// Call Activity
|
// Clear the table
|
||||||
|
|
||||||
clearTableWidget(ui->tableWidgetCalls);
|
clearTableWidget(ui->tableWidgetCalls);
|
||||||
|
|
||||||
ui->tableWidgetCalls->insertRow(ui->tableWidgetCalls->rowCount());
|
// Create the ALLCALL item
|
||||||
auto item = new QTableWidgetItem("ALLCALL");
|
auto item = new QTableWidgetItem("ALLCALL");
|
||||||
|
ui->tableWidgetCalls->insertRow(ui->tableWidgetCalls->rowCount());
|
||||||
item->setData(Qt::UserRole, QVariant("ALLCALL"));
|
item->setData(Qt::UserRole, QVariant("ALLCALL"));
|
||||||
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 0, item);
|
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 0, item);
|
||||||
ui->tableWidgetCalls->setSpan(ui->tableWidgetCalls->rowCount() - 1, 0, 1, ui->tableWidgetCalls->columnCount());
|
ui->tableWidgetCalls->setSpan(ui->tableWidgetCalls->rowCount() - 1, 0, 1, ui->tableWidgetCalls->columnCount());
|
||||||
if(selectedCall == "ALLCALL"){
|
if (isAllCallIncluded(selectedCall)) {
|
||||||
ui->tableWidgetCalls->selectRow(ui->tableWidgetCalls->rowCount() - 1);
|
ui->tableWidgetCalls->selectRow(ui->tableWidgetCalls->rowCount() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QString> calls = m_callActivity.keys();
|
// Build the table
|
||||||
|
QList < QString > calls = m_callActivity.keys();
|
||||||
qSort(calls.begin(), calls.end());
|
qSort(calls.begin(), calls.end());
|
||||||
int callsignAging = m_config.callsign_aging();
|
int callsignAging = m_config.callsign_aging();
|
||||||
foreach(QString call, calls){
|
foreach(QString call, calls) {
|
||||||
CallDetail d = m_callActivity[call];
|
CallDetail d = m_callActivity[call];
|
||||||
|
|
||||||
if(callsignAging && d.utcTimestamp.secsTo(now)/60 >= callsignAging){
|
if (callsignAging && d.utcTimestamp.secsTo(now) / 60 >= callsignAging) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8856,300 +9227,19 @@ void MainWindow::displayActivity(bool force){
|
|||||||
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 3, new QTableWidgetItem(QString("%1").arg(d.grid)));
|
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 3, new QTableWidgetItem(QString("%1").arg(d.grid)));
|
||||||
|
|
||||||
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, 4, distanceItem);
|
ui->tableWidgetCalls->setItem(ui->tableWidgetCalls->rowCount() - 1, 4, distanceItem);
|
||||||
|
|
||||||
if(call == selectedCall){
|
if (call == selectedCall) {
|
||||||
ui->tableWidgetCalls->selectRow(ui->tableWidgetCalls->rowCount() - 1);
|
ui->tableWidgetCalls->selectRow(ui->tableWidgetCalls->rowCount() - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resize the table columns
|
||||||
ui->tableWidgetCalls->resizeColumnToContents(0);
|
ui->tableWidgetCalls->resizeColumnToContents(0);
|
||||||
ui->tableWidgetCalls->resizeColumnToContents(1);
|
ui->tableWidgetCalls->resizeColumnToContents(1);
|
||||||
ui->tableWidgetCalls->resizeColumnToContents(2);
|
ui->tableWidgetCalls->resizeColumnToContents(2);
|
||||||
ui->tableWidgetCalls->resizeColumnToContents(3);
|
ui->tableWidgetCalls->resizeColumnToContents(3);
|
||||||
|
|
||||||
// Recently Directed Activity
|
|
||||||
while(!m_rxFrameQueue.isEmpty()){
|
|
||||||
ActivityDetail d = m_rxFrameQueue.dequeue();
|
|
||||||
|
|
||||||
// TODO: jsherer - is it safe to just ignore printing these?
|
|
||||||
if(d.isCompound){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isLast = d.bits == Varicode::FT8CallLast;
|
|
||||||
|
|
||||||
if(isLast){
|
|
||||||
// can also use \u0004 \u2666 \u2404
|
|
||||||
d.text = QString("%1 \u2301 ").arg(d.text);
|
|
||||||
}
|
|
||||||
|
|
||||||
int freq = d.freq/10*10;
|
|
||||||
int block = m_rxFrameBlockNumbers.contains(freq) ? m_rxFrameBlockNumbers[freq] : -1;
|
|
||||||
block = logRxTxMessageText(d.utcTimestamp, d.text, d.freq, false, block);
|
|
||||||
m_rxFrameBlockNumbers[freq] = block;
|
|
||||||
|
|
||||||
if(isLast){
|
|
||||||
m_rxFrameBlockNumbers.remove(freq);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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()){
|
|
||||||
auto buffer = m_messageBuffer[freq];
|
|
||||||
|
|
||||||
if(buffer.msgs.isEmpty()){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(buffer.msgs.last().bits == Varicode::FT8Call){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString message;
|
|
||||||
foreach(auto part, buffer.msgs){
|
|
||||||
message.append(part.text);
|
|
||||||
}
|
|
||||||
message = rstrip(message);
|
|
||||||
|
|
||||||
QString checksum = message.right(3);
|
|
||||||
message = message.left(message.length()-4);
|
|
||||||
|
|
||||||
if(Varicode::checksum16Valid(checksum, message)){
|
|
||||||
buffer.cmd.text = message;
|
|
||||||
m_rxCommandQueue.append(buffer.cmd);
|
|
||||||
} else {
|
|
||||||
qDebug() << "Buffered message failed checksum...discarding";
|
|
||||||
qDebug() << "Checksum:" << checksum;
|
|
||||||
qDebug() << "Message:" << message;
|
|
||||||
}
|
|
||||||
|
|
||||||
// regardless of valid or not, remove the "complete" buffered message from the buffer cache
|
|
||||||
m_messageBuffer.remove(freq);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command Activity
|
|
||||||
if(m_txFrameQueue.isEmpty() && !m_rxCommandQueue.isEmpty()){
|
|
||||||
int f = currentFreq();
|
|
||||||
|
|
||||||
bool processed = false;
|
|
||||||
|
|
||||||
// TODO: jsherer - should we if we have _any_ directed messages, pause the beacon??
|
|
||||||
// pauseBacon();
|
|
||||||
|
|
||||||
while(!m_rxCommandQueue.isEmpty()){
|
|
||||||
auto d = m_rxCommandQueue.dequeue();
|
|
||||||
|
|
||||||
bool isAllCall = d.to == "ALLCALL";
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we're only responding to allcall and our callsign at this point, but we'll log callsigns we've heard
|
|
||||||
if(!isAllCall && d.to != m_config.my_callsign().trimmed() && d.to != Radio::base_callsign(m_config.my_callsign()).trimmed()){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: jsherer - check to make sure we haven't replied to their allcall recently (in the past beacon interval)
|
|
||||||
if(isAllCall && m_txAllcallCommandCache.contains(Radio::base_callsign(d.from)) && m_txAllcallCommandCache[Radio::base_callsign(d.from)]->secsTo(QDateTime::currentDateTimeUtc())/60 < m_config.beacon()){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 == "?"){
|
|
||||||
reply = QString("%1 SNR %2").arg(d.from).arg(Varicode::formatSNR(d.snr));
|
|
||||||
}
|
|
||||||
// QUERIED ACK
|
|
||||||
//else if(d.cmd == "#"){
|
|
||||||
// reply = QString("%1 ACK").arg(Radio::base_callsign(d.from));
|
|
||||||
//}
|
|
||||||
// QUERIED PWR
|
|
||||||
else if(d.cmd == "%" && !isAllCall && m_config.my_dBm() >= 0){
|
|
||||||
reply = QString("%1 PWR %2").arg(d.from).arg(Varicode::formatPWR(m_config.my_dBm()));
|
|
||||||
}
|
|
||||||
// QUERIED QTH
|
|
||||||
else if(d.cmd == "@" && !isAllCall){
|
|
||||||
QString qth = m_config.my_qth();
|
|
||||||
if(qth.isEmpty()){
|
|
||||||
QString grid = m_config.my_grid();
|
|
||||||
if(grid.isEmpty()){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
qth = grid;
|
|
||||||
}
|
|
||||||
|
|
||||||
reply = QString("%1 %2").arg(d.from).arg(qth);
|
|
||||||
}
|
|
||||||
// QUERIED STATION MESSAGE
|
|
||||||
else if(d.cmd == "&" && !isAllCall){
|
|
||||||
reply = QString("%1 %2").arg(d.from).arg(m_config.my_station());
|
|
||||||
}
|
|
||||||
// QUERIED STATIONS HEARD
|
|
||||||
else if(d.cmd == "$" && !isAllCall){
|
|
||||||
int i = 0;
|
|
||||||
int maxStations = 4;
|
|
||||||
auto calls = m_callActivity.keys();
|
|
||||||
qSort(calls.begin(), calls.end(), [this](QString const &a, QString const &b){
|
|
||||||
auto left = m_callActivity[a];
|
|
||||||
auto right = m_callActivity[b];
|
|
||||||
return right.snr < left.snr;
|
|
||||||
});
|
|
||||||
|
|
||||||
QStringList lines;
|
|
||||||
foreach(auto call, calls){
|
|
||||||
if(i >= maxStations){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto d = m_callActivity[call];
|
|
||||||
lines.append(QString("%1 SNR %2").arg(d.call).arg(Varicode::formatSNR(d.snr)));
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
reply = lines.join('\n');
|
|
||||||
}
|
|
||||||
// PROCESS RETRANSMIT
|
|
||||||
else if(d.cmd == "|" && !isAllCall){
|
|
||||||
// TODO: jsherer - perhaps parse d.text and ensure it is a valid message as well as prefix it with our call...
|
|
||||||
reply = QString("%1 ACK\n%2 DE %1").arg(d.from).arg(d.text);
|
|
||||||
}
|
|
||||||
// PROCESS ALERT
|
|
||||||
else if(d.cmd == "!" && !isAllCall){
|
|
||||||
|
|
||||||
QMessageBox * msgBox = new QMessageBox(this);
|
|
||||||
msgBox->setIcon(QMessageBox::Information);
|
|
||||||
|
|
||||||
auto header = QString("Message from %3 at %1 (%2):");
|
|
||||||
header = header.arg(d.utcTimestamp.time().toString());
|
|
||||||
header = header.arg(d.freq);
|
|
||||||
header = header.arg(d.from);
|
|
||||||
msgBox->setText(header);
|
|
||||||
msgBox->setInformativeText(d.text);
|
|
||||||
|
|
||||||
|
|
||||||
auto ab = msgBox->addButton("ACK", QMessageBox::AcceptRole);
|
|
||||||
auto db = msgBox->addButton("Discard", QMessageBox::NoRole);
|
|
||||||
|
|
||||||
connect(msgBox, &QMessageBox::buttonClicked, this, [this, d, db, ab](QAbstractButton * btn){
|
|
||||||
if(btn != ab){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
addMessageText(QString("%1 ACK").arg(d.from));
|
|
||||||
toggleTx(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
msgBox->show();
|
|
||||||
|
|
||||||
continue;
|
|
||||||
} else if(d.cmd == " AGN?" && !isAllCall && !m_lastTxMessage.isEmpty()){
|
|
||||||
reply = m_lastTxMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(reply.isEmpty()){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
addMessageText(reply);
|
|
||||||
|
|
||||||
// use the last frequency
|
|
||||||
f = d.freq;
|
|
||||||
|
|
||||||
// if we're responding via allcall, pick a different frequency and mark it in the cache.
|
|
||||||
if(d.to == "ALLCALL"){
|
|
||||||
f = findFreeFreqOffset(qMax(0, f-100), qMin(f+100, 2500), 50);
|
|
||||||
m_txAllcallCommandCache.insert(Radio::base_callsign(d.from), new QDateTime(QDateTime::currentDateTimeUtc()), 25);
|
|
||||||
}
|
|
||||||
|
|
||||||
processed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(processed && ui->autoReplyButton->isChecked()){
|
|
||||||
toggleTx(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
m_rxDirty = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::postWSPRDecode (bool is_new, QStringList parts)
|
void MainWindow::postWSPRDecode (bool is_new, QStringList parts)
|
||||||
|
@ -795,7 +795,15 @@ private:
|
|||||||
QString callsignSelected();
|
QString callsignSelected();
|
||||||
bool isRecentOffset(int offset);
|
bool isRecentOffset(int offset);
|
||||||
bool isDirectedOffset(int offset);
|
bool isDirectedOffset(int offset);
|
||||||
|
void processActivity(bool force=false);
|
||||||
|
void processRxActivity();
|
||||||
|
void processCompoundActivity();
|
||||||
|
void processBufferedActivity();
|
||||||
|
void processCommandActivity();
|
||||||
|
void processSpots();
|
||||||
void displayActivity(bool force=false);
|
void displayActivity(bool force=false);
|
||||||
|
void displayBandActivity();
|
||||||
|
void displayCallActivity();
|
||||||
void postWSPRDecode (bool is_new, QStringList message_parts);
|
void postWSPRDecode (bool is_new, QStringList message_parts);
|
||||||
void enable_DXCC_entity (bool on);
|
void enable_DXCC_entity (bool on);
|
||||||
void switch_mode (Mode);
|
void switch_mode (Mode);
|
||||||
|
Loading…
Reference in New Issue
Block a user