FT8 demod: use table model and view to optimize messge table update and filtering

This commit is contained in:
f4exb 2023-01-26 18:16:40 +01:00
parent c5a9f5fe12
commit e1640ffaf4
7 changed files with 442 additions and 213 deletions

View File

@ -32,6 +32,7 @@ if(NOT SERVER_MODE)
set(demodft8_SOURCES
${demodft8_SOURCES}
ft8demodgui.cpp
ft8demodfilterproxy.cpp
ft8demodgui.ui
ft8demodsettingsdialog.cpp
ft8demodsettingsdialog.ui
@ -39,6 +40,7 @@ if(NOT SERVER_MODE)
set(demodft8_HEADERS
${demodft8_HEADERS}
ft8demodgui.h
ft8demodfilterproxy.h
ft8demodsettingsdialog.h
)
set(TARGET_NAME demodft8)

View File

@ -0,0 +1,93 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "ft8demodfilterproxy.h"
FT8DemodFilterProxy::FT8DemodFilterProxy(QObject *parent) :
QSortFilterProxyModel(parent),
m_filterActive(FILTER_NONE)
{
}
void FT8DemodFilterProxy::resetFilter()
{
m_filterActive = FILTER_NONE;
invalidateFilter();
}
void FT8DemodFilterProxy::setFilterUTC(const QString& utcString)
{
m_filterActive = FILTER_UTC;
m_utc = utcString;
invalidateFilter();
}
void FT8DemodFilterProxy::setFilterDf(int df)
{
m_filterActive = FILTER_DF;
m_df = df;
invalidateFilter();
}
void FT8DemodFilterProxy::setFilterCall(const QString& callString)
{
m_filterActive = FILTER_CALL;
m_call = callString;
invalidateFilter();
}
void FT8DemodFilterProxy::setFilterLoc(const QString& locString)
{
m_filterActive = FILTER_LOC;
m_loc = locString;
invalidateFilter();
}
bool FT8DemodFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
if (m_filterActive == FILTER_NONE) {
return true;
}
if (m_filterActive == FILTER_UTC)
{
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
return sourceModel()->data(index).toString() == m_utc;
}
if (m_filterActive == FILTER_DF)
{
QModelIndex index = sourceModel()->index(sourceRow, 4, sourceParent);
int df = sourceModel()->data(index).toInt();
return (df >= m_df - 4) && (df <= m_df + 4); // +/- 4 Hz tolerance which is about one symbol width
}
if (m_filterActive == FILTER_CALL)
{
QModelIndex indexCall1 = sourceModel()->index(sourceRow, 6, sourceParent);
QModelIndex indexCall2 = sourceModel()->index(sourceRow, 7, sourceParent);
return (sourceModel()->data(indexCall1).toString() == m_call) ||
(sourceModel()->data(indexCall2).toString() == m_call);
}
if (m_filterActive == FILTER_LOC)
{
QModelIndex index = sourceModel()->index(sourceRow, 8, sourceParent);
return sourceModel()->data(index).toString() == m_loc;
}
return true;
}

View File

@ -0,0 +1,55 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Edouard Griffiths, F4EXB //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FT8DEMODFILTERPROXY_H
#define INCLUDE_FT8DEMODFILTERPROXY_H
#include <QSortFilterProxyModel>
class FT8DemodFilterProxy : public QSortFilterProxyModel
{
Q_OBJECT
public:
FT8DemodFilterProxy(QObject *parent = nullptr);
void resetFilter();
void setFilterUTC(const QString& utcString);
void setFilterDf(int df);
void setFilterCall(const QString& utcString);
void setFilterLoc(const QString& utcString);
protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
private:
enum filterActive
{
FILTER_NONE,
FILTER_UTC,
FILTER_DF,
FILTER_CALL,
FILTER_LOC
};
bool dfInRange(int df) const;
filterActive m_filterActive;
QString m_utc;
int m_df;
QString m_call;
QString m_loc;
};
#endif // INCLUDE_FT8DEMODFILTERPROXY_H

View File

