diff --git a/doc/img/PagerDemod_plugin.png b/doc/img/PagerDemod_plugin.png
index 2cb03ec10..0f6bae41d 100644
Binary files a/doc/img/PagerDemod_plugin.png and b/doc/img/PagerDemod_plugin.png differ
diff --git a/doc/img/PagerDemod_plugin_charset.png b/doc/img/PagerDemod_plugin_charset.png
new file mode 100644
index 000000000..7493075a4
Binary files /dev/null and b/doc/img/PagerDemod_plugin_charset.png differ
diff --git a/plugins/channelrx/demodpager/CMakeLists.txt b/plugins/channelrx/demodpager/CMakeLists.txt
index 894406fc6..58f65cc96 100644
--- a/plugins/channelrx/demodpager/CMakeLists.txt
+++ b/plugins/channelrx/demodpager/CMakeLists.txt
@@ -27,10 +27,13 @@ if(NOT SERVER_MODE)
${demodpager_SOURCES}
pagerdemodgui.cpp
pagerdemodgui.ui
+ pagerdemodcharsetdialog.cpp
+ pagerdemodcharsetdialog.ui
)
set(demodpager_HEADERS
${demodpager_HEADERS}
pagerdemodgui.h
+ pagerdemodcharsetdialog.h
)
set(TARGET_NAME demodpager)
diff --git a/plugins/channelrx/demodpager/pagerdemodcharsetdialog.cpp b/plugins/channelrx/demodpager/pagerdemodcharsetdialog.cpp
new file mode 100644
index 000000000..92251ba28
--- /dev/null
+++ b/plugins/channelrx/demodpager/pagerdemodcharsetdialog.cpp
@@ -0,0 +1,118 @@
+///////////////////////////////////////////////////////////////////////////////////
+// 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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include
+
+#include "pagerdemodcharsetdialog.h"
+
+PagerDemodCharsetDialog::PagerDemodCharsetDialog(PagerDemodSettings *settings,
+ QWidget* parent) :
+ QDialog(parent),
+ m_settings(settings),
+ ui(new Ui::PagerDemodCharsetDialog)
+{
+ ui->setupUi(this);
+ if (settings->m_sevenbit.size() > 0) {
+ ui->preset->setCurrentIndex(2); // User
+ }
+ ui->readingOrder->setCurrentIndex(settings->m_rightToLeft ? 1 : 0);
+ for (int i = 0; i < settings->m_sevenbit.size(); i++) {
+ addRow(settings->m_sevenbit[i], settings->m_unicode[i]);
+ }
+ connect(ui->table, &QTableWidget::cellChanged, this, &PagerDemodCharsetDialog::on_table_cellChanged);
+}
+
+PagerDemodCharsetDialog::~PagerDemodCharsetDialog()
+{
+ delete ui;
+}
+
+void PagerDemodCharsetDialog::accept()
+{
+ m_settings->m_sevenbit.clear();
+ m_settings->m_unicode.clear();
+ for (int i = 0; i < ui->table->rowCount(); i++)
+ {
+ int sevenbit = ui->table->item(i, SEVENBIT_COL)->data(Qt::DisplayRole).toString().toInt(nullptr, 16);
+ int unicode = ui->table->item(i, UNICODE_COL)->data(Qt::DisplayRole).toString().toInt(nullptr, 16);
+ m_settings->m_sevenbit.append(sevenbit);
+ m_settings->m_unicode.append(unicode);
+ }
+ m_settings->m_rightToLeft = ui->readingOrder->currentIndex() == 1;
+ QDialog::accept();
+}
+
+void PagerDemodCharsetDialog::on_add_clicked()
+{
+ addRow(0, 0);
+}
+
+void PagerDemodCharsetDialog::on_remove_clicked()
+{
+ QModelIndexList indexList = ui->table->selectionModel()->selectedRows();
+ if (!indexList.isEmpty())
+ {
+ int row = indexList.at(0).row();
+ ui->table->removeRow(row);
+ }
+}
+
+void PagerDemodCharsetDialog::on_preset_currentIndexChanged(int index)
+{
+ ui->table->setRowCount(0);
+ ui->readingOrder->setCurrentIndex(0);
+ if (index == 1)
+ {
+ // Hebrew
+ for (int i = 0; i < 22; i++) {
+ addRow(96 + i, 0x05D0 + i);
+ }
+ ui->readingOrder->setCurrentIndex(1);
+ }
+}
+
+void PagerDemodCharsetDialog::addRow(int sevenBit, int unicode)
+{
+ ui->table->setSortingEnabled(false);
+ ui->table->blockSignals(true);
+ int row = ui->table->rowCount();
+ ui->table->setRowCount(row + 1);
+ QTableWidgetItem *sevenbitItem = new QTableWidgetItem();
+ QTableWidgetItem *unicodeItem = new QTableWidgetItem();
+ QTableWidgetItem *glyphItem = new QTableWidgetItem();
+ ui->table->setItem(row, SEVENBIT_COL, sevenbitItem);
+ ui->table->setItem(row, UNICODE_COL, unicodeItem);
+ ui->table->setItem(row, GLYPH_COL, glyphItem);
+ sevenbitItem->setFlags(Qt::ItemIsEditable | sevenbitItem->flags());
+ sevenbitItem->setData(Qt::DisplayRole, QString::number(sevenBit, 16));
+ unicodeItem->setFlags(Qt::ItemIsEditable | unicodeItem->flags());
+ unicodeItem->setData(Qt::DisplayRole, QString::number(unicode, 16));
+ glyphItem->setFlags(glyphItem->flags() & ~Qt::ItemIsEditable);
+ glyphItem->setData(Qt::DisplayRole, QChar(unicode));
+ ui->table->blockSignals(false);
+ ui->table->setSortingEnabled(true);
+}
+
+void PagerDemodCharsetDialog::on_table_cellChanged(int row, int column)
+{
+ if (column == UNICODE_COL)
+ {
+ // Update glyph to match entered unicode code point
+ int unicode = ui->table->item(row, UNICODE_COL)->data(Qt::DisplayRole).toString().toInt(nullptr, 16);
+ ui->table->item(row, GLYPH_COL)->setData(Qt::DisplayRole, QChar(unicode));
+ }
+}
diff --git a/plugins/channelrx/demodpager/pagerdemodcharsetdialog.h b/plugins/channelrx/demodpager/pagerdemodcharsetdialog.h
new file mode 100644
index 000000000..d94271d27
--- /dev/null
+++ b/plugins/channelrx/demodpager/pagerdemodcharsetdialog.h
@@ -0,0 +1,55 @@
+///////////////////////////////////////////////////////////////////////////////////
+// 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 . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef INCLUDE_PAGERDEMODCHARSETDIALOG_H
+#define INCLUDE_PAGERDEMODCHARSETDIALOG_H
+
+#include
+#include
+
+#include "ui_pagerdemodcharsetdialog.h"
+#include "pagerdemodsettings.h"
+
+class PagerDemodCharsetDialog : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit PagerDemodCharsetDialog(PagerDemodSettings* settings, QWidget* parent = 0);
+ ~PagerDemodCharsetDialog();
+
+ PagerDemodSettings *m_settings;
+
+private slots:
+ void accept();
+ void on_add_clicked();
+ void on_remove_clicked();
+ void on_preset_currentIndexChanged(int index);
+ void on_table_cellChanged(int row, int column);
+
+private:
+ Ui::PagerDemodCharsetDialog* ui;
+
+ enum Columns {
+ SEVENBIT_COL,
+ UNICODE_COL,
+ GLYPH_COL
+ };
+
+ void addRow(int sevenBit, int unicode);
+};
+
+#endif // INCLUDE_PAGERDEMODCHARSETDIALOG_H
diff --git a/plugins/channelrx/demodpager/pagerdemodcharsetdialog.ui b/plugins/channelrx/demodpager/pagerdemodcharsetdialog.ui
new file mode 100644
index 000000000..5fde2fb7e
--- /dev/null
+++ b/plugins/channelrx/demodpager/pagerdemodcharsetdialog.ui
@@ -0,0 +1,208 @@
+
+
+ PagerDemodCharsetDialog
+
+
+
+ 0
+ 0
+ 403
+ 561
+
+
+
+
+ Liberation Sans
+ 9
+
+
+
+ Set chararcter encoding
+
+
+ -
+
+
-
+
+
+ Preset
+
+
+
+ -
+
+
+ Select a pre-defined character encoding
+
+
-
+
+ Latin
+
+
+ -
+
+ Hebrew
+
+
+ -
+
+ User
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Reading Order
+
+
+
+ -
+
+
+
+ 100
+ 0
+
+
+
+ Specify the order in which characters should be displayed
+
+
-
+
+ Left-to-right
+
+
+ -
+
+ Right-to-left
+
+
+
+
+
+
+ -
+
+
-
+
+
+ QAbstractItemView::SingleSelection
+
+
+ QAbstractItemView::SelectRows
+
+
+
+ 7-bit
+
+
+
+
+ Unicode
+
+
+
+
+ Glyph
+
+
+
+
+
+
+ -
+
+
-
+
+
+ +
+
+
+
+ -
+
+
+ -
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ PagerDemodCharsetDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ PagerDemodCharsetDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/plugins/channelrx/demodpager/pagerdemodgui.cpp b/plugins/channelrx/demodpager/pagerdemodgui.cpp
index f2086c50e..31e9a0671 100644
--- a/plugins/channelrx/demodpager/pagerdemodgui.cpp
+++ b/plugins/channelrx/demodpager/pagerdemodgui.cpp
@@ -41,6 +41,7 @@
#include "maincore.h"
#include "pagerdemod.h"
+#include "pagerdemodcharsetdialog.h"
void PagerDemodGUI::resizeTable()
{
@@ -659,3 +660,12 @@ void PagerDemodGUI::tick()
m_tickCount++;
}
+
+void PagerDemodGUI::on_charset_clicked()
+{
+ PagerDemodCharsetDialog dialog(&m_settings);
+ if (dialog.exec() == QDialog::Accepted)
+ {
+ applySettings();
+ }
+}
diff --git a/plugins/channelrx/demodpager/pagerdemodgui.h b/plugins/channelrx/demodpager/pagerdemodgui.h
index d1bd09e7a..fd3c22ebc 100644
--- a/plugins/channelrx/demodpager/pagerdemodgui.h
+++ b/plugins/channelrx/demodpager/pagerdemodgui.h
@@ -105,6 +105,7 @@ private slots:
void on_fmDev_valueChanged(int value);
void on_baud_currentIndexChanged(int index);
void on_decode_currentIndexChanged(int index);
+ void on_charset_clicked();
void on_filterAddress_editingFinished();
void on_clearTable_clicked();
void on_udpEnabled_clicked(bool checked);
diff --git a/plugins/channelrx/demodpager/pagerdemodgui.ui b/plugins/channelrx/demodpager/pagerdemodgui.ui
index 44d748ee2..51121722f 100644
--- a/plugins/channelrx/demodpager/pagerdemodgui.ui
+++ b/plugins/channelrx/demodpager/pagerdemodgui.ui
@@ -465,6 +465,20 @@
+ -
+
+
+ Select character encoding
+
+
+
+
+
+
+ :/keyboard.png:/keyboard.png
+
+
+
-
@@ -614,7 +628,7 @@
-
-
+
Clear messages from table
diff --git a/plugins/channelrx/demodpager/pagerdemodsettings.cpp b/plugins/channelrx/demodpager/pagerdemodsettings.cpp
index 37175dd6b..a78c3ae5b 100644
--- a/plugins/channelrx/demodpager/pagerdemodsettings.cpp
+++ b/plugins/channelrx/demodpager/pagerdemodsettings.cpp
@@ -17,6 +17,7 @@
///////////////////////////////////////////////////////////////////////////////////
#include
+#include
#include "dsp/dspengine.h"
#include "util/simpleserializer.h"
@@ -51,6 +52,7 @@ void PagerDemodSettings::resetToDefaults()
m_reverseAPIPort = 8888;
m_reverseAPIDeviceIndex = 0;
m_reverseAPIChannelIndex = 0;
+ m_rightToLeft = 0;
for (int i = 0; i < PAGERDEMOD_MESSAGE_COLUMNS; i++)
{
@@ -86,6 +88,9 @@ QByteArray PagerDemodSettings::serialize() const
s.writeU32(19, m_reverseAPIDeviceIndex);
s.writeU32(20, m_reverseAPIChannelIndex);
s.writeBlob(21, m_scopeGUI->serialize());
+ s.writeBool(22, m_rightToLeft);
+ s.writeBlob(23, serializeIntList(m_sevenbit));
+ s.writeBlob(24, serializeIntList(m_unicode));
for (int i = 0; i < PAGERDEMOD_MESSAGE_COLUMNS; i++) {
s.writeS32(100 + i, m_messageColumnIndexes[i]);
@@ -112,6 +117,7 @@ bool PagerDemodSettings::deserialize(const QByteArray& data)
QByteArray bytetmp;
uint32_t utmp;
QString strtmp;
+ QByteArray blob;
d.readS32(1, &m_inputFrequencyOffset, 0);
d.readFloat(2, &m_rfBandwidth, 20000.0f);
@@ -154,6 +160,11 @@ bool PagerDemodSettings::deserialize(const QByteArray& data)
d.readBlob(21, &bytetmp);
m_scopeGUI->deserialize(bytetmp);
}
+ d.readBool(22, &m_rightToLeft, false);
+ d.readBlob(23, &blob);
+ deserializeIntList(blob, m_sevenbit);
+ d.readBlob(24, &blob);
+ deserializeIntList(blob, m_unicode);
for (int i = 0; i < PAGERDEMOD_MESSAGE_COLUMNS; i++) {
d.readS32(100 + i, &m_messageColumnIndexes[i], i);
@@ -171,4 +182,18 @@ bool PagerDemodSettings::deserialize(const QByteArray& data)
}
}
+QByteArray PagerDemodSettings::serializeIntList(const QList& ints) const
+{
+ QByteArray data;
+ QDataStream *stream = new QDataStream(&data, QIODevice::WriteOnly);
+ (*stream) << ints;
+ delete stream;
+ return data;
+}
+void PagerDemodSettings::deserializeIntList(const QByteArray& data, QList& ints)
+{
+ QDataStream *stream = new QDataStream(data);
+ (*stream) >> ints;
+ delete stream;
+}
diff --git a/plugins/channelrx/demodpager/pagerdemodsettings.h b/plugins/channelrx/demodpager/pagerdemodsettings.h
index 2df12a39b..f4906e0dd 100644
--- a/plugins/channelrx/demodpager/pagerdemodsettings.h
+++ b/plugins/channelrx/demodpager/pagerdemodsettings.h
@@ -60,6 +60,10 @@ struct PagerDemodSettings
uint16_t m_reverseAPIChannelIndex;
Serializable *m_scopeGUI;
+ bool m_rightToLeft; //!< Whether characters are right to left or left to right
+ QList m_sevenbit;
+ QList m_unicode;
+
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
@@ -71,6 +75,8 @@ struct PagerDemodSettings
void setScopeGUI(Serializable *scopeGUI) { m_scopeGUI = scopeGUI; }
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
+ QByteArray serializeIntList(const QList& ints) const;
+ void deserializeIntList(const QByteArray& data, QList& ints);
};
#endif /* INCLUDE_PAGERDEMODSETTINGS_H */
diff --git a/plugins/channelrx/demodpager/pagerdemodsink.cpp b/plugins/channelrx/demodpager/pagerdemodsink.cpp
index 27ddae207..c84eed06e 100644
--- a/plugins/channelrx/demodpager/pagerdemodsink.cpp
+++ b/plugins/channelrx/demodpager/pagerdemodsink.cpp
@@ -273,6 +273,21 @@ void PagerDemodSink::decodeBatch()
m_numericMessage = m_numericMessage.trimmed(); // Remove trailing spaces
if (getMessageQueueToChannel())
{
+ // Convert from 7-bit to UTF-8 using user specified encoding
+ for (int i = 0; i < m_alphaMessage; i++)
+ {
+ QChar c = m_alphaMessage[i];
+ int idx = m_settings.m_sevenbit.indexOf(c.toLatin1());
+ if (idx >= 0) {
+ c = m_settings.m_unicode[idx];
+ }
+ m_alphaMessage[i] = c;
+ }
+ // Reverse reading order, if required
+ if (m_settings.m_rightToLeft) {
+ std::reverse(m_alphaMessage.begin(), m_alphaMessage.end());
+ }
+ // Send to channel and GUI
PagerDemod::MsgPagerMessage *msg = PagerDemod::MsgPagerMessage::create(m_address, m_functionBits, m_alphaMessage, m_numericMessage, m_parityErrors, m_bchErrors);
getMessageQueueToChannel()->push(msg);
}
diff --git a/plugins/channelrx/demodpager/readme.md b/plugins/channelrx/demodpager/readme.md
index 28c7eb74d..5d28f0213 100644
--- a/plugins/channelrx/demodpager/readme.md
+++ b/plugins/channelrx/demodpager/readme.md
@@ -54,25 +54,33 @@ Specifies how messages are decoded in the Message column in the table:
The table has Numeric and Alphanumeric columns which always display the corresponding decode.
-
9: Find
+9: Character encoding
+
+Click to open the character encoding dialog, which allows a mapping from the received 7-bit alphanumeric characters to Unicode.
+
+![Character encoding dialog](../../../doc/img/PagerDemod_plugin_charset.png)
+
+Each row contains a mapping from a 7-bit value to a Unicode code point. Values should be entered in hexideicmal
+
+10: Find
Entering a regular expression in the Find field displays only messages where the address matches the given regular expression.
-10: Clear Messages from table
+11: Clear Messages from table
Pressing this button clears all messages from the table.
-11: UDP
+12: UDP
When checked, received messages are forwarded to the specified UDP address (12) and port (13).
The messages are forwarded as null terminated ASCII strings, in the format: data time address function alpha numeric
-12: UDP address
+13: UDP address
IP address of the host to forward received messages to via UDP.
-13: UDP port
+14: UDP port
UDP port number to forward received messages to.