RadioLibSmol/src/protocols/Pager.cpp
2019-05-29 10:52:43 +02:00

184 lines
4.9 KiB
C++

#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);
}