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.