1
0
mirror of https://github.com/saitohirga/WSJT-X.git synced 2025-03-21 03:28:59 -04:00

highlighting callsigns, annotating callsigns, sort hounds on more criteria

This commit is contained in:
Brian Moran 2024-08-02 16:46:22 -07:00
parent 9a7ae401e7
commit 8e6ca93259
10 changed files with 310 additions and 63 deletions

View File

@ -392,7 +392,22 @@ void MessageClient::impl::parse_message (QByteArray const& msg)
}
break;
default:
case NetworkMessage::AnnotationInfo: {
QByteArray dx_call;
bool sort_order_provided{false};
quint32 sort_order{std::numeric_limits<quint32>::max()};
in >> dx_call >> sort_order_provided >> sort_order;
TRACE_UDP ("External Callsign Info:" << dx_call << "sort_order_provided:" << sort_order_provided
<< "sort_order:" << sort_order);
if (sort_order > 50000) sort_order = 50000;
if (check_status(in) != Fail) {
Q_EMIT
self_->annotation_info(QString::fromUtf8(dx_call), sort_order_provided, sort_order);
}
}
break;
default:
// Ignore
//
// Note that although server heartbeat messages are not

View File

@ -123,7 +123,10 @@ public:
, bool fast_mode, quint32 tr_period, quint32 rx_df, QString const& dx_call
, QString const& dx_grid, bool generate_messages);
// this signal is emitted when network errors occur or if a host
// this signal is emitted if the server has sent information about a callsign
Q_SIGNAL void annotation_info (QString const& dx_call, bool sort_order_provided, quint32 sort_order);
// this signal is emitted when network errors occur or if a host
// lookup fails
Q_SIGNAL void error (QString const&) const;

View File

@ -462,6 +462,14 @@
* decoding may be impacted. A rough rule of thumb might be too
* limit the number of active highlighting requests to no more
* than 100.
*
* Using a callsign of "CLEARALL!" and anything for the
* color values will clear the internal highlighting data. It will
* NOT remove the highlighting on the screen, however. The exclamation
* symbol is used to avoid accidental clearing of all highlighting
* data via a decoded callsign, since an exclamation symbol is not
* a valid character in a callsign.
*
* The "Highlight last" field allows the sender to request that
* all instances of "Callsign" in the last period only, instead
@ -494,7 +502,33 @@
* fields an empty value implies no change, for the quint32 Rx DF
* and Frequency Tolerance fields the maximum quint32 value
* implies no change. Invalid or unrecognized values will be
* silently ignored.
* silently ignored. NOTE that if a mode/submode change occurs and
* the current frequency is NOT in the frequency table for that
* mode, a frequency change (to the default frequency for that band
* and mode) may occur.
*
* AnnotationInfo In 16 quint32
* Id (unique key) utf8
* DX Call utf8
* Sort Order Provided bool
* Sort Order quint32
*
* The server may send this message at any time. Sort orders can be used
* for sorting hound callers when in Fox mode. A typical usage is to
* "score" callsigns based on number of bands and/or modes worked using
* an external logging program during a DXpedition, to be able to give
* preference to calls that have not been worked before on any other
* band or mode. An external program can watch decodes from wsjt-x,
* then use this message to annotate the calls with a sort order. The
* hound queue can be displayed by that sort order. *
*
* If 'sort order provided' is true, the message also specifies a numeric
* sort order for the DX call.
*
* Invalid or unrecognized values will be silently ignored. A sort-order of
* ffffffff will remove the sort-order value from the internal table.
* Callsigns without a sort order will be valued at zero for sorting purposes
* in the hound display.
*/
#include <QDataStream>
@ -526,6 +560,7 @@ namespace NetworkMessage
HighlightCallsign,
SwitchConfiguration,
Configure,
AnnotationInfo,
maximum_message_type_ // ONLY add new message types
// immediately before here
};

View File

