2021-01-13 14:58:07 -05:00
|
|
|
///////////////////////////////////////////////////////////////////////////////////
|
2023-11-18 06:02:48 -05:00
|
|
|
// Copyright (C) 2021-2023 Jon Beniston, M7RCE <jon@beniston.com> //
|
|
|
|
// Copyright (C) 2021-2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com> //
|
2021-01-13 14:58:07 -05:00
|
|
|
// //
|
|
|
|
// 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 <limits>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <QDockWidget>
|
|
|
|
#include <QMainWindow>
|
|
|
|
#include <QDebug>
|
|
|
|
#include <QMessageBox>
|
|
|
|
#include <QAction>
|
|
|
|
#include <QRegExp>
|
2021-11-04 08:33:43 -04:00
|
|
|
#include <QFileDialog>
|
2022-03-01 12:20:35 -05:00
|
|
|
#include <QScrollBar>
|
2021-01-13 14:58:07 -05:00
|
|
|
|
|
|
|
#include "packetdemodgui.h"
|
|
|
|
#include "util/ax25.h"
|
|
|
|
|
|
|
|
#include "device/deviceuiset.h"
|
|
|
|
#include "dsp/dspengine.h"
|
|
|
|
#include "dsp/dspcommands.h"
|
|
|
|
#include "ui_packetdemodgui.h"
|
|
|
|
#include "plugin/pluginapi.h"
|
|
|
|
#include "util/simpleserializer.h"
|
2021-11-04 08:33:43 -04:00
|
|
|
#include "util/csv.h"
|
2021-01-13 14:58:07 -05:00
|
|
|
#include "util/db.h"
|
|
|
|
#include "util/morse.h"
|
|
|
|
#include "util/units.h"
|
|
|
|
#include "gui/basicchannelsettingsdialog.h"
|
|
|
|
#include "gui/devicestreamselectiondialog.h"
|
|
|
|
#include "dsp/dspengine.h"
|
|
|
|
#include "gui/crightclickenabler.h"
|
2022-12-20 05:31:15 -05:00
|
|
|
#include "gui/dialogpositioner.h"
|
2021-01-13 14:58:07 -05:00
|
|
|
#include "channel/channelwebapiutils.h"
|
|
|
|
#include "maincore.h"
|
|
|
|
|
|
|
|
#include "packetdemod.h"
|
|
|
|
#include "packetdemodsink.h"
|
|
|
|
|
|
|
|
#define PACKET_COL_FROM 0
|
|
|
|
#define PACKET_COL_TO 1
|
|
|
|
#define PACKET_COL_VIA 2
|
|
|
|
#define PACKET_COL_TYPE 3
|
|
|
|
#define PACKET_COL_PID 4
|
|
|
|
#define PACKET_COL_DATA_ASCII 5
|
|
|
|
#define PACKET_COL_DATA_HEX 6
|
|
|
|
|
|
|
|
void PacketDemodGUI::resizeTable()
|
|
|
|
{
|
|
|
|
// Fill table with a row of dummy data that will size the columns nicely
|
|
|
|
// Trailing spaces are for sort arrow
|
|
|
|
int row = ui->packets->rowCount();
|
|
|
|
ui->packets->setRowCount(row + 1);
|
|
|
|
ui->packets->setItem(row, PACKET_COL_FROM, new QTableWidgetItem("123456-15-"));
|
|
|
|
ui->packets->setItem(row, PACKET_COL_TO, new QTableWidgetItem("123456-15-"));
|
|
|
|
ui->packets->setItem(row, PACKET_COL_VIA, new QTableWidgetItem("123456-15-"));
|
|
|
|
ui->packets->setItem(row, PACKET_COL_TYPE, new QTableWidgetItem("Type-"));
|
|
|
|
ui->packets->setItem(row, PACKET_COL_PID, new QTableWidgetItem("PID-"));
|
|
|
|
ui->packets->setItem(row, PACKET_COL_DATA_ASCII, new QTableWidgetItem("ABCEDGHIJKLMNOPQRSTUVWXYZ"));
|
|
|
|
ui->packets->setItem(row, PACKET_COL_DATA_HEX, new QTableWidgetItem("ABCEDGHIJKLMNOPQRSTUVWXYZ"));
|
|
|
|
ui->packets->resizeColumnsToContents();
|
|
|
|
ui->packets->removeRow(row);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Columns in table reordered
|
|
|
|
void PacketDemodGUI::packets_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex)
|
|
|
|
{
|
|
|
|
(void) oldVisualIndex;
|
|
|
|
|
|
|
|
m_settings.m_columnIndexes[logicalIndex] = newVisualIndex;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Column in table resized (when hidden size is 0)
|
|
|
|
void PacketDemodGUI::packets_sectionResized(int logicalIndex, int oldSize, int newSize)
|
|
|
|
{
|
|
|
|
(void) oldSize;
|
|
|
|
|
|
|
|
m_settings.m_columnSizes[logicalIndex] = newSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Right click in table header - show column select menu
|
|
|
|
void PacketDemodGUI::columnSelectMenu(QPoint pos)
|
|
|
|
{
|
|
|
|
menu->popup(ui->packets->horizontalHeader()->viewport()->mapToGlobal(pos));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hide/show column when menu selected
|
|
|
|
void PacketDemodGUI::columnSelectMenuChecked(bool checked)
|
|
|
|
{
|
|
|
|
(void) checked;
|
|
|
|
|
|
|
|
QAction* action = qobject_cast<QAction*>(sender());
|
|
|
|
if (action != nullptr)
|
|
|
|
{
|
|
|
|
int idx = action->data().toInt(nullptr);
|
|
|
|
ui->packets->setColumnHidden(idx, !action->isChecked());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create column select menu item
|
|
|
|
QAction *PacketDemodGUI::createCheckableItem(QString &text, int idx, bool checked)
|
|
|
|
{
|
|
|
|
QAction *action = new QAction(text, this);
|
|
|
|
action->setCheckable(true);
|
|
|
|
action->setChecked(checked);
|
|
|
|
action->setData(QVariant(idx));
|
|
|
|
connect(action, SIGNAL(triggered()), this, SLOT(columnSelectMenuChecked()));
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
|
|
|
PacketDemodGUI* PacketDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel)
|
|
|
|
{
|
|
|
|
PacketDemodGUI* gui = new PacketDemodGUI(pluginAPI, deviceUISet, rxChannel);
|
|
|
|
return gui;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::destroy()
|
|
|
|
{
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::resetToDefaults()
|
|
|
|
{
|
|
|
|
m_settings.resetToDefaults();
|
|
|
|
displaySettings();
|
|
|
|
applySettings(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray PacketDemodGUI::serialize() const
|
|
|
|
{
|
|
|
|
return m_settings.serialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PacketDemodGUI::deserialize(const QByteArray& data)
|
|
|
|
{
|
|
|
|
if(m_settings.deserialize(data)) {
|
|
|
|
displaySettings();
|
|
|
|
applySettings(true);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
resetToDefaults();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add row to table
|
|
|
|
void PacketDemodGUI::packetReceived(QByteArray packet)
|
|
|
|
{
|
|
|
|
AX25Packet ax25;
|
|
|
|
|
|
|
|
if (ax25.decode(packet))
|
|
|
|
{
|
2022-03-01 12:20:35 -05:00
|
|
|
// Is scroll bar at bottom
|
|
|
|
QScrollBar *sb = ui->packets->verticalScrollBar();
|
|
|
|
bool scrollToBottom = sb->value() == sb->maximum();
|
|
|
|
|
2021-01-13 14:58:07 -05:00
|
|
|
ui->packets->setSortingEnabled(false);
|
|
|
|
int row = ui->packets->rowCount();
|
|
|
|
ui->packets->setRowCount(row + 1);
|
|
|
|
|
|
|
|
QTableWidgetItem *fromItem = new QTableWidgetItem();
|
|
|
|
QTableWidgetItem *toItem = new QTableWidgetItem();
|
|
|
|
QTableWidgetItem *viaItem = new QTableWidgetItem();
|
|
|
|
QTableWidgetItem *typeItem = new QTableWidgetItem();
|
|
|
|
QTableWidgetItem *pidItem = new QTableWidgetItem();
|
|
|
|
QTableWidgetItem *dataASCIIItem = new QTableWidgetItem();
|
|
|
|
QTableWidgetItem *dataHexItem = new QTableWidgetItem();
|
|
|
|
ui->packets->setItem(row, PACKET_COL_FROM, fromItem);
|
|
|
|
ui->packets->setItem(row, PACKET_COL_TO, toItem);
|
|
|
|
ui->packets->setItem(row, PACKET_COL_VIA, viaItem);
|
|
|
|
ui->packets->setItem(row, PACKET_COL_TYPE, typeItem);
|
|
|
|
ui->packets->setItem(row, PACKET_COL_PID, pidItem);
|
|
|
|
ui->packets->setItem(row, PACKET_COL_DATA_ASCII, dataASCIIItem);
|
|
|
|
ui->packets->setItem(row, PACKET_COL_DATA_HEX, dataHexItem);
|
|
|
|
fromItem->setText(ax25.m_from);
|
|
|
|
toItem->setText(ax25.m_to);
|
|
|
|
viaItem->setText(ax25.m_via);
|
|
|
|
typeItem->setText(ax25.m_type);
|
|
|
|
pidItem->setText(ax25.m_pid);
|
|
|
|
dataASCIIItem->setText(ax25.m_dataASCII);
|
|
|
|
dataHexItem->setText(ax25.m_dataHex);
|
2023-05-15 11:44:02 -04:00
|
|
|
filterRow(row);
|
2021-01-13 14:58:07 -05:00
|
|
|
ui->packets->setSortingEnabled(true);
|
2022-03-01 12:20:35 -05:00
|
|
|
if (scrollToBottom) {
|
|
|
|
ui->packets->scrollToBottom();
|
|
|
|
}
|
2021-01-13 14:58:07 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
qDebug() << "Unsupported AX.25 packet: " << packet;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PacketDemodGUI::handleMessage(const Message& message)
|
|
|
|
{
|
|
|
|
if (PacketDemod::MsgConfigurePacketDemod::match(message))
|
|
|
|
{
|
|
|
|
qDebug("PacketDemodGUI::handleMessage: PacketDemod::MsgConfigurePacketDemod");
|
|
|
|
const PacketDemod::MsgConfigurePacketDemod& cfg = (PacketDemod::MsgConfigurePacketDemod&) message;
|
|
|
|
m_settings = cfg.getSettings();
|
|
|
|
blockApplySettings(true);
|
2021-12-02 17:54:39 -05:00
|
|
|
m_channelMarker.updateSettings(static_cast<const ChannelMarker*>(m_settings.m_channelMarker));
|
2021-01-13 14:58:07 -05:00
|
|
|
displaySettings();
|
|
|
|
blockApplySettings(false);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (DSPSignalNotification::match(message))
|
|
|
|
{
|
|
|
|
DSPSignalNotification& notif = (DSPSignalNotification&) message;
|
2022-04-13 05:08:21 -04:00
|
|
|
m_deviceCenterFrequency = notif.getCenterFrequency();
|
2021-01-13 14:58:07 -05:00
|
|
|
m_basebandSampleRate = notif.getSampleRate();
|
2022-04-13 05:08:21 -04:00
|
|
|
ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2);
|
|
|
|
ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2));
|
|
|
|
updateAbsoluteCenterFrequency();
|
2021-01-13 14:58:07 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (MainCore::MsgPacket::match(message))
|
|
|
|
{
|
|
|
|
MainCore::MsgPacket& report = (MainCore::MsgPacket&) message;
|
|
|
|
packetReceived(report.getPacket());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::handleInputMessages()
|
|
|
|
{
|
|
|
|
Message* message;
|
|
|
|
|
|
|
|
while ((message = getInputMessageQueue()->pop()) != 0)
|
|
|
|
{
|
|
|
|
if (handleMessage(*message))
|
|
|
|
{
|
|
|
|
delete message;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::channelMarkerChangedByCursor()
|
|
|
|
{
|
|
|
|
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
|
|
|
|
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
|
|
|
|
applySettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::channelMarkerHighlightedByCursor()
|
|
|
|
{
|
2022-04-25 18:42:26 -04:00
|
|
|
setHighlighted(m_channelMarker.getHighlighted());
|
2021-01-13 14:58:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::on_deltaFrequency_changed(qint64 value)
|
|
|
|
{
|
|
|
|
m_channelMarker.setCenterFrequency(value);
|
|
|
|
m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency();
|
2022-04-13 05:08:21 -04:00
|
|
|
updateAbsoluteCenterFrequency();
|
2021-01-13 14:58:07 -05:00
|
|
|
applySettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::on_mode_currentIndexChanged(int value)
|
|
|
|
{
|
2021-01-13 18:03:55 -05:00
|
|
|
(void) value;
|
2021-01-13 14:58:07 -05:00
|
|
|
QString mode = ui->mode->currentText();
|
|
|
|
// TODO: Support 9600 FSK
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::on_rfBW_valueChanged(int value)
|
|
|
|
{
|
|
|
|
float bw = value * 100.0f;
|
|
|
|
ui->rfBWText->setText(QString("%1k").arg(value / 10.0, 0, 'f', 1));
|
|
|
|
m_channelMarker.setBandwidth(bw);
|
|
|
|
m_settings.m_rfBandwidth = bw;
|
|
|
|
applySettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::on_fmDev_valueChanged(int value)
|
|
|
|
{
|
|
|
|
ui->fmDevText->setText(QString("%1k").arg(value / 10.0, 0, 'f', 1));
|
|
|
|
m_settings.m_fmDeviation = value * 100.0;
|
|
|
|
applySettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::on_filterFrom_editingFinished()
|
|
|
|
{
|
|
|
|
m_settings.m_filterFrom = ui->filterFrom->text();
|
|
|
|
filter();
|
|
|
|
applySettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::on_filterTo_editingFinished()
|
|
|
|
{
|
|
|
|
m_settings.m_filterTo = ui->filterTo->text();
|
|
|
|
filter();
|
|
|
|
applySettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::on_filterPID_stateChanged(int state)
|
|
|
|
{
|
|
|
|
m_settings.m_filterPID = state==Qt::Checked ? "f0" : "";
|
|
|
|
filter();
|
|
|
|
applySettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::on_clearTable_clicked()
|
|
|
|
{
|
|
|
|
ui->packets->setRowCount(0);
|
|
|
|
}
|
|
|
|
|
2021-04-07 16:06:00 -04:00
|
|
|
void PacketDemodGUI::on_udpEnabled_clicked(bool checked)
|
|
|
|
{
|
|
|
|
m_settings.m_udpEnabled = checked;
|
|
|
|
applySettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::on_udpAddress_editingFinished()
|
|
|
|
{
|
|
|
|
m_settings.m_udpAddress = ui->udpAddress->text();
|
|
|
|
applySettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::on_udpPort_editingFinished()
|
|
|
|
{
|
|
|
|
m_settings.m_udpPort = ui->udpPort->text().toInt();
|
|
|
|
applySettings();
|
|
|
|
}
|
|
|
|
|
2021-01-13 14:58:07 -05:00
|
|
|
void PacketDemodGUI::filterRow(int row)
|
|
|
|
{
|
|
|
|
bool hidden = false;
|
|
|
|
if (m_settings.m_filterFrom != "")
|
|
|
|
{
|
|
|
|
QRegExp re(m_settings.m_filterFrom);
|
|
|
|
QTableWidgetItem *fromItem = ui->packets->item(row, PACKET_COL_FROM);
|
|
|
|
if (!re.exactMatch(fromItem->text()))
|
|
|
|
hidden = true;
|
|
|
|
}
|
|
|
|
if (m_settings.m_filterTo != "")
|
|
|
|
{
|
|
|
|
QRegExp re(m_settings.m_filterTo);
|
|
|
|
QTableWidgetItem *toItem = ui->packets->item(row, PACKET_COL_TO);
|
|
|
|
if (!re.exactMatch(toItem->text()))
|
|
|
|
hidden = true;
|
|
|
|
}
|
|
|
|
if (m_settings.m_filterPID != "")
|
|
|
|
{
|
|
|
|
QTableWidgetItem *pidItem = ui->packets->item(row, PACKET_COL_PID);
|
|
|
|
if (pidItem->text() != m_settings.m_filterPID)
|
|
|
|
hidden = true;
|
|
|
|
}
|
|
|
|
ui->packets->setRowHidden(row, hidden);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::filter()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < ui->packets->rowCount(); i++)
|
|
|
|
{
|
|
|
|
filterRow(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown)
|
|
|
|
{
|
|
|
|
(void) widget;
|
|
|
|
(void) rollDown;
|
2021-11-24 04:50:42 -05:00
|
|
|
|
dd maximize button to MainSpectrum and expandible Channels and Features.
Add sizeToContents in ChannelGUI and FeatureGUI, called when widget is
rolled, so we can remove resizing code from all of the individual
channels and features.
In RollupContents, use minimumSizeHint for calculated size, so that
minimumWidth can come from .ui file.
In DeviceGUI::sizeToContents(), call adjustSize(), so Device GUIs start
out at minimum needed size (which should restore appearance prior to
last patch).
In stackSubWindows, use available space for channels if no
spectrum/features present.
In stackSubWindows, fix spectrum from being sized too big, resulting in
scroll bars appearing.
Reset user-defined channel width in stackSubWindows, when channels are
removed.
Don't stack maximized windows.
There's one hack in Channel/FeatureGUI::maximizeWindow(). It seems that
when maximimzing a window, QOpenGLWidgets aren't always paint properly
immediately afterwards, so the code forces an additional update. I can't
see why the first call to paintGL doesn't work.
2022-11-11 07:24:27 -05:00
|
|
|
getRollupContents()->saveState(m_rollupState);
|
2021-11-24 04:50:42 -05:00
|
|
|
applySettings();
|
2021-01-13 14:58:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::onMenuDialogCalled(const QPoint &p)
|
|
|
|
{
|
|
|
|
if (m_contextMenuType == ContextMenuChannelSettings)
|
|
|
|
{
|
|
|
|
BasicChannelSettingsDialog dialog(&m_channelMarker, this);
|
|
|
|
dialog.setUseReverseAPI(m_settings.m_useReverseAPI);
|
|
|
|
dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress);
|
|
|
|
dialog.setReverseAPIPort(m_settings.m_reverseAPIPort);
|
|
|
|
dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex);
|
|
|
|
dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex);
|
2022-04-17 19:42:03 -04:00
|
|
|
dialog.setDefaultTitle(m_displayedName);
|
|
|
|
|
|
|
|
if (m_deviceUISet->m_deviceMIMOEngine)
|
|
|
|
{
|
|
|
|
dialog.setNumberOfStreams(m_packetDemod->getNumberOfDeviceStreams());
|
|
|
|
dialog.setStreamIndex(m_settings.m_streamIndex);
|
|
|
|
}
|
|
|
|
|
2021-01-13 14:58:07 -05:00
|
|
|
dialog.move(p);
|
2022-12-20 05:31:15 -05:00
|
|
|
new DialogPositioner(&dialog, false);
|
2021-01-13 14:58:07 -05:00
|
|
|
dialog.exec();
|
|
|
|
|
|
|
|
m_settings.m_rgbColor = m_channelMarker.getColor().rgb();
|
|
|
|
m_settings.m_title = m_channelMarker.getTitle();
|
|
|
|
m_settings.m_useReverseAPI = dialog.useReverseAPI();
|
|
|
|
m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress();
|
|
|
|
m_settings.m_reverseAPIPort = dialog.getReverseAPIPort();
|
|
|
|
m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex();
|
|
|
|
m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex();
|
|
|
|
|
|
|
|
setWindowTitle(m_settings.m_title);
|
2022-04-12 10:20:45 -04:00
|
|
|
setTitle(m_channelMarker.getTitle());
|
2021-01-13 14:58:07 -05:00
|
|
|
setTitleColor(m_settings.m_rgbColor);
|
|
|
|
|
2022-04-17 19:42:03 -04:00
|
|
|
if (m_deviceUISet->m_deviceMIMOEngine)
|
|
|
|
{
|
|
|
|
m_settings.m_streamIndex = dialog.getSelectedStreamIndex();
|
|
|
|
m_channelMarker.clearStreamIndexes();
|
|
|
|
m_channelMarker.addStreamIndex(m_settings.m_streamIndex);
|
|
|
|
updateIndexLabel();
|
|
|
|
}
|
2021-01-13 14:58:07 -05:00
|
|
|
|
|
|
|
applySettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
resetContextMenuType();
|
|
|
|
}
|
|
|
|
|
|
|
|
PacketDemodGUI::PacketDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent) :
|
|
|
|
ChannelGUI(parent),
|
|
|
|
ui(new Ui::PacketDemodGUI),
|
|
|
|
m_pluginAPI(pluginAPI),
|
|
|
|
m_deviceUISet(deviceUISet),
|
|
|
|
m_channelMarker(this),
|
2022-04-13 05:08:21 -04:00
|
|
|
m_deviceCenterFrequency(0),
|
2021-01-13 14:58:07 -05:00
|
|
|
m_doApplySettings(true),
|
|
|
|
m_tickCount(0)
|
|
|
|
{
|
|
|
|
setAttribute(Qt::WA_DeleteOnClose, true);
|
2022-04-24 06:28:56 -04:00
|
|
|
m_helpURL = "plugins/channelrx/demodpacket/readme.md";
|
|
|
|
RollupContents *rollupContents = getRollupContents();
|
|
|
|
ui->setupUi(rollupContents);
|
|
|
|
setSizePolicy(rollupContents->sizePolicy());
|
|
|
|
rollupContents->arrangeRollups();
|
|
|
|
connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
|
2021-01-13 14:58:07 -05:00
|
|
|
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
|
|
|
|
|
|
|
|
m_packetDemod = reinterpret_cast<PacketDemod*>(rxChannel);
|
|
|
|
m_packetDemod->setMessageQueueToGUI(getInputMessageQueue());
|
|
|
|
|
|
|
|
connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms
|
|
|
|
|
|
|
|
ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03)));
|
|
|
|
ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold));
|
|
|
|
ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999);
|
|
|
|
ui->channelPowerMeter->setColorTheme(LevelMeterSignalDB::ColorGreenAndBlue);
|
|
|
|
|
|
|
|
m_channelMarker.blockSignals(true);
|
|
|
|
m_channelMarker.setColor(Qt::yellow);
|
|
|
|
m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
|
|
|
|
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
|
|
|
|
m_channelMarker.setTitle("Packet Demodulator");
|
|
|
|
m_channelMarker.blockSignals(false);
|
|
|
|
m_channelMarker.setVisible(true); // activate signal on the last setting only
|
|
|
|
|
|
|
|
setTitleColor(m_channelMarker.getColor());
|
|
|
|
m_settings.setChannelMarker(&m_channelMarker);
|
2022-01-08 23:27:12 -05:00
|
|
|
m_settings.setRollupState(&m_rollupState);
|
2021-01-13 14:58:07 -05:00
|
|
|
|
|
|
|
m_deviceUISet->addChannelMarker(&m_channelMarker);
|
|
|
|
|
|
|
|
connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor()));
|
|
|
|
connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor()));
|
|
|
|
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
|
|
|
|
|
|
|
// Resize the table using dummy data
|
|
|
|
resizeTable();
|
|
|
|
// Allow user to reorder columns
|
|
|
|
ui->packets->horizontalHeader()->setSectionsMovable(true);
|
|
|
|
// Allow user to sort table by clicking on headers
|
|
|
|
ui->packets->setSortingEnabled(true);
|
|
|
|
// Add context menu to allow hiding/showing of columns
|
|
|
|
menu = new QMenu(ui->packets);
|
|
|
|
for (int i = 0; i < ui->packets->horizontalHeader()->count(); i++)
|
|
|
|
{
|
|
|
|
QString text = ui->packets->horizontalHeaderItem(i)->text();
|
|
|
|
menu->addAction(createCheckableItem(text, i, true));
|
|
|
|
}
|
|
|
|
ui->packets->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
connect(ui->packets->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(columnSelectMenu(QPoint)));
|
|
|
|
// Get signals when columns change
|
|
|
|
connect(ui->packets->horizontalHeader(), SIGNAL(sectionMoved(int, int, int)), SLOT(packets_sectionMoved(int, int, int)));
|
|
|
|
connect(ui->packets->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(packets_sectionResized(int, int, int)));
|
|
|
|
|
|
|
|
displaySettings();
|
2022-04-12 10:20:45 -04:00
|
|
|
makeUIConnections();
|
2021-01-13 14:58:07 -05:00
|
|
|
applySettings(true);
|
2023-11-13 15:51:03 -05:00
|
|
|
m_resizer.enableChildMouseTracking();
|
2021-01-13 14:58:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
PacketDemodGUI::~PacketDemodGUI()
|
|
|
|
{
|
|
|
|
delete ui;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::blockApplySettings(bool block)
|
|
|
|
{
|
|
|
|
m_doApplySettings = !block;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::applySettings(bool force)
|
|
|
|
{
|
|
|
|
if (m_doApplySettings)
|
|
|
|
{
|
|
|
|
PacketDemod::MsgConfigurePacketDemod* message = PacketDemod::MsgConfigurePacketDemod::create( m_settings, force);
|
|
|
|
m_packetDemod->getInputMessageQueue()->push(message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::displaySettings()
|
|
|
|
{
|
|
|
|
m_channelMarker.blockSignals(true);
|
|
|
|
m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
|
|
|
|
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
|
|
|
|
m_channelMarker.setTitle(m_settings.m_title);
|
|
|
|
m_channelMarker.blockSignals(false);
|
|
|
|
m_channelMarker.setColor(m_settings.m_rgbColor); // activate signal on the last setting only
|
|
|
|
|
|
|
|
setTitleColor(m_settings.m_rgbColor);
|
|
|
|
setWindowTitle(m_channelMarker.getTitle());
|
2022-04-12 10:20:45 -04:00
|
|
|
setTitle(m_channelMarker.getTitle());
|
2021-01-13 14:58:07 -05:00
|
|
|
|
|
|
|
blockApplySettings(true);
|
|
|
|
|
|
|
|
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
|
|
|
|
|
|
|
|
ui->rfBWText->setText(QString("%1k").arg(m_settings.m_rfBandwidth / 1000.0, 0, 'f', 1));
|
|
|
|
ui->rfBW->setValue(m_settings.m_rfBandwidth / 100.0);
|
|
|
|
|
|
|
|
ui->fmDevText->setText(QString("%1k").arg(m_settings.m_fmDeviation / 1000.0, 0, 'f', 1));
|
|
|
|
ui->fmDev->setValue(m_settings.m_fmDeviation / 100.0);
|
|
|
|
|
2022-04-17 19:42:03 -04:00
|
|
|
updateIndexLabel();
|
2021-01-13 14:58:07 -05:00
|
|
|
|
|
|
|
ui->filterFrom->setText(m_settings.m_filterFrom);
|
|
|
|
ui->filterTo->setText(m_settings.m_filterTo);
|
|
|
|
ui->filterPID->setChecked(m_settings.m_filterPID == "f0");
|
|
|
|
|
2021-04-07 16:06:00 -04:00
|
|
|
ui->udpEnabled->setChecked(m_settings.m_udpEnabled);
|
|
|
|
ui->udpAddress->setText(m_settings.m_udpAddress);
|
|
|
|
ui->udpPort->setText(QString::number(m_settings.m_udpPort));
|
|
|
|
|
2021-11-04 08:33:43 -04:00
|
|
|
ui->logFilename->setToolTip(QString(".csv log filename: %1").arg(m_settings.m_logFilename));
|
|
|
|
ui->logEnable->setChecked(m_settings.m_logEnabled);
|
|
|
|
|
2021-01-13 14:58:07 -05:00
|
|
|
// Order and size columns
|
|
|
|
QHeaderView *header = ui->packets->horizontalHeader();
|
|
|
|
for (int i = 0; i < PACKETDEMOD_COLUMNS; i++)
|
|
|
|
{
|
|
|
|
bool hidden = m_settings.m_columnSizes[i] == 0;
|
|
|
|
header->setSectionHidden(i, hidden);
|
|
|
|
menu->actions().at(i)->setChecked(!hidden);
|
|
|
|
if (m_settings.m_columnSizes[i] > 0)
|
|
|
|
ui->packets->setColumnWidth(i, m_settings.m_columnSizes[i]);
|
|
|
|
header->moveSection(header->visualIndex(i), m_settings.m_columnIndexes[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
filter();
|
|
|
|
|
2022-04-12 10:20:45 -04:00
|
|
|
getRollupContents()->restoreState(m_rollupState);
|
2022-04-13 05:08:21 -04:00
|
|
|
updateAbsoluteCenterFrequency();
|
2021-01-13 14:58:07 -05:00
|
|
|
blockApplySettings(false);
|
|
|
|
}
|
|
|
|
|
2022-04-22 13:21:24 -04:00
|
|
|
void PacketDemodGUI::leaveEvent(QEvent* event)
|
2021-01-13 14:58:07 -05:00
|
|
|
{
|
|
|
|
m_channelMarker.setHighlighted(false);
|
2022-04-22 13:21:24 -04:00
|
|
|
ChannelGUI::leaveEvent(event);
|
2021-01-13 14:58:07 -05:00
|
|
|
}
|
|
|
|
|
2022-11-17 09:36:12 -05:00
|
|
|
void PacketDemodGUI::enterEvent(EnterEventType* event)
|
2021-01-13 14:58:07 -05:00
|
|
|
{
|
|
|
|
m_channelMarker.setHighlighted(true);
|
2022-04-22 13:21:24 -04:00
|
|
|
ChannelGUI::enterEvent(event);
|
2021-01-13 14:58:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::tick()
|
|
|
|
{
|
|
|
|
double magsqAvg, magsqPeak;
|
|
|
|
int nbMagsqSamples;
|
|
|
|
m_packetDemod->getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples);
|
|
|
|
double powDbAvg = CalcDb::dbPower(magsqAvg);
|
|
|
|
double powDbPeak = CalcDb::dbPower(magsqPeak);
|
|
|
|
|
|
|
|
ui->channelPowerMeter->levelChanged(
|
|
|
|
(100.0f + powDbAvg) / 100.0f,
|
|
|
|
(100.0f + powDbPeak) / 100.0f,
|
|
|
|
nbMagsqSamples);
|
|
|
|
|
|
|
|
if (m_tickCount % 4 == 0) {
|
|
|
|
ui->channelPower->setText(QString::number(powDbAvg, 'f', 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
m_tickCount++;
|
|
|
|
}
|
2021-11-04 08:33:43 -04:00
|
|
|
|
|
|
|
void PacketDemodGUI::on_logEnable_clicked(bool checked)
|
|
|
|
{
|
|
|
|
m_settings.m_logEnabled = checked;
|
|
|
|
applySettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PacketDemodGUI::on_logFilename_clicked()
|
|
|
|
{
|
|
|
|
// Get filename to save to
|
|
|
|
QFileDialog fileDialog(nullptr, "Select file to log received frames to", "", "*.csv");
|
|
|
|
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
|
|
|
|
if (fileDialog.exec())
|
|
|
|
{
|
|
|
|
QStringList fileNames = fileDialog.selectedFiles();
|
|
|
|
if (fileNames.size() > 0)
|
|
|
|
{
|
|
|
|
m_settings.m_logFilename = fileNames[0];
|
|
|
|
ui->logFilename->setToolTip(QString(".csv log filename: %1").arg(m_settings.m_logFilename));
|
|
|
|
applySettings();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read .csv log and process as received frames
|
|
|
|
void PacketDemodGUI::on_logOpen_clicked()
|
|
|
|
{
|
|
|
|
QFileDialog fileDialog(nullptr, "Select .csv log file to read", "", "*.csv");
|
|
|
|
if (fileDialog.exec())
|
|
|
|
{
|
|
|
|
QStringList fileNames = fileDialog.selectedFiles();
|
|
|
|
if (fileNames.size() > 0)
|
|
|
|
{
|
|
|
|
QFile file(fileNames[0]);
|
|
|
|
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
|
|
|
|
{
|
|
|
|
QTextStream in(&file);
|
|
|
|
QString error;
|
|
|
|
QHash<QString, int> colIndexes = CSV::readHeader(in, {"Date", "Time", "Data"}, error);
|
|
|
|
if (error.isEmpty())
|
|
|
|
{
|
|
|
|
int dateCol = colIndexes.value("Date");
|
|
|
|
int timeCol = colIndexes.value("Time");
|
|
|
|
int dataCol = colIndexes.value("Data");
|
|
|
|
int maxCol = std::max({dateCol, timeCol, dataCol});
|
|
|
|
|
|
|
|
QMessageBox dialog(this);
|
|
|
|
dialog.setText("Reading packet data");
|
|
|
|
dialog.addButton(QMessageBox::Cancel);
|
|
|
|
dialog.show();
|
|
|
|
QApplication::processEvents();
|
|
|
|
int count = 0;
|
|
|
|
bool cancelled = false;
|
|
|
|
QStringList cols;
|
|
|
|
while (!cancelled && CSV::readRow(in, &cols))
|
|
|
|
{
|
|
|
|
if (cols.size() > maxCol)
|
|
|
|
{
|
|
|
|
QDate date = QDate::fromString(cols[dateCol]);
|
|
|
|
QTime time = QTime::fromString(cols[timeCol]);
|
|
|
|
QDateTime dateTime(date, time);
|
|
|
|
QByteArray bytes = QByteArray::fromHex(cols[dataCol].toLatin1());
|
|
|
|
packetReceived(bytes);
|
|
|
|
if (count % 1000 == 0)
|
|
|
|
{
|
|
|
|
QApplication::processEvents();
|
|
|
|
if (dialog.clickedButton()) {
|
|
|
|
cancelled = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dialog.close();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QMessageBox::critical(this, "Packet Demod", error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
QMessageBox::critical(this, "Packet Demod", QString("Failed to open file %1").arg(fileNames[0]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-04-12 10:20:45 -04:00
|
|
|
|
|
|
|
void PacketDemodGUI::makeUIConnections()
|
|
|
|
{
|
|
|
|
QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &PacketDemodGUI::on_deltaFrequency_changed);
|
|
|
|
QObject::connect(ui->mode, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &PacketDemodGUI::on_mode_currentIndexChanged);
|
|
|
|
QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &PacketDemodGUI::on_rfBW_valueChanged);
|
|
|
|
QObject::connect(ui->fmDev, &QSlider::valueChanged, this, &PacketDemodGUI::on_fmDev_valueChanged);
|
|
|
|
QObject::connect(ui->filterFrom, &QLineEdit::editingFinished, this, &PacketDemodGUI::on_filterFrom_editingFinished);
|
|
|
|
QObject::connect(ui->filterTo, &QLineEdit::editingFinished, this, &PacketDemodGUI::on_filterTo_editingFinished);
|
|
|
|
QObject::connect(ui->filterPID, &QCheckBox::stateChanged, this, &PacketDemodGUI::on_filterPID_stateChanged);
|
|
|
|
QObject::connect(ui->clearTable, &QPushButton::clicked, this, &PacketDemodGUI::on_clearTable_clicked);
|
|
|
|
QObject::connect(ui->udpEnabled, &QCheckBox::clicked, this, &PacketDemodGUI::on_udpEnabled_clicked);
|
|
|
|
QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &PacketDemodGUI::on_udpAddress_editingFinished);
|
|
|
|
QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &PacketDemodGUI::on_udpPort_editingFinished);
|
|
|
|
QObject::connect(ui->logEnable, &ButtonSwitch::clicked, this, &PacketDemodGUI::on_logEnable_clicked);
|
|
|
|
QObject::connect(ui->logFilename, &QToolButton::clicked, this, &PacketDemodGUI::on_logFilename_clicked);
|
|
|
|
QObject::connect(ui->logOpen, &QToolButton::clicked, this, &PacketDemodGUI::on_logOpen_clicked);
|
|
|
|
}
|
2022-04-13 05:08:21 -04:00
|
|
|
|
|
|
|
void PacketDemodGUI::updateAbsoluteCenterFrequency()
|
|
|
|
{
|
|
|
|
setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset);
|
|
|
|
}
|