Merge pull request #189 from cuddlycheetah/feature-pocsag

Feature pocsag to master
pull/254/head
F5OEO 2020-09-09 10:33:32 +02:00 zatwierdzone przez GitHub
commit b589b3c7dc
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
1 zmienionych plików z 237 dodań i 205 usunięć

Wyświetl plik

@ -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 <stdio.h>
@ -34,11 +35,10 @@ Fork and modification for rpitx (c)(F5OEO 2018)
#include <unistd.h>
#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<Size;i++)
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 < Size; i++)
{
if (!Inverted)
Message[i] = ~Message[i];
for (int j = 31; j >= 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;i<FiFoSize;i++)
if (debug)
fprintf(stderr, "\n");
}
if (debug)
fprintf(stderr, "Symbols=%d\n", Sym);
fsktest.SetSymbols(TabSymbol, Sym);
/*for(i=0;i<FiFoSize;i++)
{
TabSymbol[i]=1;
}
fsktest.SetSymbols(TabSymbol,FiFoSize);
sleep(1);*/
fsktest.stop();
fsktest.stop();
}
void print_usage(void)
{
fprintf(stderr,\
"\npocsag -%s\n\
fprintf(stderr,
"\npocsag -%s\n\
Usage:\npocsag [-f Frequency] [-i] [-r Rate]\n\
-f float central frequency Hz(50 kHz to 1500 MHz),\n\
-r int baud rate (512, 1200 or 2400. Default 1200 bps),\n\
-b int function bits (0-3. Default 3),\n\
-n use numeric messages,\n\
-t int repeat messages X times (Default 4)\n\
-i invert the modulation polarity,\n\
-d debug,\n\
-? help (this help).\n\
\n",\
PROGRAM_VERSION);
\n",
PROGRAM_VERSION);
} /* end function print_usage */
int main(int argc, char* argv[]) {
int main(int argc, char *argv[]) {
//Read in lines from STDIN.
//Lines are in the format of address:message
//The program will encode transmissions for each message, writing them
@ -537,82 +568,83 @@ int main(int argc, char* argv[]) {
//from 1-10 seconds in length, to act as a simulated "delay".
int a;
int anyargs = 1;
uint64_t SetFrequency=466230000L;
uint64_t SetFrequency = 466230000L;
int SetRate = 1200;
int SetFunctionBits = 3;
int REPEAT_COUNT = 4;
bool SetInverted = false;
bool debug = false;
while(1)
{
a = getopt(argc, argv, "b:dnf:ir:");
if(a == -1)
{
if(anyargs) break;
else a='h'; //print usage and exit
}
anyargs = 1;
while (1) {
a = getopt(argc, argv, "b:dnf:ir:t:");
switch(a)
{
case 'n': // Numeric messages
numeric = true;
if (a == -1) {
if (anyargs)
break;
else
a = 'h'; //print usage and exit
}
anyargs = 1;
switch (a) {
case 'n': // Numeric messages
numeric = true;
break;
case 't':
REPEAT_COUNT = atoi(optarg);
break;
case 'd': // Debug
debug = true;
break;
case 'd': // Debug
debug = true;
break;
case 'f': // Frequency
SetFrequency = atof(optarg);
break;
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 '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);
}