Commit ebee6b51 authored by Mathis, DB9MAT's avatar Mathis, DB9MAT
Browse files

Initial support for RustPager and DAPNET Integration; Removal of POCSAG packet creation

parent 5d530a14
/*
* RingBuffer.h
*
* Created on: Jan 20, 2017
* Author: mathis
*/
#ifndef RINGBUFFER_H_
#define RINGBUFFER_H_
#define BUFFER_FAIL 0
#define BUFFER_SUCCESS 1
#define BUFFER_SIZE 256
#define BUFFER_MASK (BUFFER_SIZE-1)
struct Buffer {
uint8_t data[BUFFER_SIZE];
uint8_t read;
uint8_t write;
};
#endif /* RINGBUFFER_H_ */
/*
* pocsag.h
*
* Created on: Nov 27, 2016
* Author: Mathis Schmieder (DB9MAT), db9mat@giev.de
*
* This library is heavily based upon the POCSAG library by ON1ARF
* https://github.com/on1arf/pocsag
* and on the SDRPager project by Ralf Wilke, DH3WR, et al.
* https://github.com/rwth-afu/SDRPager
*
* This library can be used in any non-commercial amateur radio project as long
* as the original authors and the source is distributed.
*/
#ifndef POCSAG_H_
#define POCSAG_H_
// enums:
typedef enum {
POCSAGRC_UNDETERMINED = 0, // Undetermined error
POCSAGRC_INVALIDADDRESS, // RETURN CODE FOR INVALID ADDRESS
POCSAGRC_INVALIDSOURCE, // RETURN CODE FOR INVALID ADDRESS SOURCE
POCSAGRC_INVALIDBATCH2OPT, // RETURN CODE FOR INVALUD "BATCH2" OPTION
POCSAGRC_INVALIDINVERTOPT // RETURN CODE FOR INVALID "INVERT" OPTION
} Pocsag_error;
typedef enum {
POCSAG_FAILED = 0, // failed
POCSAG_SUCCESS // success
} Pocsag_rc;
// structures
// a 2-batch pocsag message
typedef struct {
unsigned char sync[72];
// batch 1
unsigned char synccw1[4];
uint32_t batch1[16];
// batch 2
unsigned char synccw2[4];
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 GetPocsagSize();
void * GetPocsagMsgPointer();
// private functions
int _pocsag_size;
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_ */
......@@ -17,6 +17,7 @@
#define RFM69_H_
#include "stm32f4xx_hal.h"
#include "RingBuffer.h"
#define RFM69_MAX_FIFO 64 ///< Maximum bytes FIFO
......@@ -65,6 +66,7 @@ uint8_t rfm69_init(SPI_HandleTypeDef spiHandle, int highPowerDevice);
void rfm69_setCustomConfig(const uint8_t config[][2], unsigned int length);
int rfm69_setPowerDBm(int8_t dBm);
int rfm69_send(const void* data, uint32_t dataLength);
int rfm69_sendBuffer(struct Buffer* buffer);
int rfm69_receive(char* data, unsigned int dataLength);
RFM69Mode rfm69_setMode(RFM69Mode mode);
void rfm69_sleep();
......
......@@ -36,8 +36,8 @@
/* USER CODE BEGIN Includes */
#include "rfm69.h"
#include "pocsag.h"
#include <string.h>
#include "RingBuffer.h"
/* USER CODE END Includes */
/* Private variables ---------------------------------------------------------*/
......@@ -48,8 +48,7 @@ UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
char UART2_Data;
char UART_Buffer[255];
uint8_t UART_i = 0;
struct Buffer buffer = {{}, 0, 0};
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
......@@ -62,6 +61,19 @@ static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
uint8_t BufferIn(uint8_t byte)
{
uint8_t next = ((buffer.write + 1) & BUFFER_MASK);
if (buffer.read == next)
return BUFFER_FAIL;
buffer.data[buffer.write] = byte;
buffer.write = next;
return BUFFER_SUCCESS;
}
/* USER CODE END PFP */
/* USER CODE BEGIN 0 */
......@@ -110,85 +122,15 @@ int main(void)
}
}
int parse_uart(uint8_t *msg)
{
/*
* We expect a message in the form of
* P:destination address:function bits:message
*
* example:
* P:10791:3:NOOT NOOT!
*/
if (msg[0] != 'P')
{
// Wrong message format
char *answer = "EEE\r\n";
HAL_UART_Transmit(&huart2, (uint8_t*)answer, strlen(answer), 1000);
return -1;
}
// Create POCSAG message
int8_t err = 0;
uint32_t address;
uint8_t function;
char message[40];
char *p;
p = strtok((char *)msg, ":");
if (!p)
err = -1;
p = strtok(NULL, ":");
if (p)
address = atoi(p);
else
err = -1;
p = strtok(NULL, ":");
if (p)
function = atoi(p);
else
err = -1;
// get the message part
p = strtok(NULL, "");
if (p)
sprintf(message, "%s", p);
else
err = -1;
if (err < 0)
{
char *answer = "EEE\r\n";
HAL_UART_Transmit(&huart2, (uint8_t*)answer, strlen(answer), 1000);
return err;
}
CreatePocsag(address,function,&message,1);
rfm69_send((uint8_t *)GetPocsagMsgPointer(), GetPocsagSize());
rfm69_sleep();
return err;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
char UART_Aux[255];
if (huart->Instance == USART2)
{
if (UART2_Data != '\n')
{
UART_Buffer[UART_i] = UART2_Data;
UART_i++;
}
if (UART2_Data != 0x17) // end of transmission
BufferIn(UART2_Data ^= 0xff); // RFM69 needs the data inverted, hence XOR 0xFF
else
{
sprintf(UART_Aux, "%s", UART_Buffer);
parse_uart((uint8_t *)&UART_Aux);
memset(&UART_Buffer[0], 0, sizeof(UART_Buffer));
UART_i = 0;
}
rfm69_sendBuffer(&buffer);
HAL_UART_Receive_IT(&huart2, (uint8_t *)&UART2_Data, 1);
}
}
......@@ -278,7 +220,7 @@ static void MX_USART2_UART_Init(void)
{
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.BaudRate = 38400;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
......
/*
* pocsag.c
*
* Created on: Nov 27, 2016
* Author: Mathis Schmieder (DB9MAT), db9mat@giev.de
*
* This library is heavily based upon the POCSAG library by ON1ARF
* https://github.com/on1arf/pocsag
* and on the SDRPager project by Ralf Wilke, DH3WR, et al.
* https://github.com/rwth-afu/SDRPager
*
* This library can be used in any non-commercial amateur radio project as long
* as the original authors and the source is distributed.
*/
#include <stdint.h>
#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
void * GetPocsagMsgPointer() {
return((void *) &Pocsagmsg);
}; // end method Get Msg Pointer
int CreatePocsag (long int address, int function, char * text, int option_invert) {
int txtlen;
// some sanity checks
txtlen=strlen(text);
if (txtlen > 40) {
// messages can be up to 40 chars (+ terminating EOT)
txtlen=40;
}; // end if
// reinit state to 0 (no message)
_pocsag_size=0;
// some sanity checks for the address
// addreess are 21 bits
if ((address > 0x1FFFFF) || (address <= 0)) {
return(POCSAG_FAILED);
}; // end if
// source is 2 bits
if ((function < 0) || (function > 3)) {
return(POCSAG_FAILED);
}; // end if
// option "invert" should be 0 or 1
if ((option_invert < 0) || (option_invert > 1)) {
return(POCSAG_FAILED);
}; // end if
/* 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
// part 0.1: frame syncronisation pattern
if (option_invert == 0) {
memset(Pocsagmsg.sync,0xaa,72);
} else {
memset(Pocsagmsg.sync,0x55,72); // pattern 0x55 is invers of 0xaa
}; // end else - if
// part 0.2: batch syncronisation
// a batch begins with a sync codeword
// 0111 1100 1101 0010 0001 0101 1101 1000
Pocsagmsg.synccw1[0]=0x7c; Pocsagmsg.synccw1[1]=0xd2;
Pocsagmsg.synccw1[2]=0x15; Pocsagmsg.synccw1[3]=0xd8;
Pocsagmsg.synccw2[0]=0x7c; Pocsagmsg.synccw2[1]=0xd2;
Pocsagmsg.synccw2[2]=0x15; Pocsagmsg.synccw2[3]=0xd8;
// invert bits if needed
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;
};
// part 0.3: init batches with all "idle-pattern"
for (int l=0; l< 16; l++) {
Pocsagmsg.batch1[l]=0x7a89c197;
Pocsagmsg.batch2[l]=0x7a89c197;
}; // 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) {
for (int l=0; l<16; l++) {
Pocsagmsg.batch1[l] ^= 0xffffffff;
}; // end for
for (int l=0; l<16; l++) {
Pocsagmsg.batch2[l] ^= 0xffffffff;
}; // end for
};
/// convert to make endian/architecture independant
for (int l=0; l<16; l++) {
int32_t t1;
// structure to convert int32 to architecture / endian independant 4-char array
struct int32_4char {
union {
int32_t i;
unsigned char c[4];
};
} t2;
// struct int32_4char t2;
// batch 1
t1=Pocsagmsg.batch1[l];
// left most octet
t2.c[0]=(t1>>24)&0xff; t2.c[1]=(t1>>16)&0xff;
t2.c[2]=(t1>>8)&0xff; t2.c[3]=t1&0xff;
// copy back
Pocsagmsg.batch1[l]=t2.i;
// batch 2
t1=Pocsagmsg.batch2[l];
// left most octet
t2.c[0]=(t1>>24)&0xff; t2.c[1]=(t1>>16)&0xff;
t2.c[2]=(t1>>8)&0xff; t2.c[3]=t1&0xff;
// copy back
Pocsagmsg.batch2[l]=t2.i;
}; // end for
// TODO check how many batches need to be sent
// more then one batch found
// length = 2 batches (208 octets)
_pocsag_size=208;
return(POCSAG_SUCCESS);
}; // end function create_pocsag
void pocsag_replaceline(Pocsagmsg_s *msg, int line, uint32_t val) {
// sanity checks
if ((line < 0) || (line > 32)) {
return;
}; // end if
if (line < 16) {
msg->batch1[line]=val;
} else {
msg->batch2[line-16]=val;
}; // end if
}; // end replaceline
uint32_t pocsag_crc(uint32_t cw) {
uint32_t crc;
uint32_t d;
uint32_t m;
char p;
// crc
crc = cw;
d = 0xED200000;
for (m = 0x80000000; (m & 0x400) == 0; m >>= 1) {
// m ist Bitmaske mit nur einer 1, die vom MSB bis vor den Anfang
// des (CRC+Prait�t) bereichs l�uft, d.h. bis Bit 11 einschl.
if ((crc & m) != 0)
crc ^= d;
d >>= 1;
}
cw |= crc;
// parity
p = (char) (((cw >> 24) & 0xff) ^ ((cw >> 16) & 0xff) ^ ((cw >> 8) & 0xff) ^ (cw & 0xff));
p ^= (p >> 4);
p ^= (p >> 2);
p ^= (p >> 1);
p &= 0x01;
return cw | p;
}
uint32_t pocsag_encodeMCW(uint32_t msg)
{
return (0x80000000 | ((msg & 0x000fffff) << 11));
}
uint32_t pocsag_encodeACW(long int addr, int func)
{
return (((addr & 0x001ffff8) << 10) | ((func & 0x00000003) << 11));
}
char pocsag_encodeChar(char ch)
{
return isotab[ch & 0xff];
}
char pocsag_encodeDigit(char ch)
{
char mirrorTab[] = { 0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e, 0x01, 0x09 };
if (ch >= '0' && ch <= '9')
return mirrorTab[ch - '0'];
switch (ch) {
case ' ':
return 0x03;
case 'U':
return 0x0d;
case '_':
return 0x0b;
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++;
msg = ch;
msgBitsLeft = bitpercw - bitperchar + msgBitsLeft;
}
}
if (msgBitsLeft != bitpercw) {
msg <<= msgBitsLeft;
pocsag_replaceline(&Pocsagmsg,framePos,pocsag_crc(pocsag_encodeMCW(msg)));
}
}
......@@ -423,6 +423,67 @@ int rfm69_send(const void* data, uint32_t dataLength)
return dataLength;
}
/**
* send buffer over the air.
*/
int rfm69_sendBuffer(struct Buffer* buffer)
{
// switch to standby and wait for mode ready, if not in sleep mode