mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-12-23 01:55:48 -05:00
Add pager notifications.
Add option to ignore duplicates. Support plotting pager messages on the map.
This commit is contained in:
parent
f24600b909
commit
4455ac0c55
@ -29,16 +29,25 @@ if(NOT SERVER_MODE)
|
||||
pagerdemodgui.ui
|
||||
pagerdemodcharsetdialog.cpp
|
||||
pagerdemodcharsetdialog.ui
|
||||
pagerdemodnotificationdialog.cpp
|
||||
pagerdemodnotificationdialog.ui
|
||||
pagerdemodfilterdialog.cpp
|
||||
pagerdemodfilterdialog.ui
|
||||
pagerdemodicons.qrc
|
||||
)
|
||||
set(demodpager_HEADERS
|
||||
${demodpager_HEADERS}
|
||||
pagerdemodgui.h
|
||||
pagerdemodcharsetdialog.h
|
||||
pagerdemodnotificationdialog.h
|
||||
)
|
||||
|
||||
set(TARGET_NAME ${PLUGINS_PREFIX}demodpager)
|
||||
set(TARGET_LIB "Qt::Widgets")
|
||||
set(TARGET_LIB_GUI "sdrgui")
|
||||
if(Qt${QT_DEFAULT_MAJOR_VERSION}TextToSpeech_FOUND)
|
||||
list(APPEND TARGET_LIB_GUI Qt::TextToSpeech)
|
||||
endif()
|
||||
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
|
||||
else()
|
||||
set(TARGET_NAME ${PLUGINSSRV_PREFIX}demodpagersrv)
|
||||
|
BIN
plugins/channelrx/demodpager/icons/filterduplicate.png
Normal file
BIN
plugins/channelrx/demodpager/icons/filterduplicate.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
@ -31,6 +31,7 @@
|
||||
#include "dsp/dspcommands.h"
|
||||
#include "device/deviceapi.h"
|
||||
#include "util/db.h"
|
||||
#include "util/csv.h"
|
||||
#include "maincore.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(PagerDemod::MsgConfigurePagerDemod, Message)
|
||||
@ -141,7 +142,7 @@ bool PagerDemod::handleMessage(const Message& cmd)
|
||||
{
|
||||
if (MsgConfigurePagerDemod::match(cmd))
|
||||
{
|
||||
MsgConfigurePagerDemod& cfg = (MsgConfigurePagerDemod&) cmd;
|
||||
const MsgConfigurePagerDemod& cfg = (const MsgConfigurePagerDemod&) cmd;
|
||||
qDebug() << "PagerDemod::handleMessage: MsgConfigurePagerDemod";
|
||||
applySettings(cfg.getSettings(), cfg.getForce());
|
||||
|
||||
@ -149,7 +150,7 @@ bool PagerDemod::handleMessage(const Message& cmd)
|
||||
}
|
||||
else if (DSPSignalNotification::match(cmd))
|
||||
{
|
||||
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
|
||||
const DSPSignalNotification& notif = (const DSPSignalNotification&) cmd;
|
||||
m_basebandSampleRate = notif.getSampleRate();
|
||||
m_centerFrequency = notif.getCenterFrequency();
|
||||
// Forward to the sink
|
||||
@ -166,7 +167,7 @@ bool PagerDemod::handleMessage(const Message& cmd)
|
||||
else if (MsgPagerMessage::match(cmd))
|
||||
{
|
||||
// Forward to GUI
|
||||
MsgPagerMessage& report = (MsgPagerMessage&)cmd;
|
||||
const MsgPagerMessage& report = (const MsgPagerMessage&)cmd;
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MsgPagerMessage *msg = new MsgPagerMessage(report);
|
||||
@ -200,7 +201,7 @@ bool PagerDemod::handleMessage(const Message& cmd)
|
||||
<< report.getDateTime().time().toString() << ","
|
||||
<< QString("%1").arg(report.getAddress(), 7, 10, QChar('0')) << ","
|
||||
<< QString::number(report.getFunctionBits()) << ","
|
||||
<< "\"" << report.getAlphaMessage() << "\","
|
||||
<< CSV::escape(report.getAlphaMessage()) << ","
|
||||
<< report.getNumericMessage() << ","
|
||||
<< QString::number(report.getEvenParityErrors()) << ","
|
||||
<< QString::number(report.getBCHParityErrors()) << "\n";
|
||||
|
@ -131,7 +131,7 @@ bool PagerDemodBaseband::handleMessage(const Message& cmd)
|
||||
if (MsgConfigurePagerDemodBaseband::match(cmd))
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
MsgConfigurePagerDemodBaseband& cfg = (MsgConfigurePagerDemodBaseband&) cmd;
|
||||
const MsgConfigurePagerDemodBaseband& cfg = (const MsgConfigurePagerDemodBaseband&) cmd;
|
||||
qDebug() << "PagerDemodBaseband::handleMessage: MsgConfigurePagerDemodBaseband";
|
||||
|
||||
applySettings(cfg.getSettings(), cfg.getForce());
|
||||
@ -141,7 +141,7 @@ bool PagerDemodBaseband::handleMessage(const Message& cmd)
|
||||
else if (DSPSignalNotification::match(cmd))
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
DSPSignalNotification& notif = (DSPSignalNotification&) cmd;
|
||||
const DSPSignalNotification& notif = (const DSPSignalNotification&) cmd;
|
||||
qDebug() << "PagerDemodBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate();
|
||||
setBasebandSampleRate(notif.getSampleRate());
|
||||
m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate()));
|
||||
|
46
plugins/channelrx/demodpager/pagerdemodfilterdialog.cpp
Normal file
46
plugins/channelrx/demodpager/pagerdemodfilterdialog.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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 <QDebug>
|
||||
#include <QCheckBox>
|
||||
|
||||
#include "pagerdemodfilterdialog.h"
|
||||
|
||||
PagerDemodFilterDialog::PagerDemodFilterDialog(PagerDemodSettings *settings,
|
||||
QWidget* parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::PagerDemodFilterDialog),
|
||||
m_settings(settings)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->matchLastOnly->setChecked(m_settings->m_duplicateMatchLastOnly);
|
||||
ui->matchMessageOnly->setChecked(m_settings->m_duplicateMatchMessageOnly);
|
||||
}
|
||||
|
||||
PagerDemodFilterDialog::~PagerDemodFilterDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void PagerDemodFilterDialog::accept()
|
||||
{
|
||||
m_settings->m_duplicateMatchLastOnly = ui->matchLastOnly->isChecked();
|
||||
m_settings->m_duplicateMatchMessageOnly = ui->matchMessageOnly->isChecked();
|
||||
|
||||
QDialog::accept();
|
||||
}
|
41
plugins/channelrx/demodpager/pagerdemodfilterdialog.h
Normal file
41
plugins/channelrx/demodpager/pagerdemodfilterdialog.h
Normal file
@ -0,0 +1,41 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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_PAGERDEMODFILTERDIALOG_H
|
||||
#define INCLUDE_PAGERDEMODFILTERDIALOG_H
|
||||
|
||||
|
||||
#include "ui_pagerdemodfilterdialog.h"
|
||||
#include "pagerdemodsettings.h"
|
||||
|
||||
|
||||
class PagerDemodFilterDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PagerDemodFilterDialog(PagerDemodSettings* settings, QWidget* parent = 0);
|
||||
~PagerDemodFilterDialog();
|
||||
|
||||
private slots:
|
||||
void accept() override;
|
||||
|
||||
private:
|
||||
Ui::PagerDemodFilterDialog* ui;
|
||||
PagerDemodSettings *m_settings;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_PAGERDEMODFILTERDIALOG_H
|
118
plugins/channelrx/demodpager/pagerdemodfilterdialog.ui
Normal file
118
plugins/channelrx/demodpager/pagerdemodfilterdialog.ui
Normal file
@ -0,0 +1,118 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PagerDemodFilterDialog</class>
|
||||
<widget class="QDialog" name="PagerDemodFilterDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>396</width>
|
||||
<height>167</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::ContextMenuPolicy::PreventContextMenu</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Duplicate Filtering</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Duplicate Filtering</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="matchMessageOnlyLabel">
|
||||
<property name="text">
|
||||
<string>Match message only</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="matchMessageOnly">
|
||||
<property name="toolTip">
|
||||
<string>Whether both the address and message must match or only the message to be considered a duplicate</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="matchLastOnlyLabel">
|
||||
<property name="text">
|
||||
<string>Match last message only</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="matchLastOnly">
|
||||
<property name="toolTip">
|
||||
<string>Whether to match with only the last message or any message in the table</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../../../sdrgui/resources/res.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>PagerDemodFilterDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>PagerDemodFilterDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@ -16,8 +16,6 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <limits>
|
||||
#include <ctype.h>
|
||||
#include <QDockWidget>
|
||||
#include <QDebug>
|
||||
#include <QAction>
|
||||
@ -26,6 +24,7 @@
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QScrollBar>
|
||||
#include <QProcess>
|
||||
|
||||
#include "pagerdemodgui.h"
|
||||
|
||||
@ -36,6 +35,8 @@
|
||||
#include "plugin/pluginapi.h"
|
||||
#include "util/db.h"
|
||||
#include "util/csv.h"
|
||||
#include "util/units.h"
|
||||
#include "gui/crightclickenabler.h"
|
||||
#include "gui/basicchannelsettingsdialog.h"
|
||||
#include "dsp/dspengine.h"
|
||||
#include "gui/dialogpositioner.h"
|
||||
@ -43,6 +44,10 @@
|
||||
|
||||
#include "pagerdemod.h"
|
||||
#include "pagerdemodcharsetdialog.h"
|
||||
#include "pagerdemodnotificationdialog.h"
|
||||
#include "pagerdemodfilterdialog.h"
|
||||
|
||||
#include "SWGMapItem.h"
|
||||
|
||||
void PagerDemodGUI::resizeTable()
|
||||
{
|
||||
@ -50,7 +55,7 @@ void PagerDemodGUI::resizeTable()
|
||||
// Trailing spaces are for sort arrow
|
||||
int row = ui->messages->rowCount();
|
||||
ui->messages->setRowCount(row + 1);
|
||||
ui->messages->setItem(row, MESSAGE_COL_DATE, new QTableWidgetItem("Fri Apr 15 2016-"));
|
||||
ui->messages->setItem(row, MESSAGE_COL_DATE, new QTableWidgetItem("Fri Apr 15 2016--"));
|
||||
ui->messages->setItem(row, MESSAGE_COL_TIME, new QTableWidgetItem("10:17:00"));
|
||||
ui->messages->setItem(row, MESSAGE_COL_ADDRESS, new QTableWidgetItem("1000000"));
|
||||
ui->messages->setItem(row, MESSAGE_COL_MESSAGE, new QTableWidgetItem("ABCEDGHIJKLMNOPQRSTUVWXYZABCEDGHIJKLMNOPQRSTUVWXYZ"));
|
||||
@ -144,11 +149,101 @@ bool PagerDemodGUI::deserialize(const QByteArray& data)
|
||||
}
|
||||
}
|
||||
|
||||
QString PagerDemodGUI::selectMessage(int functionBits, const QString &numericMessage, const QString &alphaMessage) const
|
||||
{
|
||||
QString message;
|
||||
|
||||
// Standard way of choosing numeric or alpha decode isn't followed widely
|
||||
if (m_settings.m_decode == PagerDemodSettings::Standard)
|
||||
{
|
||||
// Encoding is based on function bits
|
||||
if (functionBits == 0) {
|
||||
message = numericMessage;
|
||||
} else {
|
||||
message = alphaMessage;
|
||||
}
|
||||
}
|
||||
else if (m_settings.m_decode == PagerDemodSettings::Inverted)
|
||||
{
|
||||
// Encoding is based on function bits, but inverted from standard
|
||||
if (functionBits == 3) {
|
||||
message = numericMessage;
|
||||
} else {
|
||||
message = alphaMessage;
|
||||
}
|
||||
}
|
||||
else if (m_settings.m_decode == PagerDemodSettings::Numeric)
|
||||
{
|
||||
// Always display as numeric
|
||||
message = numericMessage;
|
||||
}
|
||||
else if (m_settings.m_decode == PagerDemodSettings::Alphanumeric)
|
||||
{
|
||||
// Always display as alphanumeric
|
||||
message = alphaMessage;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Guess at what the encoding is
|
||||
QString numeric = numericMessage;
|
||||
QString alpha = alphaMessage;
|
||||
bool done = false;
|
||||
if (!done)
|
||||
{
|
||||
// If alpha contains control characters, possibly numeric
|
||||
for (int i = 0; i < alpha.size(); i++)
|
||||
{
|
||||
if (iscntrl(alpha[i].toLatin1()) && !isspace(alpha[i].toLatin1()))
|
||||
{
|
||||
message = numeric;
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!done) {
|
||||
// Possibly not likely to get only longer than 15 digits
|
||||
if (numeric.size() > 15)
|
||||
{
|
||||
done = true;
|
||||
message = alpha;
|
||||
}
|
||||
}
|
||||
if (!done) {
|
||||
// Default to alpha
|
||||
message = alpha;
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
|
||||
}
|
||||
|
||||
// Add row to table
|
||||
void PagerDemodGUI::messageReceived(const QDateTime dateTime, int address, int functionBits,
|
||||
const QString &numericMessage, const QString &alphaMessage,
|
||||
int evenParityErrors, int bchParityErrors)
|
||||
{
|
||||
QString message = selectMessage(functionBits, numericMessage, alphaMessage);
|
||||
QString addressString = QString("%1").arg(address, 7, 10, QChar('0'));
|
||||
|
||||
// Should we ignore the message if it is a duplicate?
|
||||
if (m_settings.m_filterDuplicates && (ui->messages->rowCount() > 0))
|
||||
{
|
||||
int startRow = m_settings.m_duplicateMatchLastOnly ? ui->messages->rowCount() - 1 : 0;
|
||||
for (int row = startRow; row < ui->messages->rowCount(); row++)
|
||||
{
|
||||
QString prevAddress = ui->messages->item(row, MESSAGE_COL_ADDRESS)->text();
|
||||
QString prevMessage = ui->messages->item(row, MESSAGE_COL_MESSAGE)->text();
|
||||
|
||||
if ((message == prevMessage) && (m_settings.m_duplicateMatchMessageOnly || (addressString == prevAddress)))
|
||||
{
|
||||
// Ignore this message
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is scroll bar at bottom
|
||||
QScrollBar *sb = ui->messages->verticalScrollBar();
|
||||
bool scrollToBottom = sb->value() == sb->maximum();
|
||||
@ -178,68 +273,8 @@ void PagerDemodGUI::messageReceived(const QDateTime dateTime, int address, int f
|
||||
ui->messages->setItem(row, MESSAGE_COL_BCH_PE, bchPEItem);
|
||||
dateItem->setText(dateTime.date().toString());
|
||||
timeItem->setText(dateTime.time().toString());
|
||||
addressItem->setText(QString("%1").arg(address, 7, 10, QChar('0')));
|
||||
// Standard way of choosing numeric or alpha decode isn't followed widely
|
||||
if (m_settings.m_decode == PagerDemodSettings::Standard)
|
||||
{
|
||||
// Encoding is based on function bits
|
||||
if (functionBits == 0) {
|
||||
messageItem->setText(numericMessage);
|
||||
} else {
|
||||
messageItem->setText(alphaMessage);
|
||||
}
|
||||
}
|
||||
else if (m_settings.m_decode == PagerDemodSettings::Inverted)
|
||||
{
|
||||
// Encoding is based on function bits, but inverted from standard
|
||||
if (functionBits == 3) {
|
||||
messageItem->setText(numericMessage);
|
||||
} else {
|
||||
messageItem->setText(alphaMessage);
|
||||
}
|
||||
}
|
||||
else if (m_settings.m_decode == PagerDemodSettings::Numeric)
|
||||
{
|
||||
// Always display as numeric
|
||||
messageItem->setText(numericMessage);
|
||||
}
|
||||
else if (m_settings.m_decode == PagerDemodSettings::Alphanumeric)
|
||||
{
|
||||
// Always display as alphanumeric
|
||||
messageItem->setText(alphaMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Guess at what the encoding is
|
||||
QString numeric = numericMessage;
|
||||
QString alpha = alphaMessage;
|
||||
bool done = false;
|
||||
if (!done)
|
||||
{
|
||||
// If alpha contains control characters, possibly numeric
|
||||
for (int i = 0; i < alpha.size(); i++)
|
||||
{
|
||||
if (iscntrl(alpha[i].toLatin1()) && !isspace(alpha[i].toLatin1()))
|
||||
{
|
||||
messageItem->setText(numeric);
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!done) {
|
||||
// Possibly not likely to get only longer than 15 digits
|
||||
if (numeric.size() > 15)
|
||||
{
|
||||
done = true;
|
||||
messageItem->setText(alpha);
|
||||
}
|
||||
}
|
||||
if (!done) {
|
||||
// Default to alpha
|
||||
messageItem->setText(alpha);
|
||||
}
|
||||
}
|
||||
addressItem->setText(addressString);
|
||||
messageItem->setText(message);
|
||||
functionItem->setText(QString("%1").arg(functionBits));
|
||||
alphaItem->setText(alphaMessage);
|
||||
numericItem->setText(numericMessage);
|
||||
@ -250,6 +285,7 @@ void PagerDemodGUI::messageReceived(const QDateTime dateTime, int address, int f
|
||||
if (scrollToBottom) {
|
||||
ui->messages->scrollToBottom();
|
||||
}
|
||||
checkNotification(row);
|
||||
}
|
||||
|
||||
bool PagerDemodGUI::handleMessage(const Message& message)
|
||||
@ -257,7 +293,7 @@ bool PagerDemodGUI::handleMessage(const Message& message)
|
||||
if (PagerDemod::MsgConfigurePagerDemod::match(message))
|
||||
{
|
||||
qDebug("PagerDemodGUI::handleMessage: PagerDemod::MsgConfigurePagerDemod");
|
||||
const PagerDemod::MsgConfigurePagerDemod& cfg = (PagerDemod::MsgConfigurePagerDemod&) message;
|
||||
const PagerDemod::MsgConfigurePagerDemod& cfg = (const PagerDemod::MsgConfigurePagerDemod&) message;
|
||||
m_settings = cfg.getSettings();
|
||||
blockApplySettings(true);
|
||||
ui->scopeGUI->updateSettings();
|
||||
@ -268,7 +304,7 @@ bool PagerDemodGUI::handleMessage(const Message& message)
|
||||
}
|
||||
else if (PagerDemod::MsgPagerMessage::match(message))
|
||||
{
|
||||
PagerDemod::MsgPagerMessage& report = (PagerDemod::MsgPagerMessage&) message;
|
||||
const PagerDemod::MsgPagerMessage& report = (const PagerDemod::MsgPagerMessage&) message;
|
||||
messageReceived(report.getDateTime(), report.getAddress(), report.getFunctionBits(),
|
||||
report.getNumericMessage(), report.getAlphaMessage(),
|
||||
report.getEvenParityErrors(), report.getBCHParityErrors());
|
||||
@ -276,7 +312,7 @@ bool PagerDemodGUI::handleMessage(const Message& message)
|
||||
}
|
||||
else if (DSPSignalNotification::match(message))
|
||||
{
|
||||
DSPSignalNotification& notif = (DSPSignalNotification&) message;
|
||||
const DSPSignalNotification& notif = (const DSPSignalNotification&) message;
|
||||
m_deviceCenterFrequency = notif.getCenterFrequency();
|
||||
m_basebandSampleRate = notif.getSampleRate();
|
||||
ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2);
|
||||
@ -479,7 +515,8 @@ PagerDemodGUI::PagerDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas
|
||||
m_deviceCenterFrequency(0),
|
||||
m_basebandSampleRate(1),
|
||||
m_doApplySettings(true),
|
||||
m_tickCount(0)
|
||||
m_tickCount(0),
|
||||
m_speech(nullptr)
|
||||
{
|
||||
setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
m_helpURL = "plugins/channelrx/demodpager/readme.md";
|
||||
@ -526,6 +563,9 @@ PagerDemodGUI::PagerDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Bas
|
||||
connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor()));
|
||||
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
||||
|
||||
CRightClickEnabler *filterDuplicatesRightClickEnabler = new CRightClickEnabler(ui->filterDuplicates);
|
||||
connect(filterDuplicatesRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(on_filterDuplicates_rightClicked(const QPoint &)));
|
||||
|
||||
// Resize the table using dummy data
|
||||
resizeTable();
|
||||
// Allow user to reorder columns
|
||||
@ -575,6 +615,7 @@ void PagerDemodGUI::customContextMenuRequested(QPoint pos)
|
||||
|
||||
PagerDemodGUI::~PagerDemodGUI()
|
||||
{
|
||||
clearFromMap();
|
||||
delete ui;
|
||||
}
|
||||
|
||||
@ -638,6 +679,8 @@ void PagerDemodGUI::displaySettings()
|
||||
ui->logFilename->setToolTip(QString(".csv log filename: %1").arg(m_settings.m_logFilename));
|
||||
ui->logEnable->setChecked(m_settings.m_logEnabled);
|
||||
|
||||
ui->filterDuplicates->setChecked(m_settings.m_filterDuplicates);
|
||||
|
||||
// Order and size columns
|
||||
QHeaderView *header = ui->messages->horizontalHeader();
|
||||
|
||||
@ -656,6 +699,7 @@ void PagerDemodGUI::displaySettings()
|
||||
getRollupContents()->restoreState(m_rollupState);
|
||||
updateAbsoluteCenterFrequency();
|
||||
blockApplySettings(false);
|
||||
enableSpeechIfNeeded();
|
||||
}
|
||||
|
||||
void PagerDemodGUI::leaveEvent(QEvent* event)
|
||||
@ -693,12 +737,39 @@ void PagerDemodGUI::tick()
|
||||
void PagerDemodGUI::on_charset_clicked()
|
||||
{
|
||||
PagerDemodCharsetDialog dialog(&m_settings);
|
||||
if (dialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
new DialogPositioner(&dialog, true);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
applySettings();
|
||||
}
|
||||
}
|
||||
|
||||
void PagerDemodGUI::on_notifications_clicked()
|
||||
{
|
||||
PagerDemodNotificationDialog dialog(&m_settings);
|
||||
new DialogPositioner(&dialog, true);
|
||||
if (dialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
enableSpeechIfNeeded();
|
||||
applySettings();
|
||||
}
|
||||
}
|
||||
|
||||
void PagerDemodGUI::on_filterDuplicates_clicked(bool checked)
|
||||
{
|
||||
m_settings.m_filterDuplicates = checked;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void PagerDemodGUI::on_filterDuplicates_rightClicked(const QPoint &p)
|
||||
{
|
||||
(void) p;
|
||||
|
||||
PagerDemodFilterDialog dialog(&m_settings);
|
||||
new DialogPositioner(&dialog, true);
|
||||
if (dialog.exec() == QDialog::Accepted) {
|
||||
applySettings();
|
||||
}
|
||||
}
|
||||
|
||||
void PagerDemodGUI::on_logEnable_clicked(bool checked)
|
||||
{
|
||||
@ -813,6 +884,8 @@ void PagerDemodGUI::makeUIConnections()
|
||||
QObject::connect(ui->udpEnabled, &QCheckBox::clicked, this, &PagerDemodGUI::on_udpEnabled_clicked);
|
||||
QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &PagerDemodGUI::on_udpAddress_editingFinished);
|
||||
QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &PagerDemodGUI::on_udpPort_editingFinished);
|
||||
QObject::connect(ui->notifications, &QToolButton::clicked, this, &PagerDemodGUI::on_notifications_clicked);
|
||||
QObject::connect(ui->filterDuplicates, &ButtonSwitch::clicked, this, &PagerDemodGUI::on_filterDuplicates_clicked);
|
||||
QObject::connect(ui->logEnable, &ButtonSwitch::clicked, this, &PagerDemodGUI::on_logEnable_clicked);
|
||||
QObject::connect(ui->logFilename, &QToolButton::clicked, this, &PagerDemodGUI::on_logFilename_clicked);
|
||||
QObject::connect(ui->logOpen, &QToolButton::clicked, this, &PagerDemodGUI::on_logOpen_clicked);
|
||||
@ -824,3 +897,178 @@ void PagerDemodGUI::updateAbsoluteCenterFrequency()
|
||||
{
|
||||
setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset);
|
||||
}
|
||||
|
||||
// Initialise text to speech engine
|
||||
// This takes 10 seconds on some versions of Linux, so only do it, if user actually
|
||||
// has speech notifications configured
|
||||
void PagerDemodGUI::enableSpeechIfNeeded()
|
||||
{
|
||||
#ifdef QT_TEXTTOSPEECH_FOUND
|
||||
if (m_speech) {
|
||||
return;
|
||||
}
|
||||
for (const auto& notification : m_settings.m_notificationSettings)
|
||||
{
|
||||
if (!notification->m_speech.isEmpty())
|
||||
{
|
||||
qDebug() << "PagerDemodGUI: Enabling text to speech";
|
||||
m_speech = new QTextToSpeech(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void PagerDemodGUI::checkNotification(int row)
|
||||
{
|
||||
QString address = ui->messages->item(row, MESSAGE_COL_ADDRESS)->text();
|
||||
QString message = ui->messages->item(row, MESSAGE_COL_MESSAGE)->text();
|
||||
|
||||
for (int i = 0; i < m_settings.m_notificationSettings.size(); i++)
|
||||
{
|
||||
QString match;
|
||||
switch (m_settings.m_notificationSettings[i]->m_matchColumn)
|
||||
{
|
||||
case MESSAGE_COL_ADDRESS:
|
||||
match = address;
|
||||
break;
|
||||
case MESSAGE_COL_MESSAGE:
|
||||
match = message;
|
||||
break;
|
||||
}
|
||||
if (!match.isEmpty())
|
||||
{
|
||||
if (m_settings.m_notificationSettings[i]->m_regularExpression.isValid())
|
||||
{
|
||||
QRegularExpressionMatch matchResult = m_settings.m_notificationSettings[i]->m_regularExpression.match(match);
|
||||
if (matchResult.hasMatch())
|
||||
{
|
||||
if (m_settings.m_notificationSettings[i]->m_highlight) {
|
||||
ui->messages->item(row, MESSAGE_COL_MESSAGE)->setTextColor(m_settings.m_notificationSettings[i]->m_highlightColor);
|
||||
}
|
||||
|
||||
if (!m_settings.m_notificationSettings[i]->m_speech.isEmpty())
|
||||
{
|
||||
QString speech = subStrings(address, message, matchResult, m_settings.m_notificationSettings[i]->m_speech);
|
||||
|
||||
speechNotification(speech);
|
||||
}
|
||||
if (!m_settings.m_notificationSettings[i]->m_command.isEmpty())
|
||||
{
|
||||
QString command = subStrings(address, message, matchResult, m_settings.m_notificationSettings[i]->m_command);
|
||||
|
||||
commandNotification(command);
|
||||
}
|
||||
if (m_settings.m_notificationSettings[i]->m_plotOnMap)
|
||||
{
|
||||
float latitude;
|
||||
float longitude;
|
||||
|
||||
if (Units::stringToLatitudeAndLongitude(message, latitude, longitude, false))
|
||||
{
|
||||
QDateTime dateTime;
|
||||
|
||||
dateTime.setDate(QDate::fromString(ui->messages->item(row, MESSAGE_COL_DATE)->text()));
|
||||
dateTime.setTime(QTime::fromString(ui->messages->item(row, MESSAGE_COL_TIME)->text()));
|
||||
|
||||
sendToMap(address, message, latitude, longitude, dateTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString PagerDemodGUI::subStrings(const QString& address, const QString& message, const QRegularExpressionMatch& match, const QString &string) const
|
||||
{
|
||||
QString s = string;
|
||||
s = s.replace("${address}", address);
|
||||
s = s.replace("${message}", message);
|
||||
for (int i = 0; i < match.capturedTexts().size(); i++)
|
||||
{
|
||||
QString escape = QString("${%1}").arg(i);
|
||||
s = s.replace(escape, match.capturedTexts()[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void PagerDemodGUI::speechNotification(const QString &speech)
|
||||
{
|
||||
#ifdef QT_TEXTTOSPEECH_FOUND
|
||||
if (m_speech) {
|
||||
m_speech->say(speech);
|
||||
} else {
|
||||
qWarning() << "PagerDemodGUI::speechNotification: Unable to say " << speech;
|
||||
}
|
||||
#else
|
||||
qWarning() << "PagerDemodGUI::speechNotification: TextToSpeech not supported. Unable to say " << speech;
|
||||
#endif
|
||||
}
|
||||
|
||||
void PagerDemodGUI::commandNotification(const QString &command)
|
||||
{
|
||||
#if QT_CONFIG(process)
|
||||
QStringList allArgs = QProcess::splitCommand(command);
|
||||
|
||||
if (allArgs.size() > 0)
|
||||
{
|
||||
QString program = allArgs[0];
|
||||
allArgs.pop_front();
|
||||
QProcess::startDetached(program, allArgs);
|
||||
}
|
||||
#else
|
||||
qWarning() << "PagerDemodGUI::commandNotification: QProcess not supported. Can't run: " << command;
|
||||
#endif
|
||||
}
|
||||
|
||||
void PagerDemodGUI::sendToMap(const QString& address, const QString& message, float latitude, float longitude, QDateTime dateTime)
|
||||
{
|
||||
QList<ObjectPipe*> mapPipes;
|
||||
MainCore::instance()->getMessagePipes().getMessagePipes(m_pagerDemod, "mapitems", mapPipes);
|
||||
|
||||
for (const auto& pipe : mapPipes)
|
||||
{
|
||||
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
|
||||
SWGSDRangel::SWGMapItem *swgMapItem = new SWGSDRangel::SWGMapItem();
|
||||
swgMapItem->setName(new QString(address));
|
||||
swgMapItem->setLatitude(latitude);
|
||||
swgMapItem->setLongitude(longitude);
|
||||
swgMapItem->setAltitude(0);
|
||||
swgMapItem->setAltitudeReference(1); // CLAMP_TO_GROUND
|
||||
swgMapItem->setFixedPosition(false);
|
||||
swgMapItem->setPositionDateTime(new QString(dateTime.toString(Qt::ISODateWithMs)));
|
||||
|
||||
swgMapItem->setImageRotation(0);
|
||||
swgMapItem->setText(new QString(message));
|
||||
swgMapItem->setImage(new QString(QString("pager.png")));
|
||||
|
||||
MainCore::MsgMapItem *msg = MainCore::MsgMapItem::create(m_pagerDemod, swgMapItem);
|
||||
messageQueue->push(msg);
|
||||
}
|
||||
|
||||
m_mapItems.insert(address);
|
||||
}
|
||||
|
||||
// Clear all items from map
|
||||
void PagerDemodGUI::clearFromMap()
|
||||
{
|
||||
for (const auto& address : m_mapItems)
|
||||
{
|
||||
QList<ObjectPipe*> mapPipes;
|
||||
MainCore::instance()->getMessagePipes().getMessagePipes(m_pagerDemod, "mapitems", mapPipes);
|
||||
|
||||
for (const auto& pipe : mapPipes)
|
||||
{
|
||||
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
|
||||
SWGSDRangel::SWGMapItem *swgMapItem = new SWGSDRangel::SWGMapItem();
|
||||
swgMapItem->setName(new QString(address));
|
||||
swgMapItem->setImage(new QString(QString("")));
|
||||
|
||||
MainCore::MsgMapItem *msg = MainCore::MsgMapItem::create(m_pagerDemod, swgMapItem);
|
||||
messageQueue->push(msg);
|
||||
}
|
||||
}
|
||||
|
||||
m_mapItems.clear();
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
#define INCLUDE_PAGERDEMODGUI_H
|
||||
|
||||
#include <QMenu>
|
||||
#include <QTextToSpeech>
|
||||
|
||||
#include "channel/channelgui.h"
|
||||
#include "dsp/channelmarker.h"
|
||||
@ -45,6 +46,18 @@ class PagerDemodGUI : public ChannelGUI {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum MessageCol {
|
||||
MESSAGE_COL_DATE,
|
||||
MESSAGE_COL_TIME,
|
||||
MESSAGE_COL_ADDRESS,
|
||||
MESSAGE_COL_MESSAGE,
|
||||
MESSAGE_COL_FUNCTION,
|
||||
MESSAGE_COL_ALPHA,
|
||||
MESSAGE_COL_NUMERIC,
|
||||
MESSAGE_COL_EVEN_PE,
|
||||
MESSAGE_COL_BCH_PE
|
||||
};
|
||||
|
||||
static PagerDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel);
|
||||
virtual void destroy();
|
||||
|
||||
@ -86,12 +99,18 @@ private:
|
||||
|
||||
QMenu *messagesMenu; // Column select context menu
|
||||
|
||||
#ifdef QT_TEXTTOSPEECH_FOUND
|
||||
QTextToSpeech *m_speech;
|
||||
#endif
|
||||
QSet<QString> m_mapItems;
|
||||
|
||||
explicit PagerDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0);
|
||||
virtual ~PagerDemodGUI();
|
||||
|
||||
void blockApplySettings(bool block);
|
||||
void applySettings(bool force = false);
|
||||
void displaySettings();
|
||||
QString selectMessage(int functionBits, const QString &numericMessage, const QString &alphaMessage) const;
|
||||
void messageReceived(const QDateTime dateTime, int address, int functionBits,
|
||||
const QString &numericMessage, const QString &alphaMessage,
|
||||
int evenParityErrors, int bchParityErrors);
|
||||
@ -105,17 +124,13 @@ private:
|
||||
void resizeTable();
|
||||
QAction *createCheckableItem(QString& text, int idx, bool checked, const char *slot);
|
||||
|
||||
enum MessageCol {
|
||||
MESSAGE_COL_DATE,
|
||||
MESSAGE_COL_TIME,
|
||||
MESSAGE_COL_ADDRESS,
|
||||
MESSAGE_COL_MESSAGE,
|
||||
MESSAGE_COL_FUNCTION,
|
||||
MESSAGE_COL_ALPHA,
|
||||
MESSAGE_COL_NUMERIC,
|
||||
MESSAGE_COL_EVEN_PE,
|
||||
MESSAGE_COL_BCH_PE
|
||||
};
|
||||
void enableSpeechIfNeeded();
|
||||
void checkNotification(int row);
|
||||
void speechNotification(const QString &speech);
|
||||
void commandNotification(const QString &command);
|
||||
QString subStrings(const QString& address, const QString& message, const QRegularExpressionMatch& match, const QString &string) const;
|
||||
void sendToMap(const QString& address, const QString& message, float latitide, float longitude, QDateTime dateTime);
|
||||
void clearFromMap();
|
||||
|
||||
private slots:
|
||||
void on_deltaFrequency_changed(qint64 value);
|
||||
@ -131,6 +146,9 @@ private slots:
|
||||
void on_udpPort_editingFinished();
|
||||
void on_channel1_currentIndexChanged(int index);
|
||||
void on_channel2_currentIndexChanged(int index);
|
||||
void on_notifications_clicked();
|
||||
void on_filterDuplicates_clicked(bool checked=false);
|
||||
void on_filterDuplicates_rightClicked(const QPoint &);
|
||||
void on_logEnable_clicked(bool checked=false);
|
||||
void on_logFilename_clicked();
|
||||
void on_logOpen_clicked();
|
||||
|
@ -29,7 +29,7 @@
|
||||
</font>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
<enum>Qt::FocusPolicy::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Pager Demodulator</string>
|
||||
@ -110,7 +110,7 @@
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
<enum>Qt::FocusPolicy::StrongFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Demod shift frequency from center in Hz</string>
|
||||
@ -127,14 +127,14 @@
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
@ -152,7 +152,7 @@
|
||||
<string>Channel power</string>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
<enum>Qt::LayoutDirection::RightToLeft</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>0.0</string>
|
||||
@ -209,7 +209,7 @@
|
||||
<item>
|
||||
<widget class="Line" name="line_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -252,7 +252,7 @@
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -268,14 +268,14 @@
|
||||
<string>10.0k</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -316,7 +316,7 @@
|
||||
<number>24</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -332,7 +332,7 @@
|
||||
<string>2.4k</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -368,7 +368,7 @@
|
||||
<item>
|
||||
<widget class="Line" name="line_10">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -413,7 +413,7 @@
|
||||
<item>
|
||||
<widget class="Line" name="line_11">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -479,7 +479,7 @@
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
@ -494,22 +494,29 @@
|
||||
<item>
|
||||
<widget class="Line" name="line_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="udpLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="udpLabel">
|
||||
<property name="text">
|
||||
<string>UDP</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="udpEnabled">
|
||||
<property name="toolTip">
|
||||
<string>Forward messages via UDP</string>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
<enum>Qt::LayoutDirection::RightToLeft</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>UDP</string>
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -522,7 +529,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
<enum>Qt::FocusPolicy::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Destination UDP address</string>
|
||||
@ -541,7 +548,7 @@
|
||||
<string>:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -560,7 +567,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::ClickFocus</enum>
|
||||
<enum>Qt::FocusPolicy::ClickFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Destination UDP port</string>
|
||||
@ -576,7 +583,7 @@
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
@ -591,7 +598,7 @@
|
||||
<item>
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -614,7 +621,7 @@
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
@ -624,6 +631,43 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="filterDuplicates">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>24</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Filter duplicate messages. Right click for options.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="pagerdemodicons.qrc">
|
||||
<normaloff>:/icons/filterduplicate.png</normaloff>:/icons/filterduplicate.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="notifications">
|
||||
<property name="toolTip">
|
||||
<string>Open notifications dialog</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/mono.png</normaloff>:/mono.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ButtonSwitch" name="logEnable">
|
||||
<property name="maximumSize">
|
||||
@ -736,7 +780,7 @@
|
||||
<string>Received messages</string>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
<set>QAbstractItemView::EditTrigger::NoEditTriggers</set>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
@ -1056,6 +1100,7 @@
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../../sdrgui/resources/res.qrc"/>
|
||||
<include location="pagerdemodicons.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
5
plugins/channelrx/demodpager/pagerdemodicons.qrc
Normal file
5
plugins/channelrx/demodpager/pagerdemodicons.qrc
Normal file
@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>icons/filterduplicate.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
173
plugins/channelrx/demodpager/pagerdemodnotificationdialog.cpp
Normal file
173
plugins/channelrx/demodpager/pagerdemodnotificationdialog.cpp
Normal file
@ -0,0 +1,173 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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 <QDebug>
|
||||
#include <QComboBox>
|
||||
#include <QPushButton>
|
||||
#include <QCheckBox>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
#include "gui/tablecolorchooser.h"
|
||||
|
||||
#include "pagerdemodnotificationdialog.h"
|
||||
#include "pagerdemodgui.h"
|
||||
|
||||
// Map main table column numbers to combo box indices
|
||||
std::vector<int> PagerDemodNotificationDialog::m_columnMap = {
|
||||
PagerDemodGUI::MESSAGE_COL_ADDRESS, PagerDemodGUI::MESSAGE_COL_MESSAGE
|
||||
};
|
||||
|
||||
PagerDemodNotificationDialog::PagerDemodNotificationDialog(PagerDemodSettings *settings,
|
||||
QWidget* parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::PagerDemodNotificationDialog),
|
||||
m_settings(settings)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
resizeTable();
|
||||
|
||||
for (int i = 0; i < m_settings->m_notificationSettings.size(); i++) {
|
||||
addRow(m_settings->m_notificationSettings[i]);
|
||||
}
|
||||
}
|
||||
|
||||
PagerDemodNotificationDialog::~PagerDemodNotificationDialog()
|
||||
{
|
||||
delete ui;
|
||||
qDeleteAll(m_colorGUIs);
|
||||
}
|
||||
|
||||
void PagerDemodNotificationDialog::accept()
|
||||
{
|
||||
qDeleteAll(m_settings->m_notificationSettings);
|
||||
m_settings->m_notificationSettings.clear();
|
||||
for (int i = 0; i < ui->table->rowCount(); i++)
|
||||
{
|
||||
PagerDemodSettings::NotificationSettings *notificationSettings = new PagerDemodSettings::NotificationSettings();
|
||||
int idx = ((QComboBox *)ui->table->cellWidget(i, NOTIFICATION_COL_MATCH))->currentIndex();
|
||||
notificationSettings->m_matchColumn = m_columnMap[idx];
|
||||
notificationSettings->m_regExp = ui->table->item(i, NOTIFICATION_COL_REG_EXP)->data(Qt::DisplayRole).toString().trimmed();
|
||||
notificationSettings->m_speech = ui->table->item(i, NOTIFICATION_COL_SPEECH)->data(Qt::DisplayRole).toString().trimmed();
|
||||
notificationSettings->m_command = ui->table->item(i, NOTIFICATION_COL_COMMAND)->data(Qt::DisplayRole).toString().trimmed();
|
||||
notificationSettings->m_highlight = !m_colorGUIs[i]->m_noColor;
|
||||
notificationSettings->m_highlightColor = m_colorGUIs[i]->m_color;
|
||||
notificationSettings->m_plotOnMap = ((QCheckBox *) ui->table->cellWidget(i, NOTIFICATION_COL_PLOT_ON_MAP))->isChecked();
|
||||
notificationSettings->updateRegularExpression();
|
||||
m_settings->m_notificationSettings.append(notificationSettings);
|
||||
}
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void PagerDemodNotificationDialog::resizeTable()
|
||||
{
|
||||
PagerDemodSettings::NotificationSettings dummy;
|
||||
dummy.m_matchColumn = PagerDemodGUI::MESSAGE_COL_ADDRESS;
|
||||
dummy.m_regExp = "1234567";
|
||||
dummy.m_speech = "${message}";
|
||||
dummy.m_command = "cmail.exe -to:user@host.com \"-subject: Paging ${address}\" \"-body: ${message}\"";
|
||||
dummy.m_highlight = true;
|
||||
dummy.m_plotOnMap = true;
|
||||
addRow(&dummy);
|
||||
ui->table->resizeColumnsToContents();
|
||||
ui->table->selectRow(0);
|
||||
on_remove_clicked();
|
||||
ui->table->selectRow(-1);
|
||||
}
|
||||
|
||||
void PagerDemodNotificationDialog::on_add_clicked()
|
||||
{
|
||||
addRow();
|
||||
}
|
||||
|
||||
// Remove selected row
|
||||
void PagerDemodNotificationDialog::on_remove_clicked()
|
||||
{
|
||||
// Selection mode is single, so only a single row should be returned
|
||||
QModelIndexList indexList = ui->table->selectionModel()->selectedRows();
|
||||
if (!indexList.isEmpty())
|
||||
{
|
||||
int row = indexList.at(0).row();
|
||||
ui->table->removeRow(row);
|
||||
m_colorGUIs.removeAt(row);
|
||||
}
|
||||
}
|
||||
|
||||
void PagerDemodNotificationDialog::addRow(PagerDemodSettings::NotificationSettings *settings)
|
||||
{
|
||||
int row = ui->table->rowCount();
|
||||
ui->table->setSortingEnabled(false);
|
||||
ui->table->setRowCount(row + 1);
|
||||
|
||||
QComboBox *match = new QComboBox();
|
||||
TableColorChooser *highlight;
|
||||
if (settings) {
|
||||
highlight = new TableColorChooser(ui->table, row, NOTIFICATION_COL_HIGHLIGHT, !settings->m_highlight, settings->m_highlightColor);
|
||||
} else {
|
||||
highlight = new TableColorChooser(ui->table, row, NOTIFICATION_COL_HIGHLIGHT, false, QColor(Qt::red).rgba());
|
||||
}
|
||||
m_colorGUIs.append(highlight);
|
||||
QCheckBox *plotOnMap = new QCheckBox();
|
||||
plotOnMap->setChecked(false);
|
||||
QWidget *matchWidget = new QWidget();
|
||||
QHBoxLayout *pLayout = new QHBoxLayout(matchWidget);
|
||||
pLayout->addWidget(match);
|
||||
pLayout->setAlignment(Qt::AlignCenter);
|
||||
pLayout->setContentsMargins(0, 0, 0, 0);
|
||||
matchWidget->setLayout(pLayout);
|
||||
|
||||
match->addItem("Address");
|
||||
match->addItem("Message");
|
||||
|
||||
QTableWidgetItem *regExpItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *speechItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *commandItem = new QTableWidgetItem();
|
||||
|
||||
if (settings != nullptr)
|
||||
{
|
||||
for (unsigned int i = 0; i < m_columnMap.size(); i++)
|
||||
{
|
||||
if (m_columnMap[i] == settings->m_matchColumn)
|
||||
{
|
||||
match->setCurrentIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
regExpItem->setData(Qt::DisplayRole, settings->m_regExp);
|
||||
speechItem->setData(Qt::DisplayRole, settings->m_speech);
|
||||
commandItem->setData(Qt::DisplayRole, settings->m_command);
|
||||
plotOnMap->setChecked(settings->m_plotOnMap);
|
||||
}
|
||||
else
|
||||
{
|
||||
match->setCurrentIndex(0);
|
||||
regExpItem->setData(Qt::DisplayRole, ".*");
|
||||
speechItem->setData(Qt::DisplayRole, "${message}");
|
||||
#ifdef _MSC_VER
|
||||
commandItem->setData(Qt::DisplayRole, "cmail.exe -to:user@host.com \"-subject: Paging ${address}\" \"-body: ${message}\"");
|
||||
#else
|
||||
commandItem->setData(Qt::DisplayRole, "sendmail -s \"Paging ${address}: ${message}\" user@host.com");
|
||||
#endif
|
||||
}
|
||||
|
||||
ui->table->setCellWidget(row, NOTIFICATION_COL_MATCH, match);
|
||||
ui->table->setItem(row, NOTIFICATION_COL_REG_EXP, regExpItem);
|
||||
ui->table->setItem(row, NOTIFICATION_COL_SPEECH, speechItem);
|
||||
ui->table->setItem(row, NOTIFICATION_COL_COMMAND, commandItem);
|
||||
ui->table->setCellWidget(row, NOTIFICATION_COL_PLOT_ON_MAP, plotOnMap);
|
||||
ui->table->setSortingEnabled(true);
|
||||
}
|
61
plugins/channelrx/demodpager/pagerdemodnotificationdialog.h
Normal file
61
plugins/channelrx/demodpager/pagerdemodnotificationdialog.h
Normal file
@ -0,0 +1,61 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2021 Jon Beniston, M7RCE //
|
||||
// //
|
||||
// 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_PAGERDEMODNOTIFICATIONDIALOG_H
|
||||
#define INCLUDE_PAGERDEMODNOTIFICATIONDIALOG_H
|
||||
|
||||
#include <QHash>
|
||||
|
||||
#include "ui_pagerdemodnotificationdialog.h"
|
||||
#include "pagerdemodsettings.h"
|
||||
|
||||
class TableColorChooser;
|
||||
|
||||
class PagerDemodNotificationDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PagerDemodNotificationDialog(PagerDemodSettings* settings, QWidget* parent = 0);
|
||||
~PagerDemodNotificationDialog();
|
||||
|
||||
private:
|
||||
void resizeTable();
|
||||
|
||||
private slots:
|
||||
void accept() override;
|
||||
void on_add_clicked();
|
||||
void on_remove_clicked();
|
||||
void addRow(PagerDemodSettings::NotificationSettings *settings=nullptr);
|
||||
|
||||
private:
|
||||
Ui::PagerDemodNotificationDialog* ui;
|
||||
PagerDemodSettings *m_settings;
|
||||
QList<TableColorChooser *> m_colorGUIs;
|
||||
|
||||
enum NotificationCol {
|
||||
NOTIFICATION_COL_MATCH,
|
||||
NOTIFICATION_COL_REG_EXP,
|
||||
NOTIFICATION_COL_SPEECH,
|
||||
NOTIFICATION_COL_COMMAND,
|
||||
NOTIFICATION_COL_HIGHLIGHT,
|
||||
NOTIFICATION_COL_PLOT_ON_MAP
|
||||
};
|
||||
|
||||
static std::vector<int> m_columnMap;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_PagerDEMODNOTIFICATIONDIALOG_H
|
175
plugins/channelrx/demodpager/pagerdemodnotificationdialog.ui
Normal file
175
plugins/channelrx/demodpager/pagerdemodnotificationdialog.ui
Normal file
@ -0,0 +1,175 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PagerDemodNotificationDialog</class>
|
||||
<widget class="QDialog" name="PagerDemodNotificationDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1100</width>
|
||||
<height>400</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Liberation Sans</family>
|
||||
<pointsize>9</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::ContextMenuPolicy::PreventContextMenu</enum>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Notifications</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="4" column="0">
|
||||
<widget class="QTableWidget" name="table">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::SelectionMode::SingleSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Match</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>ADS-B data to match</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Reg Exp</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Regular expression to match with</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Speech</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Speech for the computer to read when a match is made</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Command</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Command/script to execute when a match is made</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Highlight</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Plot on Map</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<layout class="QHBoxLayout" name="buttonsHorizontalLayout">
|
||||
<item>
|
||||
<widget class="QToolButton" name="add">
|
||||
<property name="toolTip">
|
||||
<string>Add device set control</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>+</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="remove">
|
||||
<property name="toolTip">
|
||||
<string>Remove device set control</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>-</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="buttonsHorizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>table</tabstop>
|
||||
<tabstop>add</tabstop>
|
||||
<tabstop>remove</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../../../sdrgui/resources/res.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>PagerDemodNotificationDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>PagerDemodNotificationDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@ -20,10 +20,12 @@
|
||||
|
||||
#include <QColor>
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
|
||||
#include "util/simpleserializer.h"
|
||||
#include "settings/serializable.h"
|
||||
#include "pagerdemodsettings.h"
|
||||
#include "pagerdemodgui.h"
|
||||
|
||||
PagerDemodSettings::PagerDemodSettings() :
|
||||
m_channelMarker(nullptr),
|
||||
@ -59,6 +61,9 @@ void PagerDemodSettings::resetToDefaults()
|
||||
m_reverse = false;
|
||||
m_workspaceIndex = 0;
|
||||
m_hidden = false;
|
||||
m_filterDuplicates = false;
|
||||
m_duplicateMatchMessageOnly = false;
|
||||
m_duplicateMatchLastOnly = false;
|
||||
|
||||
for (int i = 0; i < PAGERDEMOD_MESSAGE_COLUMNS; i++)
|
||||
{
|
||||
@ -110,6 +115,11 @@ QByteArray PagerDemodSettings::serialize() const
|
||||
s.writeBlob(29, m_geometryBytes);
|
||||
s.writeBool(30, m_hidden);
|
||||
|
||||
s.writeList(31, m_notificationSettings);
|
||||
s.writeBool(32, m_filterDuplicates);
|
||||
s.writeBool(33, m_duplicateMatchMessageOnly);
|
||||
s.writeBool(34, m_duplicateMatchLastOnly);
|
||||
|
||||
for (int i = 0; i < PAGERDEMOD_MESSAGE_COLUMNS; i++) {
|
||||
s.writeS32(100 + i, m_messageColumnIndexes[i]);
|
||||
}
|
||||
@ -205,6 +215,12 @@ bool PagerDemodSettings::deserialize(const QByteArray& data)
|
||||
d.readBlob(29, &m_geometryBytes);
|
||||
d.readBool(30, &m_hidden, false);
|
||||
|
||||
d.readList(31, &m_notificationSettings);
|
||||
|
||||
d.readBool(32, &m_filterDuplicates);
|
||||
d.readBool(33, &m_duplicateMatchMessageOnly);
|
||||
d.readBool(34, &m_duplicateMatchLastOnly);
|
||||
|
||||
for (int i = 0; i < PAGERDEMOD_MESSAGE_COLUMNS; i++) {
|
||||
d.readS32(100 + i, &m_messageColumnIndexes[i], i);
|
||||
}
|
||||
@ -237,3 +253,80 @@ void PagerDemodSettings::deserializeIntList(const QByteArray& data, QList<qint32
|
||||
(*stream) >> ints;
|
||||
delete stream;
|
||||
}
|
||||
|
||||
PagerDemodSettings::NotificationSettings::NotificationSettings() :
|
||||
m_matchColumn(PagerDemodGUI::MESSAGE_COL_ADDRESS),
|
||||
m_highlight(false),
|
||||
m_highlightColor(Qt::red),
|
||||
m_plotOnMap(false)
|
||||
{
|
||||
}
|
||||
|
||||
void PagerDemodSettings::NotificationSettings::updateRegularExpression()
|
||||
{
|
||||
m_regularExpression.setPattern(m_regExp);
|
||||
m_regularExpression.optimize();
|
||||
if (!m_regularExpression.isValid()) {
|
||||
qDebug() << "PagerDemodSettings::NotificationSettings: Regular expression is not valid: " << m_regExp;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray PagerDemodSettings::NotificationSettings::serialize() const
|
||||
{
|
||||
SimpleSerializer s(1);
|
||||
|
||||
s.writeS32(1, m_matchColumn);
|
||||
s.writeString(2, m_regExp);
|
||||
s.writeString(3, m_speech);
|
||||
s.writeString(4, m_command);
|
||||
s.writeBool(5, m_highlight);
|
||||
s.writeS32(6, m_highlightColor);
|
||||
s.writeBool(7, m_plotOnMap);
|
||||
|
||||
return s.final();
|
||||
}
|
||||
|
||||
bool PagerDemodSettings::NotificationSettings::deserialize(const QByteArray& data)
|
||||
{
|
||||
SimpleDeserializer d(data);
|
||||
|
||||
if (!d.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d.getVersion() == 1)
|
||||
{
|
||||
QByteArray blob;
|
||||
|
||||
d.readS32(1, &m_matchColumn);
|
||||
d.readString(2, &m_regExp);
|
||||
d.readString(3, &m_speech);
|
||||
d.readString(4, &m_command);
|
||||
d.readBool(5, &m_highlight, false);
|
||||
d.readS32(6, &m_highlightColor, QColor(Qt::red).rgba());
|
||||
d.readBool(7, &m_plotOnMap, false);
|
||||
|
||||
updateRegularExpression();
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QDataStream& operator<<(QDataStream& out, const PagerDemodSettings::NotificationSettings *settings)
|
||||
{
|
||||
out << settings->serialize();
|
||||
return out;
|
||||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream& in, PagerDemodSettings::NotificationSettings*& settings)
|
||||
{
|
||||
settings = new PagerDemodSettings::NotificationSettings();
|
||||
QByteArray data;
|
||||
in >> data;
|
||||
settings->deserialize(data);
|
||||
return in;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "dsp/dsptypes.h"
|
||||
|
||||
@ -33,6 +34,23 @@ class Serializable;
|
||||
|
||||
struct PagerDemodSettings
|
||||
{
|
||||
struct NotificationSettings {
|
||||
int m_matchColumn;
|
||||
QString m_regExp;
|
||||
QString m_speech;
|
||||
QString m_command;
|
||||
bool m_highlight;
|
||||
qint32 m_highlightColor;
|
||||
bool m_plotOnMap;
|
||||
|
||||
QRegularExpression m_regularExpression;
|
||||
|
||||
NotificationSettings();
|
||||
void updateRegularExpression();
|
||||
QByteArray serialize() const;
|
||||
bool deserialize(const QByteArray& data);
|
||||
};
|
||||
|
||||
qint32 m_baud; //!< 512, 1200 or 2400
|
||||
qint32 m_inputFrequencyOffset;
|
||||
Real m_rfBandwidth;
|
||||
@ -73,6 +91,12 @@ struct PagerDemodSettings
|
||||
QByteArray m_geometryBytes;
|
||||
bool m_hidden;
|
||||
|
||||
QList<NotificationSettings *> m_notificationSettings;
|
||||
|
||||
bool m_filterDuplicates;
|
||||
bool m_duplicateMatchMessageOnly;
|
||||
bool m_duplicateMatchLastOnly;
|
||||
|
||||
int m_messageColumnIndexes[PAGERDEMOD_MESSAGE_COLUMNS];//!< How the columns are ordered in the table
|
||||
int m_messageColumnSizes[PAGERDEMOD_MESSAGE_COLUMNS]; //!< Size of the columns in the table
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
<h1>Pager demodulator plugin</h1>
|
||||
<h1>Pager demodulator plugin</h1>
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
@ -86,6 +86,36 @@ IP address of the host to forward received messages to via UDP.
|
||||
|
||||
UDP port number to forward received messages to.
|
||||
|
||||
<h3>Filter Duplicates</h3>
|
||||
|
||||
Check to filter (discard) duplicate messages. Right click to show the Duplicate Filter options dialog:
|
||||
|
||||
- Match message only: When unchecked, compare address and message. When checked, compare only message, ignoring the address.
|
||||
- Match last message only: When unchecked the message is compared against all messages in the table. When checked, the message is compared against the last received message only.
|
||||
|
||||
<h3>Open Notifications Dialog</h3>
|
||||
|
||||
When clicked, opens the Notifications Dialog, which allows speech notifications or programs/scripts to be run when messages matching user-defined rules are received.
|
||||
|
||||
By running a program such as [cmail](https://www.inveigle.net/cmail/download) on Windows or sendmail on Linux, e-mail notifications can be sent.
|
||||
|
||||
Messages can be highlighted in a user-defined colour.
|
||||
|
||||
By checking plot on map, if a message contains a position specified as latitude and longitude, the message can be displayed on the [Map](../../feature/map/readme.md) feature.
|
||||
The format of the coordinates should follow https://en.wikipedia.org/wiki/ISO_6709, E.g: 50°40′46″N 95°48′26″W or -23.342,5.234
|
||||
|
||||
Here are a few examples:
|
||||
|
||||
![Notifications Dialog](../../../doc/img/PagerDemod_plugin_notifications.png)
|
||||
|
||||
In the Speech and Command strings, variables can be used to substitute data from the received message:
|
||||
|
||||
* ${address},
|
||||
* ${message},
|
||||
* ${1}, ${2}... are replaced with the string from the corresponding capture group in the regular expression.
|
||||
|
||||
To experiment with regular expressions, try [https://regexr.com/](https://regexr.com/).
|
||||
|
||||
<h3>15: Start/stop Logging Messages to .csv File</h3>
|
||||
|
||||
When checked, writes all received messages to a .csv file.
|
||||
|
@ -25,6 +25,7 @@
|
||||
<file>map/airport_small.png</file>
|
||||
<file>map/heliport.png</file>
|
||||
<file>map/waypoint.png</file>
|
||||
<file>map/pager.png</file>
|
||||
<file>map/map3d.html</file>
|
||||
<file>data/transmitters.csv</file>
|
||||
</qresource>
|
||||
|
BIN
plugins/feature/map/map/pager.png
Normal file
BIN
plugins/feature/map/map/pager.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
@ -38,6 +38,7 @@ const QStringList MapSettings::m_pipeTypes = {
|
||||
QStringLiteral("FT8Demod"),
|
||||
QStringLiteral("HeatMap"),
|
||||
QStringLiteral("ILSDemod"),
|
||||
QStringLiteral("PagerDemod"),
|
||||
QStringLiteral("Radiosonde"),
|
||||
QStringLiteral("StarTracker"),
|
||||
QStringLiteral("SatelliteTracker"),
|
||||
@ -55,6 +56,7 @@ const QStringList MapSettings::m_pipeURIs = {
|
||||
QStringLiteral("sdrangel.channel.ft8demod"),
|
||||
QStringLiteral("sdrangel.channel.heatmap"),
|
||||
QStringLiteral("sdrangel.channel.ilsdemod"),
|
||||
QStringLiteral("sdrangel.channel.pagerdemod"),
|
||||
QStringLiteral("sdrangel.feature.radiosonde"),
|
||||
QStringLiteral("sdrangel.feature.startracker"),
|
||||
QStringLiteral("sdrangel.feature.satellitetracker"),
|
||||
@ -96,6 +98,7 @@ MapSettings::MapSettings() :
|
||||
m_itemSettings.insert("StarTracker", new MapItemSettings("StarTracker", true, QColor(230, 230, 230), true, true, 3));
|
||||
m_itemSettings.insert("SatelliteTracker", new MapItemSettings("SatelliteTracker", true, QColor(0, 0, 255), true, false, 0, modelMinPixelSize));
|
||||
m_itemSettings.insert("Beacons", new MapItemSettings("Beacons", true, QColor(255, 0, 0), false, true, 8));
|
||||
m_itemSettings.insert("PagerDemod", new MapItemSettings("PagerDemod", true, QColor(200, 191, 231), true, false, 11));
|
||||
m_itemSettings.insert("Radiosonde", new MapItemSettings("Radiosonde", true, QColor(102, 0, 102), true, false, 11, modelMinPixelSize));
|
||||
m_itemSettings.insert("Radio Time Transmitters", new MapItemSettings("Radio Time Transmitters", true, QColor(255, 0, 0), false, true, 8));
|
||||
m_itemSettings.insert("Radar", new MapItemSettings("Radar", true, QColor(255, 0, 0), false, true, 8));
|
||||
|
@ -16,7 +16,8 @@ On top of this, it can plot data from other plugins, such as:
|
||||
* Radials and estimated position from the VOR localizer feature,
|
||||
* ILS course line and glide path from the ILS Demodulator,
|
||||
* DSC geographic call areas,
|
||||
* SID paths.
|
||||
* SID paths,
|
||||
* Pager messages that contain coordinates.
|
||||
|
||||
As well as internet and built-in data sources:
|
||||
|
||||
@ -313,6 +314,7 @@ Mapbox: https://www.mapbox.com/ Cesium: https://www.cesium.com Bing: https://www
|
||||
|
||||
Ionosonde data and MUF/coF2 contours from [KC2G](https://prop.kc2g.com/) with source data from [GIRO](https://giro.uml.edu/) and [NOAA NCEI](https://www.ngdc.noaa.gov/stp/iono/ionohome.html).
|
||||
|
||||
|
||||
Sea Marks are from OpenSeaMap: https://www.openseamap.org/
|
||||
|
||||
Railways are from OpenRailwayMap: https://www.openrailwaymap.org/
|
||||
@ -324,6 +326,7 @@ World icons created by turkkub from Flaticon: https://www.flaticon.com
|
||||
Layers and Boat icons created by Freepik from Flaticon: https://www.flaticon.com
|
||||
Railway icons created by Prosymbols Premium from Flaticon: https://www.flaticon.com
|
||||
Satellite icons created by SyafriStudio from Flaticon: https://www.flaticon.com
|
||||
Pager icons created by xnimrodx from Flaticon: https://www.flaticon.com
|
||||
|
||||
3D models are by various artists under a variety of licenses. See: https://github.com/srcejon/sdrangel-3d-models
|
||||
|
||||
|
@ -167,3 +167,11 @@ QHash<QString, int> CSV::readHeader(QTextStream &in, QStringList requiredColumns
|
||||
|
||||
return colNumbers;
|
||||
}
|
||||
|
||||
QString CSV::escape(const QString& string)
|
||||
{
|
||||
QString s = string;
|
||||
s.replace('"', "\"\"");
|
||||
s = QString("\"%1\"").arg(s);
|
||||
return s;
|
||||
}
|
||||
|
@ -52,6 +52,8 @@ struct SDRBASE_API CSV {
|
||||
static bool readRow(QTextStream &in, QStringList *row, char seperator=',');
|
||||
static QHash<QString, int> readHeader(QTextStream &in, QStringList requiredColumns, QString &error, char seperator=',');
|
||||
|
||||
static QString escape(const QString& string);
|
||||
|
||||
};
|
||||
|
||||
#endif /* INCLUDE_CSV_H */
|
||||
|
@ -269,11 +269,15 @@ public:
|
||||
// Try to convert a string to latitude and longitude. Returns false if not recognised format.
|
||||
// https://en.wikipedia.org/wiki/ISO_6709 specifies a standard syntax
|
||||
// We support both decimal and DMS formats
|
||||
static bool stringToLatitudeAndLongitude(const QString& string, float& latitude, float& longitude)
|
||||
static bool stringToLatitudeAndLongitude(const QString& string, float& latitude, float& longitude, bool exact=true)
|
||||
{
|
||||
QRegularExpressionMatch match;
|
||||
|
||||
QRegularExpression decimal(QRegularExpression::anchoredPattern("(-?[0-9]+(\\.[0-9]+)?) *,? *(-?[0-9]+(\\.[0-9]+)?)"));
|
||||
QString decimalPattern = "(-?[0-9]+(\\.[0-9]+)?) *,? *(-?[0-9]+(\\.[0-9]+)?)";
|
||||
if (exact) {
|
||||
decimalPattern = QRegularExpression::anchoredPattern(decimalPattern);
|
||||
}
|
||||
QRegularExpression decimal(decimalPattern);
|
||||
match = decimal.match(string);
|
||||
if (match.hasMatch())
|
||||
{
|
||||
@ -282,7 +286,11 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
QRegularExpression dms(QRegularExpression::anchoredPattern(QString("([0-9]+)[%1d]([0-9]+)['m]([0-9]+(\\.[0-9]+)?)[\"s]([NS]) *,? *([0-9]+)[%1d]([0-9]+)['m]([0-9]+(\\.[0-9]+)?)[\"s]([EW])").arg(QChar(0xb0))));
|
||||
QString dmsPattern = QString("([0-9]+)[%1d]([0-9]+)['m]([0-9]+(\\.[0-9]+)?)[\"s]([NS]) *,? *([0-9]+)[%1d]([0-9]+)['m]([0-9]+(\\.[0-9]+)?)[\"s]([EW])").arg(QChar(0xb0));
|
||||
if (exact) {
|
||||
dmsPattern = QRegularExpression::anchoredPattern(dmsPattern);
|
||||
}
|
||||
QRegularExpression dms(dmsPattern);
|
||||
match = dms.match(string);
|
||||
if (match.hasMatch())
|
||||
{
|
||||
@ -303,7 +311,11 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
QRegularExpression dms2(QRegularExpression::anchoredPattern(QString("([0-9]+)([NS])([0-9]{2})([0-9]{2}) *,?([0-9]+)([EW])([0-9]{2})([0-9]{2})")));
|
||||
QString dms2Pattern = "([0-9]+)([NS])([0-9]{2})([0-9]{2}) *,?([0-9]+)([EW])([0-9]{2})([0-9]{2})";
|
||||
if (exact) {
|
||||
dms2Pattern = QRegularExpression::anchoredPattern(dms2Pattern);
|
||||
}
|
||||
QRegularExpression dms2(dms2Pattern);
|
||||
match = dms2.match(string);
|
||||
if (match.hasMatch())
|
||||
{
|
||||
@ -325,7 +337,11 @@ public:
|
||||
}
|
||||
|
||||
// 512255.5900N 0024400.6105W as used on aviation charts
|
||||
QRegularExpression dms3(QRegularExpression::anchoredPattern(QString("(\\d{2})(\\d{2})((\\d{2})(\\.\\d+)?)([NS]) *,?(\\d{3})(\\d{2})((\\d{2})(\\.\\d+)?)([EW])")));
|
||||
QString dms3Pattern = "(\\d{2})(\\d{2})((\\d{2})(\\.\\d+)?)([NS]) *,?(\\d{3})(\\d{2})((\\d{2})(\\.\\d+)?)([EW])";
|
||||
if (exact) {
|
||||
dms3Pattern = QRegularExpression::anchoredPattern(dms3Pattern);
|
||||
}
|
||||
QRegularExpression dms3(dms3Pattern);
|
||||
match = dms3.match(string);
|
||||
if (match.hasMatch())
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user