diff --git a/src/pocsag/pocsag.cpp b/src/pocsag/pocsag.cpp index 8922b9d..c3dc487 100644 --- a/src/pocsag/pocsag.cpp +++ b/src/pocsag/pocsag.cpp @@ -23,6 +23,7 @@ SOFTWARE. Fork and modification for rpitx (c)(F5OEO 2018) ** 11.09.2019 : Added Numeric Pager support by cuddlycheetah (github.com/cuddlycheetah) +** 14.10.2019 : Added Repeating Transmission + Single Preamble Mode */ #include @@ -34,11 +35,10 @@ Fork and modification for rpitx (c)(F5OEO 2018) #include #include "../librpitx/src/librpitx.h" -#define PROGRAM_VERSION "0.2" +#define PROGRAM_VERSION "0.3" //Check out main() at the bottom of the file //You can modify MIN_DELAY and MAX_DELAY to fit your needs. - //Check out https://en.wikipedia.org/wiki/POCSAG //Also see http://www.itu.int/dms_pubrec/itu-r/rec/m/R-REC-M.584-2-199711-I!!PDF-E.pdf //They'll be handy when trying to understand this stuff. @@ -63,13 +63,11 @@ Fork and modification for rpitx (c)(F5OEO 2018) //to synchronize with the transmitter #define PREAMBLE_LENGTH 576 - //These bits appear as the first bit of a word, 0 for an address word and //one for a data word #define FLAG_ADDRESS 0x000000 #define FLAG_MESSAGE 0x100000 - //The last two bits of an address word's data represent the data type //0x3 for text, and 0x0 for numeric. #define FLAG_TEXT_DATA 0x3 @@ -102,7 +100,8 @@ Fork and modification for rpitx (c)(F5OEO 2018) * See https://en.wikipedia.org/wiki/Cyclic_redundancy_check#Computation * for more information. */ -uint32_t crc(uint32_t inputMsg) { +uint32_t crc(uint32_t inputMsg) +{ //Align MSB of denominatorerator with MSB of message uint32_t denominator = CRC_GENERATOR << 20; @@ -110,12 +109,14 @@ uint32_t crc(uint32_t inputMsg) { uint32_t msg = inputMsg << CRC_BITS; //We iterate until denominator has been right-shifted back to it's original value. - for (int column = 0; column <= 20; column++) { + for (int column = 0; column <= 20; column++) + { //Bit for the column we're aligned to int msgBit = (msg >> (30 - column)) & 1; //If the current bit is zero, we don't modify the message this iteration - if (msgBit != 0) { + if (msgBit != 0) + { //While we would normally subtract in long division, we XOR here. msg ^= denominator; } @@ -128,12 +129,12 @@ uint32_t crc(uint32_t inputMsg) { return msg & 0x3FF; } - /** * Calculates the even parity bit for a message. * If the number of bits in the message is even, return 0, else return 1. */ -uint32_t parity(uint32_t x) { +uint32_t parity(uint32_t x) +{ //Our parity bit uint32_t p = 0; @@ -141,7 +142,8 @@ uint32_t parity(uint32_t x) { //xoring two one-bits will cancel out and leave a zero bit. Thus //xoring any even number of one bits will result in zero, and xoring //any odd number of one bits will result in one. - for (int i = 0; i < 32; i++) { + for (int i = 0; i < 32; i++) + { p ^= (x & 1); x >>= 1; } @@ -151,7 +153,8 @@ uint32_t parity(uint32_t x) { /** * Encodes a 21-bit message by calculating and adding a CRC code and parity bit. */ -uint32_t encodeCodeword(uint32_t msg) { +uint32_t encodeCodeword(uint32_t msg) +{ uint32_t fullCRC = (msg << CRC_BITS) | crc(msg); uint32_t p = parity(fullCRC); return (fullCRC << 1) | p; @@ -165,7 +168,8 @@ uint32_t encodeCodeword(uint32_t msg) { * initial_offset indicates which word in the current batch the function is * beginning at, so that it can insert SYNC words at appropriate locations. */ -uint32_t encodeASCII(uint32_t initial_offset, char* str, uint32_t* out) { +uint32_t encodeASCII(uint32_t initial_offset, char *str, uint32_t *out) +{ //Number of words written to *out uint32_t numWordsWritten = 0; @@ -178,15 +182,18 @@ uint32_t encodeASCII(uint32_t initial_offset, char* str, uint32_t* out) { //Position of current word in the current batch uint32_t wordPosition = initial_offset; - while (*str != 0) { + while (*str != 0) + { unsigned char c = *str; str++; //Encode the character bits backwards - for (int i = 0; i < TEXT_BITS_PER_CHAR; i++) { + for (int i = 0; i < TEXT_BITS_PER_CHAR; i++) + { currentWord <<= 1; currentWord |= (c >> i) & 1; currentNumBits++; - if (currentNumBits == TEXT_BITS_PER_WORD) { + if (currentNumBits == TEXT_BITS_PER_WORD) + { //Add the MESSAGE flag to our current word and encode it. *out = encodeCodeword(currentWord | FLAG_MESSAGE); out++; @@ -195,7 +202,8 @@ uint32_t encodeASCII(uint32_t initial_offset, char* str, uint32_t* out) { numWordsWritten++; wordPosition++; - if (wordPosition == BATCH_SIZE) { + if (wordPosition == BATCH_SIZE) + { //We've filled a full batch, time to insert a SYNC word //and start a new one. *out = SYNC; @@ -208,7 +216,8 @@ uint32_t encodeASCII(uint32_t initial_offset, char* str, uint32_t* out) { } //Write remainder of message - if (currentNumBits > 0) { + if (currentNumBits > 0) + { //Pad out the word to 20 bits with zeroes currentWord <<= 20 - currentNumBits; *out = encodeCodeword(currentWord | FLAG_MESSAGE); @@ -216,7 +225,8 @@ uint32_t encodeASCII(uint32_t initial_offset, char* str, uint32_t* out) { numWordsWritten++; wordPosition++; - if (wordPosition == BATCH_SIZE) { + if (wordPosition == BATCH_SIZE) + { //We've filled a full batch, time to insert a SYNC word //and start a new one. *out = SYNC; @@ -230,36 +240,39 @@ uint32_t encodeASCII(uint32_t initial_offset, char* str, uint32_t* out) { } // Char Translationtable -char* mirrorTab = new char[10]{ 0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e, 0x01, 0x09 }; -char encodeDigit(char ch) { +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; + switch (ch) + { + case ' ': + return 0x03; - case 'u': - case 'U': - return 0x0d; + case 'u': + case 'U': + return 0x0d; - case '-': - case '_': - return 0x0b; + case '-': + case '_': + return 0x0b; - case '(': - case '[': - return 0x0f; + case '(': + case '[': + return 0x0f; - case ')': - case ']': - return 0x07; + case ')': + case ']': + return 0x07; } return 0x05; } -uint32_t encodeNumeric(uint32_t initial_offset, char* str, uint32_t* out) { +uint32_t encodeNumeric(uint32_t initial_offset, char *str, uint32_t *out) +{ //Number of words written to *out uint32_t numWordsWritten = 0; @@ -272,21 +285,24 @@ uint32_t encodeNumeric(uint32_t initial_offset, char* str, uint32_t* out) { //Position of current word in the current batch uint32_t wordPosition = initial_offset; - while (*str != 0) { + while (*str != 0) + { unsigned char c = *str; str++; //Encode the digit bits backwards - for (int i = 0; i < NUMERIC_BITS_PER_DIGIT; i++) { + 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); + digit = ((digit & 1) << 3) | + ((digit & 2) << 1) | + ((digit & 4) >> 1) | + ((digit & 8) >> 3); currentWord |= (digit >> i) & 1; currentNumBits++; - if (currentNumBits == NUMERIC_BITS_PER_WORD) { + if (currentNumBits == NUMERIC_BITS_PER_WORD) + { //Add the MESSAGE flag to our current word and encode it. *out = encodeCodeword(currentWord | FLAG_MESSAGE); out++; @@ -295,7 +311,8 @@ uint32_t encodeNumeric(uint32_t initial_offset, char* str, uint32_t* out) { numWordsWritten++; wordPosition++; - if (wordPosition == BATCH_SIZE) { + if (wordPosition == BATCH_SIZE) + { //We've filled a full batch, time to insert a SYNC word //and start a new one. *out = SYNC; @@ -308,7 +325,8 @@ uint32_t encodeNumeric(uint32_t initial_offset, char* str, uint32_t* out) { } //Write remainder of message - if (currentNumBits > 0) { + if (currentNumBits > 0) + { //Pad out the word to 20 bits with zeroes currentWord <<= 20 - currentNumBits; *out = encodeCodeword(currentWord | FLAG_MESSAGE); @@ -316,7 +334,8 @@ uint32_t encodeNumeric(uint32_t initial_offset, char* str, uint32_t* out) { numWordsWritten++; wordPosition++; - if (wordPosition == BATCH_SIZE) { + if (wordPosition == BATCH_SIZE) + { //We've filled a full batch, time to insert a SYNC word //and start a new one. *out = SYNC; @@ -329,7 +348,6 @@ uint32_t encodeNumeric(uint32_t initial_offset, char* str, uint32_t* out) { 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 @@ -337,7 +355,8 @@ uint32_t encodeNumeric(uint32_t initial_offset, char* str, uint32_t* out) { * which must precede the address word so that it is in the right spot. These * words will be filled with the idle value. */ -int addressOffset(int address) { +int addressOffset(int address) +{ return (address & 0x7) * FRAME_SIZE; } @@ -347,18 +366,20 @@ int addressOffset(int address) { * (*out) is the destination to which the transmission will be written. */ bool numeric = false; -void encodeTransmission(int address, int fb, char* message, uint32_t* out) { +void encodeTransmission(int repeatIndex, int address, int fb, char *message, uint32_t *out) +{ - //Encode preamble //Alternating 1,0,1,0 bits for 576 bits, used for receiver to synchronize //with transmitter - for (int i = 0; i < PREAMBLE_LENGTH / 32; i++) { - *out = 0xAAAAAAAA; - out++; - } + if (repeatIndex == 0) + for (int i = 0; i < PREAMBLE_LENGTH / 32; i++) + { + *out = 0xAAAAAAAA; + out++; + } - uint32_t* start = out; + uint32_t *start = out; //Sync *out = SYNC; @@ -366,7 +387,8 @@ void encodeTransmission(int address, int fb, char* message, uint32_t* out) { //Write out padding before adderss word int prefixLength = addressOffset(address); - for (int i = 0; i < prefixLength; i++) { + for (int i = 0; i < prefixLength; i++) + { *out = IDLE; out++; } @@ -375,26 +397,29 @@ void encodeTransmission(int address, int fb, char* message, uint32_t* out) { //The last two bits of word's data contain the message type (function bits) //The 3 least significant bits are dropped, as those are encoded by the //word's location. - *out = encodeCodeword( ((address >> 3) << 2) | fb); + *out = encodeCodeword(((address >> 3) << 2) | fb); out++; //Encode the message itself - if (numeric == true) { + if (numeric == true) + { out += encodeNumeric(addressOffset(address) + 1, message, out); - } else { + } + else + { out += encodeASCII(addressOffset(address) + 1, message, out); } - //Finally, write an IDLE word indicating the end of the message *out = IDLE; out++; - + //Pad out the last batch with IDLE to write multiple of BATCH_SIZE + 1 //words (+ 1 is there because of the SYNC words) size_t written = out - start; size_t padding = (BATCH_SIZE + 1) - written % (BATCH_SIZE + 1); - for (size_t i = 0; i < padding; i++) { + for (size_t i = 0; i < padding; i++) + { *out = IDLE; out++; } @@ -404,7 +429,8 @@ void encodeTransmission(int address, int fb, char* message, uint32_t* out) { * Calculates the length in words of a text POCSAG message, given the address * and the number of characters to be transmitted. */ -size_t textMessageLength(int address, int numChars) { +size_t textMessageLength(int repeatIndex, int address, int numChars) +{ size_t numWords = 0; //Padding before address word. @@ -414,8 +440,7 @@ size_t textMessageLength(int address, int numChars) { numWords++; //numChars * 7 bits per character / 20 bits per word, rounding up - numWords += (numChars * TEXT_BITS_PER_CHAR + (TEXT_BITS_PER_WORD - 1)) - / TEXT_BITS_PER_WORD; + numWords += (numChars * TEXT_BITS_PER_CHAR + (TEXT_BITS_PER_WORD - 1)) / TEXT_BITS_PER_WORD; //Idle word representing end of message numWords++; @@ -430,7 +455,8 @@ size_t textMessageLength(int address, int numChars) { //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 - numWords += PREAMBLE_LENGTH / 32; + if (repeatIndex == 0) + numWords += PREAMBLE_LENGTH / 32; return numWords; } @@ -439,7 +465,8 @@ size_t textMessageLength(int address, int numChars) { * 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 address, int numChars) { +size_t numericMessageLength(int repeatIndex, int address, int numChars) +{ size_t numWords = 0; //Padding before address word. @@ -449,8 +476,7 @@ size_t numericMessageLength(int address, int numChars) { 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; + numWords += (numChars * NUMERIC_BITS_PER_DIGIT + (NUMERIC_BITS_PER_WORD - 1)) / NUMERIC_BITS_PER_WORD; //Idle word representing end of message numWords++; @@ -465,71 +491,76 @@ size_t numericMessageLength(int address, int numChars) { //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 - numWords += PREAMBLE_LENGTH / 32; + if (repeatIndex == 0) + numWords += PREAMBLE_LENGTH / 32; return numWords; } -void SendFsk(uint64_t Freq,bool Inverted,int SR,bool debug,uint32_t *Message,int Size) +void SendFsk(uint64_t Freq, bool Inverted, int SR, bool debug, uint32_t *Message, int Size) { - - float Deviation=4500; - int FiFoSize=12000; - if(debug) fprintf(stderr,"Fifo Size = %d, Size = %d, Baud rate = %d\n",FiFoSize,Size,SR); - fskburst fsktest(Freq-Deviation,SR,Deviation*2,14,FiFoSize,1,0.0); - - unsigned char *TabSymbol=(unsigned char *)malloc(Size*32); - int Sym=0; - - for(int i=0;i= 0; j--) { - if(!Inverted) Message[i] = ~Message[i]; - for(int j=31;j>=0;j--) + TabSymbol[Sym] = (Message[i] >> j) & 0x1; + if (debug) { - TabSymbol[Sym]=(Message[i]>>j)&0x1; - if(debug) - { - fprintf(stderr,"%x",TabSymbol[Sym]); - if (j==16) fprintf(stderr," "); - } - Sym++; + fprintf(stderr, "%x", TabSymbol[Sym]); + if (j == 16) + fprintf(stderr, " "); } - if(debug) fprintf(stderr,"\n"); + Sym++; } - if(debug) fprintf(stderr,"Symbols=%d\n",Sym); - fsktest.SetSymbols(TabSymbol,Sym); - - /*for(i=0;i3) - { - fprintf(stderr,"Invalid function bits!"); - print_usage(); - exit(1); - } - break; + case 'f': // Frequency + SetFrequency = atof(optarg); + break; - case 'r': // Baud rate - SetRate = atoi(optarg); - switch(SetRate) - { - case 512: - case 1200: - case 2400: - break; - default: - fprintf(stderr,"Invalid baud rate!"); - print_usage(); - exit(1); - } - break; - - case 'i': // Invert the modulation polarity - SetInverted = true; - break; - - default: - print_usage(); - exit(1); - break; - }/* end switch a */ - }/* end while getopt() */ + case 'b': // Function bits + SetFunctionBits = -1; + sscanf(optarg, "%d", &SetFunctionBits); + if (SetFunctionBits < 0 || SetFunctionBits > 3) + { + fprintf(stderr, "Invalid function bits!"); + print_usage(); + exit(1); + } + break; + case 'r': // Baud rate + SetRate = atoi(optarg); + switch (SetRate) { + case 512: + case 1200: + case 2400: + break; + default: + fprintf(stderr, "Invalid baud rate!"); + print_usage(); + exit(1); + } + break; + case 'i': // Invert the modulation polarity + SetInverted = true; + break; + + default: + print_usage(); + exit(1); + break; + } + } dbg_setlevel(1); char line[65536]; char *endptr; srand(time(NULL)); - for (;;) { - if (fgets(line, sizeof(line), stdin) == NULL) { - //Exit on EOF - return 0; - } + int msgIndex = 0; + size_t completeLength = 0; + uint32_t *completeTransmission = + (uint32_t *)malloc(sizeof(uint32_t) * 0); + for (;;) { + if (fgets(line, sizeof(line), stdin) == NULL) + break; size_t colonIndex = 0; for (size_t i = 0; i < sizeof(line); i++) { @@ -626,48 +658,48 @@ int main(int argc, char* argv[]) { } } - int address = (int) strtol(line, &endptr, 10); - char* message = line + colonIndex + 1; + int address = (int)strtol(line, &endptr, 10); + char *message = line + colonIndex + 1; // If address is followed by a letter, this set the function bits - switch(*endptr) - { - case 'a': - case 'A': - SetFunctionBits = 0; - break; - - case 'b': - case 'B': - SetFunctionBits = 1; - break; - - case 'c': - case 'C': - SetFunctionBits = 2; - break; - - case 'd': - case 'D': - SetFunctionBits = 3; - break; - + switch (*endptr) { + case 'a': + case 'A': + SetFunctionBits = 0; + break; + case 'b': + case 'B': + SetFunctionBits = 1; + break; + case 'c': + case 'C': + SetFunctionBits = 2; + break; + case 'd': + case 'D': + SetFunctionBits = 3; + break; } - size_t messageLength = numeric - ? numericMessageLength(address, strlen(message)) - : textMessageLength(address, strlen(message)); + for (int x = 0; x < REPEAT_COUNT; x++) { + size_t messageLength = numeric + ? numericMessageLength(msgIndex, address, strlen(message)) + : textMessageLength(msgIndex, address, strlen(message)); - uint32_t* transmission = - (uint32_t*) malloc(sizeof(uint32_t) * messageLength+2); + uint32_t *transmission = + (uint32_t *)malloc(sizeof(uint32_t) * messageLength); - encodeTransmission(address, SetFunctionBits, message, transmission); - - SendFsk(SetFrequency,SetInverted,SetRate,debug,transmission,messageLength); - sleep(1); - - //Generate rand amount of silence. Silence is a sample with - //a value of 0. - - } + encodeTransmission(msgIndex, address, SetFunctionBits, message, transmission); + size_t beforeLength = completeLength + 0; + fprintf(stderr, "DEBUG DATA = I=%d P=%p T=%d L=%d\n", msgIndex, completeTransmission, completeLength, messageLength); + completeLength += messageLength; + completeTransmission = (uint32_t *)realloc(completeTransmission, sizeof(uint32_t) * completeLength); + for (size_t byteI = 0; byteI < messageLength; byteI++) { + completeTransmission[beforeLength + byteI] = transmission[byteI]; + } + // free(transmission); + msgIndex++; + } + } + SendFsk(SetFrequency, SetInverted, SetRate, debug, completeTransmission, completeLength); }