From d86e4c5112d6edb00f050e802b9a11fc0a5cbc0d Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Sun, 18 Apr 2021 15:14:44 -0400 Subject: [PATCH 1/9] Fix #404: remove XCHANNEL support from US region --- README.md | 1 - src/lmic/lmic.h | 8 ++------ src/lmic/lmic_us915.c | 23 +++++++---------------- 3 files changed, 9 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 640b5f2c..6d39ecee 100644 --- a/README.md +++ b/README.md @@ -288,7 +288,6 @@ If the library is configured for US915 operation, we make the following changes: - Add the APIs `LMIC_enableChannel()`, `LMIC_enableSubBand()`, `LMIC_disableSubBand()`, and `LMIC_selectSubBand()`. -- Add the constants `MAX_XCHANNELS`. - Add a number of additional `DR_...` symbols. ### Selecting the target radio transceiver diff --git a/src/lmic/lmic.h b/src/lmic/lmic.h index 471f1f96..8fa7d760 100644 --- a/src/lmic/lmic.h +++ b/src/lmic/lmic.h @@ -172,10 +172,8 @@ struct lmic_saved_adr_state_s { #elif CFG_LMIC_US_like // US915 spectrum ================================================= -enum { MAX_XCHANNELS = 2 }; // extra channels in RAM, channels 0-71 are immutable - struct lmic_saved_adr_state_s { - u2_t channelMap[(72+MAX_XCHANNELS+15)/16]; // enabled bits + u2_t channelMap[(72+15)/16]; // enabled bits u2_t activeChannels125khz; u2_t activeChannels500khz; }; @@ -532,9 +530,7 @@ struct lmic_t { u2_t channelDrMap[MAX_CHANNELS]; u2_t channelMap; #elif CFG_LMIC_US_like - u4_t xchFreq[MAX_XCHANNELS]; // extra channel frequencies (if device is behind a repeater) - u2_t xchDrMap[MAX_XCHANNELS]; // extra channel datarate ranges ---XXX: ditto - u2_t channelMap[(72+MAX_XCHANNELS+15)/16]; // enabled bits + u2_t channelMap[(72+15)/16]; // enabled bits u2_t activeChannels125khz; u2_t activeChannels500khz; #endif diff --git a/src/lmic/lmic_us915.c b/src/lmic/lmic_us915.c index 0d84ac13..352d687e 100644 --- a/src/lmic/lmic_us915.c +++ b/src/lmic/lmic_us915.c @@ -100,20 +100,17 @@ u4_t LMICus915_convFreq(xref2cu1_t ptr) { } bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { + LMIC_API_PARAMETER(chidx); + LMIC_API_PARAMETER(freq); + LMIC_API_PARAMETER(drmap); LMIC_API_PARAMETER(band); - if (chidx < 72 || chidx >= 72 + MAX_XCHANNELS) - return 0; // channels 0..71 are hardwired - LMIC.xchFreq[chidx - 72] = freq; - // TODO(tmm@mcci.com): don't use US SF directly, use something from the LMIC context or a static const - LMIC.xchDrMap[chidx - 72] = drmap == 0 ? DR_RANGE_MAP(US915_DR_SF10, US915_DR_SF8C) : drmap; - LMIC.channelMap[chidx >> 4] |= (1 << (chidx & 0xF)); - return 1; + return 0; // channels 0..71 are hardwired } bit_t LMIC_disableChannel(u1_t channel) { bit_t result = 0; - if (channel < 72 + MAX_XCHANNELS) { + if (channel < 72) { if (ENABLED_CHANNEL(channel)) { result = 1; if (IS_CHANNEL_125khz(channel)) @@ -128,7 +125,7 @@ bit_t LMIC_disableChannel(u1_t channel) { bit_t LMIC_enableChannel(u1_t channel) { bit_t result = 0; - if (channel < 72 + MAX_XCHANNELS) { + if (channel < 72) { if (!ENABLED_CHANNEL(channel)) { result = 1; if (IS_CHANNEL_125khz(channel)) @@ -197,13 +194,7 @@ void LMICus915_updateTx(ostime_t txbeg) { } else { // at 500kHz bandwidth, we're allowed more power. LMIC.txpow = 26; - if (chnl < 64 + 8) { - LMIC.freq = US915_500kHz_UPFBASE + (chnl - 64)*US915_500kHz_UPFSTEP; - } - else { - ASSERT(chnl < 64 + 8 + MAX_XCHANNELS); - LMIC.freq = LMIC.xchFreq[chnl - 72]; - } + LMIC.freq = US915_500kHz_UPFBASE + (chnl - 64)*US915_500kHz_UPFSTEP; } // Update global duty cycle stats From 1a1f316ce20bf75c30bd4c45944202dbaa33bd10 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Sun, 18 Apr 2021 20:50:58 -0400 Subject: [PATCH 2/9] Add channnel shuffler --- src/lmic/lmic_channelshuffle.c | 217 +++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 src/lmic/lmic_channelshuffle.c diff --git a/src/lmic/lmic_channelshuffle.c b/src/lmic/lmic_channelshuffle.c new file mode 100644 index 00000000..252bbee5 --- /dev/null +++ b/src/lmic/lmic_channelshuffle.c @@ -0,0 +1,217 @@ +/* + +Module: lmic_channelshuffle.c + +Function: + Channel scheduling without replacement. + +Copyright and License: + This file copyright (C) 2021 by + + MCCI Corporation + 3520 Krums Corners Road + Ithaca, NY 14850 + + See accompanying LICENSE file for copyright and license information. + +Author: + Terry Moore, MCCI Corporation April 2021 + +*/ + +#include "lmic.h" +#include + +/****************************************************************************\ +| +| Manifest constants and local declarations. +| +\****************************************************************************/ + +static unsigned sidewaysSum16(const uint16_t *pMask, uint16_t nEntries); +static unsigned findNthSetBit(const uint16_t *pMask, uint16_t bitnum); + +/****************************************************************************\ +| +| Read-only data. +| +\****************************************************************************/ + +/****************************************************************************\ +| +| Variables. +| +\****************************************************************************/ + +/* + +Name: LMIC_findNextChannel() + +Function: + Scan a shuffle mask, and select a channel (without replacement). + +Definition: + int LMIC_findNextChannel( + uint16_t *pShuffleMask, + const uint16_t *pEnableMask, + uint16_t nEntries, + int lastChannel + ); + +Description: + pShuffleMask and pEnableMask are bit vectors. Channels correspond to + bits in little-endian order; entry [0] has channels 0 through 15, entry + [1] channels 16 through 31, and so forth. nEntries specifies the number + of entries in the mask vectors. The enable mask is 1 for a given channel + if that channel is eligible for selection, 0 otherwise. + + This routine selects channels from the shuffle mask until all entries + are exhausted; it then refreshes the shuffle mask from the enable mask. + + If it refreshes the channel mask, lastChannel is taken as a channel number + that is to be avoided in the next selection. (This is to avoid back-to-back + use of a channel across a refresh boundary.) Otherwise lastChannel is + ignored. This avoidance can be suppresed by setting lastChannel to -1. + If only one channel is enabled, lastChannel is also ignored. If lastChannel + is actually disabled, lastChannel is also ignored. + +Returns: + A channel number, in 0 .. nEntries-1, or -1 if the enable mask is + identically zero. + +Notes: + This routine is somewhat optimized for AVR processors, which don't have + multi-bit shifts. + +*/ + +int LMIC_findNextChannel( + uint16_t *pShuffleMask, + const uint16_t *pEnableMask, + uint16_t nEntries, + int lastChannel +) { + unsigned nSet16; + uint16_t saveLastChannelVal; + + // in case someone has changed the enable mask, update + // the shuffle mask so there are no disable bits set. + for (unsigned i = 0; i < nEntries; ++i) { + pShuffleMask[i] &= pEnableMask[i]; + } + + // count the set bits in the shuffle mask (with a factor of 16 for speed) + nSet16 = sidewaysSum16(pShuffleMask, nEntries); + + // if zero, copy the enable mask to the shuffle mask, and recount + if (nSet16 == 0) { + memcpy(pShuffleMask, pEnableMask, nEntries * sizeof(*pShuffleMask)); + nSet16 = sidewaysSum16(pShuffleMask, nEntries); + } else { + // don't try to skip the last channel becuase it can't be chosen. + lastChannel = -1; + } + + // if still zero, return -1. + if (nSet16 == 0) { + return -1; + } + + // if we have to skip a channel, and we have more than one choice, turn off + // the last channel bit. Post condition: if we really clered a bit, + // saveLastChannelVal will be non-zero. + saveLastChannelVal = 0; + if (nSet16 > 16 && lastChannel >= 0 && lastChannel <= nEntries * 16) { + uint16_t const saveLastChannelMask = (1 << (lastChannel & 0xF)); + + saveLastChannelVal = pShuffleMask[lastChannel >> 4] & saveLastChannelMask; + pShuffleMask[lastChannel >> 4] &= ~saveLastChannelMask; + + // if we cleared a bit, reduce the count. + if (saveLastChannelVal > 0) + nSet16 -= 16; + } + + if (saveLastChannelVal == 0) { + // We didn't eliminate a channel, so we don't have to worry. + lastChannel = -1; + } + + // get a random number + unsigned choice = os_getRndU2() % ((uint16_t)nSet16 >> 4); + + // choose a bit based on set bit + unsigned channel = findNthSetBit(pShuffleMask, choice); + pShuffleMask[channel / 16] ^= (1 << (channel & 0xF)); + + // handle channel skip + if (lastChannel >= 0) { + pShuffleMask[lastChannel >> 4] |= saveLastChannelVal; + } + return channel; +} + +static unsigned sidewaysSum16(const uint16_t *pMask, uint16_t nEntries) { + unsigned result; + + result = 0; + for (; nEntries > 0; --nEntries, ++pMask) + { + uint16_t v = *pMask; + + // the following is an adaptation of Knuth 7.1.3 (62). To avoid + // lots of shifts (slow on AVR, and code intensive) and table lookups, + // we sum popc * 16, then divide by 16. + + // sum adjacent bits, making a series of 2-bit sums + v = v - ((v >> 1) & 0x5555u); + v = (v & 0x3333u) + ((v >> 2) & 0x3333u); + // this assumes multiplies are essentialy free; + v = (v & 0xF0F0u) + ((v & 0x0F0Fu) * 16u); + // Accumulate result, but note it's times 16. + // AVR compiler should optimize the x8 shift. + result += (v & 0xFF) + (v >> 8); + } + + // + return result; +} + +static unsigned findNthSetBit(const uint16_t *pMask, uint16_t bitnum) { + unsigned result; + result = 0; + bitnum = bitnum * 16; + for (;; result += 16) { + uint16_t m = *pMask++; + if (m == 0) + continue; + uint16_t v = m - ((m >> 1) & 0x5555u); + v = (v & 0x3333u) + ((v >> 2) & 0x3333u); + // this assumes multiplies are essentialy free; + v = (v & 0xF0F0u) + ((v & 0x0F0Fu) * 16u); + // Accumulate result, but note it's times 16. + // AVR compiler should optimize the x8 shift. + v = (v & 0xFF) + (v >> 8); + if (v <= bitnum) + bitnum -= v; + else { + // the selected bit is in this word. We need to count. + while (bitnum > 0) { + m &= m - 1; + bitnum -= 16; + } + // now the lsb of m is our choice. + // get a mask, then use Knuth 7.1.3 (59) to find the + // bit number. + m &= -m; + result += ((m & 0x5555u) ? 0 : 1) + + ((m & 0x3333u) ? 0 : 2) + + ((m & 0x0F0Fu) ? 0 : 4) + + ((m & 0x00FFu) ? 0 : 8) + ; + break; + } + } + + return result; +} \ No newline at end of file From 9a45e7eec86e3280a93cbeff2c0803493470ca72 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Sun, 18 Apr 2021 23:06:42 -0400 Subject: [PATCH 3/9] Fix #515: Fix #619: add channel shuffling --- src/lmic/lmic.h | 11 +++++- src/lmic/lmic_as923.c | 81 ++++++++++++++++++++++++++--------------- src/lmic/lmic_eu868.c | 72 +++++++++++++++++++++++------------- src/lmic/lmic_eu_like.c | 23 ++++++++---- src/lmic/lmic_in866.c | 54 +++++++++++++++++---------- src/lmic/lmic_kr920.c | 54 +++++++++++++++++---------- src/lmic/lmic_us_like.c | 64 +++++++++++++++++--------------- 7 files changed, 227 insertions(+), 132 deletions(-) diff --git a/src/lmic/lmic.h b/src/lmic/lmic.h index 8fa7d760..46aed7cf 100644 --- a/src/lmic/lmic.h +++ b/src/lmic/lmic.h @@ -161,7 +161,7 @@ struct band_t { u2_t txcap; // duty cycle limitation: 1/txcap s1_t txpow; // maximum TX power u1_t lastchnl; // last used channel - ostime_t avail; // channel is blocked until this time + ostime_t avail; // band is blocked until this time }; TYPEDEF_xref2band_t; //!< \internal @@ -529,8 +529,10 @@ struct lmic_t { // bit map of enabled datarates for each channel u2_t channelDrMap[MAX_CHANNELS]; u2_t channelMap; + u2_t channelShuffleMap; #elif CFG_LMIC_US_like u2_t channelMap[(72+15)/16]; // enabled bits + u2_t channelShuffleMap[(72+15)/16]; // enabled bits u2_t activeChannels125khz; u2_t activeChannels500khz; #endif @@ -565,7 +567,10 @@ struct lmic_t { u1_t txChnl; // channel for next TX u1_t globalDutyRate; // max rate: 1/2^k - +#if CFG_LMIC_US_like + u1_t txChnl_125kHz; ///< during joins on 500 kHz, the 125 kHz channel + /// that was last used. +#endif u1_t upRepeat; // configured up repeat s1_t adrTxPow; // ADR adjusted TX power u1_t datarate; // current data rate @@ -701,6 +706,8 @@ int LMIC_getNetworkTimeReference(lmic_time_reference_t *pReference); int LMIC_registerRxMessageCb(lmic_rxmessage_cb_t *pRxMessageCb, void *pUserData); int LMIC_registerEventCb(lmic_event_cb_t *pEventCb, void *pUserData); +int LMIC_findNextChannel(uint16_t *, const uint16_t *, uint16_t, int); + // APIs for client half of compliance. typedef u1_t lmic_compliance_rx_action_t; diff --git a/src/lmic/lmic_as923.c b/src/lmic/lmic_as923.c index f46f50b0..3db98e49 100644 --- a/src/lmic/lmic_as923.c +++ b/src/lmic/lmic_as923.c @@ -315,36 +315,59 @@ void LMICas923_setRx1Params(void) { LMIC.rps = dndr2rps(LMIC.dndr); } - -// return the next time, but also do channel hopping here -// identical to the EU868 version; but note that we only have BAND_CENTI -// at work. +/// +/// \brief change the TX channel given the desired tx time. +/// +/// \param [in] now is the time at which we want to transmit. In fact, it's always +/// the current time. +/// +/// \returns the actual time at which we can transmit. \c LMIC.txChnl is set to the +/// selected channel. +/// +/// \details +/// We scan all the bands, creating a mask of all enabled channels that are +/// feasible at the earliest possible time. We then randomly choose one from +/// that, updating the shuffle mask. +/// +/// \note +/// identical to the EU868 version; but note that we only have BAND_CENTI +/// in AS923. +/// ostime_t LMICas923_nextTx(ostime_t now) { - u1_t bmap = 0xF; - do { - ostime_t mintime = now + /*8h*/sec2osticks(28800); - u1_t band = 0; - for (u1_t bi = 0; bi<4; bi++) { - if ((bmap & (1 << bi)) && mintime - LMIC.bands[bi].avail > 0) - mintime = LMIC.bands[band = bi].avail; - } - // Find next channel in given band - u1_t chnl = LMIC.bands[band].lastchnl; - for (u1_t ci = 0; ci= MAX_CHANNELS) - chnl -= MAX_CHANNELS; - if ((LMIC.channelMap & (1 << chnl)) != 0 && // channel enabled - (LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) != 0 && - band == (LMIC.channelFreq[chnl] & 0x3)) { // in selected band - LMIC.txChnl = LMIC.bands[band].lastchnl = chnl; - return mintime; - } - } - if ((bmap &= ~(1 << band)) == 0) { - // No feasible channel found! - return mintime; - } - } while (1); + ostime_t mintime = now + /*8h*/sec2osticks(28800); + u1_t band = 0; + uint16_t availmask; + + // set mintime to the earliest time. + for (u1_t bi = 0; bi<4; bi++) { + if (mintime - LMIC.bands[bi].avail > 0) + mintime = LMIC.bands[bi].avail; + } + + // scan all the enabled channels and make a mask of candidates + availmask = 0; + for (u1_t chnl = 0; chnl < MAX_CHANNELS; ++chnl) { + // not enabled? + if ((LMIC.channelMap & (1 << chnl)) == 0) + continue; + // not feasible? + if ((LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) == 0) + continue; + // not available yet? + u1_t const band = LMIC.channelFreq[chnl] & 0x3; + if (LMIC.bands[band].avail > mintime) + continue; + availmask |= 1 << chnl; + } + + // now: calculate the mask + int candidateCh = LMIC_findNextChannel( + &LMIC.channelShuffleMap, &availmask, 1, LMIC.txChnl == 0xFF ? -1 : 0); + if (candidateCh >= 0) { + // update the channel. + LMIC.txChnl = candidateCh; + } + return mintime; } #if !defined(DISABLE_BEACONS) diff --git a/src/lmic/lmic_eu868.c b/src/lmic/lmic_eu868.c index f8d51d15..1ad198af 100644 --- a/src/lmic/lmic_eu868.c +++ b/src/lmic/lmic_eu868.c @@ -204,32 +204,54 @@ ostime_t LMICeu868_nextJoinTime(ostime_t time) { return time; } +/// +/// \brief change the TX channel given the desired tx time. +/// +/// \param [in] now is the time at which we want to transmit. In fact, it's always +/// the current time. +/// +/// \returns the actual time at which we can transmit. \c LMIC.txChnl is set to the +/// selected channel. +/// +/// \details +/// We scan all the bands, creating a mask of all enabled channels that are +/// feasible at the earliest possible time. We then randomly choose one from +/// that, updating the shuffle mask. +/// ostime_t LMICeu868_nextTx(ostime_t now) { - u1_t bmap = 0xF; - do { - ostime_t mintime = now + /*8h*/sec2osticks(28800); - u1_t band = 0; - for (u1_t bi = 0; bi<4; bi++) { - if ((bmap & (1 << bi)) && mintime - LMIC.bands[bi].avail > 0) - mintime = LMIC.bands[band = bi].avail; - } - // Find next channel in given band - u1_t chnl = LMIC.bands[band].lastchnl; - for (u1_t ci = 0; ci= MAX_CHANNELS) - chnl -= MAX_CHANNELS; - if ((LMIC.channelMap & (1 << chnl)) != 0 && // channel enabled - (LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) != 0 && - band == (LMIC.channelFreq[chnl] & 0x3)) { // in selected band - LMIC.txChnl = LMIC.bands[band].lastchnl = chnl; - return mintime; - } - } - if ((bmap &= ~(1 << band)) == 0) { - // No feasible channel found! - return mintime; - } - } while (1); + ostime_t mintime = now + /*8h*/sec2osticks(28800); + u1_t band = 0; + uint16_t availmask; + + // set mintime to the earliest time. + for (u1_t bi = 0; bi<4; bi++) { + if (mintime - LMIC.bands[bi].avail > 0) + mintime = LMIC.bands[bi].avail; + } + + // scan all the enabled channels and make a mask of candidates + availmask = 0; + for (u1_t chnl = 0; chnl < MAX_CHANNELS; ++chnl) { + // not enabled? + if ((LMIC.channelMap & (1 << chnl)) == 0) + continue; + // not feasible? + if ((LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) == 0) + continue; + // not available yet? + u1_t const band = LMIC.channelFreq[chnl] & 0x3; + if (LMIC.bands[band].avail > mintime) + continue; + availmask |= 1 << chnl; + } + + // now: calculate the mask + int candidateCh = LMIC_findNextChannel(&LMIC.channelShuffleMap, &availmask, 1, LMIC.txChnl == 0xFF ? -1 : LMIC.txChnl); + if (candidateCh >= 0) { + // update the channel. + LMIC.txChnl = candidateCh; + } + return mintime; } diff --git a/src/lmic/lmic_eu_like.c b/src/lmic/lmic_eu_like.c index 46694e36..44533790 100644 --- a/src/lmic/lmic_eu_like.c +++ b/src/lmic/lmic_eu_like.c @@ -128,7 +128,9 @@ void LMICeulike_initJoinLoop(uint8_t nDefaultChannels, s1_t adrTxPow) { #if CFG_TxContinuousMode LMIC.txChnl = 0 #else - LMIC.txChnl = os_getRndU1() % nDefaultChannels; + uint16_t enableMap = (1 << nDefaultChannels) - 1; + LMIC.channelShuffleMap = enableMap; + LMIC.txChnl = LMIC_findNextChannel(&LMIC.channelShuffleMap, &enableMap, 1, -1); #endif LMIC.adrTxPow = adrTxPow; // TODO(tmm@mcci.com) don't use EU directly, use a table. That @@ -166,12 +168,11 @@ void LMICeulike_updateTx(ostime_t txbeg) { // ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels) { u1_t failed = 0; + u2_t enableMap = (1 << nDefaultChannels) - 1; // Try each default channel with same DR // If all fail try next lower datarate - if (++LMIC.txChnl == /* NUM_DEFAULT_CHANNELS */ nDefaultChannels) - LMIC.txChnl = 0; - if ((++LMIC.txCnt % nDefaultChannels) == 0) { + if (LMIC.channelShuffleMap == 0) { // Lower DR every nth try (having all default channels with same DR) // // TODO(tmm@mcci.com) add new DR_REGION_JOIN_MIN instead of LORAWAN_DR0; @@ -179,7 +180,6 @@ ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels) { // the failed flag here. This will cause the outer caller to take the // appropriate join path. Or add new LMICeulike_GetLowestJoinDR() // - // TODO(tmm@mcci.com) - see above; please remove regional dependency from this file. #if CFG_region == LMIC_REGION_as923 // in the join of AS923 v1.1 or older, only DR2 is used. @@ -187,15 +187,22 @@ ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels) { LMIC.datarate = AS923_DR_SF10; failed = 1; #else - if (LMIC.datarate == LORAWAN_DR0) + if (LMIC.datarate == LORAWAN_DR0) { failed = 1; // we have tried all DR - signal EV_JOIN_FAILED - else { + } else { LMICcore_setDrJoin(DRCHG_NOJACC, decDR((dr_t)LMIC.datarate)); } #endif } - // Clear NEXTCHNL because join state engine controls channel hopping + + // find new channel, avoiding repeats. + int newCh = LMIC_findNextChannel(&LMIC.channelShuffleMap, &enableMap, 1, LMIC.txChnl); + if (newCh >= 0) + LMIC.txChnl = newCh; + + // Clear OP_NEXTCHNL because join state engine controls channel hopping LMIC.opmode &= ~OP_NEXTCHNL; + // Move txend to randomize synchronized concurrent joins. // Duty cycle is based on txend. ostime_t const time = LMICbandplan_nextJoinTime(os_getTime()); diff --git a/src/lmic/lmic_in866.c b/src/lmic/lmic_in866.c index 15916a15..8887fc62 100644 --- a/src/lmic/lmic_in866.c +++ b/src/lmic/lmic_in866.c @@ -173,28 +173,44 @@ u4_t LMICin866_convFreq(xref2cu1_t ptr) { return freq; } -// return the next time, but also do channel hopping here -// since there's no duty cycle limitation, and no dwell limitation, -// we simply loop through the channels sequentially. +/// +/// \brief change the TX channel given the desired tx time. +/// +/// \param [in] now is the time at which we want to transmit. In fact, it's always +/// the current time. +/// +/// \returns the actual time at which we can transmit. \c LMIC.txChnl is set to the +/// selected channel. +/// +/// \details +/// We scan all the bands, creating a mask of all enabled channels that are +/// feasible at the earliest possible time. We then randomly choose one from +/// that, updating the shuffle mask. +/// +/// Since there's no duty cycle limitation, and no dwell limitation, +/// we just choose a channel from the shuffle and return the current time. +/// ostime_t LMICin866_nextTx(ostime_t now) { - const u1_t band = BAND_MILLI; - - for (u1_t ci = 0; ci < MAX_CHANNELS; ci++) { - // Find next channel in given band - u1_t chnl = LMIC.bands[band].lastchnl; - for (u1_t ci = 0; ci= MAX_CHANNELS) - chnl -= MAX_CHANNELS; - if ((LMIC.channelMap & (1 << chnl)) != 0 && // channel enabled - (LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) != 0 && - band == (LMIC.channelFreq[chnl] & 0x3)) { // in selected band - LMIC.txChnl = LMIC.bands[band].lastchnl = chnl; - return now; - } - } + uint16_t availmask; + + // scan all the enabled channels and make a mask of candidates + availmask = 0; + for (u1_t chnl = 0; chnl < MAX_CHANNELS; ++chnl) { + // not enabled? + if ((LMIC.channelMap & (1 << chnl)) == 0) + continue; + // not feasible? + if ((LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) == 0) + continue; + availmask |= 1 << chnl; } - // no enabled channel found! just use the last channel. + // now: calculate the mask + int candidateCh = LMIC_findNextChannel(&LMIC.channelShuffleMap, &availmask, 1, LMIC.txChnl == 0xFF ? -1 : LMIC.txChnl); + if (candidateCh >= 0) { + // update the channel. + LMIC.txChnl = candidateCh; + } return now; } diff --git a/src/lmic/lmic_kr920.c b/src/lmic/lmic_kr920.c index 9ea1dac7..a9d14bb3 100644 --- a/src/lmic/lmic_kr920.c +++ b/src/lmic/lmic_kr920.c @@ -184,28 +184,44 @@ u4_t LMICkr920_convFreq(xref2cu1_t ptr) { return freq; } -// return the next time, but also do channel hopping here -// since there's no duty cycle limitation, and no dwell limitation, -// we simply loop through the channels sequentially. +/// +/// \brief change the TX channel given the desired tx time. +/// +/// \param [in] now is the time at which we want to transmit. In fact, it's always +/// the current time. +/// +/// \returns the actual time at which we can transmit. \c LMIC.txChnl is set to the +/// selected channel. +/// +/// \details +/// We scan all the bands, creating a mask of all enabled channels that are +/// feasible at the earliest possible time. We then randomly choose one from +/// that, updating the shuffle mask. +/// +/// Since there's no duty cycle limitation, and no dwell limitation, +/// we just choose a channel from the shuffle and return the current time. +/// ostime_t LMICkr920_nextTx(ostime_t now) { - const u1_t band = BAND_MILLI; - - for (u1_t ci = 0; ci < MAX_CHANNELS; ci++) { - // Find next channel in given band - u1_t chnl = LMIC.bands[band].lastchnl; - for (u1_t ci = 0; ci= MAX_CHANNELS) - chnl -= MAX_CHANNELS; - if ((LMIC.channelMap & (1 << chnl)) != 0 && // channel enabled - (LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) != 0 && - band == (LMIC.channelFreq[chnl] & 0x3)) { // in selected band - LMIC.txChnl = LMIC.bands[band].lastchnl = chnl; - return now; - } - } + uint16_t availmask; + + // scan all the enabled channels and make a mask of candidates + availmask = 0; + for (u1_t chnl = 0; chnl < MAX_CHANNELS; ++chnl) { + // not enabled? + if ((LMIC.channelMap & (1 << chnl)) == 0) + continue; + // not feasible? + if ((LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) == 0) + continue; + availmask |= 1 << chnl; } - // no enabled channel found! just use the last channel. + // now: calculate the mask + int candidateCh = LMIC_findNextChannel(&LMIC.channelShuffleMap, &availmask, 1, LMIC.txChnl == 0xFF ? -1 : LMIC.txChnl); + if (candidateCh >= 0) { + // update the channel. + LMIC.txChnl = candidateCh; + } return now; } diff --git a/src/lmic/lmic_us_like.c b/src/lmic/lmic_us_like.c index d1383bdc..deb03fd8 100644 --- a/src/lmic/lmic_us_like.c +++ b/src/lmic/lmic_us_like.c @@ -36,36 +36,34 @@ # error "LMICuslike_getFirst500kHzDR() not defined by bandplan" #endif -static void setNextChannel(uint start, uint end, uint count) { +/// +/// \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> 4; + uint16_t const mapEntries = (end - start + 15) >> 4; + + int candidate = LMIC_findNextChannel( + LMIC.channelShuffleMap + mapStart, + LMIC.channelMap + mapStart, + mapEntries, + LMIC.txChnl == 0xFF ? -1 : LMIC.txChnl + ); + + if (candidate >= 0) + LMIC.txChnl = candidate; } @@ -87,8 +85,11 @@ void LMICuslike_initDefaultChannels(bit_t fJoin) { 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 @@ -230,11 +231,10 @@ bit_t LMICuslike_isDataRateFeasible(dr_t dr) { #if !defined(DISABLE_JOIN) void LMICuslike_initJoinLoop(void) { // set an initial condition so that setNextChannel()'s preconds are met - LMIC.txChnl = 0; + LMIC.txChnl = 0xFF; // then chose a new channel. This gives us a random first channel for - // the join. Minor nit: if channel 0 is enabled, it will never be used - // as the first join channel. The join logic uses the current txChnl, + // 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); @@ -277,10 +277,13 @@ ostime_t LMICuslike_nextJoinState(void) { 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 @@ -290,6 +293,7 @@ ostime_t LMICuslike_nextJoinState(void) { } 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; From fac837d5e54394f4e77b1bf314d7425047e3c88a Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Tue, 27 Apr 2021 18:26:56 -0400 Subject: [PATCH 4/9] Fix bug in US channel shuffler (bogus assert, channel num base) --- src/lmic/lmic_us_like.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lmic/lmic_us_like.c b/src/lmic/lmic_us_like.c index deb03fd8..d2e89d36 100644 --- a/src/lmic/lmic_us_like.c +++ b/src/lmic/lmic_us_like.c @@ -51,11 +51,11 @@ static void setNextChannel(uint16_t start, uint16_t end, uint16_t count) { ASSERT(count>0); ASSERT(start> 4; uint16_t const mapEntries = (end - start + 15) >> 4; - int candidate = LMIC_findNextChannel( + int candidate = start + LMIC_findNextChannel( LMIC.channelShuffleMap + mapStart, LMIC.channelMap + mapStart, mapEntries, From fac81a9db51497b1497aac72e186781d1738a7b3 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Wed, 28 Apr 2021 19:02:56 -0400 Subject: [PATCH 5/9] Fix channel allocate where we have bands (as, eu) --- src/lmic/lmic_as923.c | 80 +++++++++++++++++++++++++++++++-------- src/lmic/lmic_eu868.c | 87 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 135 insertions(+), 32 deletions(-) diff --git a/src/lmic/lmic_as923.c b/src/lmic/lmic_as923.c index 3db98e49..6b96086a 100644 --- a/src/lmic/lmic_as923.c +++ b/src/lmic/lmic_as923.c @@ -335,36 +335,84 @@ void LMICas923_setRx1Params(void) { /// ostime_t LMICas923_nextTx(ostime_t now) { ostime_t mintime = now + /*8h*/sec2osticks(28800); - u1_t band = 0; - uint16_t availmask; + u2_t availMap; + u2_t feasibleMap; + u1_t bandMap; + + // set mintime to the earliest time of all enabled channels + // (can't just look at bands); and for a given channel, we + // can't tell if we're ready till we've checked all possible + // avail times. + bandMap = 0; + for (u1_t chnl = 0; chnl < MAX_CHANNELS; ++chnl) { + u2_t chnlBit = 1 << chnl; + + // none at any higher numbers? + if (LMIC.channelMap < chnlBit) + break; + + // not enabled? + if ((LMIC.channelMap & chnlBit) == 0) + continue; + + // not feasible? + if ((LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) == 0) + continue; + + u1_t const band = LMIC.channelFreq[chnl] & 0x3; + u1_t const thisBandBit = 1 << band; + // already considered? + if ((bandMap & thisBandBit) != 0) + continue; + + // consider this band. + bandMap |= thisBandBit; - // set mintime to the earliest time. - for (u1_t bi = 0; bi<4; bi++) { - if (mintime - LMIC.bands[bi].avail > 0) - mintime = LMIC.bands[bi].avail; + // enabled, not considered, feasible: adjust the min time. + if ((s4_t)(mintime - LMIC.bands[band].avail) > 0) + mintime = LMIC.bands[band].avail; } - // scan all the enabled channels and make a mask of candidates - availmask = 0; + // make a mask of candidates available for use + availMap = 0; for (u1_t chnl = 0; chnl < MAX_CHANNELS; ++chnl) { - // not enabled? - if ((LMIC.channelMap & (1 << chnl)) == 0) + u2_t chnlBit = 1 << chnl; + + // none at any higher numbers? + if (LMIC.channelMap < chnlBit) + break; + + // not enabled? + if ((LMIC.channelMap & chnlBit) == 0) continue; + // not feasible? if ((LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) == 0) continue; + // not available yet? + feasibleMap |= chnlBit; + u1_t const band = LMIC.channelFreq[chnl] & 0x3; - if (LMIC.bands[band].avail > mintime) + if ((s4_t)(LMIC.bands[band].avail - mintime) > 0) continue; - availmask |= 1 << chnl; + + // ok: this is a candidate. + availMap |= chnlBit; } - // now: calculate the mask - int candidateCh = LMIC_findNextChannel( - &LMIC.channelShuffleMap, &availmask, 1, LMIC.txChnl == 0xFF ? -1 : 0); + // find the next available chennel. + u2_t saveShuffleMap = LMIC.channelShuffleMap; + int candidateCh = LMIC_findNextChannel(&LMIC.channelShuffleMap, &availMap, 1, LMIC.txChnl == 0xFF ? -1 : LMIC.txChnl); + + // restore bits in the shuffleMap that were on, but might have reset + // if availMap was used to refresh shuffleMap. These are channels that + // are feasble but not yet candidates due to band saturation + LMIC.channelShuffleMap |= saveShuffleMap & feasibleMap & ~availMap; + if (candidateCh >= 0) { - // update the channel. + // update the channel; otherwise we'll just use the + // most recent one. LMIC.txChnl = candidateCh; } return mintime; diff --git a/src/lmic/lmic_eu868.c b/src/lmic/lmic_eu868.c index 1ad198af..da81b561 100644 --- a/src/lmic/lmic_eu868.c +++ b/src/lmic/lmic_eu868.c @@ -214,41 +214,96 @@ ostime_t LMICeu868_nextJoinTime(ostime_t time) { /// selected channel. /// /// \details -/// We scan all the bands, creating a mask of all enabled channels that are +/// We scan all the channels, creating a mask of all enabled channels that are /// feasible at the earliest possible time. We then randomly choose one from /// that, updating the shuffle mask. /// +/// One sublety is that we have to cope with an artifact of the shuffler. +/// It will zero out bits for candidates that are real candidates, but +/// not in the time window, and not consider them as early as it should. +/// So we keep a mask of all feasible channels, and make sure that they +/// remain set in the shuffle mask if appropriate. +/// ostime_t LMICeu868_nextTx(ostime_t now) { ostime_t mintime = now + /*8h*/sec2osticks(28800); - u1_t band = 0; - uint16_t availmask; + u2_t availMap; + u2_t feasibleMap; + u1_t bandMap; + + // set mintime to the earliest time of all enabled channels + // (can't just look at bands); and for a given channel, we + // can't tell if we're ready till we've checked all possible + // avail times. + bandMap = 0; + for (u1_t chnl = 0; chnl < MAX_CHANNELS; ++chnl) { + u2_t chnlBit = 1 << chnl; + + // none at any higher numbers? + if (LMIC.channelMap < chnlBit) + break; + + // not enabled? + if ((LMIC.channelMap & chnlBit) == 0) + continue; - // set mintime to the earliest time. - for (u1_t bi = 0; bi<4; bi++) { - if (mintime - LMIC.bands[bi].avail > 0) - mintime = LMIC.bands[bi].avail; + // not feasible? + if ((LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) == 0) + continue; + + u1_t const band = LMIC.channelFreq[chnl] & 0x3; + u1_t const thisBandBit = 1 << band; + // already considered? + if ((bandMap & thisBandBit) != 0) + continue; + + // consider this band. + bandMap |= thisBandBit; + + // enabled, not considered, feasible: adjust the min time. + if ((s4_t)(mintime - LMIC.bands[band].avail) > 0) + mintime = LMIC.bands[band].avail; } - // scan all the enabled channels and make a mask of candidates - availmask = 0; + // make a mask of candidates available for use + availMap = 0; for (u1_t chnl = 0; chnl < MAX_CHANNELS; ++chnl) { - // not enabled? - if ((LMIC.channelMap & (1 << chnl)) == 0) + u2_t chnlBit = 1 << chnl; + + // none at any higher numbers? + if (LMIC.channelMap < chnlBit) + break; + + // not enabled? + if ((LMIC.channelMap & chnlBit) == 0) continue; + // not feasible? if ((LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) == 0) continue; + // not available yet? + feasibleMap |= chnlBit; + u1_t const band = LMIC.channelFreq[chnl] & 0x3; - if (LMIC.bands[band].avail > mintime) + if ((s4_t)(LMIC.bands[band].avail - mintime) > 0) continue; - availmask |= 1 << chnl; + + // ok: this is a candidate. + availMap |= chnlBit; } - // now: calculate the mask - int candidateCh = LMIC_findNextChannel(&LMIC.channelShuffleMap, &availmask, 1, LMIC.txChnl == 0xFF ? -1 : LMIC.txChnl); + // find the next available chennel. + u2_t saveShuffleMap = LMIC.channelShuffleMap; + int candidateCh = LMIC_findNextChannel(&LMIC.channelShuffleMap, &availMap, 1, LMIC.txChnl == 0xFF ? -1 : LMIC.txChnl); + + // restore bits in the shuffleMap that were on, but might have reset + // if availMap was used to refresh shuffleMap. These are channels that + // are feasble but not yet candidates due to band saturation + LMIC.channelShuffleMap |= saveShuffleMap & feasibleMap & ~availMap; + if (candidateCh >= 0) { - // update the channel. + // update the channel; otherwise we'll just use the + // most recent one. LMIC.txChnl = candidateCh; } return mintime; From e95f3323bd37a5f21f0e3e5be95667e442ed7320 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Thu, 29 Apr 2021 01:41:50 -0400 Subject: [PATCH 6/9] Fix #722: don't allow LMIC_setupChannel() to change default channels. --- src/lmic/lmic.h | 3 +++ src/lmic/lmic_as923.c | 24 ++++++++++++++++++------ src/lmic/lmic_au915.c | 19 ++++++++++++++++++- src/lmic/lmic_eu868.c | 23 +++++++++++++++++------ src/lmic/lmic_in866.c | 22 ++++++++++++++++------ src/lmic/lmic_kr920.c | 21 ++++++++++++++++----- src/lmic/lmic_us915.c | 16 ++++++++++++++++ 7 files changed, 104 insertions(+), 24 deletions(-) diff --git a/src/lmic/lmic.h b/src/lmic/lmic.h index 46aed7cf..da1de83e 100644 --- a/src/lmic/lmic.h +++ b/src/lmic/lmic.h @@ -660,6 +660,9 @@ bit_t LMIC_enableChannel(u1_t channel); bit_t LMIC_disableSubBand(u1_t band); bit_t LMIC_selectSubBand(u1_t band); +//! \brief get the number of (fixed) default channels before the progammable channels. +u1_t LMIC_queryNumDefaultChannels(void); + void LMIC_setDrTxpow (dr_t dr, s1_t txpow); // set default/start DR/txpow void LMIC_setAdrMode (bit_t enabled); // set ADR mode (if mobile turn off) diff --git a/src/lmic/lmic_as923.c b/src/lmic/lmic_as923.c index 6b96086a..2fb425fb 100644 --- a/src/lmic/lmic_as923.c +++ b/src/lmic/lmic_as923.c @@ -226,17 +226,29 @@ bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) { return 1; } + +/// +/// \brief query number of default channels. +/// +u1_t LMIC_queryNumDefaultChannels() { + return NUM_DEFAULT_CHANNELS; +} + +/// +/// \brief LMIC_setupChannel for EU 868 +/// +/// \note according to LoRaWAN 1.3 section 5.6, "the acceptable range +/// for **ChIndex** is N to 16", where N is our \c NUM_DEFAULT_CHANNELS. +/// This routine is used internally for MAC commands, so we enforce +/// this for the extenal API as well. +/// bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { // zero the band bits in freq, just in case. freq &= ~3; if (chidx < NUM_DEFAULT_CHANNELS) { - // can't disable a default channel. - if (freq == 0) - return 0; - // can't change a default channel. - else if (freq != (LMIC.channelFreq[chidx] & ~3)) - return 0; + // can't do anything to a default channel. + return 0; } bit_t fEnable = (freq != 0); if (chidx >= MAX_CHANNELS) diff --git a/src/lmic/lmic_au915.c b/src/lmic/lmic_au915.c index 08df1ad2..50069764 100644 --- a/src/lmic/lmic_au915.c +++ b/src/lmic/lmic_au915.c @@ -144,7 +144,24 @@ u4_t LMICau915_convFreq(xref2cu1_t ptr) { return freq; } -// au915: no support for xchannels. +/// +/// \brief query number of default channels. +/// +/// \note +/// For AU, we have no programmable channels; all channels +/// are fixed. Return the total channel count. +/// +u1_t LMIC_queryNumDefaultChannels() { + return 64 + 8; +} + + +/// +/// \brief LMIC_setupChannel for AU915 +/// +/// \note there are no progammable channels for US915, so this API +/// always returns FALSE. +/// bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { LMIC_API_PARAMETER(chidx); LMIC_API_PARAMETER(freq); diff --git a/src/lmic/lmic_eu868.c b/src/lmic/lmic_eu868.c index da81b561..64277960 100644 --- a/src/lmic/lmic_eu868.c +++ b/src/lmic/lmic_eu868.c @@ -140,17 +140,28 @@ static CONST_TABLE(u4_t, bandAssignments)[] = { 865000000 /* .. 868400000 */ | BAND_CENTI, }; +/// +/// \brief query number of default channels. +/// +u1_t LMIC_queryNumDefaultChannels() { + return NUM_DEFAULT_CHANNELS; +} + +/// +/// \brief LMIC_setupChannel for EU 868 +/// +/// \note according to LoRaWAN 1.0.3 section 5.6, "the acceptable range +/// for **ChIndex** is N to 16", where N is our \c NUM_DEFAULT_CHANNELS. +/// This routine is used internally for MAC commands, so we enforce +/// this for the extenal API as well. +/// bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { // zero the band bits in freq, just in case. freq &= ~3; if (chidx < NUM_DEFAULT_CHANNELS) { - // can't disable a default channel. - if (freq == 0) - return 0; - // can't change a default channel. - else if (freq != (LMIC.channelFreq[chidx] & ~3)) - return 0; + // can't do anything to a default channel. + return 0; } bit_t fEnable = (freq != 0); if (chidx >= MAX_CHANNELS) diff --git a/src/lmic/lmic_in866.c b/src/lmic/lmic_in866.c index 8887fc62..dfeaa57f 100644 --- a/src/lmic/lmic_in866.c +++ b/src/lmic/lmic_in866.c @@ -134,17 +134,27 @@ bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) { return 1; } +/// +/// \brief query number of default channels. +/// +u1_t LMIC_queryNumDefaultChannels() { + return NUM_DEFAULT_CHANNELS; +} + +/// +/// \brief LMIC_setupChannel for IN region +/// +/// \note according to LoRaWAN 1.3 section 5.6, "the acceptable range +/// for **ChIndex** is N to 16", where N is our \c NUM_DEFAULT_CHANNELS. +/// This routine is used internally for MAC commands, so we enforce +/// this for the extenal API as well. +/// bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { // zero the band bits in freq, just in case. freq &= ~3; if (chidx < NUM_DEFAULT_CHANNELS) { - // can't disable a default channel. - if (freq == 0) - return 0; - // can't change a default channel. - else if (freq != (LMIC.channelFreq[chidx] & ~3)) - return 0; + return 0; } bit_t fEnable = (freq != 0); if (chidx >= MAX_CHANNELS) diff --git a/src/lmic/lmic_kr920.c b/src/lmic/lmic_kr920.c index a9d14bb3..f97d830e 100644 --- a/src/lmic/lmic_kr920.c +++ b/src/lmic/lmic_kr920.c @@ -145,17 +145,28 @@ bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) { return 1; } +/// +/// \brief query number of default channels. +/// +u1_t LMIC_queryNumDefaultChannels() { + return NUM_DEFAULT_CHANNELS; +} + +/// +/// \brief LMIC_setupChannel for KR920 +/// +/// \note according to LoRaWAN 1.3 section 5.6, "the acceptable range +/// for **ChIndex** is N to 16", where N is our \c NUM_DEFAULT_CHANNELS. +/// This routine is used internally for MAC commands, so we enforce +/// this for the extenal API as well. +/// bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { // zero the band bits in freq, just in case. freq &= ~3; if (chidx < NUM_DEFAULT_CHANNELS) { - // can't disable a default channel. - if (freq == 0) - return 0; // can't change a default channel. - else if (freq != (LMIC.channelFreq[chidx] & ~3)) - return 0; + return 0; } bit_t fEnable = (freq != 0); if (chidx >= MAX_CHANNELS) diff --git a/src/lmic/lmic_us915.c b/src/lmic/lmic_us915.c index 352d687e..8e64e6bd 100644 --- a/src/lmic/lmic_us915.c +++ b/src/lmic/lmic_us915.c @@ -99,6 +99,22 @@ u4_t LMICus915_convFreq(xref2cu1_t ptr) { return freq; } +/// +/// \brief query number of default channels. +/// +/// For US, we have no programmable channels; all channels +/// are fixed. Return the total channel count. +/// +u1_t LMIC_queryNumDefaultChannels() { + return 64 + 8; +} + +/// +/// \brief LMIC_setupChannel for US915 +/// +/// \note there are no progammable channels for US915, so this API +/// always returns FALSE. +/// bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { LMIC_API_PARAMETER(chidx); LMIC_API_PARAMETER(freq); From 92ffb9cefebf4463653bcd77a9320be3c43376cd Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Thu, 29 Apr 2021 01:47:44 -0400 Subject: [PATCH 7/9] Fix #722 is technically a breaking change. So start toward version 4. --- src/lmic/lmic.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lmic/lmic.h b/src/lmic/lmic.h index da1de83e..4b5f1860 100644 --- a/src/lmic/lmic.h +++ b/src/lmic/lmic.h @@ -105,7 +105,8 @@ extern "C"{ #define ARDUINO_LMIC_VERSION_CALC(major, minor, patch, local) \ ((((major)*UINT32_C(1)) << 24) | (((minor)*UINT32_C(1)) << 16) | (((patch)*UINT32_C(1)) << 8) | (((local)*UINT32_C(1)) << 0)) -#define ARDUINO_LMIC_VERSION ARDUINO_LMIC_VERSION_CALC(3, 3, 0, 0) /* v3.3.0 */ +#define ARDUINO_LMIC_VERSION ARDUINO_LMIC_VERSION_CALC(3, 99, 0, 1) + /* 3.99.0-1 */ #define ARDUINO_LMIC_VERSION_GET_MAJOR(v) \ ((((v)*UINT32_C(1)) >> 24u) & 0xFFu) From 37b8e6c891191fd7e5301d6d5768f2c81131cfb5 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Sun, 2 May 2021 18:57:35 -0400 Subject: [PATCH 8/9] Fix #723: don't accept out-of-range DRs in --- src/lmic/lmic.h | 6 +++--- src/lmic/lmic_as923.c | 10 +++++++++- src/lmic/lmic_au915.c | 10 +++++++++- src/lmic/lmic_bandplan.h | 24 +++++++++++++++++++++++- src/lmic/lmic_bandplan_as923.h | 6 +++++- src/lmic/lmic_bandplan_au915.h | 6 +++++- src/lmic/lmic_bandplan_eu868.h | 6 +++++- src/lmic/lmic_bandplan_in866.h | 6 +++++- src/lmic/lmic_bandplan_kr920.h | 6 +++++- src/lmic/lmic_bandplan_us915.h | 6 +++++- src/lmic/lmic_compliance.c | 2 +- src/lmic/lmic_eu868.c | 10 +++++++++- src/lmic/lmic_in866.c | 10 +++++++++- src/lmic/lmic_kr920.c | 10 +++++++++- src/lmic/lmic_us915.c | 10 +++++++++- src/lmic/lorabase.h | 11 +---------- 16 files changed, 112 insertions(+), 27 deletions(-) diff --git a/src/lmic/lmic.h b/src/lmic/lmic.h index 4b5f1860..deab16ab 100644 --- a/src/lmic/lmic.h +++ b/src/lmic/lmic.h @@ -1,7 +1,7 @@ /* * Copyright (c) 2014-2016 IBM Corporation. * Copyright (c) 2016 Matthijs Kooijman. - * Copyright (c) 2016-2020 MCCI Corporation. + * Copyright (c) 2016-2021 MCCI Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -96,7 +96,7 @@ extern "C"{ #endif -// LMIC version -- this is ths IBM LMIC version +// LMIC version -- this is the IBM LMIC version #define LMIC_VERSION_MAJOR 1 #define LMIC_VERSION_MINOR 6 #define LMIC_VERSION_BUILD 1468577746 @@ -105,7 +105,7 @@ extern "C"{ #define ARDUINO_LMIC_VERSION_CALC(major, minor, patch, local) \ ((((major)*UINT32_C(1)) << 24) | (((minor)*UINT32_C(1)) << 16) | (((patch)*UINT32_C(1)) << 8) | (((local)*UINT32_C(1)) << 0)) -#define ARDUINO_LMIC_VERSION ARDUINO_LMIC_VERSION_CALC(3, 99, 0, 1) +#define ARDUINO_LMIC_VERSION ARDUINO_LMIC_VERSION_CALC(3, 99, 0, 2) /* 3.99.0-1 */ #define ARDUINO_LMIC_VERSION_GET_MAJOR(v) \ diff --git a/src/lmic/lmic_as923.c b/src/lmic/lmic_as923.c index 2fb425fb..5daa45e8 100644 --- a/src/lmic/lmic_as923.c +++ b/src/lmic/lmic_as923.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2014-2016 IBM Corporation. -* Copyright (c) 2017, 2019 MCCI Corporation. +* Copyright (c) 2017, 2019-2021 MCCI Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -50,6 +50,14 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { ILLEGAL_RPS }; +bit_t +LMICas923_validDR(dr_t dr) { + // use subtract here to avoid overflow + if (dr >= LENOF_TABLE(_DR2RPS_CRC) - 2) + return 0; + return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; +} + // see table in 2.7.6 -- this assumes UplinkDwellTime = 0. static CONST_TABLE(u1_t, maxFrameLens_dwell0)[] = { 59+5, // [0] diff --git a/src/lmic/lmic_au915.c b/src/lmic/lmic_au915.c index 50069764..301cfc7b 100644 --- a/src/lmic/lmic_au915.c +++ b/src/lmic/lmic_au915.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2014-2016 IBM Corporation. -* Copyright (c) 2017, 2019 MCCI Corporation. +* Copyright (c) 2017, 2019-2021 MCCI Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -55,6 +55,14 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { ILLEGAL_RPS }; +bit_t +LMICau915_validDR(dr_t dr) { + // use subtract here to avoid overflow + if (dr >= LENOF_TABLE(_DR2RPS_CRC) - 2) + return 0; + return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; +} + static CONST_TABLE(u1_t, maxFrameLens_dwell0)[] = { 59+5, 59+5, 59+5, 123+5, 250+5, 250+5, 250+5, 0, 61+5, 137+5, 250+5, 250+5, 250+5, 250+5 }; diff --git a/src/lmic/lmic_bandplan.h b/src/lmic/lmic_bandplan.h index db5863d2..e43a7ba0 100644 --- a/src/lmic/lmic_bandplan.h +++ b/src/lmic/lmic_bandplan.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2014-2016 IBM Corporation. -* Copyright (c) 2017, 2019 MCCI Corporation. +* Copyright (c) 2017, 2019-2021 MCCI Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -166,6 +166,10 @@ # error "LMICbandplan_isDataRateFeasible() not defined by bandplan" #endif +#if !defined(LMICbandplan_validDR) +# error "LMICbandplan_validDR() not defined by bandplan" +#endif + // // Things common to lmic.c code // @@ -228,4 +232,22 @@ ostime_t LMICcore_rndDelay(u1_t secSpan); void LMICcore_setDrJoin(u1_t reason, u1_t dr); ostime_t LMICcore_adjustForDrift(ostime_t delay, ostime_t hsym, rxsyms_t rxsyms_in); +// this has been exported to clients forever by lmic.h. including lorabase.h; +// but with multiband lorabase can't really safely do this; it's really an LMIC-ism. +// As are the rest of the static inlines.. + +///< \brief return non-zero if given DR is valid for this region. +static inline bit_t validDR (dr_t dr) { return LMICbandplan_validDR(dr); } // in range + +///< \brief region-specific table mapping DR to RPS/CRC bits; index by dr+1 +extern CONST_TABLE(u1_t, _DR2RPS_CRC)[]; + +static inline rps_t updr2rps (dr_t dr) { return (rps_t)TABLE_GET_U1(_DR2RPS_CRC, dr+1); } +static inline rps_t dndr2rps (dr_t dr) { return setNocrc(updr2rps(dr),1); } +static inline dr_t incDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+2)==ILLEGAL_RPS ? dr : (dr_t)(dr+1); } // increase data rate +static inline dr_t decDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr )==ILLEGAL_RPS ? dr : (dr_t)(dr-1); } // decrease data rate +static inline dr_t assertDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)==ILLEGAL_RPS ? (dr_t)DR_DFLTMIN : dr; } // force into a valid DR +static inline dr_t lowerDR (dr_t dr, u1_t n) { while(n--){dr=decDR(dr);} return dr; } // decrease data rate by n steps + + #endif // _lmic_bandplan_h_ diff --git a/src/lmic/lmic_bandplan_as923.h b/src/lmic/lmic_bandplan_as923.h index 16f4518d..69bb83d7 100644 --- a/src/lmic/lmic_bandplan_as923.h +++ b/src/lmic/lmic_bandplan_as923.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2014-2016 IBM Corporation. -* Copyright (c) 2017, 2019 MCCI Corporation. +* Copyright (c) 2017, 2019-2021 MCCI Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -108,4 +108,8 @@ void LMICas923_updateTx(ostime_t txbeg); ostime_t LMICas923_nextJoinTime(ostime_t now); #define LMICbandplan_nextJoinTime(now) LMICas923_nextJoinTime(now) +#undef LMICbandplan_validDR +bit_t LMICas923_validDR(dr_t dr); +#define LMICbandplan_validDR(dr) LMICas923_validDR(dr) + #endif // _lmic_bandplan_as923_h_ diff --git a/src/lmic/lmic_bandplan_au915.h b/src/lmic/lmic_bandplan_au915.h index f17194bc..78637799 100644 --- a/src/lmic/lmic_bandplan_au915.h +++ b/src/lmic/lmic_bandplan_au915.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2014-2016 IBM Corporation. -* Copyright (c) 2017, 2019 MCCI Corporation. +* Copyright (c) 2017, 2019-2021 MCCI Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -66,4 +66,8 @@ void LMICau915_setRx1Params(void); void LMICau915_updateTx(ostime_t txbeg); #define LMICbandplan_updateTx(txbeg) LMICau915_updateTx(txbeg) +#undef LMICbandplan_validDR +bit_t LMICau915_validDR(dr_t dr); +#define LMICbandplan_validDR(dr) LMICau915_validDR(dr) + #endif // _lmic_bandplan_au915_h_ diff --git a/src/lmic/lmic_bandplan_eu868.h b/src/lmic/lmic_bandplan_eu868.h index efff7d5c..83ec4056 100644 --- a/src/lmic/lmic_bandplan_eu868.h +++ b/src/lmic/lmic_bandplan_eu868.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2014-2016 IBM Corporation. -* Copyright (c) 2017, 2019 MCCI Corporation. +* Copyright (c) 2017, 2019-2021 MCCI Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -88,4 +88,8 @@ ostime_t LMICeu868_nextJoinTime(ostime_t now); void LMICeu868_setRx1Params(void); #define LMICbandplan_setRx1Params() LMICeu868_setRx1Params() +#undef LMICbandplan_validDR +bit_t LMICeu868_validDR(dr_t dr); +#define LMICbandplan_validDR(dr) LMICeu868_validDR(dr) + #endif // _lmic_eu868_h_ diff --git a/src/lmic/lmic_bandplan_in866.h b/src/lmic/lmic_bandplan_in866.h index dad10cab..04dae753 100644 --- a/src/lmic/lmic_bandplan_in866.h +++ b/src/lmic/lmic_bandplan_in866.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2014-2016 IBM Corporation. -* Copyright (c) 2017, 2019 MCCI Corporation. +* Copyright (c) 2017, 2019-2021 MCCI Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -81,4 +81,8 @@ void LMICin866_initDefaultChannels(bit_t join); void LMICin866_setRx1Params(void); #define LMICbandplan_setRx1Params() LMICin866_setRx1Params() +#undef LMICbandplan_validDR +bit_t LMICin866_validDR(dr_t dr); +#define LMICbandplan_validDR(dr) LMICin866_validDR(dr) + #endif // _lmic_bandplan_in866_h_ diff --git a/src/lmic/lmic_bandplan_kr920.h b/src/lmic/lmic_bandplan_kr920.h index 2c22f227..49beda0c 100644 --- a/src/lmic/lmic_bandplan_kr920.h +++ b/src/lmic/lmic_bandplan_kr920.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2014-2016 IBM Corporation. -* Copyright (c) 2017, 2019 MCCI Corporation. +* Copyright (c) 2017, 2019-2021 MCCI Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -88,4 +88,8 @@ void LMICkr920_setRx1Params(void); void LMICkr920_updateTx(ostime_t txbeg); #define LMICbandplan_updateTx(t) LMICkr920_updateTx(t) +#undef LMICbandplan_validDR +bit_t LMICkr920_validDR(dr_t dr); +#define LMICbandplan_validDR(dr) LMICkr920_validDR(dr) + #endif // _lmic_kr920_h_ diff --git a/src/lmic/lmic_bandplan_us915.h b/src/lmic/lmic_bandplan_us915.h index e08a7952..2c739c2e 100644 --- a/src/lmic/lmic_bandplan_us915.h +++ b/src/lmic/lmic_bandplan_us915.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2014-2016 IBM Corporation. -* Copyright (c) 2017, 2019 MCCI Corporation. +* Copyright (c) 2017, 2019-2021 MCCI Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -66,4 +66,8 @@ void LMICus915_setRx1Params(void); void LMICus915_updateTx(ostime_t txbeg); #define LMICbandplan_updateTx(txbeg) LMICus915_updateTx(txbeg) +#undef LMICbandplan_validDR +bit_t LMICus915_validDR(dr_t dr); +#define LMICbandplan_validDR(dr) LMICus915_validDR(dr) + #endif // _lmic_bandplan_us915_h_ diff --git a/src/lmic/lmic_compliance.c b/src/lmic/lmic_compliance.c index d1356b94..8d767b26 100644 --- a/src/lmic/lmic_compliance.c +++ b/src/lmic/lmic_compliance.c @@ -284,7 +284,7 @@ static void evMessage( break; } case LORAWAN_COMPLIANCE_CMD_LINK: { - // not clear what this request does. + // we are required to initiate a Link break; } case LORAWAN_COMPLIANCE_CMD_JOIN: { diff --git a/src/lmic/lmic_eu868.c b/src/lmic/lmic_eu868.c index 64277960..bf4f2a14 100644 --- a/src/lmic/lmic_eu868.c +++ b/src/lmic/lmic_eu868.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2014-2016 IBM Corporation. -* Copyright (c) 2017, 2019 MCCI Corporation. +* Copyright (c) 2017, 2019-2021 MCCI Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -49,6 +49,14 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { ILLEGAL_RPS }; +bit_t +LMICeu868_validDR(dr_t dr) { + // use subtract here to avoid overflow + if (dr >= LENOF_TABLE(_DR2RPS_CRC) - 2) + return 0; + return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; +} + static CONST_TABLE(u1_t, maxFrameLens)[] = { 59+5, 59+5, 59+5, 123+5, 250+5, 250+5, 250+5, 250+5 }; diff --git a/src/lmic/lmic_in866.c b/src/lmic/lmic_in866.c index dfeaa57f..3cb066ec 100644 --- a/src/lmic/lmic_in866.c +++ b/src/lmic/lmic_in866.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2014-2016 IBM Corporation. -* Copyright (c) 2017, 2019 MCCI Corporation. +* Copyright (c) 2017, 2019-2021 MCCI Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -49,6 +49,14 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { ILLEGAL_RPS }; +bit_t +LMICin866_validDR(dr_t dr) { + // use subtract here to avoid overflow + if (dr >= LENOF_TABLE(_DR2RPS_CRC) - 2) + return 0; + return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; +} + static CONST_TABLE(u1_t, maxFrameLens)[] = { 59+5, 59+5, 59+5, 123+5, 250+5, 250+5, 0, 250+5 }; diff --git a/src/lmic/lmic_kr920.c b/src/lmic/lmic_kr920.c index f97d830e..e295516d 100644 --- a/src/lmic/lmic_kr920.c +++ b/src/lmic/lmic_kr920.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2014-2016 IBM Corporation. -* Copyright (c) 2017, 2019 MCCI Corporation. +* Copyright (c) 2017, 2019-2021 MCCI Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,6 +47,14 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { ILLEGAL_RPS, // [6] }; +bit_t +LMICkr920_validDR(dr_t dr) { + // use subtract here to avoid overflow + if (dr >= LENOF_TABLE(_DR2RPS_CRC) - 2) + return 0; + return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; +} + static CONST_TABLE(u1_t, maxFrameLens)[] = { 59+5, 59+5, 59+5, 123+5, 250+5, 250+5 }; diff --git a/src/lmic/lmic_us915.c b/src/lmic/lmic_us915.c index 8e64e6bd..0cbf8529 100644 --- a/src/lmic/lmic_us915.c +++ b/src/lmic/lmic_us915.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2014-2016 IBM Corporation. -* Copyright (c) 2017, 2019 MCCI Corporation. +* Copyright (c) 2017, 2019-2021 MCCI Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -55,6 +55,14 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { ILLEGAL_RPS // [14] }; +bit_t +LMICus915_validDR(dr_t dr) { + // use subtract here to avoid overflow + if (dr >= LENOF_TABLE(_DR2RPS_CRC) - 2) + return 0; + return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; +} + static CONST_TABLE(u1_t, maxFrameLens)[] = { 19+5, 61+5, 133+5, 250+5, 250+5, 0, 0,0, 61+5, 133+5, 250+5, 250+5, 250+5, 250+5 diff --git a/src/lmic/lorabase.h b/src/lmic/lorabase.h index fc0fd2d6..f668f42a 100644 --- a/src/lmic/lorabase.h +++ b/src/lmic/lorabase.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2014-2016 IBM Corporation. - * Copyritght (c) 2017 MCCI Corporation. + * Copyright (c) 2017-2021 MCCI Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -638,17 +638,8 @@ static inline rps_t makeRps (sf_t sf, bw_t bw, cr_t cr, int ih, int nocrc) { #define MAKERPS(sf,bw,cr,ih,nocrc) ((rps_t)((sf) | ((bw)<<3) | ((cr)<<5) | ((nocrc)?(1<<7):0) | ((ih&0xFF)<<8))) // Two frames with params r1/r2 would interfere on air: same SFx + BWx static inline int sameSfBw(rps_t r1, rps_t r2) { return ((r1^r2)&0x1F) == 0; } - -extern CONST_TABLE(u1_t, _DR2RPS_CRC)[]; -static inline rps_t updr2rps (dr_t dr) { return (rps_t)TABLE_GET_U1(_DR2RPS_CRC, dr+1); } -static inline rps_t dndr2rps (dr_t dr) { return setNocrc(updr2rps(dr),1); } static inline int isFasterDR (dr_t dr1, dr_t dr2) { return dr1 > dr2; } static inline int isSlowerDR (dr_t dr1, dr_t dr2) { return dr1 < dr2; } -static inline dr_t incDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+2)==ILLEGAL_RPS ? dr : (dr_t)(dr+1); } // increase data rate -static inline dr_t decDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr )==ILLEGAL_RPS ? dr : (dr_t)(dr-1); } // decrease data rate -static inline dr_t assertDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)==ILLEGAL_RPS ? (dr_t)DR_DFLTMIN : dr; } // force into a valid DR -static inline bit_t validDR (dr_t dr) { return TABLE_GET_U1(_DR2RPS_CRC, dr+1)!=ILLEGAL_RPS; } // in range -static inline dr_t lowerDR (dr_t dr, u1_t n) { while(n--){dr=decDR(dr);} return dr; } // decrease data rate by n steps // // BEG: Keep in sync with lorabase.hpp From 13c8759d4e0a755b6693299440d7ada859352b88 Mon Sep 17 00:00:00 2001 From: Terry Moore Date: Sun, 2 May 2021 20:19:48 -0400 Subject: [PATCH 9/9] Fix #726: adopt semantic versions completely --- src/lmic/lmic.h | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/lmic/lmic.h b/src/lmic/lmic.h index deab16ab..b959511e 100644 --- a/src/lmic/lmic.h +++ b/src/lmic/lmic.h @@ -120,10 +120,35 @@ extern "C"{ #define ARDUINO_LMIC_VERSION_GET_LOCAL(v) \ ((v) & 0xFFu) +/// \brief convert a semantic version to an ordinal integer. +#define ARDUINO_LMIC_VERSION_TO_ORDINAL(v) \ + (((v) & 0xFFFFFF00u) | (((v) - 1) & 0xFFu)) + +/// \brief compare two semantic versions +/// \return \c true if \p a is less than \p b (as a semantic version). +#define ARDUINO_LMIC_VERSION_COMPARE_LT(a, b) \ + (ARDUINO_LMIC_VERSION_TO_ORDINAL(a) < ARDUINO_LMIC_VERSION_TO_ORDINAL(b)) + +/// \brief compare two semantic versions +/// \return \c true if \p a is less than or equal to \p b (as a semantic version). +#define ARDUINO_LMIC_VERSION_COMPARE_LE(a, b) \ + (ARDUINO_LMIC_VERSION_TO_ORDINAL(a) <= ARDUINO_LMIC_VERSION_TO_ORDINAL(b)) + +/// \brief compare two semantic versions +/// \return \c true if \p a is greater than \p b (as a semantic version). +#define ARDUINO_LMIC_VERSION_COMPARE_GT(a, b) \ + (ARDUINO_LMIC_VERSION_TO_ORDINAL(a) > ARDUINO_LMIC_VERSION_TO_ORDINAL(b)) + +/// \brief compare two semantic versions +/// \return \c true if \p a is greater than or equal to \p b (as a semantic version). +#define ARDUINO_LMIC_VERSION_COMPARE_GE(a, b) \ + (ARDUINO_LMIC_VERSION_TO_ORDINAL(a) >= ARDUINO_LMIC_VERSION_TO_ORDINAL(b)) + + //! Only For Antenna Tuning Tests ! //#define CFG_TxContinuousMode 1 -// since this was annouunced as the API variable, we keep it. But it's not used, +// since this was announced as the API variable, we keep it. But it's not used, // MAX_LEN_FRAME is what the code uses. enum { MAX_FRAME_LEN = MAX_LEN_FRAME }; //!< Library cap on max frame length @@ -132,10 +157,10 @@ enum { MAX_MISSED_BCNS = (2 * 60 * 60 + 127) / 128 }; //!< threshold for d // note that we need 100 ppm timing accuracy for // this, to keep the timing error to +/- 700ms. enum { MAX_RXSYMS = 350 }; // Stop tracking beacon if sync error grows beyond this. A 0.4% clock error - // at SF9.125k means 512 ms; one sybol is 4.096 ms, + // at SF9.125k means 512 ms; one symbol is 4.096 ms, // so this needs to be at least 125 for an STM32L0. // And for 100ppm clocks and 2 hours of beacon misses, - // this needs to accomodate 1.4 seconds of error at + // this needs to accommodate 1.4 seconds of error at // 4.096 ms/sym or at least 342 symbols. enum { LINK_CHECK_CONT = 0 , // continue with this after reported dead link