summaryrefslogtreecommitdiff
path: root/src/lib/MCCI_LoRaWAN_LMIC_library/src/lmic/lmic_us_like.c
diff options
context:
space:
mode:
authorDaniel Friesel <derf@finalrewind.org>2021-09-23 22:22:47 +0200
committerDaniel Friesel <derf@finalrewind.org>2021-09-23 22:22:47 +0200
commit8578e1ea7d078b60864b084094dbb02b6cac99c3 (patch)
tree19a02eef376f5b89c7044c48dcc0c0f43ea9d416 /src/lib/MCCI_LoRaWAN_LMIC_library/src/lmic/lmic_us_like.c
parent30a29dcd0d064ab8403a9afb32c59800bb346840 (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.c369
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