@ -40,6 +40,26 @@ void ActiveStations::changeFont (QFont const& font)
updateGeometry ();
}
void ActiveStations::clearStations() {
m_textbuffer.clear();
m_decodes_by_frequency.clear();
}
void ActiveStations::addLine(QString line) {
QString m_textbuffer = "";
// "012700 -1 0.2 210 ~ KJ7COA JA2HGF -14"
unsigned freq = line.mid(16, 4).toUInt();
m_decodes_by_frequency[freq] = line;
// show them in frequency order
QMap<int, QString>::const_iterator i = m_decodes_by_frequency.constBegin();
m_textbuffer.clear();
while (i != m_decodes_by_frequency.constEnd()) {
m_textbuffer.append(i.value());
++i;
}
this->displayRecentStations(m_mode, m_textbuffer);
}
void ActiveStations::read_settings ()
{
SettingsGroup group {settings_, "ActiveStations"};
@ -60,8 +80,7 @@ void ActiveStations::write_settings ()
settings_->setValue("WantedOnly",ui->cbWantedOnly->isChecked());
}
void ActiveStations::displayRecentStations(QString mode, QString const& t)
{
void ActiveStations::setupUi(QString mode) {
if(mode!=m_mode) {
m_mode=mode;
ui->cbReadyOnly->setText(" Ready only");
@ -71,24 +90,37 @@ void ActiveStations::displayRecentStations(QString mode, QString const& t)
ui->cbReadyOnly->setText("* CQ only");
} else if(m_mode=="Q65-pileup") {
ui->header_label2->setText(" N Freq Call Grid El Age(h)");
ui->cbWantedOnly->setText(QCoreApplication::translate("ActiveStations", "Wanted only", nullptr));
} else if(m_mode=="Fox Mode" || m_mode=="SuperFox Mode" ) {
ui->header_label2->setText(" UTC dB DT Freq " + tr("Message"));
ui->cbWantedOnly->setText(QCoreApplication::translate("ActiveStations", "My call only", nullptr));
this->setClickOK(true);
} else {
ui->header_label2->setText(" N Call Grid Az S/N Freq Tx Age Pts");
ui->label->setText("Rate:");
ui->cbWantedOnly->setText(QCoreApplication::translate("ActiveStations", "Wanted only", nullptr));
}
bool b=(m_mode.left(3)=="Q65");
ui->bandChanges->setVisible(!b);
ui->cbReadyOnly->setVisible(m_mode!="Q65-pileup");
ui->cbWantedOnly->setVisible(m_mode!="Q65-pileup");
ui->label_2->setVisible(!b);
ui->label_3->setVisible(!b);
ui->score->setVisible(!b);
ui->sbMaxRecent->setVisible(!b);
bool is_fox_mode =(m_mode=="Fox Mode");
ui->bandChanges->setVisible(!b && !is_fox_mode);
ui->cbReadyOnly->setVisible(m_mode != "Q65-pileup" && !is_fox_mode);
ui->cbWantedOnly->setVisible(m_mode != "Q65-pileup"); // this is used for "My call only" in Fox mode
ui->label_2->setVisible(!b && !is_fox_mode);
ui->label_3->setVisible(!b && !is_fox_mode);
ui->score->setVisible(!b && !is_fox_mode);
ui->sbMaxRecent->setVisible(!b && !is_fox_mode);
b=(m_mode!="Q65-pileup");
b=(m_mode!="Q65-pileup" && !is_fox_mode);
ui->sbMaxAge->setVisible(b);
ui->label->setVisible(b);
ui->rate->setVisible(b);
}
}
void ActiveStations::displayRecentStations(QString mode, QString const& t)
{
setupUi(mode);
bool bClickOK=m_clickOK;
m_clickOK=false;
ui->RecentStationsPlainTextEdit->setPlainText(t);
@ -143,7 +175,10 @@ void ActiveStations::on_textEdit_clicked()
if(text!="") {
int nline=text.left(2).toInt();
if(QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) nline=-nline;
emit callSandP(nline);
if ("Fox Mode" != m_mode)
emit callSandP(nline);
else
emit queueActiveWindowHound(text);
}
}
}

View File

@ -3,6 +3,7 @@
#define ARRL_DIGI_H_
#include <QWidget>
#include <QMap>
class QSettings;
class QFont;
@ -20,6 +21,7 @@ public:
explicit ActiveStations(QSettings *, QFont const&, QWidget * parent = 0);
~ActiveStations();
void displayRecentStations(QString mode, QString const&);
void setupUi(QString display_mode);
void changeFont (QFont const&);
int maxRecent();
int maxAge();
@ -30,6 +32,8 @@ public:
void setRate(int n);
void setBandChanges(int n);
void setScore(int n);
void clearStations();
void addLine(QString);
bool m_clickOK=false;
bool m_bReadyOnly;
@ -41,14 +45,16 @@ private:
Q_SIGNAL void callSandP(int nline);
Q_SIGNAL void activeStationsDisplay();
Q_SIGNAL void cursorPositionChanged();
Q_SIGNAL void queueActiveWindowHound(QString text);
Q_SLOT void on_cbReadyOnly_toggled(bool b);
Q_SLOT void on_cbWantedOnly_toggled(bool b);
Q_SLOT void on_textEdit_clicked();
// qint64 m_msec0=0;
QString m_mode="";
QSettings * settings_;
QString m_textbuffer=""; // F/H mode band decodes
QMap<int, QString> m_decodes_by_frequency; // store decodes for F/H band awareness by frequency
QScopedPointer<Ui::ActiveStations> ui;
};

