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")) {