@ -38,6 +38,200 @@
#include "ft8demod.h"
#include "ft8demodsettingsdialog.h"
FT8MessagesTableModel::FT8MessagesTableModel(QObject *parent)
: QAbstractTableModel(parent)
{}
int FT8MessagesTableModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : m_ft8Messages.size();
}
int FT8MessagesTableModel::columnCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : m_columnCount;
}
QVariant FT8MessagesTableModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
if (index.row() >= m_ft8Messages.size() || index.row() < 0) {
return QVariant();
}
if (role == Qt::DisplayRole)
{
const auto &ft8Message = m_ft8Messages.at(index.row());
switch (index.column()) {
case FT8DemodSettings::MESSAGE_COL_UTC:
return ft8Message.m_utc;
case FT8DemodSettings::MESSAGE_COL_PASS:
return ft8Message.m_pass;
case FT8DemodSettings::MESSAGE_COL_OKBITS:
return ft8Message.m_okBits;
case FT8DemodSettings::MESSAGE_COL_DT:
return tr("%1").arg(ft8Message.m_dt, 4, 'f', 1);
case FT8DemodSettings::MESSAGE_COL_DF:
return ft8Message.m_df;
case FT8DemodSettings::MESSAGE_COL_SNR:
return ft8Message.m_snr;
case FT8DemodSettings::MESSAGE_COL_CALL1:
return ft8Message.m_call1;
case FT8DemodSettings::MESSAGE_COL_CALL2:
return ft8Message.m_call2;
case FT8DemodSettings::MESSAGE_COL_LOC:
return ft8Message.m_loc;
case FT8DemodSettings::MESSAGE_COL_INFO:
return ft8Message.m_info;
default:
break;
}
}
if (role == Qt::TextAlignmentRole)
{
switch (index.column()) {
case FT8DemodSettings::MESSAGE_COL_DT:
case FT8DemodSettings::MESSAGE_COL_DF:
case FT8DemodSettings::MESSAGE_COL_SNR:
return Qt::AlignRight;
default:
break;
}
}
return QVariant();
}
QVariant FT8MessagesTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if ((role == Qt::DisplayRole) && (orientation == Qt::Horizontal))
{
switch (section) {
case FT8DemodSettings::MESSAGE_COL_UTC:
return tr("UTC");
case FT8DemodSettings::MESSAGE_COL_PASS:
return tr("P");
case FT8DemodSettings::MESSAGE_COL_OKBITS:
return tr("OKb");
case FT8DemodSettings::MESSAGE_COL_DT:
return tr("dt");
case FT8DemodSettings::MESSAGE_COL_DF:
return tr("df");
case FT8DemodSettings::MESSAGE_COL_SNR:
return tr("SNR");
case FT8DemodSettings::MESSAGE_COL_CALL1:
return tr("Call1");
case FT8DemodSettings::MESSAGE_COL_CALL2:
return tr("Call2");
case FT8DemodSettings::MESSAGE_COL_LOC:
return tr("Loc");
case FT8DemodSettings::MESSAGE_COL_INFO:
return tr("Info");
default:
break;
}
}
if (role == Qt::ToolTipRole)
{
switch (section) {
case FT8DemodSettings::MESSAGE_COL_UTC:
return tr("Sequence UTC time HHMMSS");
case FT8DemodSettings::MESSAGE_COL_PASS:
return tr("Successful decoder pass index");
case FT8DemodSettings::MESSAGE_COL_OKBITS:
return tr("Number of correct bits before correction");
case FT8DemodSettings::MESSAGE_COL_DT:
return tr("Message start time delay in sequence (s)");
case FT8DemodSettings::MESSAGE_COL_DF:
return tr("Carrier frequency shift (Hz)");
case FT8DemodSettings::MESSAGE_COL_SNR:
return tr("Signal to noise ratio (dB) in 2.5 kHz bandwidth");
case FT8DemodSettings::MESSAGE_COL_CALL1:
return tr("Fist call area");
case FT8DemodSettings::MESSAGE_COL_CALL2:
return tr("Second call area");
case FT8DemodSettings::MESSAGE_COL_LOC:
return tr("Locator area");
case FT8DemodSettings::MESSAGE_COL_INFO:
return tr("Decoder information");
default:
break;
}
}
return QVariant();
}
const QVector<FT8MesssageData> &FT8MessagesTableModel::getMessages() const
{
return m_ft8Messages;
}
void FT8MessagesTableModel::messagesReceived(const QList<FT8Message>& messages)
{
int position = m_ft8Messages.size();
int rows = messages.size();
beginInsertRows(QModelIndex(), position, position + rows - 1);
for (const auto& message : messages)
{
m_ft8Messages.push_back(FT8MesssageData{
message.ts.toString("HHmmss"),
message.pass,
message.nbCorrectBits,
message.dt,
(int) message.df,
message.snr,
message.call1,
message.call2,
message.loc,
message.decoderInfo
});
}
endInsertRows();
}
void FT8MessagesTableModel::setDefaultMessage()
{
if (m_ft8Messages.size() != 0) {
return;
}
beginInsertRows(QModelIndex(), 0, 0);
m_ft8Messages.push_back(FT8MesssageData{
"000000",
0,
174,
-8.0,
8000,
-24,
"CQ PA900RAALTE",
"PA900RAALTE",
"JN000",
"OSD-0-73"
});
endInsertRows();
}
void FT8MessagesTableModel::clearMessages()
{
if (m_ft8Messages.size() == 0) {
return;
}
beginRemoveRows(QModelIndex(), 0, m_ft8Messages.size()-1);
m_ft8Messages.clear();
endRemoveRows();
}
FT8DemodGUI* FT8DemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel)
{
FT8DemodGUI* gui = new FT8DemodGUI(pluginAPI, deviceUISet, rxChannel);
@ -216,16 +410,13 @@ void FT8DemodGUI::on_filterIndex_valueChanged(int value)
void FT8DemodGUI::on_moveToBottom_clicked()
{
ui->messages->scrollToBottom();
ui->messagesView->scrollToBottom();
}
void FT8DemodGUI::on_filterMessages_toggled(bool checked)
{
m_filterMessages = checked;
for (int row = 0; row < ui->messages->rowCount(); row++) {
filterMessageRow(row);
}
filterMessages();
}
void FT8DemodGUI::on_applyBandPreset_clicked()
@ -246,8 +437,8 @@ void FT8DemodGUI::on_applyBandPreset_clicked()
void FT8DemodGUI::on_clearMessages_clicked()
{
ui->messages->setRowCount(0);
ui->nbDecodesInTable->setText("0");
m_messagesModel.clearMessages();
}
void FT8DemodGUI::on_recordWav_toggled(bool checked)
@ -429,11 +620,8 @@ FT8DemodGUI::FT8DemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban
applyBandwidths(m_settings.m_filterBank[m_settings.m_filterIndex].m_spanLog2, true); // does applySettings(true)
DialPopup::addPopupsToChildDials(this);
// Resize the table using dummy data
resizeMessageTable();
populateBandPresets();
connect(ui->messages, &QTableWidget::cellClicked, this, &FT8DemodGUI::messageCellClicked);
setupMessagesView();
}
FT8DemodGUI::~FT8DemodGUI()
@ -665,83 +853,19 @@ void FT8DemodGUI::updateAbsoluteCenterFrequency()
setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset);
}
void FT8DemodGUI::resizeMessageTable()
{
// Fill table with a row of dummy data that will size the columns nicely
// Trailing spaces are for sort arrow
int row = ui->messages->rowCount();
ui->messages->setRowCount(row + 1);
ui->messages->setItem(row, MESSAGE_COL_UTC, new QTableWidgetItem("000000"));
ui->messages->setItem(row, MESSAGE_COL_N, new QTableWidgetItem("0"));
ui->messages->setItem(row, MESSAGE_COL_SNR, new QTableWidgetItem("-24"));
ui->messages->setItem(row, MESSAGE_COL_DEC, new QTableWidgetItem("174"));
ui->messages->setItem(row, MESSAGE_COL_DT, new QTableWidgetItem("-0.0"));
ui->messages->setItem(row, MESSAGE_COL_DF, new QTableWidgetItem("0000"));
ui->messages->setItem(row, MESSAGE_COL_CALL1, new QTableWidgetItem("CQ PA900RAALTE"));
ui->messages->setItem(row, MESSAGE_COL_CALL2, new QTableWidgetItem("PA900RAALTE"));
ui->messages->setItem(row, MESSAGE_COL_LOC, new QTableWidgetItem("JN000"));
ui->messages->setItem(row, MESSAGE_COL_INFO, new QTableWidgetItem("OSD-0-73"));
ui->messages->resizeColumnsToContents();
ui->messages->removeRow(row);
}
void FT8DemodGUI::messagesReceived(const QList<FT8Message>& messages)
{
ui->nbDecodesText->setText(tr("%1").arg(messages.size()));
// Is scroll bar at bottom
QScrollBar *sb = ui->messages->verticalScrollBar();
QScrollBar *sb = ui->messagesView->verticalScrollBar();
bool scrollToBottom = sb->value() == sb->maximum();
// Add to messages table
int row = ui->messages->rowCount();
for (const auto& message : messages)
{
ui->messages->setRowCount(row + 1);
QTableWidgetItem *utcItem = new QTableWidgetItem();
QTableWidgetItem *passItem = new QTableWidgetItem();
QTableWidgetItem *snrItem = new QTableWidgetItem();
QTableWidgetItem *correctItem = new QTableWidgetItem();
QTableWidgetItem *dtItem = new QTableWidgetItem();
QTableWidgetItem *dfItem = new QTableWidgetItem();
QTableWidgetItem *call1Item = new QTableWidgetItem();
QTableWidgetItem *call2Item = new QTableWidgetItem();
QTableWidgetItem *locItem = new QTableWidgetItem();
QTableWidgetItem *infoItem = new QTableWidgetItem();
ui->messages->setItem(row, MESSAGE_COL_UTC, utcItem);
ui->messages->setItem(row, MESSAGE_COL_N, passItem);
ui->messages->setItem(row, MESSAGE_COL_SNR, snrItem);
ui->messages->setItem(row, MESSAGE_COL_DEC, correctItem);
ui->messages->setItem(row, MESSAGE_COL_DT, dtItem);
ui->messages->setItem(row, MESSAGE_COL_DF, dfItem);
ui->messages->setItem(row, MESSAGE_COL_CALL1, call1Item);
ui->messages->setItem(row, MESSAGE_COL_CALL2, call2Item);
ui->messages->setItem(row, MESSAGE_COL_LOC, locItem);
ui->messages->setItem(row, MESSAGE_COL_INFO, infoItem);
utcItem->setText(message.ts.toString("HHmmss"));
passItem->setText(tr("%1").arg(message.pass));
correctItem->setText(tr("%1").arg(message.nbCorrectBits));
dtItem->setText(tr("%1").arg(message.dt, 4, 'f', 1));
dfItem->setText(tr("%1").arg((int) message.df, 4));
snrItem->setText(tr("%1").arg(message.snr, 3));
call1Item->setText(message.call1);
call2Item->setText(message.call2);
locItem->setText(message.loc);
infoItem->setText(message.decoderInfo);
filterMessageRow(row);
row++;
}
ui->nbDecodesInTable->setText(tr("%1").arg(row));
m_messagesModel.messagesReceived(messages);
ui->nbDecodesInTable->setText(tr("%1").arg(m_messagesModel.countAllMessages()));
if (scrollToBottom) {
ui->messages->scrollToBottom();
ui->messagesView->scrollToBottom();
}
}
@ -757,45 +881,43 @@ void FT8DemodGUI::populateBandPresets()
ui->bandPreset->blockSignals(false);
}
void FT8DemodGUI::messageCellClicked(int row, int col)
void FT8DemodGUI::messageViewClicked(const QModelIndex &index)
{
m_selectedColumn = col;
m_selectedValue = ui->messages->item(row, col)->text();
qDebug("FT8DemodGUI::messageCellChanged: %d %s", m_selectedColumn, qPrintable(m_selectedValue));
if (index.isValid())
{
m_selectedColumn = index.column();
m_selectedData = index.data();
filterMessages();
}
}
void FT8DemodGUI::filterMessageRow(int row)
void FT8DemodGUI::filterMessages()
{
if (!m_filterMessages)
{
ui->messages->setRowHidden(row, false);
m_messagesFilterProxy.resetFilter();
return;
}
if ((m_selectedColumn == MESSAGE_COL_CALL1) || (m_selectedColumn == MESSAGE_COL_CALL2))
{
const QString& call1 = ui->messages->item(row, MESSAGE_COL_CALL1)->text();
const QString& call2 = ui->messages->item(row, MESSAGE_COL_CALL2)->text();
bool visible = ((call1 == m_selectedValue) || (call2 == m_selectedValue));
ui->messages->setRowHidden(row, !visible);
return;
if ((m_selectedColumn == FT8DemodSettings::MESSAGE_COL_CALL1) || (m_selectedColumn == FT8DemodSettings::MESSAGE_COL_CALL2)) {
m_messagesFilterProxy.setFilterCall(m_selectedData.toString());
} else if (m_selectedColumn == FT8DemodSettings::MESSAGE_COL_LOC) {
m_messagesFilterProxy.setFilterLoc(m_selectedData.toString());
} else if (m_selectedColumn == FT8DemodSettings::MESSAGE_COL_UTC) {
m_messagesFilterProxy.setFilterUTC(m_selectedData.toString());
} else if (m_selectedColumn == FT8DemodSettings::MESSAGE_COL_DF) {
m_messagesFilterProxy.setFilterDf(m_selectedData.toInt());
}
if (m_selectedColumn == MESSAGE_COL_LOC)
{
const QString& loc = ui->messages->item(row, MESSAGE_COL_LOC)->text();
bool visible = (loc == m_selectedValue);
ui->messages->setRowHidden(row, !visible);
return;
}
if (m_selectedColumn == MESSAGE_COL_UTC)
{
const QString& utc = ui->messages->item(row, MESSAGE_COL_UTC)->text();
bool visible = (utc == m_selectedValue);
ui->messages->setRowHidden(row, !visible);
return;
}
ui->messages->setRowHidden(row, false);
}
void FT8DemodGUI::setupMessagesView()
{
m_messagesFilterProxy.setSourceModel(&m_messagesModel);
ui->messagesView->setModel(&m_messagesFilterProxy);
// resize columns
m_messagesModel.setDefaultMessage();
ui->messagesView->resizeColumnsToContents();
m_messagesModel.clearMessages();
connect(ui->messagesView, &QTableView::clicked, this, &FT8DemodGUI::messageViewClicked);
}

View File

@ -17,6 +17,8 @@
#ifndef INCLUDE_SSBDEMODGUI_H
#define INCLUDE_SSBDEMODGUI_H
#include <QAbstractTableModel>
#include "channel/channelgui.h"
#include "dsp/channelmarker.h"
#include "dsp/movingaverage.h"
@ -24,10 +26,12 @@
#include "util/ft8message.h"
#include "settings/rollupstate.h"
#include "ft8demodsettings.h"
#include "ft8demodfilterproxy.h"
class QModelIndex;
class PluginAPI;
class DeviceUISet;
class AudioFifo;
class FT8Demod;
class SpectrumVis;
@ -37,6 +41,41 @@ namespace Ui {
class FT8DemodGUI;
}
struct FT8MesssageData
{
QString m_utc;
int m_pass;
int m_okBits;
float m_dt;
int m_df;
int m_snr;
QString m_call1;
QString m_call2;
QString m_loc;
QString m_info;
};
class FT8MessagesTableModel : public QAbstractTableModel
{
Q_OBJECT
public:
FT8MessagesTableModel(QObject *parent = nullptr);
virtual int rowCount(const QModelIndex &parent) const;
virtual int columnCount(const QModelIndex &parent) const;
virtual QVariant data(const QModelIndex &index, int role) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
const QVector<FT8MesssageData> &getMessages() const;
void messagesReceived(const QList<FT8Message>& messages);
void setDefaultMessage();
void clearMessages();
int countAllMessages() const { return m_ft8Messages.size(); }
private:
QVector<FT8MesssageData> m_ft8Messages;
static const int m_columnCount = 10;
};
class FT8DemodGUI : public ChannelGUI {
Q_OBJECT
@ -84,11 +123,15 @@ private:
bool m_filterMessages;
int m_selectedColumn;
QString m_selectedValue;
QVariant m_selectedData;
FT8Demod* m_ft8Demod;
SpectrumVis* m_spectrumVis;
MessageQueue m_inputMessageQueue;
FT8MessagesTableModel m_messagesModel;
FT8DemodFilterProxy m_messagesFilterProxy;
explicit FT8DemodGUI(PluginAPI* pluginAPI, DeviceUISet* deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
virtual ~FT8DemodGUI();
@ -104,23 +147,10 @@ private:
void leaveEvent(QEvent*);
void enterEvent(EnterEventType*);
void resizeMessageTable();
void messagesReceived(const QList<FT8Message>& messages);
void populateBandPresets();
void filterMessageRow(int row);
enum MessageCol {
MESSAGE_COL_UTC,
MESSAGE_COL_N,
MESSAGE_COL_DEC,
MESSAGE_COL_DT,
MESSAGE_COL_DF,
MESSAGE_COL_SNR,
MESSAGE_COL_CALL1,
MESSAGE_COL_CALL2,
MESSAGE_COL_LOC,
MESSAGE_COL_INFO,
};
void filterMessages();
void setupMessagesView();
private slots:
void on_deltaFrequency_changed(qint64 value);
@ -142,7 +172,7 @@ private slots:
void onMenuDialogCalled(const QPoint& p);
void handleInputMessages();
void tick();
void messageCellClicked(int row, int col);
void messageViewClicked(const QModelIndex &index);
};
#endif // INCLUDE_SSBDEMODGUI_H

View File

@ -1005,19 +1005,13 @@
</layout>
</item>
<item>
<widget class="QTableWidget" name="messages">
<widget class="QTableView" name="messagesView">
<property name="font">
<font>
<family>Liberation Mono</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="toolTip">
<string>Decoded messages</string>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
@ -1027,86 +1021,6 @@
<attribute name="verticalHeaderDefaultSectionSize">
<number>15</number>
</attribute>
<column>
<property name="text">
<string>UTC</string>
</property>
<property name="toolTip">
<string>Sequence UTC time HHMMSS</string>
</property>
</column>
<column>
<property name="text">
<string>P</string>
</property>
<property name="toolTip">
<string>Successful decoder pass index</string>
</property>
</column>
<column>
<property name="text">
<string>OKb</string>
</property>
<property name="toolTip">
<string>Number of correct bits before correction</string>
</property>
</column>
<column>
<property name="text">
<string>dt</string>
</property>
<property name="toolTip">
<string>Time delay</string>
</property>
</column>
<column>
<property name="text">
<string>df</string>
</property>
<property name="toolTip">
<string>Frequency shift</string>
</property>
</column>
<column>
<property name="text">
<string>SNR</string>
</property>
<property name="toolTip">
<string>Signal to noise ratio (dB) in 2.5 kHz bandwidth</string>
</property>
</column>
<column>
<property name="text">
<string>Call1</string>
</property>
<property name="toolTip">
<string>Fist call area</string>
</property>
</column>
<column>
<property name="text">
<string>Call2</string>
</property>
<property name="toolTip">
<string>Second call area</string>
</property>
</column>
<column>
<property name="text">
<string>Loc</string>
</property>
<property name="toolTip">
<string>Locator area</string>
</property>
</column>
<column>
<property name="text">
<string>Info</string>
</property>
<property name="toolTip">
<string>Extra decoder information</string>
</property>
</column>
</widget>
</item>
</layout>

View File

@ -50,6 +50,19 @@ struct FT8DemodBandPreset
struct FT8DemodSettings
{
enum MessageCol {
MESSAGE_COL_UTC,
MESSAGE_COL_PASS,
MESSAGE_COL_OKBITS,
MESSAGE_COL_DT,
MESSAGE_COL_DF,
MESSAGE_COL_SNR,
MESSAGE_COL_CALL1,
MESSAGE_COL_CALL2,
MESSAGE_COL_LOC,
MESSAGE_COL_INFO,
};
qint32 m_inputFrequencyOffset;
// Real m_rfBandwidth;
// Real m_lowCutoff;