[Pager][WIP] Added Pager files

This commit is contained in:
jgromes 2019-05-29 10:52:43 +02:00
parent 79f1560d41
commit 05498c2598
5 changed files with 315 additions and 0 deletions

View file

@ -0,0 +1,78 @@
/*
RadioLib Pager (POCSAG) Transmit Example
This example sends POCSAG messages using SX1278's
FSK modem.
Other modules that can be used for POCSAG:
- SX127x/RFM9x
- RF69
- SX1231
- CC1101
- SX126x
*/
// include the library
#include <RadioLib.h>
// SX1278 module is in slot A on the shield
SX1278 fsk = RadioShield.ModuleA;
// create Pager client instance using the FSK module
PagerClient pager(&fsk);
void setup() {
Serial.begin(9600);
// initialize SX1278
Serial.print(F("[SX1278] Initializing ... "));
// carrier frequency: 434.0 MHz
// bit rate: 48.0 kbps
// frequency deviation: 50.0 kHz
// Rx bandwidth: 125.0 kHz
// output power: 13 dBm
// current limit: 100 mA
// sync word: 0x2D 0x01
int state = fsk.beginFSK();
if(state == ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while(true);
}
// initalize Pager client
Serial.print(F("[Pager] Initializing ... "));
// base (center) frequency: 434.0 MHz
// speed: 1200 bps
state = pager.begin(434.0, 1200);
if(state == ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while(true);
}
}
void loop() {
// transmit numeric (BCD) message to the destination pager
Serial.print(F("[Pager] Transmitting message ... "));
int state = pager.transmit("0123456789*U -()", 0x01234567);
// NOTE: Only characters 0123456789*U-() and space
// can be sent in a BCD message! To send ASCII
// characters, you have to set encoding to ASCII.
/*
int state = pager.transmit("Hello World!", 0x01234567, ASCII);
*/
if (state == ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
}
// wait for a second before transmitting again
delay(1000);
}

View file

@ -38,6 +38,7 @@ MQTTClient KEYWORD1
HTTPClient KEYWORD1
RTTYClient KEYWORD1
MorseClient KEYWORD1
PagerClient KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
@ -210,6 +211,7 @@ ERR_FRAME_NO_RESPONSE LITERAL1
ASCII LITERAL1
ASCII_EXTENDED LITERAL1
ITA2 LITERAL1
BCD LITERAL1
ERR_INVALID_RTTY_SHIFT LITERAL1
ERR_UNSUPPORTED_ENCODING LITERAL1

View file

@ -62,6 +62,7 @@
#include "protocols/PhysicalLayer.h"
#include "protocols/Morse.h"
#include "protocols/RTTY.h"
#include "protocols/Pager.h"
// transport layer protocols
#include "protocols/TransportLayer.h"

184
src/protocols/Pager.cpp Normal file
View file

