736 lines
19 KiB
C++
736 lines
19 KiB
C++
#include "Pager.h"
|
|
#if !defined(RADIOLIB_EXCLUDE_PAGER)
|
|
|
|
// 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;
|
|
|
|
#if defined(ESP8266) || defined(ESP32)
|
|
ICACHE_RAM_ATTR
|
|
#endif
|
|
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, bool invert, uint16_t shift) {
|
|
// 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
|
|
_shiftHz = shift;
|
|
_shift = _shiftHz/step;
|
|
inv = invert;
|
|
|
|
// 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(size_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)_shiftHz / 1000.0f);
|
|
RADIOLIB_ASSERT(state);
|
|
|
|
// now set up the direct mode reception
|
|
Module* mod = _phy->getMod();
|
|
mod->pinMode(pin, INPUT);
|
|
_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, uint32_t* addr) {
|
|
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, addr);
|
|
|
|
if(state == RADIOLIB_ERR_NONE) {
|
|
// check tone-only tramsissions
|
|
if(length == 0) {
|
|
length = 6;
|
|
strncpy((char*)data, "<tone>", length + 1);
|
|
}
|
|
|
|
// 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, uint32_t* addr) {
|
|
// 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_found = ((cw & RADIOLIB_PAGER_ADDRESS_BITS_MASK) >> (RADIOLIB_PAGER_ADDRESS_POS - 3)) | (framePos/2);
|
|
if((addr_found & _filterMask) == (_filterAddr & _filterMask)) {
|
|
// we have a match!
|
|
match = true;
|
|
if(addr) {
|
|
*addr = addr_found;
|
|
}
|
|
|
|
// 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;
|
|
uint32_t start = mod->micros();
|
|
|
|
// figure out the shift direction - start by assuming the bit is 0
|
|
int16_t change = _shift;
|
|
|
|
// now check if it's actually 1
|
|
if(codeWord & mask) {
|
|
change = -_shift;
|
|
}
|
|
|
|
// finally, check if inversion is enabled
|
|
if(inv) {
|
|
change = -change;
|
|
}
|
|
|
|
// now transmit the shifted frequency
|
|
_phy->transmitDirect(_baseRaw + change);
|
|
|
|
// this is pretty silly, while(mod->micros() ... ) would be enough
|
|
// but for some reason, MegaCore throws a linker error on it
|
|
// "relocation truncated to fit: R_AVR_7_PCREL against `no symbol'"
|
|
uint32_t now = mod->micros();
|
|
while(now - start < _bitDuration) {
|
|
now = mod->micros();
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
#endif
|