added numeric paging

This commit is contained in:
cheetah 2025-04-05 01:39:34 +02:00
parent fde63704eb
commit 4f4bc0129a
8 changed files with 179 additions and 6 deletions

View file

@ -42,6 +42,9 @@
<label for="text">Text</label>
<input type="text" id="text" name="text" placeholder="Enter text..." />
<label for="numeric">Numeric 4-bit BCD</label>
<input type="checkbox" name="numeric"/>
<div class="button-group">
<button type="submit" name="add">Queue</button>
<button type="submit" name="addtx">Direct Transmit</button>

View file

@ -68,6 +68,7 @@ void txControllerBatchStart();
int txControllerBatchQueueCount();
String txHandleUmlauts(String message);
void txControllerInsert(uint32_t ric, uint8_t functionBit, char *text);
void txControllerInsert(uint32_t ric, uint8_t functionBit, bool numeric, char *text);
void setup_radio();
void setup_network();
void setup_mqtt();

View file

@ -20,10 +20,16 @@
#define CRC_BITS 10
#define CRC_GENERATOR 0b11101101001
#define NUMERIC_BITS_PER_WORD 20
#define NUMERIC_BITS_PER_DIGIT 4
uint32_t crc(uint32_t inputMsg);
uint32_t parity(uint32_t x);
uint32_t encodeCodeword(uint32_t msg);
uint32_t encodeASCII(uint32_t initial_offset, char *str, uint32_t *out);
char encodeDigit(char ch);
uint32_t encodeNumeric(uint32_t initial_offset, char *str, uint32_t *out);
int addressOffset(int address);
void encodeTransmission(int repeatIndex, int address, int fb, char *message, uint32_t *out);
size_t textMessageLength(int repeatIndex, int address, int numChars);
void encodeTransmission(int repeatIndex, int address, int fb, bool numeric, char *message, uint32_t *out);
size_t textMessageLength(int repeatIndex, int address, int numChars);
size_t numericMessageLength(int repeatIndex, int address, int numChars);

View file

@ -53,6 +53,7 @@ public:
int8_t getQueueCount();
bool isActive();
void queuePage(uint32_t ric, uint8_t functionBit, char *text);
void queuePage(uint32_t ric, uint8_t functionBit, bool numeric, char *text);
bool transmitBatch();
void test();

View file

