Skip to content

Commit

Permalink
Merge pull request #727 from mcci-catena/issue515
Browse files Browse the repository at this point in the history
Fix channel sequencing
  • Loading branch information
terrillmoore authored May 6, 2021
2 parents 1a6074b + 13c8759 commit 9a3792d
Show file tree
Hide file tree
Showing 20 changed files with 801 additions and 209 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
60 changes: 46 additions & 14 deletions src/lmic/lmic.h
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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, 2)
/* 3.99.0-1 */

#define ARDUINO_LMIC_VERSION_GET_MAJOR(v) \
((((v)*UINT32_C(1)) >> 24u) & 0xFFu)
Expand All @@ -119,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

Expand All @@ -131,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
Expand All @@ -161,7 +187,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 All @@ -172,10 +198,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;
};
Expand Down Expand Up @@ -531,10 +555,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
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 channelShuffleMap[(72+15)/16]; // enabled bits
u2_t activeChannels125khz;
u2_t activeChannels500khz;
#endif
Expand Down Expand Up @@ -569,7 +593,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 @@ -659,6 +686,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)

Expand Down Expand Up @@ -705,6 +735,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
163 changes: 127 additions & 36 deletions src/lmic/lmic_as923.c
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -226,17 +234,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)
Expand Down Expand Up @@ -315,36 +335,107 @@ 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);
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;

// enabled, not considered, feasible: adjust the min time.
if ((s4_t)(mintime - LMIC.bands[band].avail) > 0)
mintime = LMIC.bands[band].avail;
}

// make a mask of candidates available for use
availMap = 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;

// not available yet?
feasibleMap |= chnlBit;

u1_t const band = LMIC.channelFreq[chnl] & 0x3;
if ((s4_t)(LMIC.bands[band].avail - mintime) > 0)
continue;

// ok: this is a candidate.
availMap |= chnlBit;
}

// 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; otherwise we'll just use the
// most recent one.
LMIC.txChnl = candidateCh;
}
return mintime;
}

#if !defined(DISABLE_BEACONS)
Expand Down
29 changes: 27 additions & 2 deletions src/lmic/lmic_au915.c
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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 };
Expand Down Expand Up @@ -144,7 +152,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);
Expand Down
Loading

0 comments on commit 9a3792d

Please sign in to comment.