Add POCSAG processing and display code.

This commit is contained in:
Jonathan Naylor 2018-06-11 21:30:49 +01:00
parent 27c7342a48
commit b398bba1ea
33 changed files with 356 additions and 16 deletions

View File

@ -298,6 +298,25 @@ void CDisplay::clearNXDN()
}
}
void CDisplay::writePOCSAG(uint32_t ric, const std::string& message)
{
m_timer1.start();
m_mode1 = MODE_POCSAG;
writePOCSAGInt(ric, message);
}
void CDisplay::clearPOCSAG()
{
if (m_timer1.hasExpired()) {
clearPOCSAGInt();
m_timer1.stop();
m_mode1 = MODE_IDLE;
} else {
m_mode1 = MODE_POCSAG;
}
}
void CDisplay::writeCW()
{
m_timer1.start();
@ -336,6 +355,11 @@ void CDisplay::clock(unsigned int ms)
m_mode1 = MODE_IDLE;
m_timer1.stop();
break;
case MODE_POCSAG:
clearPOCSAGInt();
m_mode1 = MODE_IDLE;
m_timer1.stop();
break;
case MODE_CW:
clearCWInt();
m_mode1 = MODE_IDLE;

View File

@ -23,6 +23,8 @@
#include <string>
#include <cstdint>
class CDisplay
{
public:
@ -61,8 +63,10 @@ public:
void writeNXDNBER(float ber);
void clearNXDN();
void writePOCSAG(uint32_t ric, const std::string& message);
void clearPOCSAG();
void writeCW();
void clearCW();
virtual void close() = 0;
@ -99,6 +103,9 @@ protected:
virtual void writeNXDNBERInt(float ber);
virtual void clearNXDNInt() = 0;
virtual void writePOCSAGInt(uint32_t ric, const std::string& message) = 0;
virtual void clearPOCSAGInt() = 0;
virtual void writeCWInt() = 0;
virtual void clearCWInt() = 0;

View File

@ -995,6 +995,18 @@ void CHD44780::clearNXDNInt()
}
}
void CHD44780::writePOCSAGInt(uint32_t ric, const std::string& message)
{
::lcdPosition(m_fd, m_cols - 5, m_rows - 1);
::lcdPuts(m_fd, "POCSAG TX");
}
void CHD44780::clearPOCSAGInt()
{
::lcdPosition(m_fd, m_cols - 5, m_rows - 1);
::lcdPuts(m_fd, " Idle");
}
void CHD44780::writeCWInt()
{
::lcdPosition(m_fd, m_cols - 5, m_rows - 1);

View File

@ -121,6 +121,9 @@ protected:
virtual void writeNXDNRSSIInt(unsigned char rssi);
virtual void clearNXDNInt();
virtual void writePOCSAG(uint32_t ric, const std::string& message);
virtual void clearPOCSAGInt();
virtual void writeCWInt();
virtual void clearCWInt();

BIN
Images/POCSAG.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

View File

@ -514,6 +514,14 @@ void CLCDproc::clearNXDNInt()
socketPrintf(m_socketfd, "output 16"); // Set LED5 color green
}
void CLCDproc::writePOCSAGInt(uint32_t ric, const std::string& message)
{
}
void CLCDproc::clearPOCSAGInt()
{
}
void CLCDproc::writeCWInt()
{
}

View File

@ -60,6 +60,9 @@ protected:
virtual void writeNXDNRSSIInt(unsigned char rssi);
virtual void clearNXDNInt();
virtual void writePOCSAGInt(uint32_t ric, const std::string& message);
virtual void clearPOCSAGInt();
virtual void writeCWInt();
virtual void clearCWInt();

View File