View File

@ -560,6 +560,22 @@ void DisplayText::displayHoundToBeCalled(QString t, bool bAtTop, QColor bg, QCol
insertText(t, bg, fg, "", "", bAtTop ? QTextCursor::Start : QTextCursor::End);
}
void DisplayText::setHighlightedHoundText(QString t) {
QColor bg;
QColor fg;
highlight_types types{Highlight::Call};
set_colours(m_config, &bg, &fg, types);
// t is multiple lines of text, each line is a hound calling
// iterate through each line and highlight the callsign
auto lines = t.split(QChar('\n'), Qt::SkipEmptyParts);
clear();
foreach (auto line, lines)
{
auto fields = line.split(QChar(' '), Qt::SkipEmptyParts);
insertText(line, bg, fg, fields.first(), QString{});
}
}
namespace
{
void update_selection (QTextCursor& cursor, QColor const& bg, QColor const& fg)
@ -619,6 +635,11 @@ void DisplayText::highlight_callsign (QString const& callsign, QColor const& bg,
{
return;
}
if (callsign == "CLEARALL!") // programmatic means of clearing all highlighting
{
highlighted_calls_.clear();
return;
}
auto regexp = callsign;
// allow for hashed callsigns and escape any regexp metacharacters
QRegularExpression target {QString {"<?"}

View File

@ -36,6 +36,7 @@ public:
double TRperiod, bool bSuperfox);
void displayQSY(QString text);
void displayHoundToBeCalled(QString t, bool bAtTop=false, QColor bg = QColor {}, QColor fg = QColor {});
void setHighlightedHoundText(QString text);
void new_period ();
QString CQPriority(){return m_CQPriority;};
qint32 m_points;

View File

@ -241,6 +241,7 @@ namespace
QRegExp message_alphabet {"[- @A-Za-z0-9+./?#<>;$]*"};
// grid exact match excluding RR73
QRegularExpression grid_regexp {"\\A(?![Rr]{2}73)[A-Ra-r]{2}[0-9]{2}([A-Xa-x]{2}){0,1}\\z"};
QRegularExpression non_r_db_regexp {"\\A[-+]{1}[0-9]{1,2}\\z"};
auto quint32_max = std::numeric_limits<quint32>::max ();
constexpr int N_WIDGETS {38};
constexpr int default_rx_audio_buffer_frames {-1}; // lets Qt decide
@ -697,7 +698,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
// initialize decoded text font and hook up font change signals
// defer initialization until after construction otherwise menu fonts do not get set
// with 50 ms delay we are on the save side
// with 50 ms delay we are on the safe side
QTimer::singleShot (50, this, SLOT (initialize_fonts ()));
connect (&m_config, &Configuration::text_font_changed, [this] (QFont const& font) {
set_application_font (font);
@ -3103,9 +3104,15 @@ void MainWindow::on_actionActiveStations_triggered()
m_ActiveStationsWidget->activateWindow();
configActiveStations();
connect(m_ActiveStationsWidget.data(), SIGNAL(callSandP(int)),this,SLOT(callSandP2(int)));
// connect up another signal to handle clicks in the Activity window when in Fox mode
connect(m_ActiveStationsWidget.data(), SIGNAL(queueActiveWindowHound(QString)),this,SLOT(queueActiveWindowHound2(QString)),static_cast<Qt::ConnectionType>(Qt::UniqueConnection));
connect(m_ActiveStationsWidget.data(), SIGNAL(activeStationsDisplay()),this,SLOT(ARRL_Digi_Display()));
m_ActiveStationsWidget->setScore(m_score);
if(m_mode=="Q65") m_ActiveStationsWidget->setRate(m_score);
QString as_mode = m_mode;
if(m_mode=="FT8" && SpecOp::FOX==m_specOp) as_mode="Fox Mode"; // TODO - active stations for hound mode?
m_ActiveStationsWidget->setupUi(as_mode);
}
void MainWindow::on_actionOpen_triggered() //Open File
@ -3865,6 +3872,12 @@ void MainWindow::ARRL_Digi_Display()
readWidebandDecodes();
return;
}
if (m_mode == "Fox Mode") { // ARRL_Digi_Display can be shown for other modes
if (m_ActiveStationsWidget != NULL) {
m_ActiveStationsWidget->setClickOK(true);
}
return;
}
QMutableMapIterator<QString,RecentCall> icall(m_recentCall);
QString deCall,deGrid;
int age=0;
@ -3935,10 +3948,53 @@ void MainWindow::ARRL_Digi_Display()
t += (t1 + list[k] + "\n");
if(i>=maxRecent) break;
}
if(m_ActiveStationsWidget!=NULL) m_ActiveStationsWidget->displayRecentStations(m_mode,t);
bool is_fox_mode = (m_mode=="FT8" && m_specOp == SpecOp::FOX);
if(m_ActiveStationsWidget!=NULL && !is_fox_mode) m_ActiveStationsWidget->displayRecentStations(m_mode,t);
m_ActiveStationsWidget->setClickOK(true);
}
void MainWindow::queueActiveWindowHound2(QString line) {
// Active Window shows what's going on outside of current F/H display rules (calling below 1000Hz e.g.)
// TODO should we allow calling a station that's calling another station, not us?
if (m_mode == "FT8" and m_specOp == SpecOp::FOX) {
// process the line to get the callsign
QStringList w = line.split(' ', SkipEmptyParts);
// make sure our call is the first in the list, or the station is CQing (not a directed CQ)
if ( (w.size() > 7) &&
(w[5] == m_config.my_callsign() || w[5] == "<"+m_config.my_callsign()+">" || w[5]=="CQ") &&
( w[7].contains(grid_regexp) || w[7].contains(non_r_db_regexp) )){
QString caller = w[6];
QString grid = "";
QString db = w[1];
int db_i = w[1].toInt();
db = (db_i >=0 ? "+":"") + QStringLiteral("%1").arg(db_i, (db_i >=0 ? 2:3), 10, QLatin1Char('0')); // +00, -01 etc.
// houndcall rpt grid
if (w[7].contains(grid_regexp)) grid = w[7];
if (w[7].contains(non_r_db_regexp)) {
LOG_INFO(QString("%1 called with signal report %2").arg(caller).arg(w[7]));
}
if (caller.length() > 2) {
// make sure it's not already in the queue
for ( QString hs : m_houndQueue) {
if (hs.startsWith(caller)) {
LOG_INFO(QString("%1 already in queue. Skipping").arg(hs));
return;
}
}
QString caller_rpt = (caller+" ").mid(0,12)+db;
if (m_houndQueue.count() < MAX_HOUNDS_IN_QUEUE) {
// add it to the queue
m_houndQueue.enqueue(caller_rpt + " " + grid);
refreshHoundQueueDisplay();
// TODO: remove from active stations window too?
}
}
} else {
LOG_INFO(QString("queueActiveWindowHound2 - skipping %1").arg(line));
}
}
}
void MainWindow::callSandP2(int n)
{
bool bCtrl = (n<0);
@ -4035,12 +4091,20 @@ void MainWindow::activeWorked(QString call, QString band)
void MainWindow::readFromStdout() //readFromStdout
{
bool bDisplayPoints = false;
QString all_decodes;
if(m_ActiveStationsWidget!=NULL) {
bDisplayPoints=(m_mode=="FT4" or m_mode=="FT8") and
(m_specOp==SpecOp::ARRL_DIGI or m_ActiveStationsWidget->isVisible());
}
while(proc_jt9.canReadLine()) {
auto line_read = proc_jt9.readLine ();
if (m_mode == "FT8" and m_specOp == SpecOp::FOX and m_ActiveStationsWidget != NULL) { // see if we should add this to ActiveStations window
QString the_line = QString(line_read);
if (!m_ActiveStationsWidget->wantedOnly() ||
(the_line.contains(" " + m_config.my_callsign() + " ") ||
the_line.contains(" <" + m_config.my_callsign() + "> ")))
all_decodes.append(line_read);
}
if (auto p = std::strpbrk (line_read.constData (), "\n\r")) {
// truncate before line ending chars
line_read = line_read.left (p - line_read.constData ());
@ -4124,6 +4188,10 @@ void MainWindow::readFromStdout() //readFromStdout
if(m_TRperiod>=60) ntime=4;
if (line_read.left(ntime) != m_tBlankLine && QString::fromUtf8(line_read.constData()).left(4).contains(QRegularExpression {"\\d\\d\\d\\d"})) {
ui->decodedTextBrowser->new_period ();
if (m_specOp == SpecOp::FOX and m_ActiveStationsWidget != NULL) { // clear the ActiveStations window
m_ActiveStationsWidget->clearStations();
m_ActiveStationsWidget->displayRecentStations("Fox Mode", "");
}
if (m_config.insert_blank ()
&& SpecOp::FOX != m_specOp) {
QString band;
@ -4439,6 +4507,9 @@ void MainWindow::readFromStdout() //readFromStdout
}
}
}
if (m_mode == "FT8" and m_specOp == SpecOp::FOX and m_ActiveStationsWidget != NULL) {
m_ActiveStationsWidget->addLine(all_decodes);
}
}
//
@ -4652,7 +4723,7 @@ void MainWindow::guiUpdate()
m_bTxTime = (t2p >= tx1) and (t2p < tx2);
if(m_mode=="Echo") m_bTxTime = m_bTxTime and m_bEchoTxOK;
if(m_mode=="FT8" and ui->tx5->currentText().contains("/B ")) {
//FT8 beacon transmissiion from Tx5 only at top of a UTC minute
//FT8 beacon transmission from Tx5 only at top of a UTC minute
double t4p=fmod(tsec,4*m_TRperiod);
if(t4p >= 30.0) m_bTxTime=false;
}
@ -4731,7 +4802,7 @@ void MainWindow::guiUpdate()
ui->TxFreqSpinBox->setValue(750); //SuperFox transmits at 750 Hz
} else {
if (ui->TxFreqSpinBox->value() > 900) {
ui->TxFreqSpinBox->setValue(300);
ui->TxFreqSpinBox->setValue(500);
}
}
}
@ -7226,7 +7297,7 @@ void MainWindow::on_actionFT8_triggered()
ui->rh_decodes_title_label->setText(tr ("Rx Frequency"));
if(SpecOp::FOX==m_specOp) {
ui->lh_decodes_title_label->setText(tr ("Stations calling DXpedition %1").arg (m_config.my_callsign()));
ui->lh_decodes_headings_label->setText( "Call Grid dB Freq Dist Age Continent");
ui->lh_decodes_headings_label->setText( "Call Grid dB Freq Dist Age Cont Score");
} else {
ui->lh_decodes_title_label->setText(tr ("Band Activity"));
ui->lh_decodes_headings_label->setText( " UTC dB DT Freq " + tr ("Message"));
@ -7252,16 +7323,18 @@ void MainWindow::on_actionFT8_triggered()
if(m_config.superFox()) {
ui->TxFreqSpinBox->setValue(750); //SuperFox transmits at 750 Hz
} else {
ui->TxFreqSpinBox->setValue(300);
ui->TxFreqSpinBox->setValue(500);
}
// 01234567890123456789012345678901234567
displayWidgets(nWidgets("11101000010011100001000000000010000000"));
displayWidgets(nWidgets("11101000010011100001000000000011000000"));
ui->cbRxAll->setText(tr("Show Already Worked"));
if(m_config.superFox()) {
ui->labDXped->setText(tr ("Super Fox"));
} else {
ui->labDXped->setText(tr ("Fox"));
}
on_fox_log_action_triggered();
if (m_ActiveStationsWidget) m_ActiveStationsWidget->setClickOK(true); // allow clicks
}
if(SpecOp::HOUND == m_specOp) {
ui->houndButton->setChecked(true);
@ -7272,6 +7345,7 @@ void MainWindow::on_actionFT8_triggered()
ui->cbHoldTxFreq->setChecked(true);
// 01234567890123456789012345678901234567
displayWidgets(nWidgets("11101000010011000001000000000011000000"));
ui->cbRxAll->setText(tr("Rx All Freqs"));
if(m_config.superFox()) {
ui->labDXped->setText(tr ("Super Hound"));
ui->cbRxAll->setEnabled(false);
@ -7288,6 +7362,7 @@ void MainWindow::on_actionFT8_triggered()
ui->txb4->setEnabled(false);
ui->txb5->setEnabled(false);
ui->txb6->setEnabled(false);
if (m_ActiveStationsWidget) m_ActiveStationsWidget->setClickOK(false);
} else {
switch_mode (Modes::FT8);
}
@ -9995,15 +10070,21 @@ QString MainWindow::sortHoundCalls(QString t, int isort, int max_dB)
* 2: Grid
* 3: SNR (reverse order)
* 4: Distance (reverse order)
* 5: Age (reverse order)
* 6: Continent
* 7: User defined (reverse order)
*
*/
QMap<QString,QString> map;
QStringList lines,lines2;
QString msg,houndCall,t1;
QString ABC{"ABCDEFGHIJKLMNOPQRSTUVWXYZ _"};
QList<int> reverse_sorted{3,4,5,6};
QString Continents{" AF AN AS EU NA OC SA UN "}; // matches what we get from AD1C's country list
QList<int> list;
int i,j,k,n,nlines;
bool bReverse=(isort >= 3);
bool bReverse = reverse_sorted.contains(isort);
isort=qAbs(isort);
// Save only the most recent transmission from each caller.
@ -10012,7 +10093,7 @@ QString MainWindow::sortHoundCalls(QString t, int isort, int max_dB)
for(i=0; i<nlines; i++) {
msg=lines.at(i); //key = callsign
if(msg.mid(13,1)==" ") msg=msg.mid(0,13) + "...." + msg.mid(17);
houndCall=msg.split(" ").at(0); //value = "call grid snr freq dist age"
houndCall=msg.split(" ").at(0); //value = "call grid snr freq dist age continent user-defined
map[houndCall]=msg;
}
@ -10020,16 +10101,43 @@ QString MainWindow::sortHoundCalls(QString t, int isort, int max_dB)
t="";
for(auto a: map.keys()) {
t1=map[a].split(" ",SkipEmptyParts).at(2);
// add the user-defined value to the end of the line. Example:
// JJ0NCC PM97 15 2724 7580 2 AS 67
// PY7ZZ HI21 -13 1673 10549 1 SA -
QString annotated_value_s{" - "}; // default
qint32 annotated_value = 0;
if (m_annotated_callsigns.contains(a)) {
annotated_value = m_annotated_callsigns.value(a);
annotated_value_s = QString::number(annotated_value);
}
map[a] += QString(" %1").arg(annotated_value_s, 8);
int nsnr=t1.toInt(); // get snr
if(nsnr <= max_dB) { // keep only if snr in specified range
if(isort==1) t += map[a] + "\n";
if(isort==3 or isort==4) {
i=2; // sort Hound calls by snr
if(isort==4) i=4; // sort Hound calls by distance
if (isort==3 or isort==4 or isort==5) { // numeric ones: snr, distance, age
if (isort==3)
i=2; // sort Hound calls by snr
else
i=isort; // part of the line that we want
t1=map[a].split(" ",SkipEmptyParts).at(i);
n=1000*(t1.toInt()+100) + j; // pack (snr or dist) and index j into n
n=1000*(t1.toInt()+100) + j; // pack (snr or dist or age) and index j into n
list.insert(j,n); // add n to list at [j]
}
if (isort == 6) { // sort by continent
i = 6;
QStringList parts = map[a].split(" ", SkipEmptyParts);
if (parts.size() <= i + 1) {
n = j;
} else {
QString cont = map[a].split(" ", SkipEmptyParts).at(i);
int cont_n = Continents.indexOf(" " + cont + " ");
n = 1000 + 1000 * cont_n + j; // index may return -1, so add 1000 to make it positive
}
list.insert(j, n);
}
if(isort==2) { // sort Hound calls by grid
t1=map[a].split(" ",SkipEmptyParts).at(1);
@ -10041,6 +10149,11 @@ QString MainWindow::sortHoundCalls(QString t, int isort, int max_dB)
list.insert(j,n); // add n to list at [j]
}
if(isort==7) { // annotated value provided by external app
n = 1000 + 1000 * std::max((qint32) 0, annotated_value) + j;
list.insert(j, n);
}
lines2.insert(j,map[a]); // add map[a] to lines2 at [j]
j++;
}
@ -10109,14 +10222,16 @@ void MainWindow::selectHound(QString line, bool bTopQueue)
m_houndCallers=m_houndCallers.remove(line+"\n"); // Remove t from sorted Hound list
m_nSortedHounds--;
ui->decodedTextBrowser->setText(m_houndCallers); // Populate left window with Hound callers
ui->decodedTextBrowser->setHighlightedHoundText(m_houndCallers); // Populate left window with Hound callers
QString t1=houndCall + " ";
QString t2=rpt;
QString t1_with_grid;
if(rpt.mid(0,1) != "-" and rpt.mid(0,1) != "+") t2="+" + rpt;
if(t2.length()==2) t2=t2.mid(0,1) + "0" + t2.mid(1,1);
t1=t1.mid(0,12) + t2;
ui->houndQueueTextBrowser->displayHoundToBeCalled(t1, bTopQueue); // Add hound call and rpt to tb4
// display the callers, highlighting calls if necessary
ui->houndQueueTextBrowser->insertText(bTopQueue ? t1 + "\n" : t1, QColor{}, QColor{}, houndCall, "", bTopQueue ? QTextCursor::Start : QTextCursor::End);
t1_with_grid=t1 + " " + houndGrid; // Append the grid
if (bTopQueue)
@ -10131,6 +10246,9 @@ void MainWindow::selectHound(QString line, bool bTopQueue)
QTextCursor cursor = ui->houndQueueTextBrowser->textCursor();
cursor.setPosition(0); // Scroll to top of list
ui->houndQueueTextBrowser->setTextCursor(cursor);
cursor = ui->decodedTextBrowser->textCursor();
cursor.setPosition(0); // Highlighting happens in the forward direction
ui->decodedTextBrowser->setTextCursor(cursor);
}
//------------------------------------------------------------------------------
@ -10214,8 +10332,11 @@ void MainWindow::houndCallers()
if(t.length()>30) {
m_isort=ui->comboBoxHoundSort->currentIndex();
QString t1=sortHoundCalls(t,m_isort,m_max_dB);
ui->decodedTextBrowser->setText(t1);
ui->decodedTextBrowser->setHighlightedHoundText(t1);
}
QTextCursor cursor = ui->decodedTextBrowser->textCursor();
cursor.setPosition(0); // Set scroll at top, in preparation for highlighting messages
ui->decodedTextBrowser->setTextCursor(cursor);
f.close();
}
}
@ -10252,7 +10373,7 @@ void MainWindow::updateFoxQSOsInProgressDisplay()
QString hc = m_foxQSOinProgress.at(i);
QString status = m_foxQSO[hc].ncall > m_maxStrikes ? QString(" (rx) ") : QString(" ");
QString str = (hc + " ").left(13) + QString::number(m_foxQSO[hc].ncall) + status;
ui->foxTxListTextBrowser->displayHoundToBeCalled(str);
ui->foxTxListTextBrowser->insertText(str, QColor{}, QColor{}, hc, "", QTextCursor::End);
}
}
@ -10350,7 +10471,7 @@ list1Done:
m_foxQSO[hc].rcvd = -99; //Have not received R+rpt
m_foxQSO[hc].tFoxRrpt = -1; //Have not received R+rpt
m_foxQSO[hc].tFoxTxRR73 = -1; //Have not sent RR73
rm_tb4(hc); //Remove this Hound from tb4
refreshHoundQueueDisplay();
if(list2.size()==m_Nslots) {
break;
@ -10480,24 +10601,13 @@ void MainWindow::update_foxLogWindow_rate()
}
}
void MainWindow::rm_tb4(QString houndCall)
void MainWindow::refreshHoundQueueDisplay()
{
if(houndCall=="") return;
QString t="";
QString tb4=ui->houndQueueTextBrowser->toPlainText();
QStringList list=tb4.split("\n");
int n=list.size();
int j=0;
for (int i=0; i<n; i++) {
if(j>0) t += "\n";
QString line=list.at(i);
if(!line.contains(houndCall + " ")) {
j++;
t += line;
}
ui->houndQueueTextBrowser->clear();
for (QString line: m_houndQueue) {
auto hc = line.mid(0, 12).trimmed();
ui->houndQueueTextBrowser->insertText(line, QColor{}, QColor{}, hc, "", QTextCursor::End);
}
t.replace("\n\n","\n");
ui->houndQueueTextBrowser->setText(t);
}
void MainWindow::doubleClickOnFoxQueue(Qt::KeyboardModifiers modifiers)
@ -10521,14 +10631,9 @@ void MainWindow::doubleClickOnFoxQueue(Qt::KeyboardModifiers modifiers)
}
}
m_houndQueue.prepend(houndLine);
ui->houndQueueTextBrowser->clear();
for (QString line: m_houndQueue)
{
ui->houndQueueTextBrowser->displayHoundToBeCalled(line.mid(0,16), false);
}
refreshHoundQueueDisplay();
} else
{
rm_tb4(houndCall);
writeFoxQSO(" Del: " + houndCall);
QQueue <QString> tmpQueue;
while (!m_houndQueue.isEmpty())
@ -10538,6 +10643,7 @@ void MainWindow::doubleClickOnFoxQueue(Qt::KeyboardModifiers modifiers)
if (hc != houndCall) tmpQueue.enqueue(t);
}
m_houndQueue.swap(tmpQueue);
refreshHoundQueueDisplay();
}
}
@ -10559,7 +10665,7 @@ void MainWindow::doubleClickOnFoxInProgress(Qt::KeyboardModifiers modifiers)
void MainWindow::foxQueueTopCallCommand()
{
m_decodedText2 = true;
if(SpecOp::FOX==m_specOp && m_decodedText2 && m_houndQueue.count() < 10)
if(SpecOp::FOX==m_specOp && m_decodedText2 && m_houndQueue.count() < MAX_HOUNDS_IN_QUEUE)
{
QTextCursor cursor = ui->decodedTextBrowser->textCursor();
@ -10687,7 +10793,7 @@ void MainWindow::foxTest()
hc1=line.mid(i0+6);
int i1=hc1.indexOf(" ");
hc1=hc1.mid(0,i1);
rm_tb4(hc1);
writeFoxQSO(" Del: " + hc1);
QQueue<QString> tmpQueue;
while(!m_houndQueue.isEmpty()) {
@ -10696,6 +10802,7 @@ void MainWindow::foxTest()
if(hc != hc1) tmpQueue.enqueue(t);
}
m_houndQueue.swap(tmpQueue);
refreshHoundQueueDisplay();
}
if(line.contains("Rx:")) {
msg=line.mid(37+6);
@ -10870,13 +10977,18 @@ void MainWindow::set_mode (QString const& mode)
else if ("Echo" == mode) on_actionEcho_triggered ();
}
void MainWindow::configActiveStations()
{
if(m_ActiveStationsWidget!=NULL and (m_mode=="Q65" or m_mode=="FT4" or m_mode=="FT8")) {
if(m_specOp==SpecOp::Q65_PILEUP) {
m_ActiveStationsWidget->displayRecentStations("Q65-pileup","");
void MainWindow::configActiveStations() {
if (m_ActiveStationsWidget != NULL and (m_mode == "Q65" or m_mode == "FT4" or m_mode == "FT8")) {
if (m_specOp == SpecOp::Q65_PILEUP) {
m_ActiveStationsWidget->displayRecentStations("Q65-pileup", "");
} else {
m_ActiveStationsWidget->displayRecentStations(m_mode,"");
if (m_specOp == SpecOp::FOX)
if (m_config.superFox())
m_ActiveStationsWidget->displayRecentStations("SuperFox Mode", "");
else
m_ActiveStationsWidget->displayRecentStations("Fox Mode", "");
else
m_ActiveStationsWidget->displayRecentStations(m_mode, "");
}
}
}

View File

@ -57,6 +57,7 @@
#define MAX_NUM_SYMBOLS 250
#define TX_SAMPLE_RATE 48000
#define NRING 3456000
#define MAX_HOUNDS_IN_QUEUE 10
extern int volatile itone[MAX_NUM_SYMBOLS]; //Audio tones for all Tx symbols
extern int volatile icw[NUM_CW_SYMBOLS]; //Dits for CW ID
@ -345,6 +346,8 @@ private slots:
, bool fast_mode, quint32 tr_period, quint32 rx_df, QString const& dx_call
, QString const& dx_grid, bool generate_messages);
void callSandP2(int nline);
void refreshHoundQueueDisplay();
void queueActiveWindowHound2(QString text);
private:
Q_SIGNAL void initializeAudioOutputStream (QAudioDeviceInfo,
@ -702,6 +705,7 @@ private:
QMap<QString,FoxQSO> m_foxQSO; //Key = HoundCall, value = parameters for QSO in progress
QMap<QString,QString> m_loggedByFox; //Key = HoundCall, value = logged band
QMap<QString,qint32> m_annotated_callsigns; //Key = HoundCall, value = provided by api call
struct FixupQSO //Info for fixing Fox's log from file "FoxQSO.txt"
{

View File

@ -2375,6 +2375,21 @@ Double-click to reset to the standard 73 message</string>
<string>Distance</string>
</property>
</item>
<item>
<property name="text">
<string>Age</string>
</property>
</item>
<item>
<property name="text">
<string>Continent</string>
</property>
</item>
<item>
<property name="text">
<string>Score</string>
</property>
</item>
</widget>
</item>
<item row="1" column="1" rowspan="10">