Add POCSAG processing and display code.
This commit is contained in:
parent
27c7342a48
commit
b398bba1ea
24
Display.cpp
24
Display.cpp
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
12
HD44780.cpp
12
HD44780.cpp
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 175 KiB |
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
32
Nextion.cpp
32
Nextion.cpp
|
@ -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\"");
|
||||
|
|
|
@ -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.
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Reference in New Issue