@ -616,7 +616,7 @@ void CNextion::writeNXDNInt(const char* source, bool group, unsigned int dest, c
if (m_mode != MODE_NXDN) {
sendCommand("page NXDN");
sendCommandAction(6U);
sendCommandAction(7U);
}
char text[30U];
@ -679,6 +679,36 @@ void CNextion::clearNXDNInt()
sendCommand("t3.txt=\"\"");
}
void CNextion::writePOCSAGInt(uint32_t ric, const std::string& message)
{
if (m_mode != MODE_POCSAG) {
sendCommand("page POCSAG");
sendCommandAction(6U);
}
char text[30U];
::sprintf(text, "dim=%u", m_brightness);
sendCommand(text);
::sprintf(text, "t0.txt=\"RIC: %u\"", ric);
sendCommand(text);
sendCommandAction(132U);
::sprintf(text, "t1.txt=\"MSG: %s\"", message.c_str());
sendCommand(text);
sendCommandAction(133U);
m_clockDisplayTimer.stop();
m_mode = MODE_POCSAG;
}
void CNextion::clearPOCSAGInt()
{
sendCommand("t1.txt=\"MMDVM IDLE\"");
sendCommandAction(134U);
}
void CNextion::writeCWInt()
{
sendCommand("t1.txt=\"Sending CW Ident\"");

View File

@ -68,6 +68,9 @@ protected:
virtual void writeNXDNBERInt(float ber);
virtual void clearNXDNInt();
virtual void writePOCSAGInt(uint32_t ric, const std::string& message);
virtual void clearPOCSAGInt();
virtual void writeCWInt();
virtual void clearCWInt();

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -126,6 +126,20 @@ void CNullDisplay::clearNXDNInt()
#endif
}
void CNullDisplay::writePOCSAGInt(uint32_t ric, const std::string& message)
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 1);
#endif
}
void CNullDisplay::clearPOCSAGInt()
{
#if defined(RASPBERRY_PI)
::digitalWrite(LED_STATUS, 0);
#endif
}
void CNullDisplay::writeCWInt()
{
}

View File

@ -53,6 +53,9 @@ protected:
virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type);
virtual void clearNXDNInt();
virtual void writePOCSAGInt(uint32_t ric, const std::string& message);
virtual void clearPOCSAGInt();
virtual void writeCWInt();
virtual void clearCWInt();

View File