@ -0,0 +1,184 @@
#include "Pager.h"
PagerClient::PagerClient(PhysicalLayer* phy) {
_phy = phy;
}
int16_t PagerClient::begin(float base, uint16_t speed) {
// calculate duration of 1 bit in us
_bitDuration = (uint32_t)1000000/speed;
// calculate 24-bit frequency
_base = (base * (uint32_t(1) << _phy->getDivExponent())) / _phy->getCrystalFreq();
// calculate module carrier frequency resolution
uint16_t step = round((_phy->getCrystalFreq() * 1000000) / (uint32_t(1) << _phy->getDivExponent()));
// calculate raw frequency shift
_shift = FREQ_SHIFT_HZ/step;
// set module frequency deviation to 0
int16_t state = _phy->setFrequencyDeviation(0);
return(state);
}
int16_t PagerClient::transmit(String& str, uint32_t addr, uint8_t encoding) {
return(PagerClient::transmit(str.c_str(), addr, encoding));
}
int16_t PagerClient::transmit(const char* str, uint32_t addr, uint8_t encoding) {
return(PagerClient::transmit((uint8_t*)str, strlen(str), addr, encoding));
}
int16_t PagerClient::transmit(uint8_t* data, size_t len, uint32_t addr, uint8_t encoding) {
// get symbol bit length based on encoding
uint8_t symbolLength = 0;
if(encoding == BCD) {
symbolLength = 4;
// replace ASCII characters with BCD representations
for(size_t i = 0; i < len; i++) {
switch(data[i]) {
case '*':
data[i] = 0x0A;
break;
case 'U':
data[i] = 0x0B;
break;
case ' ':
data[i] = 0x0C;
break;
case '-':
data[i] = 0x0D;
break;
case ')':
data[i] = 0x0E;
break;
case '(':
data[i] = 0x0F;
break;
default:
data[i] -= '0';
break;
}
}
} else if(encoding == ASCII) {
symbolLength = 7;
} else {
return(ERR_UNKNOWN);
}
// get target position in batch (3 LSB from address determine frame position in batch)
uint8_t framePos = addr & 0b0000000000000111;
// get address that will be written into address frame
uint16_t frameAddr = (addr & 0b1111111111111000) >> 3;
// calculate the number of 20-bit data blocks
size_t numDataBlocks = (len * symbolLength) / MESSAGE_BITS_LENGTH;
if((len * symbolLength) % MESSAGE_BITS_LENGTH > 0) {
numDataBlocks += 1;
}
// calculate number of batches
size_t numBatches = (1 + framePos + numDataBlocks) / 16 + 1;
if((1 + numDataBlocks) % 16 == 0) {
numBatches -= 1;
}
// calculate message length in 32-bit code words
size_t msgLen = PREAMBLE_LENGTH + (1 + 16) * numBatches;
// build the message
uint32_t* msg = new uint32_t[msgLen];
// TODO: BCD padding?
memset(msg, 0x00, msgLen);
// set preamble
for(size_t i = 0; i < PREAMBLE_LENGTH; i++) {
msg[i] = PREAMBLE_CODE_WORD;
}
// set frame synchronization code words
for(size_t i = 0; i < numBatches; i++) {
msg[PREAMBLE_LENGTH + i*(1 + 16)] = FRAME_SYNC_CODE_WORD;
}
// set unused code words to idle
for(size_t i = 0; i < framePos; i++) {
msg[PREAMBLE_LENGTH + 1 + i] = IDLE_CODE_WORD;
}
// write address code word
msg[PREAMBLE_LENGTH + 1 + framePos] = addParity(encodeBCH((addr << 1) | ADDRESS_CODE_WORD));
// split the data into 20-bit blocks
size_t bitPos = MESSAGE_BITS_LENGTH;
size_t blockPos = PREAMBLE_LENGTH + 1;
for(size_t i = 0; i < len; i++) {
// check if the next data symbol fits into the remaining space in current 20-bit block
if(bitPos >= symbolLength) {
// insert the whole data symbol into current block
msg[blockPos] |= (uint32_t)data[i] << (bitPos - symbolLength);
bitPos -= symbolLength;
} else {
// split the symbol between two blocks
uint8_t msbPart = data[i];
size_t lsbLen = symbolLength - bitPos;
msg[blockPos] |= msbPart >> lsbLen;
blockPos++;
bitPos = MESSAGE_BITS_LENGTH;
uint8_t lsbPart = data[i] & ((1 << lsbLen) - 1);
msg[blockPos] |= (uint32_t)lsbPart << (bitPos - lsbLen);
bitPos -= lsbLen;
}
}
// write message code words
// transmit the message
PagerClient::write(msg, msgLen);
delete[] msg;
// turn transmitter off
_phy->standby();
return(ERR_NONE);
}
void PagerClient::write(uint32_t* data, size_t len) {
// write code words from buffer
for(size_t i = 0; i < len; i++) {
PagerClient::write(data[i]);
}
}
void PagerClient::write(uint32_t codeWord) {
// write single code word
for(uint8_t i = 0; i <= 31; i++) {
uint32_t mask = (uint32_t)0x01 << i;
if(codeWord & mask) {
// send 1
uint32_t start = micros();
_phy->transmitDirect(_base + _shift);
while(micros() - start < _bitDuration);
} else {
// send 0
uint32_t start = micros();
_phy->transmitDirect(_base - _shift);
while(micros() - start < _bitDuration);
}
}
}
uint32_t PagerClient::encodeBCH(uint32_t data) {
return(data);
}
uint32_t PagerClient::addParity(uint32_t msg) {
return(msg);
}

50
src/protocols/Pager.h Normal file
View file

@ -0,0 +1,50 @@
#ifndef _RADIOLIB_PAGER_H
#define _RADIOLIB_PAGER_H
#include "TypeDef.h"
#include "PhysicalLayer.h"
// supported encoding schemes
#define ASCII 0
#define BCD 1
#define PREAMBLE_LENGTH 18
#define MESSAGE_BITS_LENGTH 20
#define FREQ_SHIFT_HZ 4500
#define PREAMBLE_CODE_WORD 0xAAAAAAAA
#define FRAME_SYNC_CODE_WORD 0x7CD215D8
#define IDLE_CODE_WORD 0x7A89C197
#define ADDRESS_CODE_WORD 0
#define MESSAGE_CODE_WORD 1
#define BCH_GENERATOR_POLYNOMIAL 0b11101101001 // x^10 + x^9 + x^8 + x^6 + x^5 + x^3 + 1
class PagerClient {
public:
PagerClient(PhysicalLayer* phy);
// basic methods
int16_t begin(float base, uint16_t speed);
int16_t transmit(String& str, uint32_t addr, uint8_t encoding = BCD);
int16_t transmit(const char* str, uint32_t addr, uint8_t encoding = BCD);
int16_t transmit(uint8_t* data, size_t len, uint32_t addr, uint8_t encoding = BCD);
// TODO: add receiving + option to listen to all packets
private:
PhysicalLayer* _phy;
uint32_t _base;
uint16_t _shift;
uint16_t _bitDuration;
void write(uint32_t* data, size_t len);
void write(uint32_t b);
uint32_t encodeBCH(uint32_t data);
uint32_t addParity(uint32_t msg);
};
#endif