mirror of
				https://github.com/manuelbl/ttn-esp32.git
				synced 2025-10-31 02:30:35 +01:00 
			
		
		
		
	Upgrade to mcci-catena/arduino-lmic 4.0.1-pre
This commit is contained in:
		
							
								
								
									
										3
									
								
								.vscode/c_cpp_properties.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.vscode/c_cpp_properties.json
									
									
									
									
										vendored
									
									
								
							| @ -29,7 +29,8 @@ | ||||
|             "compilerPath": "/usr/bin/clang", | ||||
|             "cStandard": "c11", | ||||
|             "cppStandard": "c++11", | ||||
|             "intelliSenseMode": "clang-x64" | ||||
|             "intelliSenseMode": "clang-x64", | ||||
|             "compileCommands": "${workspaceFolder}/examples/hello_world/build/compile_commands.json" | ||||
|         } | ||||
|     ], | ||||
|     "version": 4 | ||||
|  | ||||
							
								
								
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							| @ -1,6 +1,6 @@ | ||||
| MIT License | ||||
|  | ||||
| Copyright (c) 2018 Manuel Bleichenbacher | ||||
| Copyright (c) 2018-2021 Manuel Bleichenbacher | ||||
|  | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
|  | ||||
| @ -12,11 +12,10 @@ This ESP32 component provides LoRaWAN communication with [The Things Network](ht | ||||
|  | ||||
| The library is based on the LMIC library from IBM (specifically the well-maintained version by MCCI – see their [GitHub repository](https://github.com/mcci-catena/arduino-lmic)) and provides a high-level API specifically targeted at The Things Network. | ||||
|  | ||||
| ## New in version 3.3 | ||||
| ## New in version 4.0 | ||||
|  | ||||
| - Verified compatibility with ESP-IDF v4.2 | ||||
| - Upgraded underlying library mcci-catena/arduino-lmic to v3.3.0 (no relevant changes) | ||||
| - Ensure interrupt code is in IRAM | ||||
| - Verified compatibility with ESP-IDF v4.3 | ||||
| - Upgraded underlying library mcci-catena/arduino-lmic to v4.0.1 (improved channel shuffling) | ||||
|  | ||||
|  | ||||
| ## Get Started | ||||
|  | ||||
| @ -13,7 +13,7 @@ | ||||
|         "url": "https://github.com/manuelbl/ttn-esp32.git", | ||||
|         "branch": "master" | ||||
|     }, | ||||
|     "version": "3.3.0", | ||||
|     "version": "4.0.0-pre", | ||||
|     "license": "MIT License", | ||||
|     "export": { | ||||
|         "include": [ | ||||
|  | ||||
| @ -1476,7 +1476,12 @@ ostime_t LMICcore_adjustForDrift (ostime_t delay, ostime_t hsym, rxsyms_t rxsyms | ||||
|         // a compile-time configuration. (In other words, assume that millis() | ||||
|         // clock is accurate to 0.1%.) You should never use clockerror to | ||||
|         // compensate for system-late problems. | ||||
|         u2_t const maxError = LMIC_kMaxClockError_ppm * MAX_CLOCK_ERROR / (1000 * 1000); | ||||
|         // note about compiler: The initializer for maxError is coded for | ||||
|         // maximum portability.  On 16-bit systems, some compilers complain | ||||
|         // if we write x / (1000 * 1000).  x / 1000 / 1000 uses constants, | ||||
|         // is generally acceptable so it can be optimized in compiler's own | ||||
|         // way. | ||||
|         u2_t const maxError = LMIC_kMaxClockError_ppm * MAX_CLOCK_ERROR / 1000 / 1000; | ||||
|         if (! LMIC_ENABLE_arbitrary_clock_error && clockerr > maxError) | ||||
|             { | ||||
|             clockerr = maxError; | ||||
| @ -1616,21 +1621,9 @@ static bit_t processJoinAccept (void) { | ||||
|     // initDefaultChannels(0) for EU-like, nothing otherwise | ||||
|     LMICbandplan_joinAcceptChannelClear(); | ||||
|  | ||||
|     if (!LMICbandplan_hasJoinCFlist() && dlen > LEN_JA) { | ||||
|             // if no JoinCFList, we're supposed to continue | ||||
|             // the join per 2.2.5 of LoRaWAN regional 2.2.4 | ||||
|             // https://github.com/mcci-catena/arduino-lmic/issues/19 | ||||
|     } else if ( LMICbandplan_hasJoinCFlist() && dlen > LEN_JA ) { | ||||
|         dlen = OFF_CFLIST; | ||||
|         for( u1_t chidx=3; chidx<8; chidx++, dlen+=3 ) { | ||||
|             u4_t freq = LMICbandplan_convFreq(&LMIC.frame[dlen]); | ||||
|             if( freq ) { | ||||
|                 LMIC_setupChannel(chidx, freq, 0, -1); | ||||
| #if LMIC_DEBUG_LEVEL > 1 | ||||
|                 LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": Setup channel, idx=%d, freq=%"PRIu32"\n", os_getTime(), chidx, freq); | ||||
| #endif | ||||
|             } | ||||
|         } | ||||
|     // process the CFList if present | ||||
|     if (dlen == LEN_JAEXT) { | ||||
|         LMICbandplan_processJoinAcceptCFList(); | ||||
|     } | ||||
|  | ||||
|     // already incremented when JOIN REQ got sent off | ||||
| @ -2882,7 +2875,11 @@ dr_t LMIC_feasibleDataRateForFrame(dr_t dr, u1_t payloadSize) { | ||||
| } | ||||
|  | ||||
| static bit_t isTxPathBusy(void) { | ||||
|     return (LMIC.opmode & (OP_TXDATA|OP_JOINING)) != 0; | ||||
|     return (LMIC.opmode & (OP_POLL | OP_TXDATA | OP_JOINING | OP_TXRXPEND)) != 0; | ||||
| } | ||||
|  | ||||
| bit_t LMIC_queryTxReady (void) { | ||||
|     return ! isTxPathBusy(); | ||||
| } | ||||
|  | ||||
| static bit_t adjustDrForFrameIfNotBusy(u1_t len) { | ||||
| @ -2902,6 +2899,10 @@ void LMIC_setTxData (void) { | ||||
| } | ||||
|  | ||||
| void LMIC_setTxData_strict (void) { | ||||
|     if (isTxPathBusy()) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     LMICOS_logEventUint32(__func__, ((u4_t)LMIC.pendTxPort << 24u) | ((u4_t)LMIC.pendTxConf << 16u) | (LMIC.pendTxLen << 0u)); | ||||
|     LMIC.opmode |= OP_TXDATA; | ||||
|     if( (LMIC.opmode & OP_JOINING) == 0 ) { | ||||
| @ -2920,7 +2921,7 @@ lmic_tx_error_t LMIC_setTxData2 (u1_t port, xref2u1_t data, u1_t dlen, u1_t conf | ||||
|  | ||||
| // send a message w/o callback; do not adjust data rate | ||||
| lmic_tx_error_t LMIC_setTxData2_strict (u1_t port, xref2u1_t data, u1_t dlen, u1_t confirmed) { | ||||
|     if ( LMIC.opmode & OP_TXDATA ) { | ||||
|     if (isTxPathBusy()) { | ||||
|         // already have a message queued | ||||
|         return LMIC_ERROR_TX_BUSY; | ||||
|     } | ||||
| @ -2940,7 +2941,7 @@ lmic_tx_error_t LMIC_setTxData2_strict (u1_t port, xref2u1_t data, u1_t dlen, u1 | ||||
|             return LMIC_ERROR_TX_FAILED; | ||||
|         } | ||||
|     } | ||||
|     return 0; | ||||
|     return LMIC_ERROR_SUCCESS; | ||||
| } | ||||
|  | ||||
| // send a message with callback; try to adjust data rate | ||||
|  | ||||
| @ -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,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(4, 0, 1, 1)  /* 4.0.1-pre1 */ | ||||
|  | ||||
| #define	ARDUINO_LMIC_VERSION_GET_MAJOR(v)	\ | ||||
| 	((((v)*UINT32_C(1)) >> 24u) & 0xFFu) | ||||
| @ -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 | ||||
|  | ||||
| @ -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 | ||||
| @ -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 | ||||
|  | ||||
| @ -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; | ||||
| }; | ||||
| @ -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 | ||||
| @ -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 | ||||
| @ -659,6 +686,12 @@ 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 programmable channels. | ||||
| u1_t  LMIC_queryNumDefaultChannels(void); | ||||
|  | ||||
| //! \brief check whether the LMIC is ready for a transmit packet | ||||
| bit_t LMIC_queryTxReady(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) | ||||
|  | ||||
| @ -705,6 +738,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; | ||||
|  | ||||
|  | ||||
| @ -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] | ||||
| @ -226,16 +234,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 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)) | ||||
|                 // can't do anything to a default channel. | ||||
|                 return 0; | ||||
|         } | ||||
|         bit_t fEnable = (freq != 0); | ||||
| @ -315,36 +335,109 @@ 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; | ||||
|         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; | ||||
|         feasibleMap = 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; | ||||
|  | ||||
|                 // This channel is feasible. But might not be available. | ||||
|                 feasibleMap |= chnlBit; | ||||
|  | ||||
|                 // not available yet? | ||||
|                 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; | ||||
|         } | ||||
|                 // 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); | ||||
| } | ||||
|  | ||||
| #if !defined(DISABLE_BEACONS) | ||||
|  | ||||
| @ -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 }; | ||||
| @ -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); | ||||
|  | ||||
| @ -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,14 @@ | ||||
| # error "LMICbandplan_isDataRateFeasible() not defined by bandplan" | ||||
| #endif | ||||
|  | ||||
| #if !defined(LMICbandplan_validDR) | ||||
| # error "LMICbandplan_validDR() not defined by bandplan" | ||||
| #endif | ||||
|  | ||||
| #if !defined(LMICbandplan_processJoinAcceptCFList) | ||||
| # error "LMICbandplan_processJoinAcceptCFList() not defined by bandplan" | ||||
| #endif | ||||
|  | ||||
| // | ||||
| // Things common to lmic.c code | ||||
| // | ||||
| @ -228,4 +236,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_ | ||||
|  | ||||
| @ -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_ | ||||
|  | ||||
| @ -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_ | ||||
|  | ||||
| @ -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_ | ||||
|  | ||||
| @ -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_ | ||||
|  | ||||
| @ -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_ | ||||
|  | ||||
| @ -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_ | ||||
|  | ||||
							
								
								
									
										217
									
								
								src/lmic/lmic_channelshuffle.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								src/lmic/lmic_channelshuffle.c
									
									
									
									
									
										Normal file
									
								
							| @ -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 <string.h> | ||||
|  | ||||
| /****************************************************************************\ | ||||
| | | ||||
| |   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; | ||||
| } | ||||
| @ -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: { | ||||
|  | ||||
| @ -118,30 +118,6 @@ Revision history: | ||||
|                                 (1 << LMIC_REGION_in866) |      \ | ||||
|                                 0) | ||||
|  | ||||
| // | ||||
| // Our input is a -D of one of CFG_eu868, CFG_us915, CFG_as923, CFG_au915, CFG_in866 | ||||
| // More will be added in the the future. So at this point we create CFG_region with | ||||
| // following values. These are in order of the sections in the manual. Not all of the | ||||
| // below are supported yet. | ||||
| // | ||||
| // CFG_as923jp is treated as a special case of CFG_as923, so it's not included in | ||||
| // the below. | ||||
| // | ||||
| // TODO(tmm@mcci.com) consider moving this block to a central file as it's not | ||||
| // user-editable. | ||||
| // | ||||
| # define CFG_LMIC_REGION_MASK   \ | ||||
|                         ((defined(CFG_eu868) << LMIC_REGION_eu868) | \ | ||||
|                          (defined(CFG_us915) << LMIC_REGION_us915) | \ | ||||
|                          (defined(CFG_cn783) << LMIC_REGION_cn783) | \ | ||||
|                          (defined(CFG_eu433) << LMIC_REGION_eu433) | \ | ||||
|                          (defined(CFG_au915) << LMIC_REGION_au915) | \ | ||||
|                          (defined(CFG_cn490) << LMIC_REGION_cn490) | \ | ||||
|                          (defined(CFG_as923) << LMIC_REGION_as923) | \ | ||||
|                          (defined(CFG_kr920) << LMIC_REGION_kr920) | \ | ||||
|                          (defined(CFG_in866) << LMIC_REGION_in866) | \ | ||||
|                          0) | ||||
|  | ||||
| // the selected region. | ||||
| // TODO(tmm@mcci.com) consider moving this block to a central file as it's not | ||||
| // user-editable. | ||||
| @ -171,11 +147,94 @@ Revision history: | ||||
| # define CFG_region     0 | ||||
| #endif | ||||
|  | ||||
| // a bitmask of EU-like regions -- these are regions which have up to 16 | ||||
| // channels indidually programmable via downloink. | ||||
| // | ||||
| // TODO(tmm@mcci.com) consider moving this block to a central file as it's not | ||||
| // user-editable. | ||||
| // LMIC_CFG_*_ENA -- either 1 or 0 based on whether that region | ||||
| // is enabled.  Note: these must be after the code that special-cases | ||||
| // CFG_as923jp. | ||||
| #if defined(CFG_eu868) | ||||
| # define LMIC_CFG_eu868_ENA  1 | ||||
| #else | ||||
| # define LMIC_CFG_eu868_ENA  0 | ||||
| #endif | ||||
|  | ||||
| #if defined(CFG_us915) | ||||
| # define LMIC_CFG_us915_ENA  1 | ||||
| #else | ||||
| # define LMIC_CFG_us915_ENA  0 | ||||
| #endif | ||||
|  | ||||
| #if defined(CFG_cn783) | ||||
| # define LMIC_CFG_cn783_ENA  1 | ||||
| #else | ||||
| # define LMIC_CFG_cn783_ENA  0 | ||||
| #endif | ||||
|  | ||||
| #if defined(CFG_eu433) | ||||
| # define LMIC_CFG_eu433_ENA  1 | ||||
| #else | ||||
| # define LMIC_CFG_eu433_ENA  0 | ||||
| #endif | ||||
|  | ||||
| #if defined(CFG_au915) | ||||
| # define LMIC_CFG_au915_ENA  1 | ||||
| #else | ||||
| # define LMIC_CFG_au915_ENA  0 | ||||
| #endif | ||||
|  | ||||
| #if defined(CFG_cn490) | ||||
| # define LMIC_CFG_cn490_ENA  1 | ||||
| #else | ||||
| # define LMIC_CFG_cn490_ENA  0 | ||||
| #endif | ||||
|  | ||||
| #if defined(CFG_as923) | ||||
| # define LMIC_CFG_as923_ENA  1 | ||||
| #else | ||||
| # define LMIC_CFG_as923_ENA  0 | ||||
| #endif | ||||
|  | ||||
| #if defined(CFG_kr920) | ||||
| # define LMIC_CFG_kr920_ENA  1 | ||||
| #else | ||||
| # define LMIC_CFG_kr920_ENA  0 | ||||
| #endif | ||||
|  | ||||
| #if defined(CFG_in866) | ||||
| # define LMIC_CFG_in866_ENA  1 | ||||
| #else | ||||
| # define LMIC_CFG_in866_ENA  0 | ||||
| #endif | ||||
|  | ||||
| /// \brief Bitmask of configured regions | ||||
| /// | ||||
| /// Our input is a -D of one of CFG_eu868, CFG_us915, CFG_as923, CFG_au915, CFG_in866 | ||||
| /// More will be added in the the future. So at this point we create CFG_region with | ||||
| /// following values. These are in order of the sections in the manual. Not all of the | ||||
| /// below are supported yet. | ||||
| /// | ||||
| /// CFG_as923jp is treated as a special case of CFG_as923, so it's not included in | ||||
| /// the below. | ||||
| /// | ||||
| /// TODO(tmm@mcci.com) consider moving this block to a central file as it's not | ||||
| /// user-editable. | ||||
| /// | ||||
| # define CFG_LMIC_REGION_MASK                                         \ | ||||
|                         ((LMIC_CFG_eu868_ENA << LMIC_REGION_eu868) | \ | ||||
|                          (LMIC_CFG_us915_ENA << LMIC_REGION_us915) | \ | ||||
|                          (LMIC_CFG_cn783_ENA << LMIC_REGION_cn783) | \ | ||||
|                          (LMIC_CFG_eu433_ENA << LMIC_REGION_eu433) | \ | ||||
|                          (LMIC_CFG_au915_ENA << LMIC_REGION_au915) | \ | ||||
|                          (LMIC_CFG_cn490_ENA << LMIC_REGION_cn490) | \ | ||||
|                          (LMIC_CFG_as923_ENA << LMIC_REGION_as923) | \ | ||||
|                          (LMIC_CFG_kr920_ENA << LMIC_REGION_kr920) | \ | ||||
|                          (LMIC_CFG_in866_ENA << LMIC_REGION_in866) | \ | ||||
|                          0) | ||||
|  | ||||
| /// \brief a bitmask of EU-like regions | ||||
| /// | ||||
| /// EU-like regions have up to 16 channels individually programmable via downlink. | ||||
| /// | ||||
| /// TODO(tmm@mcci.com) consider moving this block to a central file as it's not | ||||
| /// user-editable. | ||||
| #define CFG_LMIC_EU_like_MASK   (                               \ | ||||
|                                 (1 << LMIC_REGION_eu868) |      \ | ||||
|                              /* (1 << LMIC_REGION_us915) | */   \ | ||||
| @ -188,12 +247,14 @@ Revision history: | ||||
|                                 (1 << LMIC_REGION_in866) |      \ | ||||
|                                 0) | ||||
|  | ||||
| // a bitmask of` US-like regions -- these are regions with 64 fixed 125 kHz channels | ||||
| // overlaid by 8 500 kHz channels. The channel frequencies can't be changed, but | ||||
| // subsets of channels can be selected via masks. | ||||
| // | ||||
| // TODO(tmm@mcci.com) consider moving this block to a central file as it's not | ||||
| // user-editable. | ||||
| /// \brief bitmask of` US-like regions | ||||
| /// | ||||
| /// US-like regions have 64 fixed 125-kHz channels overlaid by 8 500-kHz | ||||
| /// channels. The channel frequencies can't be changed, but | ||||
| /// subsets of channels can be selected via masks. | ||||
| /// | ||||
| /// TODO(tmm@mcci.com) consider moving this block to a central file as it's not | ||||
| /// user-editable. | ||||
| #define CFG_LMIC_US_like_MASK   (                               \ | ||||
|                              /* (1 << LMIC_REGION_eu868) | */   \ | ||||
|                                 (1 << LMIC_REGION_us915) |      \ | ||||
| @ -211,16 +272,19 @@ Revision history: | ||||
| // TODO(tmm@mcci.com) consider moving this block to a central file as it's not | ||||
| // user-editable. | ||||
| // | ||||
|  | ||||
| /// \brief true if configured region is EU-like, false otherwise. | ||||
| #define CFG_LMIC_EU_like        (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_EU_like_MASK)) | ||||
| /// \brief true if configured region is US-like, false otherwise. | ||||
| #define CFG_LMIC_US_like        (!!(CFG_LMIC_REGION_MASK & CFG_LMIC_US_like_MASK)) | ||||
|  | ||||
| // | ||||
| // The supported LMIC LoRaWAAN spec versions. These need to be numerically ordered, | ||||
| // The supported LMIC LoRaWAN spec versions. These need to be numerically ordered, | ||||
| // so that we can (for example) compare | ||||
| // | ||||
| //    LMIC_LORAWAN_SPEC_VERSION < LMIC_LORAWAN_SPEC_VERSION_1_0_3. | ||||
| // | ||||
| #define LMIC_LORAWAN_SPEC_VERSION_1_0_2  0x01000200u | ||||
| #define LMIC_LORAWAN_SPEC_VERSION_1_0_3  0x01000300u | ||||
| #define LMIC_LORAWAN_SPEC_VERSION_1_0_2  0x01000200u  /// Refers to LoRaWAN spec 1.0.2 | ||||
| #define LMIC_LORAWAN_SPEC_VERSION_1_0_3  0x01000300u  /// Refers to LoRaWAN spec 1.0.3 | ||||
|  | ||||
| #endif /* _LMIC_CONFIG_PRECONDITIONS_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 | ||||
| @ -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 | ||||
| }; | ||||
| @ -140,16 +148,27 @@ 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)) | ||||
|                 // can't do anything to a default channel. | ||||
|                 return 0; | ||||
|         } | ||||
|         bit_t fEnable = (freq != 0); | ||||
| @ -204,32 +223,111 @@ 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 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) { | ||||
|         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; | ||||
|         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; | ||||
|         feasibleMap = 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; | ||||
|  | ||||
|                 // This channel is feasible. But might not be available. | ||||
|                 feasibleMap |= chnlBit; | ||||
|  | ||||
|                 // not available yet? | ||||
|                 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; | ||||
|         } | ||||
|                 // 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); | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @ -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()); | ||||
| @ -220,6 +227,27 @@ ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels) { | ||||
| } | ||||
| #endif // !DISABLE_JOIN | ||||
|  | ||||
| #if !defined(DISABLE_JOIN) | ||||
| void LMICeulike_processJoinAcceptCFList(void) { | ||||
|     if ( LMICbandplan_hasJoinCFlist() && | ||||
|          LMIC.frame[OFF_CFLIST + 15] == LORAWAN_JoinAccept_CFListType_FREQUENCIES) { | ||||
|         u1_t dlen; | ||||
|         u1_t nDefault = LMIC_queryNumDefaultChannels(); | ||||
|  | ||||
|         dlen = OFF_CFLIST; | ||||
|         for( u1_t chidx = nDefault; chidx < nDefault + 5; chidx++, dlen+=3 ) { | ||||
|             u4_t freq = LMICbandplan_convFreq(&LMIC.frame[dlen]); | ||||
|             if( freq ) { | ||||
|                 LMIC_setupChannel(chidx, freq, 0, -1); | ||||
| #if LMIC_DEBUG_LEVEL > 1 | ||||
|                 LMIC_DEBUG_PRINTF("%"LMIC_PRId_ostime_t": Setup channel, idx=%d, freq=%"PRIu32"\n", os_getTime(), chidx, freq); | ||||
| #endif | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| #endif // !DISABLE_JOIN | ||||
|  | ||||
| void LMICeulike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer) { | ||||
|     os_copyMem( | ||||
|             pStateBuffer->channelFreq, | ||||
|  | ||||
| @ -66,6 +66,11 @@ enum { BAND_MILLI = 0, BAND_CENTI = 1, BAND_DECI = 2, BAND_AUX = 3 }; | ||||
| // there's a CFList on joins for EU-like plans | ||||
| #define LMICbandplan_hasJoinCFlist()    (1) | ||||
|  | ||||
| /// \brief process CFLists from JoinAccept for EU-like regions | ||||
| void LMICeulike_processJoinAcceptCFList(void); | ||||
| /// \brief by default, EU-like plans use LMICeulike_processJoinAcceptCFList | ||||
| #define LMICbandplan_processJoinAcceptCFList    LMICeulike_processJoinAcceptCFList | ||||
|  | ||||
| #define LMICbandplan_advanceBeaconChannel()     \ | ||||
|         do { /* nothing */ } while (0) | ||||
|  | ||||
|  | ||||
| @ -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 | ||||
| }; | ||||
| @ -134,16 +142,26 @@ 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; | ||||
|         } | ||||
|         bit_t fEnable = (freq != 0); | ||||
| @ -173,28 +191,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; | ||||
|         uint16_t availmask; | ||||
|  | ||||
|         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; | ||||
|                         } | ||||
|                 } | ||||
|         // 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; | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -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 | ||||
| }; | ||||
| @ -145,16 +153,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 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; | ||||
|         } | ||||
|         bit_t fEnable = (freq != 0); | ||||
| @ -184,28 +203,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; | ||||
|         uint16_t availmask; | ||||
|  | ||||
|         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; | ||||
|                         } | ||||
|                 } | ||||
|         // 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; | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -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 | ||||
| @ -99,21 +107,34 @@ 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); | ||||
|         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; | ||||
| } | ||||
|  | ||||
| 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 +149,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,14 +218,8 @@ 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]; | ||||
|                 } | ||||
|         } | ||||
|  | ||||
|         // Update global duty cycle stats | ||||
|         if (LMIC.globalDutyRate != 0) { | ||||
|  | ||||
| @ -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<end); | ||||
|         ASSERT(count <= (end - start)); | ||||
|         // We used to pick a random channel once and then just increment. That is not per spec. | ||||
|         // Now we use a new random number each time, because they are not very expensive. | ||||
|         // Regarding the algo below, we cannot pick a number and scan until we hit an enabled channel. | ||||
|         // That would result in the first enabled channel following a set of disabled ones | ||||
|         // being used more frequently than the other enabled channels. | ||||
|         ASSERT((start & 0xF) == 0); | ||||
|         uint16_t const mapStart = start >> 4; | ||||
|         uint16_t const mapEntries = (end - start + 15) >> 4; | ||||
|  | ||||
|         // Last used channel is in range. It is not a candidate, per spec. | ||||
|         uint lastTxChan = LMIC.txChnl; | ||||
|         if (start <= lastTxChan && lastTxChan<end && | ||||
|                 // Adjust count only if still enabled. Otherwise, no chance of selection. | ||||
|                 ENABLED_CHANNEL(lastTxChan)) { | ||||
|                 --count; | ||||
|                 if (count == 0) { | ||||
|                         return; // Only one active channel, so keep using it. | ||||
|                 } | ||||
|         } | ||||
|         int candidate = start + LMIC_findNextChannel( | ||||
|                                 LMIC.channelShuffleMap + mapStart, | ||||
|                                 LMIC.channelMap + mapStart, | ||||
|                                 mapEntries, | ||||
|                                 LMIC.txChnl == 0xFF ? -1 : LMIC.txChnl | ||||
|                                 ); | ||||
|  | ||||
|         uint nth = os_getRndU1() % count; | ||||
|         for (u1_t chnl = start; chnl<end; chnl++) { | ||||
|                 // Scan for nth enabled channel that is not the last channel used | ||||
|                 if (chnl != lastTxChan && ENABLED_CHANNEL(chnl) && (nth--) == 0) { | ||||
|                         LMIC.txChnl = chnl; | ||||
|                         return; | ||||
|                 } | ||||
|         } | ||||
|         // No feasible channel found! Keep old one. | ||||
|         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; | ||||
| @ -311,6 +315,32 @@ ostime_t LMICuslike_nextJoinState(void) { | ||||
| } | ||||
| #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, | ||||
|  | ||||
| @ -63,8 +63,14 @@ LMICuslike_isValidBeacon1(const uint8_t *d) { | ||||
| // provide a default LMICbandplan_joinAcceptChannelClear() | ||||
| #define LMICbandplan_joinAcceptChannelClear() do { } while (0) | ||||
|  | ||||
| // no CFList on joins for US-like plans | ||||
| #define LMICbandplan_hasJoinCFlist()    (0) | ||||
| /// \brief there's a CFList on joins for US-like plans | ||||
| #define LMICbandplan_hasJoinCFlist()    (1) | ||||
|  | ||||
| /// \brief process CFLists from JoinAccept for EU-like regions | ||||
| void LMICuslike_processJoinAcceptCFList(void); | ||||
| /// \brief by default, EU-like plans use LMICuslike_processJoinAcceptCFList | ||||
| #define LMICbandplan_processJoinAcceptCFList    LMICuslike_processJoinAcceptCFList | ||||
|  | ||||
|  | ||||
| #define LMICbandplan_advanceBeaconChannel()     \ | ||||
|         do { LMIC.bcnChnl = (LMIC.bcnChnl+1) & 7; } while (0) | ||||
|  | ||||
| @ -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 | ||||
| @ -445,6 +445,13 @@ enum { | ||||
|     LEN_JA          = 17, | ||||
|     LEN_JAEXT       = 17+16 | ||||
| }; | ||||
|  | ||||
| enum { | ||||
|     // JoinAccept CFList types | ||||
|     LORAWAN_JoinAccept_CFListType_FREQUENCIES = 0,  ///< the CFList contains 5 frequencies | ||||
|     LORAWAN_JoinAccept_CFListType_MASK = 1,         ///< the CFList contains channel-mask data | ||||
| }; | ||||
|  | ||||
| enum { | ||||
|     // Data frame format | ||||
|     OFF_DAT_HDR      = 0, | ||||
| @ -638,17 +645,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 | ||||
|  | ||||
		Reference in New Issue
	
	Block a user