[Pager] Implemented POCSAG (#7)
This commit is contained in:
parent
2980d8dbf1
commit
1f75ee0cc1
8 changed files with 1186 additions and 0 deletions
121
examples/Pager/Pager_Receive/Pager_Receive.ino
Normal file
121
examples/Pager/Pager_Receive/Pager_Receive.ino
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
RadioLib Pager (POCSAG) Receive Example
|
||||
|
||||
This example shows how to receive FSK packets without using
|
||||
SX127x packet engine.
|
||||
|
||||
This example receives POCSAG messages using SX1278's
|
||||
FSK modem in direct mode.
|
||||
|
||||
Other modules that can be used to receive POCSAG:
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
- Si443x/RFM2x
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration#sx127xrfm9x---lora-modem
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(5, 2, 9, 3);
|
||||
|
||||
// DIO2 pin: 5
|
||||
const int pin = 4;
|
||||
|
||||
// create Pager client instance using the FSK module
|
||||
PagerClient pager(&radio);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//SX1278 radio = RadioShield.ModuleA;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with default settings
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.beginFSK();
|
||||
|
||||
// when using one of the non-LoRa modules
|
||||
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if (state == RADIOLIB_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 == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while (true);
|
||||
}
|
||||
|
||||
// start receiving POCSAG messages
|
||||
Serial.print(F("[Pager] Starting to listen ... "));
|
||||
// address of this "pager": 1234567
|
||||
state = pager.startReceive(pin, 1234567);
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while (true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// the number of batches to wait for
|
||||
// 2 batches will usually be enough to fit short and medium messages
|
||||
if (pager.available() >= 2) {
|
||||
Serial.print(F("[Pager] Received pager data, decoding ... "));
|
||||
|
||||
// you can read the data as an Arduino String
|
||||
String str;
|
||||
int state = pager.readData(str);
|
||||
|
||||
// you can also receive data as byte array
|
||||
/*
|
||||
byte byteArr[8];
|
||||
size_t numBytes = 0;
|
||||
int state = radio.receive(byteArr, &numBytes);
|
||||
*/
|
||||
|
||||
if (state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
|
||||
// print the received data
|
||||
Serial.print(F("[Pager] Data:\t"));
|
||||
Serial.println(str);
|
||||
|
||||
} else {
|
||||
// some error occurred
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
102
examples/Pager/Pager_Transmit/Pager_Transmit.ino
Normal file
102
examples/Pager/Pager_Transmit/Pager_Transmit.ino
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
RadioLib Pager (POCSAG) Transmit Example
|
||||
|
||||
This example sends POCSAG messages using SX1278's
|
||||
FSK modem.
|
||||
|
||||
Other modules that can be used to send POCSAG:
|
||||
- SX127x/RFM9x
|
||||
- RF69
|
||||
- SX1231
|
||||
- CC1101
|
||||
- SX126x
|
||||
- nRF24
|
||||
- Si443x/RFM2x
|
||||
- SX128x
|
||||
|
||||
For default module settings, see the wiki page
|
||||
https://github.com/jgromes/RadioLib/wiki/Default-configuration
|
||||
|
||||
For full API reference, see the GitHub Pages
|
||||
https://jgromes.github.io/RadioLib/
|
||||
*/
|
||||
|
||||
// include the library
|
||||
#include <RadioLib.h>
|
||||
|
||||
// SX1278 has the following connections:
|
||||
// NSS pin: 10
|
||||
// DIO0 pin: 2
|
||||
// RESET pin: 9
|
||||
// DIO1 pin: 3
|
||||
SX1278 radio = new Module(5, 2, 9, 3);
|
||||
|
||||
// or using RadioShield
|
||||
// https://github.com/jgromes/RadioShield
|
||||
//SX1278 radio = RadioShield.ModuleA;
|
||||
|
||||
// create Pager client instance using the FSK module
|
||||
PagerClient pager(&radio);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
|
||||
// initialize SX1278 with default settings
|
||||
Serial.print(F("[SX1278] Initializing ... "));
|
||||
int state = radio.beginFSK();
|
||||
|
||||
// when using one of the non-LoRa modules
|
||||
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
|
||||
// int state = radio.begin();
|
||||
|
||||
if(state == RADIOLIB_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 == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
while(true);
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.print(F("[Pager] Transmitting messages ... "));
|
||||
|
||||
// the simples form of "message" is just a tone on the destination pager
|
||||
int state = pager.sendTone(1234567);
|
||||
delay(500);
|
||||
|
||||
// next, transmit numeric (BCD) message to the destination pager
|
||||
// NOTE: Only characters 0123456789*U-() and space
|
||||
// can be sent in a BCD message!
|
||||
state |= pager.transmit("0123456789*U -()", 1234567);
|
||||
delay(500);
|
||||
|
||||
// finally, let's transmit an ASCII message now
|
||||
state |= pager.transmit("Hello World!", 1234567, RADIOLIB_PAGER_ASCII);
|
||||
delay(500);
|
||||
|
||||
// we can also send only a tone
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
Serial.println(F("success!"));
|
||||
} else {
|
||||
Serial.print(F("failed, code "));
|
||||
Serial.println(state);
|
||||
}
|
||||
|
||||
// wait for a second before transmitting again
|
||||
delay(3000);
|
||||
}
|
|
@ -49,6 +49,7 @@ HellClient KEYWORD1
|
|||
AFSKClient KEYWORD1
|
||||
FSK4Client KEYWORD1
|
||||
APRSClient KEYWORD1
|
||||
PagerClient KEYWORD1
|
||||
|
||||
# SSTV modes
|
||||
Scottie1 KEYWORD1
|
||||
|
@ -239,6 +240,9 @@ noTone KEYWORD2
|
|||
sendPosition KEYWORD2
|
||||
sendMicE KEYWORD2
|
||||
|
||||
# Pager
|
||||
sendTone KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
@ -330,3 +334,6 @@ RADIOLIB_ERR_INVALID_NUM_REPEATERS LITERAL1
|
|||
RADIOLIB_ERR_INVALID_REPEATER_CALLSIGN LITERAL1
|
||||
|
||||
RADIOLIB_ERR_RANGING_TIMEOUT LITERAL1
|
||||
|
||||
RADIOLIB_ERR_INVALID_PAYLOAD LITERAL1
|
||||
RADIOLIB_ERR_ADDRESS_NOT_FOUND LITERAL1
|
||||
|
|
|
@ -87,6 +87,7 @@
|
|||
#include "protocols/AX25/AX25.h"
|
||||
#include "protocols/Hellschreiber/Hellschreiber.h"
|
||||
#include "protocols/Morse/Morse.h"
|
||||
#include "protocols/Pager/Pager.h"
|
||||
#include "protocols/RTTY/RTTY.h"
|
||||
#include "protocols/SSTV/SSTV.h"
|
||||
#include "protocols/FSK4/FSK4.h"
|
||||
|
|
|
@ -405,6 +405,18 @@
|
|||
*/
|
||||
#define RADIOLIB_ERR_RANGING_TIMEOUT (-901)
|
||||
|
||||
// Pager-specific status codes
|
||||
|
||||
/*!
|
||||
\brief The provided payload data configuration is invalid.
|
||||
*/
|
||||
#define RADIOLIB_ERR_INVALID_PAYLOAD (-1001)
|
||||
|
||||
/*!
|
||||
\brief The requested address was not found in the received data.
|
||||
*/
|
||||
#define RADIOLIB_ERR_ADDRESS_NOT_FOUND (-1002)
|
||||
|
||||
/*!
|
||||
\}
|
||||
*/
|
||||
|
|
708
src/protocols/Pager/Pager.cpp
Normal file
708
src/protocols/Pager/Pager.cpp
Normal file
|
@ -0,0 +1,708 @@
|
|||
#include "Pager.h"
|
||||
|
||||
// this is a massive hack, but we need a global-scope ISR to manage the bit reading
|
||||
// let's hope nobody ever tries running two POCSAG receivers at the same time
|
||||
static PhysicalLayer* _readBitInstance = NULL;
|
||||
static RADIOLIB_PIN_TYPE _readBitPin = RADIOLIB_NC;
|
||||
static void PagerClientReadBit(void) {
|
||||
if(_readBitInstance) {
|
||||
_readBitInstance->readBit(_readBitPin);
|
||||
}
|
||||
}
|
||||
|
||||
PagerClient::PagerClient(PhysicalLayer* phy) {
|
||||
_phy = phy;
|
||||
_readBitInstance = _phy;
|
||||
}
|
||||
|
||||
int16_t PagerClient::begin(float base, uint16_t speed) {
|
||||
// calculate duration of 1 bit in us
|
||||
_speed = (float)speed/1000.0f;
|
||||
_bitDuration = (uint32_t)1000000/speed;
|
||||
|
||||
// calculate 24-bit frequency
|
||||
_base = base;
|
||||
_baseRaw = (_base * 1000000.0) / _phy->getFreqStep();
|
||||
|
||||
// calculate module carrier frequency resolution
|
||||
uint16_t step = round(_phy->getFreqStep());
|
||||
|
||||
// calculate raw frequency shift
|
||||
_shift = RADIOLIB_PAGER_FREQ_SHIFT_HZ/step;
|
||||
|
||||
// initialize BCH encoder
|
||||
encoderInit();
|
||||
|
||||
// configure for direct mode
|
||||
return(_phy->startDirect());
|
||||
}
|
||||
|
||||
int16_t PagerClient::sendTone(uint32_t addr) {
|
||||
return(PagerClient::transmit(NULL, 0, addr));
|
||||
}
|
||||
|
||||
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) {
|
||||
if(addr > RADIOLIB_PAGER_ADDRESS_MAX) {
|
||||
return(RADIOLIB_ERR_INVALID_ADDRESS_WIDTH);
|
||||
}
|
||||
|
||||
if(((data == NULL) && (len > 0)) || ((data != NULL) && (len == 0))) {
|
||||
return(RADIOLIB_ERR_INVALID_PAYLOAD);
|
||||
}
|
||||
|
||||
// get symbol bit length based on encoding
|
||||
uint8_t symbolLength = 0;
|
||||
uint32_t function = 0;
|
||||
if(encoding == RADIOLIB_PAGER_BCD) {
|
||||
symbolLength = 4;
|
||||
function = RADIOLIB_PAGER_FUNC_BITS_NUMERIC;
|
||||
|
||||
} else if(encoding == RADIOLIB_PAGER_ASCII) {
|
||||
symbolLength = 7;
|
||||
function = RADIOLIB_PAGER_FUNC_BITS_ALPHA;
|
||||
|
||||
} else {
|
||||
return(RADIOLIB_ERR_INVALID_ENCODING);
|
||||
|
||||
}
|
||||
|
||||
if(len == 0) {
|
||||
function = RADIOLIB_PAGER_FUNC_BITS_TONE;
|
||||
}
|
||||
|
||||
// get target position in batch (3 LSB from address determine frame position in batch)
|
||||
uint8_t framePos = 2*(addr & 0x07);
|
||||
|
||||
// get address that will be written into address frame
|
||||
uint32_t frameAddr = ((addr >> 3) << RADIOLIB_PAGER_ADDRESS_POS) | function;
|
||||
|
||||
// calculate the number of 20-bit data blocks
|
||||
size_t numDataBlocks = (len * symbolLength) / RADIOLIB_PAGER_MESSAGE_BITS_LENGTH;
|
||||
if((len * symbolLength) % RADIOLIB_PAGER_MESSAGE_BITS_LENGTH > 0) {
|
||||
numDataBlocks += 1;
|
||||
}
|
||||
|
||||
// calculate number of batches
|
||||
size_t numBatches = (1 + framePos + numDataBlocks) / RADIOLIB_PAGER_BATCH_LEN + 1;
|
||||
if((1 + numDataBlocks) % RADIOLIB_PAGER_BATCH_LEN == 0) {
|
||||
numBatches -= 1;
|
||||
}
|
||||
|
||||
// calculate message length in 32-bit code words
|
||||
size_t msgLen = RADIOLIB_PAGER_PREAMBLE_LENGTH + (1 + RADIOLIB_PAGER_BATCH_LEN) * numBatches;
|
||||
|
||||
#if defined(RADIOLIB_STATIC_ONLY)
|
||||
uint32_t msg[RADIOLIB_STATIC_ARRAY_SIZE];
|
||||
#else
|
||||
uint32_t* msg = new uint32_t[msgLen];
|
||||
#endif
|
||||
|
||||
// build the message
|
||||
memset(msg, 0x00, msgLen*sizeof(uint32_t));
|
||||
|
||||
// set preamble
|
||||
for(size_t i = 0; i < RADIOLIB_PAGER_PREAMBLE_LENGTH; i++) {
|
||||
msg[i] = RADIOLIB_PAGER_PREAMBLE_CODE_WORD;
|
||||
}
|
||||
|
||||
// start by setting everything after preamble to idle
|
||||
for(size_t i = RADIOLIB_PAGER_PREAMBLE_LENGTH; i < msgLen; i++) {
|
||||
msg[i] = RADIOLIB_PAGER_IDLE_CODE_WORD;
|
||||
}
|
||||
|
||||
// set frame synchronization code words
|
||||
for(size_t i = 0; i < numBatches; i++) {
|
||||
msg[RADIOLIB_PAGER_PREAMBLE_LENGTH + i*(1 + RADIOLIB_PAGER_BATCH_LEN)] = RADIOLIB_PAGER_FRAME_SYNC_CODE_WORD;
|
||||
}
|
||||
|
||||
// write address code word
|
||||
msg[RADIOLIB_PAGER_PREAMBLE_LENGTH + 1 + framePos] = encodeBCH(frameAddr);
|
||||
|
||||
// write the data as 20-bit code blocks
|
||||
if(len > 0) {
|
||||
int8_t remBits = 0;
|
||||
uint8_t dataPos = 0;
|
||||
for(uint8_t i = 0; i < numDataBlocks + numBatches - 1; i++) {
|
||||
uint8_t blockPos = RADIOLIB_PAGER_PREAMBLE_LENGTH + 1 + framePos + 1 + i;
|
||||
|
||||
// check if we need to skip a frame sync marker
|
||||
if(((blockPos - (RADIOLIB_PAGER_PREAMBLE_LENGTH + 1)) % RADIOLIB_PAGER_BATCH_LEN) == 0) {
|
||||
blockPos++;
|
||||
i++;
|
||||
}
|
||||
|
||||
// mark this as a message code word
|
||||
msg[blockPos] = RADIOLIB_PAGER_MESSAGE_CODE_WORD << (RADIOLIB_PAGER_CODE_WORD_LEN - 1);
|
||||
|
||||
// first insert the remainder from previous code word (if any)
|
||||
if(remBits > 0) {
|
||||
// this doesn't apply to BCD messages, so no need to check that here
|
||||
uint8_t prev = Module::flipBits(data[dataPos - 1]);
|
||||
prev >>= 1;
|
||||
msg[blockPos] |= (uint32_t)prev << (RADIOLIB_PAGER_CODE_WORD_LEN - 1 - remBits);
|
||||
}
|
||||
|
||||
// set all message symbols until we overflow to the next code word or run out of message symbols
|
||||
int8_t symbolPos = RADIOLIB_PAGER_CODE_WORD_LEN - 1 - symbolLength - remBits;
|
||||
while(symbolPos > (RADIOLIB_PAGER_FUNC_BITS_POS - symbolLength)) {
|
||||
|
||||
// for BCD, encode the symbol
|
||||
uint8_t symbol = data[dataPos++];
|
||||
if(encoding == RADIOLIB_PAGER_BCD) {
|
||||
symbol = encodeBCD(symbol);
|
||||
}
|
||||
symbol = Module::flipBits(symbol);
|
||||
symbol >>= (8 - symbolLength);
|
||||
|
||||
// insert the next message symbol
|
||||
msg[blockPos] |= (uint32_t)symbol << symbolPos;
|
||||
symbolPos -= symbolLength;
|
||||
|
||||
// check if we ran out of message symbols
|
||||
if(dataPos >= len) {
|
||||
// in BCD mode, pad the rest of the code word with spaces (0xC)
|
||||
if(encoding == RADIOLIB_PAGER_BCD) {
|
||||
uint8_t numSteps = (symbolPos - RADIOLIB_PAGER_FUNC_BITS_POS + symbolLength)/symbolLength;
|
||||
for(uint8_t i = 0; i < numSteps; i++) {
|
||||
symbol = encodeBCD(' ');
|
||||
symbol = Module::flipBits(symbol);
|
||||
symbol >>= (8 - symbolLength);
|
||||
msg[blockPos] |= (uint32_t)symbol << symbolPos;
|
||||
symbolPos -= symbolLength;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure the parity bits are not set due to overflow
|
||||
msg[blockPos] &= ~(RADIOLIB_PAGER_BCH_BITS_MASK);
|
||||
|
||||
// save the number of overflown bits
|
||||
remBits = RADIOLIB_PAGER_FUNC_BITS_POS - symbolPos - symbolLength;
|
||||
|
||||
// do the FEC
|
||||
msg[blockPos] = encodeBCH(msg[blockPos]);
|
||||
}
|
||||
}
|
||||
|
||||
// transmit the message
|
||||
PagerClient::write(msg, msgLen);
|
||||
|
||||
#if !defined(RADIOLIB_STATIC_ONLY)
|
||||
delete[] msg;
|
||||
#endif
|
||||
|
||||
// turn transmitter off
|
||||
_phy->standby();
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
int16_t PagerClient::startReceive(RADIOLIB_PIN_TYPE pin, uint32_t addr, uint32_t mask) {
|
||||
// save the variables
|
||||
_readBitPin = pin;
|
||||
_filterAddr = addr;
|
||||
_filterMask = mask;
|
||||
|
||||
// set the carrier frequency
|
||||
int16_t state = _phy->setFrequency(_base);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// set bitrate
|
||||
state = _phy->setBitRate(_speed);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// set frequency deviation to 4.5 khz
|
||||
state = _phy->setFrequencyDeviation((float)RADIOLIB_PAGER_FREQ_SHIFT_HZ / 1000.0f);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// now set up the direct mode reception
|
||||
_phy->setDirectSyncWord(RADIOLIB_PAGER_FRAME_SYNC_CODE_WORD, 32);
|
||||
_phy->setDirectAction(PagerClientReadBit);
|
||||
_phy->receiveDirect();
|
||||
|
||||
return(state);
|
||||
}
|
||||
|
||||
size_t PagerClient::available() {
|
||||
return(_phy->available() + sizeof(uint32_t))/(sizeof(uint32_t) * (RADIOLIB_PAGER_BATCH_LEN + 1));
|
||||
}
|
||||
|
||||
int16_t PagerClient::readData(String& str, size_t len) {
|
||||
int16_t state = RADIOLIB_ERR_NONE;
|
||||
|
||||
// determine the message length, based on user input or the amount of received data
|
||||
size_t length = len;
|
||||
if(length == 0) {
|
||||
// one batch can contain at most 80 message symbols
|
||||
length = available()*80;
|
||||
}
|
||||
|
||||
// build a temporary buffer
|
||||
#if defined(RADIOLIB_STATIC_ONLY)
|
||||
uint8_t data[RADIOLIB_STATIC_ARRAY_SIZE + 1];
|
||||
#else
|
||||
uint8_t* data = new uint8_t[length + 1];
|
||||
if(!data) {
|
||||
return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED);
|
||||
}
|
||||
#endif
|
||||
|
||||
// read the received data
|
||||
state = readData(data, &length);
|
||||
|
||||
if(state == RADIOLIB_ERR_NONE) {
|
||||
// check tone-only tramsissions
|
||||
if(length == 0) {
|
||||
length = 6;
|
||||
strncpy((char*)data, "<tone>", length);
|
||||
}
|
||||
|
||||
// add null terminator
|
||||
data[length] = 0;
|
||||
|
||||
// initialize Arduino String class
|
||||
str = String((char*)data);
|
||||
}
|
||||
|
||||
// deallocate temporary buffer
|
||||
#if !defined(RADIOLIB_STATIC_ONLY)
|
||||
delete[] data;
|
||||
#endif
|
||||
|
||||
return(state);
|
||||
}
|
||||
|
||||
int16_t PagerClient::readData(uint8_t* data, size_t* len) {
|
||||
// find the correct address
|
||||
bool match = false;
|
||||
uint8_t framePos = 0;
|
||||
uint8_t symbolLength = 0;
|
||||
while(!match && _phy->available()) {
|
||||
uint32_t cw = read();
|
||||
framePos++;
|
||||
|
||||
// check if it's the idle code word
|
||||
if(cw == RADIOLIB_PAGER_IDLE_CODE_WORD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if it's the sync word
|
||||
if(cw == RADIOLIB_PAGER_FRAME_SYNC_CODE_WORD) {
|
||||
framePos = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// not an idle code word, check if it's an address word
|
||||
if(cw & (RADIOLIB_PAGER_MESSAGE_CODE_WORD << (RADIOLIB_PAGER_CODE_WORD_LEN - 1))) {
|
||||
// this is pretty weird, it seems to be a message code word without address
|
||||
continue;
|
||||
}
|
||||
|
||||
// should be an address code word, extract the address
|
||||
uint32_t addr = ((cw & RADIOLIB_PAGER_ADDRESS_BITS_MASK) >> (RADIOLIB_PAGER_ADDRESS_POS - 3)) | (framePos/2);
|
||||
if((addr & _filterMask) == (_filterAddr & _filterMask)) {
|
||||
// we have a match!
|
||||
match = true;
|
||||
|
||||
// determine the encoding from the function bits
|
||||
if((cw & RADIOLIB_PAGER_FUNCTION_BITS_MASK) == RADIOLIB_PAGER_FUNC_BITS_NUMERIC) {
|
||||
symbolLength = 4;
|
||||
} else {
|
||||
symbolLength = 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!match) {
|
||||
// address not found
|
||||
return(RADIOLIB_ERR_ADDRESS_NOT_FOUND);
|
||||
}
|
||||
|
||||
// we have the address, start pulling out the message
|
||||
bool complete = false;
|
||||
size_t decodedBytes = 0;
|
||||
uint32_t prevCw = 0;
|
||||
bool overflow = false;
|
||||
int8_t ovfBits = 0;
|
||||
while(!complete && _phy->available()) {
|
||||
uint32_t cw = read();
|
||||
|
||||
// check if it's the idle code word
|
||||
if(cw == RADIOLIB_PAGER_IDLE_CODE_WORD) {
|
||||
complete = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// skip the sync words
|
||||
if(cw == RADIOLIB_PAGER_FRAME_SYNC_CODE_WORD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check overflow from previous code word
|
||||
uint8_t bitPos = RADIOLIB_PAGER_CODE_WORD_LEN - 1 - symbolLength;
|
||||
if(overflow) {
|
||||
overflow = false;
|
||||
|
||||
// this is a bit convoluted - first, build masks for both previous and current code word
|
||||
uint8_t currPos = RADIOLIB_PAGER_CODE_WORD_LEN - 1 - symbolLength + ovfBits;
|
||||
uint8_t prevPos = RADIOLIB_PAGER_MESSAGE_END_POS;
|
||||
uint32_t prevMask = (0x7FUL << prevPos) & ~((uint32_t)0x7FUL << (RADIOLIB_PAGER_MESSAGE_END_POS + ovfBits));
|
||||
uint32_t currMask = (0x7FUL << currPos) & ~((uint32_t)1 << (RADIOLIB_PAGER_CODE_WORD_LEN - 1));
|
||||
|
||||
// next, get the two parts of the message symbol and stick them together
|
||||
uint8_t prevSymbol = (prevCw & prevMask) >> prevPos;
|
||||
uint8_t currSymbol = (cw & currMask) >> currPos;
|
||||
uint32_t symbol = prevSymbol << (symbolLength - ovfBits) | currSymbol;
|
||||
|
||||
// finally, we can flip the bits
|
||||
symbol = Module::flipBits((uint8_t)symbol);
|
||||
symbol >>= (8 - symbolLength);
|
||||
|
||||
// decode BCD and we're done
|
||||
if(symbolLength == 4) {
|
||||
symbol = decodeBCD(symbol);
|
||||
}
|
||||
data[decodedBytes++] = symbol;
|
||||
|
||||
// adjust the bit position of the next message symbol
|
||||
bitPos += ovfBits;
|
||||
bitPos -= symbolLength;
|
||||
}
|
||||
|
||||
// get the message symbols based on the encoding type
|
||||
while(bitPos >= RADIOLIB_PAGER_MESSAGE_END_POS) {
|
||||
// get the message symbol from the code word and reverse bits
|
||||
uint32_t symbol = (cw & (0x7FUL << bitPos)) >> bitPos;
|
||||
symbol = Module::flipBits((uint8_t)symbol);
|
||||
symbol >>= (8 - symbolLength);
|
||||
|
||||
// decode BCD if needed
|
||||
if(symbolLength == 4) {
|
||||
symbol = decodeBCD(symbol);
|
||||
}
|
||||
data[decodedBytes++] = symbol;
|
||||
|
||||
// now calculate if the next symbol is overflowing to the following code word
|
||||
int8_t remBits = bitPos - RADIOLIB_PAGER_MESSAGE_END_POS;
|
||||
if(remBits < symbolLength) {
|
||||
// overflow!
|
||||
prevCw = cw;
|
||||
overflow = true;
|
||||
ovfBits = remBits;
|
||||
}
|
||||
bitPos -= symbolLength;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// save the number of decoded bytes
|
||||
*len = decodedBytes;
|
||||
return(RADIOLIB_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
|
||||
Module* mod = _phy->getMod();
|
||||
for(int8_t i = 31; i >= 0; i--) {
|
||||
uint32_t mask = (uint32_t)0x01 << i;
|
||||
if(codeWord & mask) {
|
||||
// send 1
|
||||
uint32_t start = mod->micros();
|
||||
_phy->transmitDirect(_baseRaw + _shift);
|
||||
while(mod->micros() - start < _bitDuration);
|
||||
} else {
|
||||
// send 0
|
||||
uint32_t start = mod->micros();
|
||||
_phy->transmitDirect(_baseRaw - _shift);
|
||||
while(mod->micros() - start < _bitDuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t PagerClient::read() {
|
||||
uint32_t codeWord = 0;
|
||||
codeWord |= (uint32_t)_phy->read() << 24;
|
||||
codeWord |= (uint32_t)_phy->read() << 16;
|
||||
codeWord |= (uint32_t)_phy->read() << 8;
|
||||
codeWord |= (uint32_t)_phy->read();
|
||||
|
||||
// TODO BCH error correction here
|
||||
return(codeWord);
|
||||
}
|
||||
|
||||
uint8_t PagerClient::encodeBCD(char c) {
|
||||
switch(c) {
|
||||
case '*':
|
||||
return(0x0A);
|
||||
case 'U':
|
||||
return(0x0B);
|
||||
case ' ':
|
||||
return(0x0C);
|
||||
case '-':
|
||||
return(0x0D);
|
||||
case ')':
|
||||
return(0x0E);
|
||||
case '(':
|
||||
return(0x0F);
|
||||
}
|
||||
return(c - '0');
|
||||
}
|
||||
|
||||
char PagerClient::decodeBCD(uint8_t b) {
|
||||
switch(b) {
|
||||
case 0x0A:
|
||||
return('*');
|
||||
case 0x0B:
|
||||
return('U');
|
||||
case 0x0C:
|
||||
return(' ');
|
||||
case 0x0D:
|
||||
return('-');
|
||||
case 0x0E:
|
||||
return(')');
|
||||
case 0x0F:
|
||||
return('(');
|
||||
}
|
||||
return(b + '0');
|
||||
}
|
||||
|
||||
/*
|
||||
BCH Encoder based on https://www.codeproject.com/articles/13189/pocsag-encoder
|
||||
|
||||
Significantly cleaned up and slightly fixed.
|
||||
*/
|
||||
void PagerClient::encoderInit() {
|
||||
/*
|
||||
* generate GF(2**m) from the irreducible polynomial p(X) in p[0]..p[m]
|
||||
* lookup tables: index->polynomial form _bchAlphaTo[] contains j=alpha**i;
|
||||
* polynomial form -> index form _bchIndexOf[j=alpha**i] = i alpha=2 is the
|
||||
* primitive element of GF(2**m)
|
||||
*/
|
||||
|
||||
int32_t mask = 1;
|
||||
_bchAlphaTo[RADIOLIB_PAGER_BCH_M] = 0;
|
||||
|
||||
for(uint8_t i = 0; i < RADIOLIB_PAGER_BCH_M; i++) {
|
||||
_bchAlphaTo[i] = mask;
|
||||
|
||||
_bchIndexOf[_bchAlphaTo[i]] = i;
|
||||
|
||||
if(RADIOLIB_PAGER_BCH_PRIMITIVE_POLY & ((uint32_t)0x01 << i)) {
|
||||
_bchAlphaTo[RADIOLIB_PAGER_BCH_M] ^= mask;
|
||||
}
|
||||
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
_bchIndexOf[_bchAlphaTo[RADIOLIB_PAGER_BCH_M]] = RADIOLIB_PAGER_BCH_M;
|
||||
mask >>= 1;
|
||||
|
||||
for(uint8_t i = RADIOLIB_PAGER_BCH_M + 1; i < RADIOLIB_PAGER_BCH_N; i++) {
|
||||
if(_bchAlphaTo[i - 1] >= mask) {
|
||||
_bchAlphaTo[i] = _bchAlphaTo[RADIOLIB_PAGER_BCH_M] ^ ((_bchAlphaTo[i - 1] ^ mask) << 1);
|
||||
} else {
|
||||
_bchAlphaTo[i] = _bchAlphaTo[i - 1] << 1;
|
||||
}
|
||||
|
||||
_bchIndexOf[_bchAlphaTo[i]] = i;
|
||||
}
|
||||
|
||||
_bchIndexOf[0] = -1;
|
||||
|
||||
/*
|
||||
* Compute generator polynomial of BCH code of length = 31, redundancy = 10
|
||||
* (OK, this is not very efficient, but we only do it once, right? :)
|
||||
*/
|
||||
|
||||
int32_t ii = 0;
|
||||
int32_t jj = 1;
|
||||
int32_t ll = 0;
|
||||
int32_t kaux = 0;
|
||||
bool test = false;
|
||||
int32_t aux = 0;
|
||||
int32_t cycle[15][6] = { { 0 } };
|
||||
int32_t size[15] = { 0 };
|
||||
|
||||
// Generate cycle sets modulo 31
|
||||
cycle[0][0] = 0; size[0] = 1;
|
||||
cycle[1][0] = 1; size[1] = 1;
|
||||
|
||||
do {
|
||||
// Generate the jj-th cycle set
|
||||
ii = 0;
|
||||
do {
|
||||
ii++;
|
||||
cycle[jj][ii] = (cycle[jj][ii - 1] * 2) % RADIOLIB_PAGER_BCH_N;
|
||||
size[jj]++;
|
||||
aux = (cycle[jj][ii] * 2) % RADIOLIB_PAGER_BCH_N;
|
||||
} while(aux != cycle[jj][0]);
|
||||
|
||||
// Next cycle set representative
|
||||
ll = 0;
|
||||
do {
|
||||
ll++;
|
||||
test = false;
|
||||
for(ii = 1; ((ii <= jj) && !test); ii++) {
|
||||
// Examine previous cycle sets
|
||||
for(kaux = 0; ((kaux < size[ii]) && !test); kaux++) {
|
||||
test = (ll == cycle[ii][kaux]);
|
||||
}
|
||||
}
|
||||
} while(test && (ll < (RADIOLIB_PAGER_BCH_N - 1)));
|
||||
|
||||
if(!test) {
|
||||
jj++; // next cycle set index
|
||||
cycle[jj][0] = ll;
|
||||
size[jj] = 1;
|
||||
}
|
||||
|
||||
} while(ll < (RADIOLIB_PAGER_BCH_N - 1));
|
||||
|
||||
// Search for roots 1, 2, ..., d-1 in cycle sets
|
||||
int32_t rdncy = 0;
|
||||
int32_t min[11];
|
||||
kaux = 0;
|
||||
|
||||
for(ii = 1; ii <= jj; ii++) {
|
||||
min[kaux] = 0;
|
||||
for(jj = 0; jj < size[ii]; jj++) {
|
||||
for(uint8_t root = 1; root < RADIOLIB_PAGER_BCH_D; root++) {
|
||||
if(root == cycle[ii][jj]) {
|
||||
min[kaux] = ii;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(min[kaux]) {
|
||||
rdncy += size[min[kaux]];
|
||||
kaux++;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t noterms = kaux;
|
||||
int32_t zeros[11];
|
||||
kaux = 1;
|
||||
|
||||
for(ii = 0; ii < noterms; ii++) {
|
||||
for(jj = 0; jj < size[min[ii]]; jj++) {
|
||||
zeros[kaux] = cycle[min[ii]][jj];
|
||||
kaux++;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute generator polynomial
|
||||
_bchG[0] = _bchAlphaTo[zeros[1]];
|
||||
_bchG[1] = 1; // g(x) = (X + zeros[1]) initially
|
||||
|
||||
for(ii = 2; ii <= rdncy; ii++) {
|
||||
_bchG[ii] = 1;
|
||||
for(jj = ii - 1; jj > 0; jj--) {
|
||||
if(_bchG[jj] != 0) {
|
||||
_bchG[jj] = _bchG[jj - 1] ^ _bchAlphaTo[(_bchIndexOf[_bchG[jj]] + zeros[ii]) % RADIOLIB_PAGER_BCH_N];
|
||||
} else {
|
||||
_bchG[jj] = _bchG[jj - 1];
|
||||
}
|
||||
}
|
||||
_bchG[0] = _bchAlphaTo[(_bchIndexOf[_bchG[0]] + zeros[ii]) % RADIOLIB_PAGER_BCH_N];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
BCH Encoder based on https://www.codeproject.com/articles/13189/pocsag-encoder
|
||||
|
||||
Significantly cleaned up and slightly fixed.
|
||||
*/
|
||||
uint32_t PagerClient::encodeBCH(uint32_t dat) {
|
||||
// we only use the 21 most significant bits
|
||||
int32_t data[21];
|
||||
int32_t j1 = 0;
|
||||
for(int32_t i = 31; i > 10; i--) {
|
||||
if(dat & ((uint32_t)1<<i)) {
|
||||
data[j1++]=1;
|
||||
} else {
|
||||
data[j1++]=0;
|
||||
}
|
||||
}
|
||||
|
||||
// reset the M(x)+r array elements
|
||||
int32_t Mr[RADIOLIB_PAGER_BCH_N];
|
||||
memset(Mr, 0x00, RADIOLIB_PAGER_BCH_N*sizeof(int32_t));
|
||||
|
||||
// copy the contents of data into Mr and add the zeros
|
||||
memcpy(Mr, data, RADIOLIB_PAGER_BCH_K*sizeof(int32_t));
|
||||
|
||||
int32_t j = 0;
|
||||
int32_t start = 0;
|
||||
int32_t end = RADIOLIB_PAGER_BCH_N - RADIOLIB_PAGER_BCH_K;
|
||||
while(end < RADIOLIB_PAGER_BCH_N) {
|
||||
for(int32_t i = end; i > start-2; --i) {
|
||||
if(Mr[start]) {
|
||||
Mr[i] ^= _bchG[j];
|
||||
++j;
|
||||
} else {
|
||||
++start;
|
||||
j = 0;
|
||||
end = start + RADIOLIB_PAGER_BCH_N - RADIOLIB_PAGER_BCH_K;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t bb[11];
|
||||
j = 0;
|
||||
for(int32_t i = start; i < end; ++i) {
|
||||
bb[j] = Mr[i];
|
||||
++j;
|
||||
}
|
||||
|
||||
int32_t iEvenParity = 0;
|
||||
int32_t recd[32];
|
||||
for(uint8_t i = 0; i < 21; i++) {
|
||||
recd[31 - i] = data[i];
|
||||
if(data[i] == 1) {
|
||||
iEvenParity++;
|
||||
}
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < 11; i++) {
|
||||
recd[10 - i] = bb[i];
|
||||
if(bb[i] == 1) {
|
||||
iEvenParity++;
|
||||
}
|
||||
}
|
||||
|
||||
if((iEvenParity % 2) == 0) {
|
||||
recd[0] = 0;
|
||||
} else {
|
||||
recd[0] = 1;
|
||||
}
|
||||
|
||||
int32_t Codeword[32];
|
||||
memcpy(Codeword, recd, sizeof(int32_t)*32);
|
||||
|
||||
int32_t iResult = 0;
|
||||
for(int32_t i = 0; i < 32; i++) {
|
||||
if(Codeword[i]) {
|
||||
iResult |= ((uint32_t)1<<i);
|
||||
}
|
||||
}
|
||||
|
||||
return(iResult);
|
||||
}
|
216
src/protocols/Pager/Pager.h
Normal file
216
src/protocols/Pager/Pager.h
Normal file
|
@ -0,0 +1,216 @@
|
|||
#if !defined(_RADIOLIB_PAGER_H) && !defined(RADIOLIB_EXCLUDE_PAGER)
|
||||
#define _RADIOLIB_PAGER_H
|
||||
|
||||
#include "../../TypeDef.h"
|
||||
#include "../PhysicalLayer/PhysicalLayer.h"
|
||||
|
||||
// frequency shift in Hz
|
||||
#define RADIOLIB_PAGER_FREQ_SHIFT_HZ (4500)
|
||||
|
||||
// supported encoding schemes
|
||||
#define RADIOLIB_PAGER_ASCII (0)
|
||||
#define RADIOLIB_PAGER_BCD (1)
|
||||
|
||||
// preamble length in 32-bit code words
|
||||
#define RADIOLIB_PAGER_PREAMBLE_LENGTH (18)
|
||||
|
||||
// protocol-specified code words
|
||||
#define RADIOLIB_PAGER_PREAMBLE_CODE_WORD (0xAAAAAAAA)
|
||||
#define RADIOLIB_PAGER_FRAME_SYNC_CODE_WORD (0x7CD215D8)
|
||||
#define RADIOLIB_PAGER_IDLE_CODE_WORD (0x7A89C197)
|
||||
|
||||
// code word type identification flags
|
||||
#define RADIOLIB_PAGER_ADDRESS_CODE_WORD (0UL)
|
||||
#define RADIOLIB_PAGER_MESSAGE_CODE_WORD (1UL)
|
||||
|
||||
// length of code word in bits
|
||||
#define RADIOLIB_PAGER_CODE_WORD_LEN (32)
|
||||
|
||||
// number of message bits in a single code block
|
||||
#define RADIOLIB_PAGER_ADDRESS_POS (13)
|
||||
#define RADIOLIB_PAGER_FUNC_BITS_POS (11)
|
||||
#define RADIOLIB_PAGER_MESSAGE_BITS_LENGTH (20)
|
||||
#define RADIOLIB_PAGER_MESSAGE_END_POS (11)
|
||||
|
||||
// number of code words in a batch
|
||||
#define RADIOLIB_PAGER_BATCH_LEN (16)
|
||||
|
||||
// mask for address bits in a single code word
|
||||
#define RADIOLIB_PAGER_ADDRESS_BITS_MASK (0x7FFFE000UL)
|
||||
|
||||
// mask for function bits in a single code word
|
||||
#define RADIOLIB_PAGER_FUNCTION_BITS_MASK (0x00001800UL)
|
||||
|
||||
// mask for BCH bits in a single code word
|
||||
#define RADIOLIB_PAGER_BCH_BITS_MASK (0x000007FFUL)
|
||||
|
||||
// message type functional bits
|
||||
#define RADIOLIB_PAGER_FUNC_BITS_NUMERIC (0b00UL << RADIOLIB_PAGER_FUNC_BITS_POS)
|
||||
#define RADIOLIB_PAGER_FUNC_BITS_TONE (0b01UL << RADIOLIB_PAGER_FUNC_BITS_POS)
|
||||
#define RADIOLIB_PAGER_FUNC_BITS_ALPHA (0b11UL << RADIOLIB_PAGER_FUNC_BITS_POS)
|
||||
|
||||
// the maximum allowed address (2^22 - 1)
|
||||
#define RADIOLIB_PAGER_ADDRESS_MAX (2097151)
|
||||
|
||||
// BCH(31, 21) code constants
|
||||
#define RADIOLIB_PAGER_BCH_M (5)
|
||||
#define RADIOLIB_PAGER_BCH_N (31)
|
||||
#define RADIOLIB_PAGER_BCH_K (21)
|
||||
#define RADIOLIB_PAGER_BCH_D (5)
|
||||
|
||||
// BCH(31, 21) primitive polynomial x^5 + x^2 + 1
|
||||
#define RADIOLIB_PAGER_BCH_PRIMITIVE_POLY (0x25)
|
||||
|
||||
/*!
|
||||
\class PagerClient
|
||||
|
||||
\brief Client for Pager communication.
|
||||
*/
|
||||
class PagerClient {
|
||||
public:
|
||||
/*!
|
||||
\brief Default constructor.
|
||||
|
||||
\param phy Pointer to the wireless module providing PhysicalLayer communication.
|
||||
*/
|
||||
explicit PagerClient(PhysicalLayer* phy);
|
||||
|
||||
// basic methods
|
||||
|
||||
/*!
|
||||
\brief Initialization method.
|
||||
|
||||
\param base Base (center) frequency to be used in MHz.
|
||||
|
||||
\param speed Bit rate to use in bps. Common POCSAG decoders can receive 512, 1200 and 2400 bps.
|
||||
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t begin(float base, uint16_t speed);
|
||||
|
||||
/*!
|
||||
\brief Method to send a tone-only alert to a destination pager.
|
||||
|
||||
\param addr Address of the destination pager. Allowed values are 0 to 2097151 - values above 2000000 are reserved.
|
||||
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t sendTone(uint32_t addr);
|
||||
|
||||
/*!
|
||||
\brief Arduino String transmit method.
|
||||
|
||||
\param str Address of Arduino string that will be transmitted.
|
||||
|
||||
\param addr Address of the destination pager. Allowed values are 0 to 2097151 - values above 2000000 are reserved.
|
||||
|
||||
\param encoding Encoding to be used (BCD or ASCII). Defaults to RADIOLIB_PAGER_BCD.
|
||||
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t transmit(String& str, uint32_t addr, uint8_t encoding = RADIOLIB_PAGER_BCD);
|
||||
|
||||
/*!
|
||||
\brief C-string transmit method.
|
||||
|
||||
\param str C-string that will be transmitted.
|
||||
|
||||
\param addr Address of the destination pager. Allowed values are 0 to 2097151 - values above 2000000 are reserved.
|
||||
|
||||
\param encoding Encoding to be used (BCD or ASCII). Defaults to RADIOLIB_PAGER_BCD.
|
||||
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t transmit(const char* str, uint32_t addr, uint8_t encoding = RADIOLIB_PAGER_BCD);
|
||||
|
||||
/*!
|
||||
\brief Binary transmit method. Will transmit arbitrary binary data.
|
||||
|
||||
\param data Binary data that will be transmitted.
|
||||
|
||||
\param len Length of binary data to transmit (in bytes).
|
||||
|
||||
\param addr Address of the destination pager. Allowed values are 0 to 2097151 - values above 2000000 are reserved.
|
||||
|
||||
\param encoding Encoding to be used (BCD or ASCII). Defaults to RADIOLIB_PAGER_BCD.
|
||||
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t transmit(uint8_t* data, size_t len, uint32_t addr, uint8_t encoding = RADIOLIB_PAGER_BCD);
|
||||
|
||||
/*!
|
||||
\brief Start reception of POCSAG packets.
|
||||
|
||||
\param pin Pin to receive digital data on (e.g., DIO2 for SX127x).
|
||||
|
||||
\param addr Address of this "pager". Allowed values are 0 to 2097151 - values above 2000000 are reserved.
|
||||
|
||||
\param mask Address filter mask - set individual bits to enable or disable match on that bit of the address.¨Set to 0xFFFFF (all bits checked) by default.
|
||||
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t startReceive(RADIOLIB_PIN_TYPE pin, uint32_t addr, uint32_t mask = 0xFFFFF);
|
||||
|
||||
/*!
|
||||
\brief Get the number of POCSAG batches available in buffer. Limited by the size of direct mode buffer!
|
||||
|
||||
\returns Number of available batches.
|
||||
*/
|
||||
size_t available();
|
||||
|
||||
/*!
|
||||
\brief Reads data that was received after calling startReceive method.
|
||||
|
||||
\param str Address of Arduino String to save the received data.
|
||||
|
||||
\param len Expected number of characters in the message. When set to 0, the message length will be retreived automatically.
|
||||
When more bytes than received are requested, only the number of bytes requested will be returned.
|
||||
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t readData(String& str, size_t len = 0);
|
||||
|
||||
/*!
|
||||
\brief Reads data that was received after calling startReceive method.
|
||||
|
||||
\param data Pointer to array to save the received message.
|
||||
|
||||
\param len Pointer to variable holding the number of bytes that will be read. When set to 0, the packet length will be retreived automatically.
|
||||
When more bytes than received are requested, only the number of bytes requested will be returned.
|
||||
Upon completion, the number of bytes received will be written to this variable.
|
||||
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t readData(uint8_t* data, size_t* len);
|
||||
|
||||
#if !defined(RADIOLIB_GODMODE)
|
||||
private:
|
||||
#endif
|
||||
PhysicalLayer* _phy;
|
||||
|
||||
float _base;
|
||||
float _speed;
|
||||
uint32_t _baseRaw;
|
||||
uint16_t _shift;
|
||||
uint16_t _bitDuration;
|
||||
uint32_t _readBatchPos;
|
||||
uint32_t _filterAddr;
|
||||
uint32_t _filterMask;
|
||||
|
||||
// BCH encoder
|
||||
int32_t _bchAlphaTo[RADIOLIB_PAGER_BCH_N + 1];
|
||||
int32_t _bchIndexOf[RADIOLIB_PAGER_BCH_N + 1];
|
||||
int32_t _bchG[RADIOLIB_PAGER_BCH_N - RADIOLIB_PAGER_BCH_K + 1];
|
||||
|
||||
void write(uint32_t* data, size_t len);
|
||||
void write(uint32_t codeWord);
|
||||
uint32_t read();
|
||||
|
||||
uint8_t encodeBCD(char c);
|
||||
char decodeBCD(uint8_t b);
|
||||
|
||||
void encoderInit();
|
||||
uint32_t encodeBCH(uint32_t data);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -190,6 +190,24 @@ class PhysicalLayer {
|
|||
|
||||
// configuration methods
|
||||
|
||||
/*!
|
||||
\brief Sets carrier frequency. Allowed values range from 137.0 MHz to 525.0 MHz.
|
||||
|
||||
\param freq Carrier frequency to be set in MHz.
|
||||
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
virtual int16_t setFrequency(float freq) = 0;
|
||||
|
||||
/*!
|
||||
\brief Sets FSK bit rate. Allowed values range from 1.2 to 300 kbps. Only available in FSK mode.
|
||||
|
||||
\param br Bit rate to be set (in kbps).
|
||||
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
virtual int16_t setBitRate(float br) = 0;
|
||||
|
||||
/*!
|
||||
\brief Sets FSK frequency deviation from carrier frequency. Allowed values depend on bit rate setting and must be lower than 200 kHz.
|
||||
Only available in FSK mode. Must be implemented in module class.
|
||||
|
@ -353,6 +371,7 @@ class PhysicalLayer {
|
|||
friend class SSTVClient;
|
||||
friend class AX25Client;
|
||||
friend class FSK4Client;
|
||||
friend class PagerClient;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Reference in a new issue