@ -22,15 +22,24 @@
// #define DUMP_POCSAG
const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
const unsigned char ASCII_EOT = 0x04U;
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
const unsigned char BIT_MASK_TABLE8[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
#define WRITE_BIT8(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE8[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE8[(i)&7])
#define READ_BIT8(p,i) (p[(i)>>3] & BIT_MASK_TABLE8[(i)&7])
CPOCSAGControl::CPOCSAGControl(CPOCSAGNetwork* network, CDisplay* display) :
m_network(network),
m_display(display),
m_queue(5000U, "POCSAG Control"),
m_frames(0U),
m_count(0U),
m_output(),
m_buffer(),
m_ric(0U),
m_text(),
m_state(PS_NONE),
m_fp(NULL)
{
assert(display != NULL);
@ -38,6 +47,8 @@ m_fp(NULL)
CPOCSAGControl::~CPOCSAGControl()
{
m_output.clear();
m_buffer.clear();
}
unsigned int CPOCSAGControl::readModem(unsigned char* data)
@ -55,20 +66,203 @@ unsigned int CPOCSAGControl::readModem(unsigned char* data)
return len;
}
void CPOCSAGControl::writeNetwork()
bool CPOCSAGControl::processData()
{
unsigned char netData[40U];
unsigned int length = m_network->read(netData);
if (length == 0U)
return;
assert(m_network != NULL);
unsigned char data[40U];
unsigned int length = m_network->read(data);
if (length == 0U)
return false;
m_ric = 0U;
m_ric |= (data[0U] << 16) & 0x00FF0000U;
m_ric |= (data[1U] << 8) & 0x0000FF00U;
m_ric |= (data[2U] << 0) & 0x000000FFU;
m_text = std::string((char*)(data + 3U), length - 3U);
m_buffer.clear();
addAddress();
packASCII();
// Ensure data is an even number of words
if ((m_buffer.size() % 2U) == 1U)
m_buffer.push_back(POCSAG_IDLE_WORD);
return true;
}
void CPOCSAGControl::clock(unsigned int ms)
{
if (m_network != NULL)
writeNetwork();
if (m_state == PS_NONE) {
bool ret = processData();
if (!ret)
return;
m_display->writePOCSAG(m_ric, m_text);
m_state = PS_WAITING;
m_frames = 0U;
m_count = 1U;
}
m_output.clear();
m_output.push_back(POCSAG_SYNC_WORD);
for (unsigned int i = 0U; i < POCSAG_FRAME_ADDRESSES; i++) {
if (m_state == PS_WAITING) {
if (i == (m_ric % POCSAG_FRAME_ADDRESSES)) {
uint32_t w1 = m_buffer.front();
m_buffer.pop_front();
uint32_t w2 = m_buffer.front();
m_buffer.pop_front();
m_output.push_back(w1);
m_output.push_back(w2);
m_state = PS_SENDING;
} else {
m_output.push_back(POCSAG_IDLE_WORD);
m_output.push_back(POCSAG_IDLE_WORD);
}
} else if (m_state == PS_SENDING) {
uint32_t w1 = m_buffer.front();
m_buffer.pop_front();
uint32_t w2 = m_buffer.front();
m_buffer.pop_front();
m_output.push_back(w1);
m_output.push_back(w2);
if (m_buffer.empty()) {
bool ret = processData();
if (ret) {
m_display->writePOCSAG(m_ric, m_text);
m_state = PS_WAITING;
m_count++;
} else {
m_state = PS_ENDING;
}
}
} else { // PS_ENDING
m_output.push_back(POCSAG_IDLE_WORD);
m_output.push_back(POCSAG_IDLE_WORD);
}
}
writeQueue();
m_frames++;
if (m_state == PS_ENDING) {
LogMessage("POCSAG, transmitted %u frames of data from %u messages", m_frames, m_count);
m_display->clearPOCSAG();
m_state = PS_NONE;
}
}
void CPOCSAGControl::addAddress()
{
uint32_t word = 0x00001800U;
word |= (m_ric / POCSAG_FRAME_ADDRESSES) << 13;
addBCHAndParity(word);
m_buffer.push_back(word);
}
void CPOCSAGControl::packASCII()
{
uint32_t word = 0x80000000U;
unsigned int n = 30U;
for (std::string::const_iterator it = m_text.cbegin(); it != m_text.cend(); ++it) {
unsigned char MASK = 0x40U;
for (unsigned int j = 0U; j < 7U; j++, MASK >>= 1) {
bool b = (*it & MASK) == MASK;
if (b)
word |= (0x01U << n);
n--;
if (n == 10U) {
addBCHAndParity(word);
m_buffer.push_back(word);
word = 0x80000000U;
n = 30U;
}
}
}
// Add at least one EOT to the message
do {
unsigned char MASK = 0x70U;
for (unsigned int j = 0U; j < 7U; j++, MASK >>= 1) {
bool b = (ASCII_EOT & MASK) == MASK;
if (b)
word |= (0x01U << n);
n--;
if (n == 10U) {
addBCHAndParity(word);
m_buffer.push_back(word);
word = 0x80000000U;
n = 30U;
}
}
} while (n != 30U);
}
void CPOCSAGControl::addBCHAndParity(uint32_t& word) const
{
uint32_t temp = word;
for (unsigned int i = 0U; i < 21U; i++, temp <<= 1) {
if (temp & 0x80000000U)
temp ^= 0xED200000U;
}
word |= (temp >> 21);
temp = word;
unsigned int parity = 0U;
for (unsigned int i = 0U; i < 32U; i++, temp <<= 1) {
if (temp & 0x80000000U)
parity++;
}
if ((parity % 2U) == 1U)
word |= 0x00000001U;
}
void CPOCSAGControl::writeQueue()
{
// Convert 32-bit words to bytes
unsigned int n = 0U;
unsigned char data[POCSAG_FRAME_LENGTH_BYTES];
for (unsigned int i = 0U; i < POCSAG_FRAME_LENGTH_WORDS; i++) {
uint32_t w = m_output.front();
m_output.pop_front();
for (unsigned int j = 0U; j < 31U; j++, w <<= 1) {
bool b = (w & 0x80000000U) == 0x80000000U;
WRITE_BIT8(data, n, b);
n++;
}
}
unsigned char len = POCSAG_FRAME_LENGTH_BYTES;
CUtils::dump(1U, "Data to MMDVM", data, len);
unsigned int space = m_queue.freeSpace();
if (space < (len + 1U)) {
LogError("POCSAG, overflow in the POCSAG RF queue");
return;
}
m_queue.addData(&len, 1U);
m_queue.addData(data, len);
}
bool CPOCSAGControl::openFile()
@ -88,7 +282,7 @@ bool CPOCSAGControl::openFile()
if (m_fp == NULL)
return false;
::fwrite("POCSAG", 1U, 3U, m_fp);
::fwrite("POCSAG", 1U, 6U, m_fp);
return true;
}

