Skip to content

Commit

Permalink
Fix #515: Fix #619: add channel shuffling
Browse files Browse the repository at this point in the history
  • Loading branch information
terrillmoore committed Apr 19, 2021
1 parent 1a1f316 commit 9a45e7e
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 132 deletions.
11 changes: 9 additions & 2 deletions src/lmic/lmic.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand Down
81 changes: 52 additions & 29 deletions src/lmic/lmic_as923.c
Original file line number Diff line number Diff line change
Expand Up @@ -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; ci++) {
if ((chnl = (chnl + 1)) >= 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)
Expand Down
72 changes: 47 additions & 25 deletions src/lmic/lmic_eu868.c
Original file line number Diff line number Diff line change
Expand Up @@ -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; ci++) {
if ((chnl = (chnl + 1)) >= 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;
}


Expand Down
23 changes: 15 additions & 8 deletions src/lmic/lmic_eu_like.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -166,36 +168,41 @@ 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;
// then we can eliminate the LMIC_REGION_as923 below because we'll set
// 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.
// no need to change the DR.
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());
Expand Down
54 changes: 35 additions & 19 deletions src/lmic/lmic_in866.c
Original file line number Diff line number Diff line change
Expand Up @@ -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; ci++) {
if ((chnl = (chnl + 1)) >= 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;
}

Expand Down
54 changes: 35 additions & 19 deletions src/lmic/lmic_kr920.c
Original file line number Diff line number Diff line change
Expand Up @@ -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; ci++) {
if ((chnl = (chnl + 1)) >= 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;
}

Expand Down
Loading

0 comments on commit 9a45e7e

Please sign in to comment.