Commit 78e6f029 authored by Mathis Schmieder's avatar Mathis Schmieder
Browse files

Use some of RWTH Aachen's code for POCSAG generation; allow numerical messages to be generated

parent 6831a132
......@@ -37,20 +37,28 @@ typedef struct {
uint32_t batch2[16];
} Pocsagmsg_s; // end struct
#define POC_BITS_PER_CW 20;
#define POC_BITS_PER_CHAR 7;
#define POC_BITS_PER_DIGIT 4;
// data
Pocsagmsg_s Pocsagmsg;
// public functions
int CreatePocsag(long int, int, char *, int, int);
int CreatePocsag(long int, int, char *, int);
int GetPocsagSize();
void * GetPocsagMsgPointer();
// private functions
int _pocsag_size;
uint32_t pocsag_createcrc(uint32_t);
unsigned char pocsag_flip7charbitorder (unsigned char);
void pocsag_replaceline (Pocsagmsg_s *, int, uint32_t);
uint32_t pocsag_encodeMCW(uint32_t msg);
uint32_t pocsag_encodeACW(long int addr, int func);
char pocsag_encodeChar(char ch);
char pocsag_encodeDigit(char ch);
void pocsag_encodeNumber(uint32_t addr, uint8_t func, char * text);
void pocsag_encodeText(int addr, int func, char * text);
uint32_t pocsag_crc(uint32_t cw);
#endif /* POCSAG_H_ */
......@@ -134,7 +134,7 @@ int parse_uart(uint8_t *msg)
uint8_t function;
char message[40];
char *p;
p = strtok(msg, ":");
p = strtok((char *)msg, ":");
if (!p)
err = -1;
......@@ -166,7 +166,7 @@ int parse_uart(uint8_t *msg)
return err;
}
CreatePocsag(address,function,&message, 0, 1);
CreatePocsag(address,function,&message,1);
rfm69_send((uint8_t *)GetPocsagMsgPointer(), GetPocsagSize());
rfm69_sleep();
return err;
......
......@@ -10,6 +10,22 @@
#include <string.h>
#include <Pocsag.h>
static char isotab[] = { 0x00, 0x40, 0x20, 0x60, 0x10, 0x50, 0x30, 0x70, 0x08, 0x48, 0x28, 0x68, 0x18,
0x58, 0x38, 0x78, 0x04, 0x44, 0x24, 0x64, 0x14, 0x54, 0x34, 0x74, 0x0c, 0x4c, 0x2c, 0x6c, 0x1c, 0x5c, 0x3c,
0x7c, 0x02, 0x42, 0x22, 0x62, 0x12, 0x52, 0x32, 0x72, 0x0a, 0x4a, 0x2a, 0x6a, 0x1a, 0x5a, 0x3a, 0x7a, 0x06,
0x46, 0x26, 0x66, 0x16, 0x56, 0x36, 0x76, 0x0e, 0x4e, 0x2e, 0x6e, 0x1e, 0x5e, 0x3e, 0x7e, 0x01, 0x41, 0x21,
0x61, 0x11, 0x51, 0x31, 0x71, 0x09, 0x49, 0x29, 0x69, 0x19, 0x59, 0x39, 0x79, 0x05, 0x45, 0x25, 0x65, 0x15,
0x55, 0x35, 0x75, 0x0d, 0x4d, 0x2d, 0x6d, 0x1d, 0x5d, 0x3d, 0x7d, 0x03, 0x43, 0x23, 0x63, 0x13, 0x53, 0x33,
0x73, 0x0b, 0x4b, 0x2b, 0x6b, 0x1b, 0x5b, 0x3b, 0x7b, 0x07, 0x47, 0x27, 0x67, 0x17, 0x57, 0x37, 0x77, 0x0f,
0x4f, 0x2f, 0x6f, 0x1f, 0x5f, 0x3f, 0x7f, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x01, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
0x3a, 0x3a, 0x3a, 0x6d, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a,
0x3a, 0x3a, 0x3a, 0x1d, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x5d, 0x3a, 0x3a, 0x3f, 0x3a, 0x3a, 0x3a, 0x3a, 0x6f,
0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x1f,
0x3a, 0x3a, 0x3a, 0x3a, 0x3a, 0x5f, 0x3a, 0x3a, 0x3a };
int GetPocsagSize() {
return(_pocsag_size);
}; // end method Get Size
......@@ -19,27 +35,8 @@ void * GetPocsagMsgPointer() {
return((void *) &Pocsagmsg);
}; // end method Get Msg Pointer
int CreatePocsag (long int address, int source, char * text, int option_batch2, int option_invert) {
int CreatePocsag (long int address, int function, char * text, int option_invert) {
int txtlen;
unsigned char c; // tempory var for character being processed
int stop; /// temp var
char lastchar; // memorise last char of input text. (changed at the beginning of the function
// reset when done
unsigned char txtcoded[56]; // encoded text can be up to 56 octets
int txtcodedlen;
// local vars to encode address line
int currentframe;
uint32_t addressline;
// counters to encode text
int bitcount_in, bitcount_out, bytecount_in, bytecount_out;
// table to convert size to n-times 1 bit mask
const unsigned char size2mask[7] = {0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f};
// some sanity checks
txtlen=strlen(text);
......@@ -58,12 +55,7 @@ int CreatePocsag (long int address, int source, char * text, int option_batch2,
}; // end if
// source is 2 bits
if ((source < 0) || (source > 3)) {
return(POCSAG_FAILED);
}; // end if
// option "batch2" goes from 0 to 2
if ((option_batch2 < 0) || (option_batch2 > 2)) {
if ((function < 0) || (function > 3)) {
return(POCSAG_FAILED);
}; // end if
......@@ -74,51 +66,15 @@ int CreatePocsag (long int address, int source, char * text, int option_batch2,
/* TODO check if needed
// replace terminating \0 by EOT
lastchar=text[txtlen];
text[txtlen]=0x04; // EOT (end of transmission)
txtlen++; // increase size by 1 (for EOT)
*/
// create packet
// A POCSAG packet has the following structure:
// - transmission syncronisation (576 bit "01" pattern)
// one or multiple "batches".
// - a batch starts with a 32 frame syncronisation pattern
// - followed by 8 * 2 * 32 bits of actual data or "idle" information:
// the "POCSAG message"
// A POSAG message consists of one or multiple batches. A batch is 64
// octets: 8 frames of 2 codewords of 32 bits each
// This application can generate one pocsag-message containing one
// text-message of up to 40 characters + a terminating EOT
// (end-of-transmission, ASCII 0x04).
// the message (i.e. the destination-address + the text messate itself)
// always starts in the first batch but not necessairy at the beginning
// of the batch. The start-position of message inside the batch is
// determined by (the 3 lowest bits of) the address.
// if the message reaches the end of the first batch, if overflows
// into the 2nd batch.
// As a result, we do not know beforehand if the POCSAG frame will be
// one or two POCSAG message, so we will initialise both batches. If
// -and the end of the process- it turns out that the message fits into
// one single batch, there are three options:
// - truncate the message and send a POCSAG-frame containing one POCSAG-
// message
// - repeat the first batch in the 2nd batch and send both batches
// - leave the 2nd batch empty (i.e. filled with the "idle" pattern) and
// send that
// note that before actually transmitting the pattern, all bits are inverted 0-to-1
// for the parts of the frame with a fixed text (syncronisation pattern), the
// bits are inverted in the code
// part 0.1: frame syncronisation pattern
if (option_invert == 0) {
memset(Pocsagmsg.sync,0xaa,72);
......@@ -140,7 +96,6 @@ int CreatePocsag (long int address, int source, char * text, int option_batch2,
if (option_invert == 1) {
Pocsagmsg.synccw1[0] ^= 0xff; Pocsagmsg.synccw1[1] ^= 0xff;
Pocsagmsg.synccw1[2] ^= 0xff; Pocsagmsg.synccw1[3] ^= 0xff;
Pocsagmsg.synccw2[0] ^= 0xff; Pocsagmsg.synccw2[1] ^= 0xff;
Pocsagmsg.synccw2[2] ^= 0xff; Pocsagmsg.synccw2[3] ^= 0xff;
};
......@@ -151,186 +106,12 @@ int CreatePocsag (long int address, int source, char * text, int option_batch2,
Pocsagmsg.batch2[l]=0x7a89c197;
}; // end for
// part 1:
// address line:
// the format of a pocsag codeword containing an address is as follows:
// 0aaaaaaa aaaaaaaa aaassccc ccccccp
// "0" = fixed (indicating an codeword containing an address)
// a[18] = 18 upper bits of the address
// s[2] = 2 bits source (encoding 4 different "address-spaces")
// c[10] = 10 bits of crc
// p = parity bit
// the lowest 3 bits of address are not encoded in the address line as
// found in the POCSAG message. However, they are used to determine
// where in the POCSAG-message, the message is started
currentframe = ((address & 0x7)<<1);
addressline=address >> 3;
// add address source
addressline<<=2;
addressline += source;
//// replace address line
pocsag_replaceline(&Pocsagmsg,currentframe,pocsag_createcrc(addressline<<11));
// part 2.1:
// convert text to podsag format
// POCSAG codewords containing a message have the following format:
// 1ttttttt tttttttt tttttccc ccccccp
// "1" = fixed (indicating a codeword containing an encoded message)
// t[20] = 20 bits of text
// c[10] = 10 bits CRC
// p = parity bit
// text-messages are encoded using 7 bits ascii, IN INVERTED BITORDER (MSB first)
// As up to 20 bits can be stored in a codeword, each codeword contains 3 chars
// minus 1 bit. Hence, the text is "wrapped" around to the next codewords
// The message is first encoded and stored into a tempory array
// init vars
// clear txtcoded
memset(txtcoded,0x00,56); // 56 octets, all "0x00"
bitcount_out = 7; // 1 char can contain up to 7 bits
bytecount_out = 0;
bitcount_in = 7;
bytecount_in=0;
c=pocsag_flip7charbitorder(text[0]); // start with first char
txtcodedlen=0;
txtcoded[0] = 0x80; // output, initialise as empty with leftmost bit="1"
// loop for all chars
stop=0;
while (!stop) {
int bits2copy;
unsigned char t; // tempory var bits to be copied
// how many bits to copy?
// minimum of "bits available for input" and "bits that can be stored"
if (bitcount_in > bitcount_out) {
bits2copy = bitcount_out;
} else {
bits2copy = bitcount_in;
}; // end if
// copy "bits2copy" bits, shifted the left if needed
t = c & (size2mask[bits2copy-1] << (bitcount_in - bits2copy) );
// where to place ?
// move left to write if needed
if (bitcount_in > bitcount_out) {
// move to right and export
t >>= (bitcount_in-bitcount_out);
} else if (bitcount_in < bitcount_out) {
// move to left
t <<= (bitcount_out-bitcount_in);
}; // end else - if
// now copy bits
txtcoded[txtcodedlen] |= t;
// decrease bit counters
bitcount_in -= bits2copy;
bitcount_out -= bits2copy;
// outbound queue is full
if (bitcount_out == 0) {
// go to next position in txtcoded
bytecount_out++;
txtcodedlen++;
// data is stored in codeblocks, each containing 20 bits of usefull data
// The 1st octet of every codeblock always starts with a "1" (leftmost char)
// to indicate the codeblock contains data
// The 1st octet of a codeblock contains 8 bits: "1" + 7 databits
// The 2nd octet of a codeblock contains 8 bits: 8 databits
// the 3th octet of a codeblock contains 5 bits: 5 databits
if (bytecount_out == 1) {
// 2nd char of codeword:
// all 8 bits used, init with all 0
txtcoded[txtcodedlen]=0x00;
bitcount_out=8;
} else if (bytecount_out == 2) {
// 3th char of codeword:
// only 5 bits used, init with all 0
txtcoded[txtcodedlen]=0x00;
bitcount_out=5;
} else if (bytecount_out >= 3) {
// 1st char of codeword (wrap around of "bytecount_out" value)
// init with leftmost bit as "1"
txtcoded[txtcodedlen]=0x80;
bitcount_out = 7;
// wrap around "bytecount_out" value
bytecount_out = 0;
}; // end elsif - elsif - if
}; // end if
// ingress queue is empty
if (bitcount_in == 0) {
bytecount_in++;
// any more text ?
if (bytecount_in < txtlen) {
// reinit vars
c=pocsag_flip7charbitorder(text[bytecount_in]);
bitcount_in=7; // only use 7 bits of every char
} else {
// no more text
// done, so ... stop!!
stop=1;
continue;
}; // end else - if
}; // end if
}; // end while (stop)
// increase txtcodedlen. Was used as index (0 to ...) above. Add one to
// make it indicate a lenth
txtcodedlen++;
// Part 2.2: copy coded message into pocsag message
// now insert text message
for (int l=0; l < txtcodedlen; l+= 3) {
uint32_t t;
// move up to next frame
currentframe++;
// copying is done octet per octet to be architecture / endian independant
t = (uint32_t) txtcoded[l] << 24; // copy octet to bits 24 to 31 in 32 bit struct
t |= (uint32_t) txtcoded[l+1] << 16; // copy octet to bits 16 to 23 in 32 bit struct
t |= (uint32_t) txtcoded[l+2] << 11; // copy octet to bits 11 to 15 in 32 bit struct
pocsag_replaceline(&Pocsagmsg,currentframe,pocsag_createcrc(t));
// note: move up 3 chars at a time (see "for" above)
}; // end for
// Encode text or numbers
// TODO check for proper function byte meanings.
if ( function == 3 )
pocsag_encodeText(address, function, text);
if ( function == 0 )
pocsag_encodeNumber(address, function, text);
// invert bits if needed
if (option_invert) {
......@@ -346,8 +127,6 @@ int CreatePocsag (long int address, int source, char * text, int option_batch2,
/// convert to make endian/architecture independant
for (int l=0; l<16; l++) {
int32_t t1;
......@@ -384,35 +163,7 @@ int CreatePocsag (long int address, int source, char * text, int option_batch2,
}; // end for
// done
// return
// reset last char in input char (was overwritten at the beginning of the function)
text[txtlen]=lastchar;
// If only one single batch used
if (currentframe < 16) {
// batch2 option:
// 0: truncate to one batch
// 1: copy batch1 to batch2
// 2: leave batch2 as "idle"
if (option_batch2 == 0) {
// done. set length to one single batch (140 octets)
_pocsag_size=140;
return(POCSAG_SUCCESS);
} else if (option_batch2 == 1) {
memcpy(Pocsagmsg.batch2,Pocsagmsg.batch1,64); // 16 codewords of 32 bits
}; // end of
// return for (option_batch2 == 1) or (option_batch2 == 2)
// set length to 2 batches (208 octets)
_pocsag_size=208;
return(POCSAG_SUCCESS);
};
// TODO check how many batches need to be sent
// more then one batch found
// length = 2 batches (208 octets)
......@@ -439,72 +190,168 @@ void pocsag_replaceline(Pocsagmsg_s *msg, int line, uint32_t val) {
}; // end replaceline
unsigned char pocsag_flip7charbitorder(unsigned char c_in) {
char c_out;
uint32_t pocsag_crc(uint32_t cw) {
uint32_t crc;
uint32_t d;
uint32_t m;
char p;
// init
c_out=0x00;
// crc
crc = cw;
d = 0xED200000;
// copy and shift 6 bits
for (int l=0; l<6; l++) {
if (c_in & 0x01) {
c_out |= 1;
}; // end if
for (m = 0x80000000; (m & 0x400) == 0; m >>= 1) {
// m ist Bitmaske mit nur einer 1, die vom MSB bis vor den Anfang
// des (CRC+Praitt) bereichs luft, d.h. bis Bit 11 einschl.
if ((crc & m) != 0)
crc ^= d;
// shift data
c_out <<= 1; c_in >>= 1;
}; // end for
d >>= 1;
}
// copy 7th bit, do not shift
if (c_in & 0x01) {
c_out |= 1;
}; // end if
cw |= crc;
return(c_out);
// parity
}; // end flip2charbitorder
p = (char) (((cw >> 24) & 0xff) ^ ((cw >> 16) & 0xff) ^ ((cw >> 8) & 0xff) ^ (cw & 0xff));
uint32_t pocsag_createcrc(uint32_t in) {
p ^= (p >> 4);
p ^= (p >> 2);
p ^= (p >> 1);
p &= 0x01;
// local vars
uint32_t cw; // codeword
return cw | p;
}
uint32_t local_cw = 0;
uint32_t parity = 0;
uint32_t pocsag_encodeMCW(uint32_t msg)
{
return (0x80000000 | ((msg & 0x000fffff) << 11));
}
// init cw
cw=in;
uint32_t pocsag_encodeACW(long int addr, int func)
{
return (((addr & 0x001ffff8) << 10) | ((func & 0x00000003) << 11));
}
// move up 11 bits to make place for crc + parity
local_cw=in; /* bch */
char pocsag_encodeChar(char ch)
{
return isotab[ch & 0xff];
}
// calculate crc
for (int bit=1; bit<=21; bit++, cw <<= 1) {
if (cw & 0x80000000) {
cw ^= 0xED200000;
}; // end if
}; // end for
char pocsag_encodeDigit(char ch)
{
char mirrorTab[] = { 0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e, 0x01, 0x09 };
local_cw |= (cw >> 21);
if (ch >= '0' && ch <= '9')
return mirrorTab[ch - '0'];
// parity
cw=local_cw;
switch (ch) {
case ' ':
return 0x03;
for (int bit=1; bit<=32; bit++, cw<<=1) {
if (cw & 0x80000000) {
parity++;
}; // end if
}; // end for
case 'U':
return 0x0d;
case '_':
return 0x0b;
// make even parity
if (parity % 2) {
local_cw++;
}; // end if
case '[':
return 0x0f;
case ']':
return 0x07;
}
return 0x05;
}
void pocsag_encodeNumber(uint32_t addr, uint8_t func, char * text) {
uint32_t msg = 0;
uint8_t msgBitsLeft;
uint8_t framePos = ((addr & 0x7)<<1);
// Adress-Codewort erzeugen und im Puffer speichern.
pocsag_replaceline(&Pocsagmsg,framePos,pocsag_crc(pocsag_encodeACW(addr, func)));
framePos++;
// Komplette Nachricht codieren und speichern.
msgBitsLeft = POC_BITS_PER_CW;
for (int i = 0; i < strlen(text); i++) {
char ch = pocsag_encodeDigit(text[i]);
msg <<= POC_BITS_PER_DIGIT;
msg |= ch;
msgBitsLeft -= POC_BITS_PER_DIGIT;
if (msgBitsLeft == 0) {
pocsag_replaceline(&Pocsagmsg,framePos,pocsag_crc(pocsag_encodeMCW(msg)));
framePos++;
msgBitsLeft = POC_BITS_PER_CW;
}
}
// Wenn das letzte Codewort nicht komplett ist, wird es mit Spaces
// aufgefuellt.
uint8_t bitpercw = POC_BITS_PER_CW;
if ( msgBitsLeft != bitpercw ) {
while (msgBitsLeft > 0) {
msg <<= POC_BITS_PER_DIGIT;
msg |= 0x03; /* Space */
msgBitsLeft -= POC_BITS_PER_DIGIT;
}
pocsag_replaceline(&Pocsagmsg,framePos,pocsag_crc(pocsag_encodeMCW(msg)));
}
}
void pocsag_encodeText(int addr, int func, char * text) {
uint32_t msg = 0;
uint8_t msgBitsLeft;
uint8_t framePos = ((addr & 0x7)<<1);
// Adress-Codewort erzeugen und im Puffer speichern.
pocsag_replaceline(&Pocsagmsg,framePos,pocsag_crc(pocsag_encodeACW(addr, func)));
framePos++;
// Komplette Nachricht codieren und speichern.
msgBitsLeft = POC_BITS_PER_CW;
uint8_t bitpercw = POC_BITS_PER_CW;
uint8_t bitperchar = POC_BITS_PER_CHAR;
for (int i = 0; i < strlen(text); i++) {
char ch = pocsag_encodeChar(text[i]);
if (msgBitsLeft >= bitperchar) {
msg <<= POC_BITS_PER_CHAR;
msg |= ch;
msgBitsLeft -= POC_BITS_PER_CHAR;
if (msgBitsLeft == 0) {
pocsag_replaceline(&Pocsagmsg,framePos,pocsag_crc(pocsag_encodeMCW(msg)));
framePos++;
msgBitsLeft = POC_BITS_PER_CW;
}
} else {
msg <<= msgBitsLeft;
msg |= ch >> (bitperchar - msgBitsLeft);
pocsag_replaceline(&Pocsagmsg,framePos,pocsag_crc(pocsag_encodeMCW(msg)));
framePos++;
// done
return(local_cw);
msg = ch;
msgBitsLeft = bitpercw - bitperchar + msgBitsLeft;
}
}
}; // end createcrc
if (msgBitsLeft != bitpercw) {
msg <<= msgBitsLeft;