diff --git a/data/index.html b/data/index.html
index 9e49fe7..6023998 100644
--- a/data/index.html
+++ b/data/index.html
@@ -42,6 +42,9 @@
+
+
+
diff --git a/include/main.h b/include/main.h
index e9ef1f4..6991e04 100644
--- a/include/main.h
+++ b/include/main.h
@@ -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();
diff --git a/include/pocsag_encoder.h b/include/pocsag_encoder.h
index 14c154d..314a5b7 100644
--- a/include/pocsag_encoder.h
+++ b/include/pocsag_encoder.h
@@ -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);
\ No newline at end of file
+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);
\ No newline at end of file
diff --git a/include/pocsag_transmitter.h b/include/pocsag_transmitter.h
index d7c0ca9..56a5ab4 100644
--- a/include/pocsag_transmitter.h
+++ b/include/pocsag_transmitter.h
@@ -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();
diff --git a/src/main.cpp b/src/main.cpp
index 7f9feab..5da5f07 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -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();
diff --git a/src/pocsag_encoder.cpp b/src/pocsag_encoder.cpp
index eddf827..493977d 100644
--- a/src/pocsag_encoder.cpp
+++ b/src/pocsag_encoder.cpp
@@ -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;
+}
\ No newline at end of file
diff --git a/src/pocsag_transmitter.cpp b/src/pocsag_transmitter.cpp
index 8f060eb..c0c7683 100644
--- a/src/pocsag_transmitter.cpp
+++ b/src/pocsag_transmitter.cpp
@@ -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
diff --git a/src/webserver.cpp b/src/webserver.cpp
index 76c40e0..8413d30 100644
--- a/src/webserver.cpp
+++ b/src/webserver.cpp
@@ -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")) {