@ -341,6 +341,10 @@ void txControllerInsert(uint32_t ric, uint8_t functionBit, char *text)
{
transmitter.queuePage(ric, functionBit, text);
}
void txControllerInsert(uint32_t ric, uint8_t functionBit, bool numeric, char *text)
{
transmitter.queuePage(ric, functionBit, numeric, text);
}
void txControllerBatchStart()
{
transmitter.transmitBatch();

View file

@ -142,6 +142,115 @@ uint32_t encodeASCII(uint32_t initial_offset, char *str, uint32_t *out)
return numWordsWritten;
}
// Char Translationtable
char *mirrorTab = new char[10]{0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e, 0x01, 0x09};
char encodeDigit(char ch)
{
if (ch >= '0' && ch <= '9')
return mirrorTab[ch - '0'];
switch (ch)
{
case ' ':
return 0x03;
case 'u':
case 'U':
return 0x0d;
case '-':
case '_':
return 0x0b;
case '(':
case '[':
return 0x0f;
case ')':
case ']':
return 0x07;
}
return 0x05;
}
uint32_t encodeNumeric(uint32_t initial_offset, char *str, uint32_t *out)
{
//Number of words written to *out
uint32_t numWordsWritten = 0;
//Data for the current word we're writing
uint32_t currentWord = 0;
//Nnumber of bits we've written so far to the current word
uint32_t currentNumBits = 0;
//Position of current word in the current batch
uint32_t wordPosition = initial_offset;
while (*str != 0)
{
unsigned char c = *str;
str++;
//Encode the digit bits backwards
for (int i = 0; i < NUMERIC_BITS_PER_DIGIT; i++)
{
currentWord <<= 1;
char digit = encodeDigit(c);
digit = ((digit & 1) << 3) |
((digit & 2) << 1) |
((digit & 4) >> 1) |
((digit & 8) >> 3);
currentWord |= (digit >> i) & 1;
currentNumBits++;
if (currentNumBits == NUMERIC_BITS_PER_WORD)
{
//Add the MESSAGE flag to our current word and encode it.
*out = encodeCodeword(currentWord | POCSAG_FLAG_MESSAGE);
out++;
currentWord = 0;
currentNumBits = 0;
numWordsWritten++;
wordPosition++;
if (wordPosition == POCSAG_BATCH_SIZE)
{
//We've filled a full batch, time to insert a SYNC word
//and start a new one.
*out = POCSAG_SYNC;
out++;
numWordsWritten++;
wordPosition = 0;
}
}
}
}
//Write remainder of message
if (currentNumBits > 0)
{
//Pad out the word to 20 bits with zeroes
currentWord <<= 20 - currentNumBits;
*out = encodeCodeword(currentWord | POCSAG_FLAG_MESSAGE);
out++;
numWordsWritten++;
wordPosition++;
if (wordPosition == POCSAG_BATCH_SIZE)
{
//We've filled a full batch, time to insert a SYNC word
//and start a new one.
*out = POCSAG_SYNC;
out++;
numWordsWritten++;
wordPosition = 0;
}
}
return numWordsWritten;
}
/**
* An address of 21 bits, but only 18 of those bits are encoded in the address
* word itself. The remaining 3 bits are derived from which frame in the batch
@ -159,7 +268,7 @@ int addressOffset(int address)
* (*message) is a null terminated C string.
* (*out) is the destination to which the transmission will be written.
*/
void encodeTransmission(int repeatIndex, int address, int fb, char *message, uint32_t *out)
void encodeTransmission(int repeatIndex, int address, int fb, bool numeric, char *message, uint32_t *out)
{
// Encode preamble
// Alternating 1,0,1,0 bits for 576 bits, used for receiver to synchronize
@ -192,7 +301,11 @@ void encodeTransmission(int repeatIndex, int address, int fb, char *message, uin
*out = encodeCodeword(((address >> 3) << 2) | fb);
out++;
out += encodeASCII(addressOffset(address) + 1, message, out);
if (numeric == true)
out += encodeNumeric(addressOffset(address) + 1, message, out);
else
out += encodeASCII(addressOffset(address) + 1, message, out);
// out += encodeASCII(addressOffset(address) + 1, message, out);
// Finally, write an IDLE word indicating the end of the message
*out = POCSAG_IDLE;
@ -244,3 +357,40 @@ size_t textMessageLength(int repeatIndex, int address, int numChars)
return numWords;
}
/**
* Calculates the length in words of a numeric POCSAG message, given the address
* and the number of characters to be transmitted.
*/
size_t numericMessageLength(int repeatIndex, int address, int numChars)
{
size_t numWords = 0;
//Padding before address word.
numWords += addressOffset(address);
//Address word itself
numWords++;
//numChars * 7 bits per character / 20 bits per word, rounding up
numWords += (numChars * NUMERIC_BITS_PER_DIGIT + (NUMERIC_BITS_PER_WORD - 1)) / NUMERIC_BITS_PER_WORD;
//Idle word representing end of message
numWords++;
//Pad out last batch with idles
numWords += POCSAG_BATCH_SIZE - (numWords % POCSAG_BATCH_SIZE);
//Batches consist of 16 words each and are preceded by a sync word.
//So we add one word for every 16 message words
numWords += numWords / POCSAG_BATCH_SIZE;
//Preamble of 576 alternating 1,0,1,0 bits before the message
//Even though this comes first, we add it to the length last so it
//doesn't affect the other word-based calculations
if (repeatIndex == 0)
numWords += POCSAG_PREAMBLE_LENGTH / 32;
return numWords;
}

View file

@ -63,12 +63,16 @@ uint32_t POCSAGTransmitter::getTransmitWord()
return transmitterData[ transmitterOffset ];
}
void POCSAGTransmitter::queuePage(uint32_t ric, uint8_t functionBit, char *text)
{
this->queuePage(ric, functionBit, false, text);
}
void POCSAGTransmitter::queuePage(uint32_t ric, uint8_t functionBit, bool numeric, char *text)
{
size_t beforeLength = transmitterLength + 0;
size_t messageLength = textMessageLength(queueCount, ric, strlen(text));
// uint32_t *transmission = (uint32_t *)malloc(sizeof(uint32_t) * messageLength);
uint32_t *encodingBufferPointer = encodingBuffer;
encodeTransmission(queueCount, ric, functionBit, text, encodingBufferPointer);
encodeTransmission(queueCount, ric, functionBit, numeric, text, encodingBufferPointer);
transmitterLength += messageLength;
#ifdef POCSAG_DEBUG

View file

@ -69,6 +69,7 @@ static void redirectHomeResponse(AsyncWebServerRequest *request, String location
uint32_t address = 0;
int ric = -1;
int fun = 0;
bool numeric;
String message;
//
if (request->hasArg("text")) {
@ -82,13 +83,16 @@ static void redirectHomeResponse(AsyncWebServerRequest *request, String location
if (request->hasArg("fun")) {
fun = request->arg("fun").toInt();
}
if (request->hasArg("numeric")) {
numeric = true;
}
Serial.printf("RIC: %d -> %d MSG: %s\n\n", ric, address, message);
if (ric > 0) {
char *messageText = new char[message.length() + 1]{};
message.toCharArray(messageText, message.length() + 1);
// messageText[ message.length() ] = 0x20;
txControllerInsert(address, fun + 0, messageText);
txControllerInsert(address, fun + 0, numeric, messageText);
delete []messageText;
if (request->hasArg("addtx")) {