diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index 2263d4a8..93da73df 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -307,7 +307,7 @@ void LoRaWANNode::createSession(uint16_t lwMode, uint8_t initialDr) { // setup JoinRequest uplink/downlink frequencies and datarates if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { - this->selectChannelPlanDyn(true); + this->selectChannelPlanDyn(); } else { this->selectChannelPlanFix(); } @@ -761,7 +761,7 @@ int16_t LoRaWANNode::processJoinAccept(LoRaWANJoinEvent_t *joinEvent) { // in case of dynamic band, reset the channels to clear JoinRequest-specific channels if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { - this->selectChannelPlanDyn(false); + this->selectChannelPlanDyn(); } uint8_t cOcts[5]; @@ -1139,7 +1139,7 @@ void LoRaWANNode::adrBackoff() { } if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { - this->selectChannelPlanDyn(false); // revert to default frequencies + this->selectChannelPlanDyn(); // revert to default frequencies } else { this->selectChannelPlanFix(); // go back to default selected subband } @@ -2962,7 +2962,7 @@ void LoRaWANNode::getChannelPlanMask(uint64_t* chMaskGrp0123, uint32_t* chMaskGr } } -void LoRaWANNode::selectChannelPlanDyn(bool joinRequest) { +void LoRaWANNode::selectChannelPlanDyn() { RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Setting up dynamic channels"); size_t num = 0; diff --git a/src/protocols/LoRaWAN/LoRaWAN.h b/src/protocols/LoRaWAN/LoRaWAN.h index ae942c63..45bdb4bc 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.h +++ b/src/protocols/LoRaWAN/LoRaWAN.h @@ -1068,7 +1068,7 @@ class LoRaWANNode { // setup uplink/downlink channel data rates and frequencies // for dynamic channels, there is a small set of predefined channels // in case of JoinRequest, add some optional extra frequencies - void selectChannelPlanDyn(bool joinRequest = false); + void selectChannelPlanDyn(); // setup uplink/downlink channel data rates and frequencies // for fixed bands, we only allow one sub-band at a time to be selected diff --git a/src/protocols/M17/M17.cpp b/src/protocols/M17/M17.cpp new file mode 100644 index 00000000..cc77f559 --- /dev/null +++ b/src/protocols/M17/M17.cpp @@ -0,0 +1,168 @@ +#include "M17.h" + +#include "../../utils/CRC.h" + +#include +#include + +#if !RADIOLIB_EXCLUDE_M17 + +M17Client::M17Client(PhysicalLayer* phy) : FSK4Client(phy) { + phyLayer = phy; +} + +int16_t M17Client::begin(float base, char* addr) { + int16_t state = FSK4Client::begin(base, RADIOLIB_M17_SHIFT_HZ, RADIOLIB_M17_RATE_BAUD); + RADIOLIB_ASSERT(state); + + // FSK4: 0, 1600, 3200, 4800 + // M17: 800, 2400, -800, -2400 + int16_t offsets[] = { 800, 800, -4000, -7200 }; + FSK4Client::setCorrection(offsets); + + this->encodeAddr(addr, this->src); + + /*Module* mod = this->phyLayer->getMod(); + while(true) { + FSK4Client::write(0); + mod->hal->delay(1000); + FSK4Client::write(1); + mod->hal->delay(1000); + FSK4Client::write(2); + mod->hal->delay(1000); + FSK4Client::write(3); + mod->hal->delay(1000); + }*/ + + return(state); +} + +int16_t M17Client::transmit(uint8_t* data, size_t len, char* dst) { + uint8_t lsf[RADIOLIB_M17_LSF_MAXLEN_BYTES_ENCODED] = { 0 }; + size_t lsfLen = encodeLsf(dst, RADIOLIB_M17_LSF_MODE_PACKET | RADIOLIB_M17_LSF_DATA_TYPE_DATA | RADIOLIB_M17_LSF_ENC_NONE, lsf); + + // send preamble + for(size_t i = 0; i < RADIOLIB_M17_PRE_LEN_BYTES; i++) { + FSK4Client::write(RADIOLIB_M17_PRE_PATTERN_LSF); + } + + // send sync burst + FSK4Client::write(RADIOLIB_M17_SYNC_BURST_LSF >> 8); + FSK4Client::write(RADIOLIB_M17_SYNC_BURST_LSF & 0xFF); + + // send payload + FSK4Client::write(lsf, lsfLen); + + // dummy data + /*for(size_t i = 0; i < 200; i++) { + FSK4Client::write(0x00); + FSK4Client::write(0x55); + FSK4Client::write(0xAA); + FSK4Client::write(0xFF); + }*/ + + // send EOT + for(size_t i = 0; i < RADIOLIB_M17_EOT_LEN_BYTES / 2; i++) { + FSK4Client::write(RADIOLIB_M17_EOT_PATTERN >> 8); + FSK4Client::write(RADIOLIB_M17_EOT_PATTERN & 0xFF); + } + + return(RADIOLIB_ERR_NONE); +} + +int16_t M17Client::encodeAddr(char* in, uint8_t* out) { + //RADIOLIB_ASSERT_PTR(in); + //RADIOLIB_ASSERT_PTR(out); + + // TODO check max len and encodable/reserved addresses + uint64_t res = 0; + size_t len = strlen(in); + for(size_t i = 0; i < len; i++) { + uint8_t val = 0; + char c = in[i]; + if((c >= 'A') && (c <= 'Z')) { + val = c - 'A' + 1; + } else if((c >= '0') && (c <= '9')) { + val = c - '0' + 27; + } else if(c == '-') { + val = 37; + } else if(c == '/') { + val = 38; + } else if(c == '.') { + val = 39; + } else { + return(RADIOLIB_ERR_INVALID_CALLSIGN); + } + res += val * pow(40, i); + } + + // set the output + for(size_t i = 0; i < RADIOLIB_M17_ADDR_LEN; i++) { + out[i] = (res >> (i * 8)) & 0xFF; + } + + return(RADIOLIB_ERR_NONE); +} + +size_t M17Client::encodeLsf(char* dst, uint16_t type, uint8_t* out, uint8_t* meta, size_t metaLen) { + if(!out) { + return(0); + } + uint8_t* framePtr = out; + + // encode destination address + this->encodeAddr(dst, framePtr); + framePtr += RADIOLIB_M17_ADDR_LEN; + + // copy the source address + memcpy(framePtr, this->src, RADIOLIB_M17_ADDR_LEN); + framePtr += RADIOLIB_M17_ADDR_LEN; + + // set the type bits + (*framePtr++) = (type & 0xFF00) >> 8; + (*framePtr++) = type & 0x00FF; + + // TODO check meta + metaLen valid + if(meta) { + memcpy(framePtr, meta, metaLen); + } + framePtr += 14; + + // add CRC + RadioLibCRCInstance.size = 16; + RadioLibCRCInstance.poly = 0x5935; + RadioLibCRCInstance.init = 0xFFFF; + RadioLibCRCInstance.out = 0x0000; + uint16_t crc16 = RadioLibCRCInstance.checksum(out, 240/8 - sizeof(uint16_t)); + (*framePtr++) = (crc16 & 0xFF00) >> 8; + (*framePtr++) = crc16 & 0x00FF; + + // TODO add flush bits + framePtr++; + + // TODO convolutional encoding + framePtr+=30; + + // TODO puncturing + framePtr-=15; + + // TODO interleaving + + // randomize + size_t len = framePtr - out; + randomize(out, len); + return(len); +} + +void M17Client::randomize(uint8_t* buff, size_t len) { + if(!buff) { + return; + } + + for(size_t i = 0; i < len; i++) { + buff[i] ^= m17_randomizer[this->randIndex++]; + this->randIndex %= RADIOLIB_M17_RANDOMIZER_LEN; + } +} + +#endif diff --git a/src/protocols/M17/M17.h b/src/protocols/M17/M17.h new file mode 100644 index 00000000..befbe693 --- /dev/null +++ b/src/protocols/M17/M17.h @@ -0,0 +1,98 @@ +#if !defined(_RADIOLIB_M17_H) +#define _RADIOLIB_M17_H + +#include "../../TypeDef.h" + +#if !RADIOLIB_EXCLUDE_M17 + +#include "../PhysicalLayer/PhysicalLayer.h" +#include "../FSK4/FSK4.h" + +// basic M17 properties +#define RADIOLIB_M17_SHIFT_HZ (1600) +#define RADIOLIB_M17_RATE_BAUD (4800) + +// preamble +#define RADIOLIB_M17_PRE_LEN_BYTES (192/4) +#define RADIOLIB_M17_PRE_PATTERN_LSF (0x77) +#define RADIOLIB_M17_PRE_PATTERN_BERT (0xDD) + +// end-of-transmission +#define RADIOLIB_M17_EOT_LEN_BYTES (192/4) +#define RADIOLIB_M17_EOT_PATTERN (0x555D) + +// sync-burst +#define RADIOLIB_M17_SYNC_BURST_LSF (0x55F7) +#define RADIOLIB_M17_SYNC_BURST_BERT (0xDF55) +#define RADIOLIB_M17_SYNC_BURST_STREAM (0xFF5D) +#define RADIOLIB_M17_SYNC_BURST_PACKET (0x75FF) + +// link setup frame (LFS) bit fields MSB LSB DESCRIPTION +#define RADIOLIB_M17_LSF_MODE_PACKET (0x00UL << 0) // 0 0 LSF packet/stream indicator: packet +#define RADIOLIB_M17_LSF_MODE_STREAM (0x01UL << 0) // 0 0 stream +#define RADIOLIB_M17_LSF_DATA_TYPE_DATA (0x01UL << 1) // 2 1 data type: data +#define RADIOLIB_M17_LSF_DATA_TYPE_VOICE (0x02UL << 1) // 2 1 voice +#define RADIOLIB_M17_LSF_DATA_TYPE_VOICE_DATA (0x03UL << 1) // 2 1 data + voice +#define RADIOLIB_M17_LSF_ENC_NONE (0x00UL << 3) // 4 3 encryption: none +#define RADIOLIB_M17_LSF_ENC_SCRAMBLER (0x01UL << 3) // 4 3 scrambler +#define RADIOLIB_M17_LSF_ENC_AES (0x02UL << 3) // 4 3 AES +#define RADIOLIB_M17_LSF_ENC_OTHER (0x03UL << 3) // 4 3 other +#define RADIOLIB_M17_LSF_AES_LEN_128 (0x00UL << 5) // 6 5 encryption key length: 128-bit +#define RADIOLIB_M17_LSF_AES_LEN_192 (0x01UL << 5) // 6 5 192-bit +#define RADIOLIB_M17_LSF_AES_LEN_256 (0x02UL << 5) // 6 5 256-bit +#define RADIOLIB_M17_LSF_SCRAMLER_LEN_8 (0x00UL << 5) // 6 5 scrambler length: 8-bit +#define RADIOLIB_M17_LSF_SCRAMLER_LEN_16 (0x01UL << 5) // 6 5 16-bit +#define RADIOLIB_M17_LSF_SCRAMLER_LEN_24 (0x02UL << 5) // 6 5 24-bit + +// maximum length of LSF frame before puncturing +#define RADIOLIB_M17_LSF_MAXLEN_BYTES_ENCODED (368/8) + +#define RADIOLIB_M17_ADDR_LEN (6) + +#define RADIOLIB_M17_RANDOMIZER_LEN (46) + +static const uint8_t m17_randomizer[RADIOLIB_M17_RANDOMIZER_LEN] = { + 0xD6, 0xB5, 0xE2, 0x30, 0x82, 0xFF, 0x84, 0x62, + 0xBA, 0x4E, 0x96, 0x90, 0xD8, 0x98, 0xDD, 0x5D, + 0x0C, 0xC8, 0x52, 0x43, 0x91, 0x1D, 0xF8, 0x6E, + 0x68, 0x2F, 0x35, 0xDA, 0x14, 0xEA, 0xCD, 0x76, + 0x19, 0x8D, 0xD5, 0x80, 0xD1, 0x33, 0x87, 0x13, + 0x57, 0x18, 0x2D, 0x29, 0x78, 0xC3 +}; + +/*! + \class M17Client + \brief Client for M17 transmissions. +*/ +class M17Client: public FSK4Client { + public: + /*! + \brief Constructor for 4-FSK mode. + \param phy Pointer to the wireless module providing PhysicalLayer communication. + */ + explicit M17Client(PhysicalLayer* phy); + + /*! + \brief Initialization method. + \param base Base (space) frequency to be used in MHz. + \returns \ref status_codes + */ + int16_t begin(float base, char* addr); + + int16_t transmit(uint8_t* data, size_t len, char* dst); + +#if !RADIOLIB_GODMODE + private: +#endif + PhysicalLayer* phyLayer; + uint8_t src[RADIOLIB_M17_ADDR_LEN] = { 0 }; + uint8_t randIndex = 0; + + int16_t encodeAddr(char* in, uint8_t* out); + size_t encodeLsf(char* dst, uint16_t type, uint8_t* out, uint8_t* meta = NULL, size_t metaLen = 0); + void randomize(uint8_t* buff, size_t len); +}; + +#endif + +#endif \ No newline at end of file