diff options
author | Daniel Friesel <derf@finalrewind.org> | 2021-09-23 22:22:47 +0200 |
---|---|---|
committer | Daniel Friesel <derf@finalrewind.org> | 2021-09-23 22:22:47 +0200 |
commit | 8578e1ea7d078b60864b084094dbb02b6cac99c3 (patch) | |
tree | 19a02eef376f5b89c7044c48dcc0c0f43ea9d416 /src/lib/MCCI_LoRaWAN_LMIC_library/src/lmic/lmic_us_like.c | |
parent | 30a29dcd0d064ab8403a9afb32c59800bb346840 (diff) |
Import partially adapted MCCI LoRaWAN LMIC library. Needs further work.
Diffstat (limited to 'src/lib/MCCI_LoRaWAN_LMIC_library/src/lmic/lmic_us_like.c')
-rw-r--r-- | src/lib/MCCI_LoRaWAN_LMIC_library/src/lmic/lmic_us_like.c | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/src/lib/MCCI_LoRaWAN_LMIC_library/src/lmic/lmic_us_like.c b/src/lib/MCCI_LoRaWAN_LMIC_library/src/lmic/lmic_us_like.c new file mode 100644 index 0000000..31e4cc1 --- /dev/null +++ b/src/lib/MCCI_LoRaWAN_LMIC_library/src/lmic/lmic_us_like.c @@ -0,0 +1,369 @@ +/* +* Copyright (c) 2014-2016 IBM Corporation. +* Copyright (c) 2017, 2019 MCCI Corporation. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the <organization> nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define LMIC_DR_LEGACY 0 + +#include "lmic_bandplan.h" + +#if CFG_LMIC_US_like + +#ifndef LMICuslike_getFirst500kHzDR +# error "LMICuslike_getFirst500kHzDR() not defined by bandplan" +#endif + +/// +/// \brief set LMIC.txChan to the next selected channel. +/// +/// \param [in] start first channel number +/// \param [in] end one past the last channel number +/// +/// \details +/// We set up a call to LMIC_findNextChannel using the channelShuffleMap and +/// the channelEnableMap. We subset these based on start and end. \p start must +/// be a multiple of 16. +/// +static void setNextChannel(uint16_t start, uint16_t end, uint16_t count) { + ASSERT(count>0); + ASSERT(start<end); + ASSERT(count <= (end - start)); + ASSERT((start & 0xF) == 0); + uint16_t const mapStart = start >> 4; + uint16_t const mapEntries = (end - start + 15) >> 4; + + int candidate = start + LMIC_findNextChannel( + LMIC.channelShuffleMap + mapStart, + LMIC.channelMap + mapStart, + mapEntries, + LMIC.txChnl == 0xFF ? -1 : LMIC.txChnl + ); + + if (candidate >= 0) + LMIC.txChnl = candidate; +} + + + +bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) { + LMIC_API_PARAMETER(bandidx); + LMIC_API_PARAMETER(txpow); + LMIC_API_PARAMETER(txcap); + + // nothing; just succeed. + return 1; +} + + +void LMICuslike_initDefaultChannels(bit_t fJoin) { + LMIC_API_PARAMETER(fJoin); + + // things work the same for join as normal. + for (u1_t i = 0; i<4; i++) + LMIC.channelMap[i] = 0xFFFF; + LMIC.channelMap[4] = 0x00FF; + os_clearMem(LMIC.channelShuffleMap, sizeof(LMIC.channelShuffleMap)); + LMIC.activeChannels125khz = 64; + LMIC.activeChannels500khz = 8; + // choose a random channel. + LMIC.txChnl = 0xFF; +} + +// verify that a given setting is permitted +bit_t LMICuslike_canMapChannels(u1_t chpage, u2_t chmap) { + /* + || MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON and MCMD_LinkADRReq_ChMaskCntl_USLIKE_125OFF are special. The + || channel map appllies to 500kHz (ch 64..71) and in addition + || all channels 0..63 are turned off or on. MCMC_LADR_CHP_BANK + || is also special, in that it enables subbands. + */ + if (chpage < MCMD_LinkADRReq_ChMaskCntl_USLIKE_SPECIAL) { + // operate on channels 0..15, 16..31, 32..47, 48..63, 64..71 + if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_500K) { + if (chmap & 0xFF00) { + // those are reserved bits, fail. + return 0; + } + } else { + return 1; + } + } else if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_BANK) { + if (chmap == 0 || (chmap & 0xFF00) != 0) { + // no bits set, or reserved bitsset , fail. + return 0; + } + } else if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON || + chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125OFF) { + u1_t const en125 = chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON; + + // if disabling all 125kHz chans, must have at least one 500kHz chan + // don't allow reserved bits to be set in chmap. + if ((! en125 && chmap == 0) || (chmap & 0xFF00) != 0) + return 0; + } else { + return 0; + } + + // if we get here, it looks legal. + return 1; +} + +// map channels. return true if configuration looks valid. +bit_t LMICuslike_mapChannels(u1_t chpage, u2_t chmap) { + /* + || MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON and MCMD_LinkADRReq_ChMaskCntl_USLIKE_125OFF are special. The + || channel map appllies to 500kHz (ch 64..71) and in addition + || all channels 0..63 are turned off or on. MCMC_LADR_CHP_BANK + || is also special, in that it enables subbands. + */ + if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_BANK) { + // each bit enables a bank of channels + for (u1_t subband = 0; subband < 8; ++subband, chmap >>= 1) { + if (chmap & 1) { + LMIC_enableSubBand(subband); + } else { + LMIC_disableSubBand(subband); + } + } + } else { + u1_t base, top; + + if (chpage < MCMD_LinkADRReq_ChMaskCntl_USLIKE_SPECIAL) { + // operate on channels 0..15, 16..31, 32..47, 48..63 + // note that the chpage hasn't been shifted right, so + // it's really the base. + base = chpage; + top = base + 16; + if (base == 64) { + top = 72; + } + } else /* if (chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON || + chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125OFF) */ { + u1_t const en125 = chpage == MCMD_LinkADRReq_ChMaskCntl_USLIKE_125ON; + + // enable or disable all 125kHz channels + for (u1_t chnl = 0; chnl < 64; ++chnl) { + if (en125) + LMIC_enableChannel(chnl); + else + LMIC_disableChannel(chnl); + } + + // then apply mask to top 8 channels. + base = 64; + top = 72; + } + + // apply chmap to channels in [base..top-1]. + // Use enable/disable channel to keep activeChannel counts in sync. + for (u1_t chnl = base; chnl < top; ++chnl, chmap >>= 1) { + if (chmap & 0x0001) + LMIC_enableChannel(chnl); + else + LMIC_disableChannel(chnl); + } + } + + LMICOS_logEventUint32("LMICuslike_mapChannels", ((u4_t)LMIC.activeChannels125khz << 16u)|(LMIC.activeChannels500khz << 0u)); + return (LMIC.activeChannels125khz > 0) || (LMIC.activeChannels500khz > 0); +} + +// US does not have duty cycling - return now as earliest TX time +// but also do the channel hopping dance. +ostime_t LMICuslike_nextTx(ostime_t now) { + // TODO(tmm@mcci.com): use a static const for US-like + if (LMIC.datarate >= LMICuslike_getFirst500kHzDR()) { // 500kHz + if (LMIC.activeChannels500khz > 0) { + setNextChannel(64, 64 + 8, LMIC.activeChannels500khz); + } else if (LMIC.activeChannels125khz > 0) { + LMIC.datarate = lowerDR(LMICuslike_getFirst500kHzDR(), 1); + setNextChannel(0, 64, LMIC.activeChannels125khz); + LMICOS_logEvent("LMICuslike_nextTx: no 500k, choose 125k"); + } else { + LMICOS_logEvent("LMICuslike_nextTx: no channels at all (500)"); + } + } + else { // 125kHz + if (LMIC.activeChannels125khz > 0) { + setNextChannel(0, 64, LMIC.activeChannels125khz); + } else if (LMIC.activeChannels500khz > 0) { + LMIC.datarate = LMICuslike_getFirst500kHzDR(); + setNextChannel(64, 64 + 8, LMIC.activeChannels500khz); + LMICOS_logEvent("LMICuslike_nextTx: no 125k, choose 500k"); + } else { + LMICOS_logEvent("LMICuslike_nextTx: no channels at all (125)"); + } + } + return now; +} + +bit_t LMICuslike_isDataRateFeasible(dr_t dr) { + if (dr >= LMICuslike_getFirst500kHzDR()) { // 500kHz + return LMIC.activeChannels500khz > 0; + } else { + return LMIC.activeChannels125khz > 6; + } +} + +#if !defined(DISABLE_JOIN) +void LMICuslike_initJoinLoop(void) { + // set an initial condition so that setNextChannel()'s preconds are met + LMIC.txChnl = 0xFF; + + // then chose a new channel. This gives us a random first channel for + // the join. The join logic uses the current txChnl, + // then changes after the rx window expires; so we need to set a valid + // starting point. + setNextChannel(0, 64, LMIC.activeChannels125khz); + + // make sure LMIC.txend is valid. + LMIC.txend = os_getTime(); + ASSERT((LMIC.opmode & OP_NEXTCHNL) == 0); + + // make sure the datarate is set to DR2 per LoRaWAN regional reqts V1.0.2, + // section 2.*.2 + LMICcore_setDrJoin(DRCHG_SET, LMICbandplan_getInitialDrJoin()); + + // TODO(tmm@mcci.com) need to implement the transmit randomization and + // duty cycle restrictions from LoRaWAN V1.0.2 section 7. +} +#endif // !DISABLE_JOIN + +#if !defined(DISABLE_JOIN) +// +// TODO(tmm@mcci.com): +// +// The definition of this is a little strange. this seems to return a time, but +// in reality it returns 0 if the caller should continue scanning through +// channels, and 1 if the caller has scanned all channels on this session, +// and therefore should reset to the beginning. The IBM 1.6 code is the +// same way, so apparently I just carried this across. We should declare +// as bool_t and change callers to use the result clearly as a flag. +// +ostime_t LMICuslike_nextJoinState(void) { + // Try the following: + // DR0 (SF10) on a random channel 0..63 + // (honoring enable mask) + // DR4 (SF8C) on a random 500 kHz channel 64..71 + // (always determined by + // previously selected + // 125 kHz channel) + // + u1_t failed = 0; + // TODO(tmm@mcci.com) parameterize for US-like + if (LMIC.datarate != LMICuslike_getFirst500kHzDR()) { + // assume that 500 kHz equiv of last 125 kHz channel + // is also enabled, and use it next. + LMIC.txChnl_125kHz = LMIC.txChnl; + LMIC.txChnl = 64 + (LMIC.txChnl >> 3); + LMICcore_setDrJoin(DRCHG_SET, LMICuslike_getFirst500kHzDR()); + } + else { + // restore invariant + LMIC.txChnl = LMIC.txChnl_125kHz; + setNextChannel(0, 64, LMIC.activeChannels125khz); + + // TODO(tmm@mcci.com) parameterize + s1_t dr = LMICuslike_getJoin125kHzDR(); + if ((++LMIC.txCnt & 0x7) == 0) { + failed = 1; // All DR exhausted - signal failed + } + LMICcore_setDrJoin(DRCHG_SET, dr); + } + // tell the main loop that we've already selected a channel. + LMIC.opmode &= ~OP_NEXTCHNL; + + // TODO(tmm@mcci.com): change delay to (0:1) secs + a known t0, but randomized; + // starting adding a bias after 1 hour, 25 hours, etc.; and limit the duty + // cycle on power up. For testability, add a way to set the join start time + // externally (a test API) so we can check this feature. + // See https://github.com/mcci-catena/arduino-lmic/issues/2 + // Current code doesn't match LoRaWAN 1.0.2 requirements. + + LMIC.txend = os_getTime() + + (isTESTMODE() + // Avoid collision with JOIN ACCEPT being sent by GW (but we missed it - GW is still busy) + ? DNW2_SAFETY_ZONE + // Otherwise: randomize join (street lamp case): + // SF10:16, SF9=8,..SF8C:1secs + : LMICcore_rndDelay(16 >> LMIC.datarate)); + // 1 - triggers EV_JOIN_FAILED event + return failed; +} +#endif + +#if !defined(DISABLE_JOIN) +void LMICuslike_processJoinAcceptCFList(void) { + if ( LMICbandplan_hasJoinCFlist() && + LMIC.frame[OFF_CFLIST + 15] == LORAWAN_JoinAccept_CFListType_MASK ) { + u1_t dlen; + + dlen = OFF_CFLIST; + for( u1_t chidx = 0; chidx < 8 * sizeof(LMIC.channelMap); chidx += 16, dlen += 2 ) { + u2_t mask = os_rlsbf2(&LMIC.frame[dlen]); +#if LMIC_DEBUG_LEVEL > 1 + LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": Setup channel mask, group=%u, mask=%04x\n", os_getTime(), chidx, mask); +#endif + for ( u1_t chnum = chidx; chnum < chidx + 16; ++chnum, mask >>= 1) { + if (chnum >= 72) { + break; + } else if (mask & 1) { + LMIC_enableChannel(chnum); + } else { + LMIC_disableChannel(chnum); + } + } + } + } +} +#endif // !DISABLE_JOIN + +void LMICuslike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer) { + os_copyMem( + pStateBuffer->channelMap, + LMIC.channelMap, + sizeof(LMIC.channelMap) + ); + pStateBuffer->activeChannels125khz = LMIC.activeChannels125khz; + pStateBuffer->activeChannels500khz = LMIC.activeChannels500khz; +} + +void LMICuslike_restoreAdrState(const lmic_saved_adr_state_t *pStateBuffer) { + os_copyMem( + LMIC.channelMap, + pStateBuffer->channelMap, + sizeof(LMIC.channelMap) + ); + LMIC.activeChannels125khz = pStateBuffer->activeChannels125khz; + LMIC.activeChannels500khz = pStateBuffer->activeChannels500khz; +} + + +bit_t LMICuslike_compareAdrState(const lmic_saved_adr_state_t *pStateBuffer) { + return memcmp(pStateBuffer->channelMap, LMIC.channelMap, sizeof(LMIC.channelMap)) != 0; +} + +#endif // CFG_LMIC_US_like |