View File

@ -25,7 +25,10 @@
#include "Display.h"
#include "Defines.h"
#include <cstdint>
#include <string>
#include <deque>
class CPOCSAGControl {
public:
@ -41,10 +44,27 @@ private:
CDisplay* m_display;
CRingBuffer<unsigned char> m_queue;
unsigned int m_frames;
unsigned int m_count;
enum POCSAG_STATE {
PS_NONE,
PS_WAITING,
PS_SENDING,
PS_ENDING
};
std::deque<uint32_t> m_output;
std::deque<uint32_t> m_buffer;
uint32_t m_ric;
std::string m_text;
POCSAG_STATE m_state;
FILE* m_fp;
void writeNetwork();
bool processData();
void writeQueue();
void addAddress();
void packASCII();
void addBCHAndParity(uint32_t& word) const;
bool openFile();
bool writeFile(const unsigned char* data);
void closeFile();

View File

@ -26,6 +26,8 @@ const unsigned int POCSAG_RADIO_SYMBOL_LENGTH = 20U; // At 24 kHz sample ra
const unsigned int POCSAG_FRAME_LENGTH_WORDS = 17U;
const unsigned int POCSAG_FRAME_LENGTH_BYTES = POCSAG_FRAME_LENGTH_WORDS * sizeof(uint32_t);
const unsigned int POCSAG_FRAME_ADDRESSES = 8U;
const uint32_t POCSAG_SYNC_WORD = 0x7CD215D8U;
const uint32_t POCSAG_IDLE_WORD = 0x7A89C197U;

View File

@ -114,7 +114,7 @@ void CPOCSAGNetwork::enable(bool enabled)
if (enabled && !m_enabled)
reset();
unsigned char c = enabled ? 0xFFU : 0x00U;
unsigned char c = enabled ? 0x00U : 0xFFU;
m_socket.write(&c, 1U, m_address, m_port);

View File

@ -395,6 +395,20 @@ void CTFTSerial::clearNXDNInt()
displayText(" ");
}
void CTFTSerial::writePOCSAGInt(uint32_t ric, const std::string& message)
{
gotoPosPixel(15U, 90U);
displayText("POCSAG TX");
m_mode = MODE_CW;
}
void CTFTSerial::clearPOCSAGInt()
{
gotoPosPixel(45U, 90U);
displayText("IDLE");
}
void CTFTSerial::writeCWInt()
{
gotoPosPixel(45U, 90U);

View File

@ -55,6 +55,9 @@ protected:
virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type);
virtual void clearNXDNInt();
virtual void writePOCSAGInt(uint32_t ric, const std::string& message);
virtual void clearPOCSAGInt();
virtual void writeCWInt();
virtual void clearCWInt();