mirror of
				https://github.com/manuelbl/ttn-esp32.git
				synced 2025-10-31 10:40: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", |             "compilerPath": "/usr/bin/clang", | ||||||
|             "cStandard": "c11", |             "cStandard": "c11", | ||||||
|             "cppStandard": "c++11", |             "cppStandard": "c++11", | ||||||
|             "intelliSenseMode": "clang-x64" |             "intelliSenseMode": "clang-x64", | ||||||
|  |             "compileCommands": "${workspaceFolder}/examples/hello_world/build/compile_commands.json" | ||||||
|         } |         } | ||||||
|     ], |     ], | ||||||
|     "version": 4 |     "version": 4 | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								LICENSE
									
									
									
									
									
								
							| @ -1,6 +1,6 @@ | |||||||
| MIT License | 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 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
| of this software and associated documentation files (the "Software"), to deal | 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. | 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 | - Verified compatibility with ESP-IDF v4.3 | ||||||
| - Upgraded underlying library mcci-catena/arduino-lmic to v3.3.0 (no relevant changes) | - Upgraded underlying library mcci-catena/arduino-lmic to v4.0.1 (improved channel shuffling) | ||||||
| - Ensure interrupt code is in IRAM |  | ||||||
|  |  | ||||||
|  |  | ||||||
| ## Get Started | ## Get Started | ||||||
|  | |||||||
| @ -13,7 +13,7 @@ | |||||||
|         "url": "https://github.com/manuelbl/ttn-esp32.git", |         "url": "https://github.com/manuelbl/ttn-esp32.git", | ||||||
|         "branch": "master" |         "branch": "master" | ||||||
|     }, |     }, | ||||||
|     "version": "3.3.0", |     "version": "4.0.0-pre", | ||||||
|     "license": "MIT License", |     "license": "MIT License", | ||||||
|     "export": { |     "export": { | ||||||
|         "include": [ |         "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() |         // a compile-time configuration. (In other words, assume that millis() | ||||||
|         // clock is accurate to 0.1%.) You should never use clockerror to |         // clock is accurate to 0.1%.) You should never use clockerror to | ||||||
|         // compensate for system-late problems. |         // 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) |         if (! LMIC_ENABLE_arbitrary_clock_error && clockerr > maxError) | ||||||
|             { |             { | ||||||
|             clockerr = maxError; |             clockerr = maxError; | ||||||
| @ -1616,21 +1621,9 @@ static bit_t processJoinAccept (void) { | |||||||
|     // initDefaultChannels(0) for EU-like, nothing otherwise |     // initDefaultChannels(0) for EU-like, nothing otherwise | ||||||
|     LMICbandplan_joinAcceptChannelClear(); |     LMICbandplan_joinAcceptChannelClear(); | ||||||
|  |  | ||||||
|     if (!LMICbandplan_hasJoinCFlist() && dlen > LEN_JA) { |     // process the CFList if present | ||||||
|             // if no JoinCFList, we're supposed to continue |     if (dlen == LEN_JAEXT) { | ||||||
|             // the join per 2.2.5 of LoRaWAN regional 2.2.4 |         LMICbandplan_processJoinAcceptCFList(); | ||||||
|             // 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 |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // already incremented when JOIN REQ got sent off |     // 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) { | 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) { | static bit_t adjustDrForFrameIfNotBusy(u1_t len) { | ||||||
| @ -2902,6 +2899,10 @@ void LMIC_setTxData (void) { | |||||||
| } | } | ||||||
|  |  | ||||||
| void LMIC_setTxData_strict (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)); |     LMICOS_logEventUint32(__func__, ((u4_t)LMIC.pendTxPort << 24u) | ((u4_t)LMIC.pendTxConf << 16u) | (LMIC.pendTxLen << 0u)); | ||||||
|     LMIC.opmode |= OP_TXDATA; |     LMIC.opmode |= OP_TXDATA; | ||||||
|     if( (LMIC.opmode & OP_JOINING) == 0 ) { |     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 | // 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) { | 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 |         // already have a message queued | ||||||
|         return LMIC_ERROR_TX_BUSY; |         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 LMIC_ERROR_TX_FAILED; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return 0; |     return LMIC_ERROR_SUCCESS; | ||||||
| } | } | ||||||
|  |  | ||||||
| // send a message with callback; try to adjust data rate | // send a message with callback; try to adjust data rate | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| /* | /* | ||||||
|  * Copyright (c) 2014-2016 IBM Corporation. |  * Copyright (c) 2014-2016 IBM Corporation. | ||||||
|  * Copyright (c) 2016 Matthijs Kooijman. |  * Copyright (c) 2016 Matthijs Kooijman. | ||||||
|  * Copyright (c) 2016-2020 MCCI Corporation. |  * Copyright (c) 2016-2021 MCCI Corporation. | ||||||
|  * All rights reserved. |  * All rights reserved. | ||||||
|  * |  * | ||||||
|  *  Redistribution and use in source and binary forms, with or without |  *  Redistribution and use in source and binary forms, with or without | ||||||
| @ -96,7 +96,7 @@ | |||||||
| extern "C"{ | extern "C"{ | ||||||
| #endif | #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_MAJOR 1 | ||||||
| #define LMIC_VERSION_MINOR 6 | #define LMIC_VERSION_MINOR 6 | ||||||
| #define LMIC_VERSION_BUILD 1468577746 | #define LMIC_VERSION_BUILD 1468577746 | ||||||
| @ -105,7 +105,8 @@ extern "C"{ | |||||||
| #define ARDUINO_LMIC_VERSION_CALC(major, minor, patch, local)	\ | #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)) | 	((((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)	\ | #define	ARDUINO_LMIC_VERSION_GET_MAJOR(v)	\ | ||||||
| 	((((v)*UINT32_C(1)) >> 24u) & 0xFFu) | 	((((v)*UINT32_C(1)) >> 24u) & 0xFFu) | ||||||
| @ -119,10 +120,35 @@ extern "C"{ | |||||||
| #define	ARDUINO_LMIC_VERSION_GET_LOCAL(v)	\ | #define	ARDUINO_LMIC_VERSION_GET_LOCAL(v)	\ | ||||||
| 	((v) & 0xFFu) | 	((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 ! | //! Only For Antenna Tuning Tests ! | ||||||
| //#define CFG_TxContinuousMode 1 | //#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. | // MAX_LEN_FRAME is what the code uses. | ||||||
| enum { MAX_FRAME_LEN      =  MAX_LEN_FRAME };   //!< Library cap on max frame length | 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 |                                      // note that we need 100 ppm timing accuracy for | ||||||
|                                      // this, to keep the timing error to +/- 700ms. |                                      // 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 | 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. |                                      // so this needs to be at least 125 for an STM32L0. | ||||||
|                                      // And for 100ppm clocks and 2 hours of beacon misses, |                                      // 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. |                                      // 4.096 ms/sym or at least 342 symbols. | ||||||
|  |  | ||||||
| enum { LINK_CHECK_CONT    =  0  ,    // continue with this after reported dead link | 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 |     u2_t     txcap;     // duty cycle limitation: 1/txcap | ||||||
|     s1_t     txpow;     // maximum TX power |     s1_t     txpow;     // maximum TX power | ||||||
|     u1_t     lastchnl;  // last used channel |     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 | TYPEDEF_xref2band_t; //!< \internal | ||||||
|  |  | ||||||
| @ -172,10 +198,8 @@ struct lmic_saved_adr_state_s { | |||||||
|  |  | ||||||
| #elif CFG_LMIC_US_like  // US915 spectrum ================================================= | #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 { | 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        activeChannels125khz; | ||||||
|     u2_t        activeChannels500khz; |     u2_t        activeChannels500khz; | ||||||
| }; | }; | ||||||
| @ -531,10 +555,10 @@ struct lmic_t { | |||||||
|     // bit map of enabled datarates for each channel |     // bit map of enabled datarates for each channel | ||||||
|     u2_t        channelDrMap[MAX_CHANNELS]; |     u2_t        channelDrMap[MAX_CHANNELS]; | ||||||
|     u2_t        channelMap; |     u2_t        channelMap; | ||||||
|  |     u2_t        channelShuffleMap; | ||||||
| #elif CFG_LMIC_US_like | #elif CFG_LMIC_US_like | ||||||
|     u4_t        xchFreq[MAX_XCHANNELS];    // extra channel frequencies (if device is behind a repeater) |     u2_t        channelMap[(72+15)/16];  // enabled bits | ||||||
|     u2_t        xchDrMap[MAX_XCHANNELS];   // extra channel datarate ranges  ---XXX: ditto |     u2_t        channelShuffleMap[(72+15)/16];  // enabled bits | ||||||
|     u2_t        channelMap[(72+MAX_XCHANNELS+15)/16];  // enabled bits |  | ||||||
|     u2_t        activeChannels125khz; |     u2_t        activeChannels125khz; | ||||||
|     u2_t        activeChannels500khz; |     u2_t        activeChannels500khz; | ||||||
| #endif | #endif | ||||||
| @ -569,7 +593,10 @@ struct lmic_t { | |||||||
|  |  | ||||||
|     u1_t        txChnl;          // channel for next TX |     u1_t        txChnl;          // channel for next TX | ||||||
|     u1_t        globalDutyRate;  // max rate: 1/2^k |     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 |     u1_t        upRepeat;     // configured up repeat | ||||||
|     s1_t        adrTxPow;     // ADR adjusted TX power |     s1_t        adrTxPow;     // ADR adjusted TX power | ||||||
|     u1_t        datarate;     // current data rate |     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_disableSubBand(u1_t band); | ||||||
| bit_t LMIC_selectSubBand(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_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) | 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_registerRxMessageCb(lmic_rxmessage_cb_t *pRxMessageCb, void *pUserData); | ||||||
| int LMIC_registerEventCb(lmic_event_cb_t *pEventCb, 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. | // APIs for client half of compliance. | ||||||
| typedef u1_t lmic_compliance_rx_action_t; | typedef u1_t lmic_compliance_rx_action_t; | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
| * Copyright (c) 2014-2016 IBM Corporation. | * Copyright (c) 2014-2016 IBM Corporation. | ||||||
| * Copyright (c) 2017, 2019 MCCI Corporation. | * Copyright (c) 2017, 2019-2021 MCCI Corporation. | ||||||
| * All rights reserved. | * All rights reserved. | ||||||
| * | * | ||||||
| *  Redistribution and use in source and binary forms, with or without | *  Redistribution and use in source and binary forms, with or without | ||||||
| @ -50,6 +50,14 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { | |||||||
|         ILLEGAL_RPS |         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. | // see table in 2.7.6 -- this assumes UplinkDwellTime = 0. | ||||||
| static CONST_TABLE(u1_t, maxFrameLens_dwell0)[] = { | static CONST_TABLE(u1_t, maxFrameLens_dwell0)[] = { | ||||||
| 	59+5,   // [0] | 	59+5,   // [0] | ||||||
| @ -226,16 +234,28 @@ bit_t LMIC_setupBand(u1_t bandidx, s1_t txpow, u2_t txcap) { | |||||||
|         return 1; |         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) { | 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. |         // zero the band bits in freq, just in case. | ||||||
|         freq &= ~3; |         freq &= ~3; | ||||||
|  |  | ||||||
|         if (chidx < NUM_DEFAULT_CHANNELS) { |         if (chidx < NUM_DEFAULT_CHANNELS) { | ||||||
|                 // can't disable a default channel. |                 // can't do anything to a default channel. | ||||||
|                 if (freq == 0) |  | ||||||
|                         return 0; |  | ||||||
|                 // can't change a default channel. |  | ||||||
|                 else if (freq != (LMIC.channelFreq[chidx] & ~3)) |  | ||||||
|                 return 0; |                 return 0; | ||||||
|         } |         } | ||||||
|         bit_t fEnable = (freq != 0); |         bit_t fEnable = (freq != 0); | ||||||
| @ -315,37 +335,110 @@ void LMICas923_setRx1Params(void) { | |||||||
| 	LMIC.rps = dndr2rps(LMIC.dndr); | 	LMIC.rps = dndr2rps(LMIC.dndr); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /// | ||||||
| // return the next time, but also do channel hopping here | /// \brief change the TX channel given the desired tx time. | ||||||
| // identical to the EU868 version; but note that we only have BAND_CENTI | /// | ||||||
| // at work. | /// \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) { | ostime_t LMICas923_nextTx(ostime_t now) { | ||||||
|         u1_t bmap = 0xF; |  | ||||||
|         do { |  | ||||||
|         ostime_t mintime = now + /*8h*/sec2osticks(28800); |         ostime_t mintime = now + /*8h*/sec2osticks(28800); | ||||||
|                 u1_t band = 0; |         u2_t availMap; | ||||||
|                 for (u1_t bi = 0; bi<4; bi++) { |         u2_t feasibleMap; | ||||||
|                         if ((bmap & (1 << bi)) && mintime - LMIC.bands[bi].avail > 0) |         u1_t bandMap; | ||||||
|                                 mintime = LMIC.bands[band = bi].avail; |  | ||||||
|  |         // 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; |         return mintime; | ||||||
| } | } | ||||||
|                 } |  | ||||||
|                 if ((bmap &= ~(1 << band)) == 0) { |  | ||||||
|                         // No feasible channel  found! |  | ||||||
|                         return mintime; |  | ||||||
|                 } |  | ||||||
|         } while (1); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #if !defined(DISABLE_BEACONS) | #if !defined(DISABLE_BEACONS) | ||||||
| void LMICas923_setBcnRxParams(void) { | void LMICas923_setBcnRxParams(void) { | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
| * Copyright (c) 2014-2016 IBM Corporation. | * Copyright (c) 2014-2016 IBM Corporation. | ||||||
| * Copyright (c) 2017, 2019 MCCI Corporation. | * Copyright (c) 2017, 2019-2021 MCCI Corporation. | ||||||
| * All rights reserved. | * All rights reserved. | ||||||
| * | * | ||||||
| *  Redistribution and use in source and binary forms, with or without | *  Redistribution and use in source and binary forms, with or without | ||||||
| @ -55,6 +55,14 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { | |||||||
|         ILLEGAL_RPS |         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)[] = { | static CONST_TABLE(u1_t, maxFrameLens_dwell0)[] = { | ||||||
|         59+5,  59+5,  59+5, 123+5, 250+5, 250+5, 250+5, 0, |         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 }; |         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; |         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) { | bit_t LMIC_setupChannel(u1_t chidx, u4_t freq, u2_t drmap, s1_t band) { | ||||||
|         LMIC_API_PARAMETER(chidx); |         LMIC_API_PARAMETER(chidx); | ||||||
|         LMIC_API_PARAMETER(freq); |         LMIC_API_PARAMETER(freq); | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
| * Copyright (c) 2014-2016 IBM Corporation. | * Copyright (c) 2014-2016 IBM Corporation. | ||||||
| * Copyright (c) 2017, 2019 MCCI Corporation. | * Copyright (c) 2017, 2019-2021 MCCI Corporation. | ||||||
| * All rights reserved. | * All rights reserved. | ||||||
| * | * | ||||||
| *  Redistribution and use in source and binary forms, with or without | *  Redistribution and use in source and binary forms, with or without | ||||||
| @ -166,6 +166,14 @@ | |||||||
| # error "LMICbandplan_isDataRateFeasible() not defined by bandplan" | # error "LMICbandplan_isDataRateFeasible() not defined by bandplan" | ||||||
| #endif | #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 | // 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); | void LMICcore_setDrJoin(u1_t reason, u1_t dr); | ||||||
| ostime_t LMICcore_adjustForDrift(ostime_t delay, ostime_t hsym, rxsyms_t rxsyms_in); | 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_ | #endif // _lmic_bandplan_h_ | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
| * Copyright (c) 2014-2016 IBM Corporation. | * Copyright (c) 2014-2016 IBM Corporation. | ||||||
| * Copyright (c) 2017, 2019 MCCI Corporation. | * Copyright (c) 2017, 2019-2021 MCCI Corporation. | ||||||
| * All rights reserved. | * All rights reserved. | ||||||
| * | * | ||||||
| *  Redistribution and use in source and binary forms, with or without | *  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); | ostime_t LMICas923_nextJoinTime(ostime_t now); | ||||||
| #define LMICbandplan_nextJoinTime(now)     LMICas923_nextJoinTime(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_ | #endif // _lmic_bandplan_as923_h_ | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
| * Copyright (c) 2014-2016 IBM Corporation. | * Copyright (c) 2014-2016 IBM Corporation. | ||||||
| * Copyright (c) 2017, 2019 MCCI Corporation. | * Copyright (c) 2017, 2019-2021 MCCI Corporation. | ||||||
| * All rights reserved. | * All rights reserved. | ||||||
| * | * | ||||||
| *  Redistribution and use in source and binary forms, with or without | *  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); | void LMICau915_updateTx(ostime_t txbeg); | ||||||
| #define LMICbandplan_updateTx(txbeg)    LMICau915_updateTx(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_ | #endif // _lmic_bandplan_au915_h_ | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
| * Copyright (c) 2014-2016 IBM Corporation. | * Copyright (c) 2014-2016 IBM Corporation. | ||||||
| * Copyright (c) 2017, 2019 MCCI Corporation. | * Copyright (c) 2017, 2019-2021 MCCI Corporation. | ||||||
| * All rights reserved. | * All rights reserved. | ||||||
| * | * | ||||||
| *  Redistribution and use in source and binary forms, with or without | *  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); | void LMICeu868_setRx1Params(void); | ||||||
| #define LMICbandplan_setRx1Params()     LMICeu868_setRx1Params() | #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_ | #endif // _lmic_eu868_h_ | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
| * Copyright (c) 2014-2016 IBM Corporation. | * Copyright (c) 2014-2016 IBM Corporation. | ||||||
| * Copyright (c) 2017, 2019 MCCI Corporation. | * Copyright (c) 2017, 2019-2021 MCCI Corporation. | ||||||
| * All rights reserved. | * All rights reserved. | ||||||
| * | * | ||||||
| *  Redistribution and use in source and binary forms, with or without | *  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); | void LMICin866_setRx1Params(void); | ||||||
| #define LMICbandplan_setRx1Params()     LMICin866_setRx1Params() | #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_ | #endif // _lmic_bandplan_in866_h_ | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
| * Copyright (c) 2014-2016 IBM Corporation. | * Copyright (c) 2014-2016 IBM Corporation. | ||||||
| * Copyright (c) 2017, 2019 MCCI Corporation. | * Copyright (c) 2017, 2019-2021 MCCI Corporation. | ||||||
| * All rights reserved. | * All rights reserved. | ||||||
| * | * | ||||||
| *  Redistribution and use in source and binary forms, with or without | *  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); | void LMICkr920_updateTx(ostime_t txbeg); | ||||||
| #define LMICbandplan_updateTx(t)        LMICkr920_updateTx(t) | #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_ | #endif // _lmic_kr920_h_ | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
| * Copyright (c) 2014-2016 IBM Corporation. | * Copyright (c) 2014-2016 IBM Corporation. | ||||||
| * Copyright (c) 2017, 2019 MCCI Corporation. | * Copyright (c) 2017, 2019-2021 MCCI Corporation. | ||||||
| * All rights reserved. | * All rights reserved. | ||||||
| * | * | ||||||
| *  Redistribution and use in source and binary forms, with or without | *  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); | void LMICus915_updateTx(ostime_t txbeg); | ||||||
| #define LMICbandplan_updateTx(txbeg)    LMICus915_updateTx(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_ | #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; |             break; | ||||||
|             } |             } | ||||||
|         case LORAWAN_COMPLIANCE_CMD_LINK: { |         case LORAWAN_COMPLIANCE_CMD_LINK: { | ||||||
|             // not clear what this request does. |             // we are required to initiate a Link | ||||||
|             break; |             break; | ||||||
|         } |         } | ||||||
|         case LORAWAN_COMPLIANCE_CMD_JOIN: { |         case LORAWAN_COMPLIANCE_CMD_JOIN: { | ||||||
|  | |||||||
| @ -118,30 +118,6 @@ Revision history: | |||||||
|                                 (1 << LMIC_REGION_in866) |      \ |                                 (1 << LMIC_REGION_in866) |      \ | ||||||
|                                 0) |                                 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. | // the selected region. | ||||||
| // TODO(tmm@mcci.com) consider moving this block to a central file as it's not | // TODO(tmm@mcci.com) consider moving this block to a central file as it's not | ||||||
| // user-editable. | // user-editable. | ||||||
| @ -171,11 +147,94 @@ Revision history: | |||||||
| # define CFG_region     0 | # define CFG_region     0 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| // a bitmask of EU-like regions -- these are regions which have up to 16 | // LMIC_CFG_*_ENA -- either 1 or 0 based on whether that region | ||||||
| // channels indidually programmable via downloink. | // is enabled.  Note: these must be after the code that special-cases | ||||||
| // | // CFG_as923jp. | ||||||
| // TODO(tmm@mcci.com) consider moving this block to a central file as it's not | #if defined(CFG_eu868) | ||||||
| // user-editable. | # 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   (                               \ | #define CFG_LMIC_EU_like_MASK   (                               \ | ||||||
|                                 (1 << LMIC_REGION_eu868) |      \ |                                 (1 << LMIC_REGION_eu868) |      \ | ||||||
|                              /* (1 << LMIC_REGION_us915) | */   \ |                              /* (1 << LMIC_REGION_us915) | */   \ | ||||||
| @ -188,12 +247,14 @@ Revision history: | |||||||
|                                 (1 << LMIC_REGION_in866) |      \ |                                 (1 << LMIC_REGION_in866) |      \ | ||||||
|                                 0) |                                 0) | ||||||
|  |  | ||||||
| // a bitmask of` US-like regions -- these are regions with 64 fixed 125 kHz channels | /// \brief bitmask of` US-like regions | ||||||
| // overlaid by 8 500 kHz channels. The channel frequencies can't be changed, but | /// | ||||||
| // subsets of channels can be selected via masks. | /// US-like regions have 64 fixed 125-kHz channels overlaid by 8 500-kHz | ||||||
| // | /// channels. The channel frequencies can't be changed, but | ||||||
| // TODO(tmm@mcci.com) consider moving this block to a central file as it's not | /// subsets of channels can be selected via masks. | ||||||
| // user-editable. | /// | ||||||
|  | /// TODO(tmm@mcci.com) consider moving this block to a central file as it's not | ||||||
|  | /// user-editable. | ||||||
| #define CFG_LMIC_US_like_MASK   (                               \ | #define CFG_LMIC_US_like_MASK   (                               \ | ||||||
|                              /* (1 << LMIC_REGION_eu868) | */   \ |                              /* (1 << LMIC_REGION_eu868) | */   \ | ||||||
|                                 (1 << LMIC_REGION_us915) |      \ |                                 (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 | // TODO(tmm@mcci.com) consider moving this block to a central file as it's not | ||||||
| // user-editable. | // 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)) | #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)) | #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 | // so that we can (for example) compare | ||||||
| // | // | ||||||
| //    LMIC_LORAWAN_SPEC_VERSION < LMIC_LORAWAN_SPEC_VERSION_1_0_3. | //    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_2  0x01000200u  /// Refers to LoRaWAN spec 1.0.2 | ||||||
| #define LMIC_LORAWAN_SPEC_VERSION_1_0_3  0x01000300u | #define LMIC_LORAWAN_SPEC_VERSION_1_0_3  0x01000300u  /// Refers to LoRaWAN spec 1.0.3 | ||||||
|  |  | ||||||
| #endif /* _LMIC_CONFIG_PRECONDITIONS_H_ */ | #endif /* _LMIC_CONFIG_PRECONDITIONS_H_ */ | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
| * Copyright (c) 2014-2016 IBM Corporation. | * Copyright (c) 2014-2016 IBM Corporation. | ||||||
| * Copyright (c) 2017, 2019 MCCI Corporation. | * Copyright (c) 2017, 2019-2021 MCCI Corporation. | ||||||
| * All rights reserved. | * All rights reserved. | ||||||
| * | * | ||||||
| *  Redistribution and use in source and binary forms, with or without | *  Redistribution and use in source and binary forms, with or without | ||||||
| @ -49,6 +49,14 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { | |||||||
|         ILLEGAL_RPS |         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)[] = { | static CONST_TABLE(u1_t, maxFrameLens)[] = { | ||||||
|         59+5, 59+5, 59+5, 123+5, 250+5, 250+5, 250+5, 250+5 |         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, |   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) { | 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. |         // zero the band bits in freq, just in case. | ||||||
|         freq &= ~3; |         freq &= ~3; | ||||||
|  |  | ||||||
|         if (chidx < NUM_DEFAULT_CHANNELS) { |         if (chidx < NUM_DEFAULT_CHANNELS) { | ||||||
|                 // can't disable a default channel. |                 // can't do anything to a default channel. | ||||||
|                 if (freq == 0) |  | ||||||
|                         return 0; |  | ||||||
|                 // can't change a default channel. |  | ||||||
|                 else if (freq != (LMIC.channelFreq[chidx] & ~3)) |  | ||||||
|                 return 0; |                 return 0; | ||||||
|         } |         } | ||||||
|         bit_t fEnable = (freq != 0); |         bit_t fEnable = (freq != 0); | ||||||
| @ -204,33 +223,112 @@ ostime_t LMICeu868_nextJoinTime(ostime_t time) { | |||||||
|         return 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) { | ostime_t LMICeu868_nextTx(ostime_t now) { | ||||||
|         u1_t bmap = 0xF; |  | ||||||
|         do { |  | ||||||
|         ostime_t mintime = now + /*8h*/sec2osticks(28800); |         ostime_t mintime = now + /*8h*/sec2osticks(28800); | ||||||
|                 u1_t band = 0; |         u2_t availMap; | ||||||
|                 for (u1_t bi = 0; bi<4; bi++) { |         u2_t feasibleMap; | ||||||
|                         if ((bmap & (1 << bi)) && mintime - LMIC.bands[bi].avail > 0) |         u1_t bandMap; | ||||||
|                                 mintime = LMIC.bands[band = bi].avail; |  | ||||||
|  |         // 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; |         return mintime; | ||||||
| } | } | ||||||
|                 } |  | ||||||
|                 if ((bmap &= ~(1 << band)) == 0) { |  | ||||||
|                         // No feasible channel  found! |  | ||||||
|                         return mintime; |  | ||||||
|                 } |  | ||||||
|         } while (1); |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #if !defined(DISABLE_BEACONS) | #if !defined(DISABLE_BEACONS) | ||||||
|  | |||||||
| @ -128,7 +128,9 @@ void LMICeulike_initJoinLoop(uint8_t nDefaultChannels, s1_t adrTxPow) { | |||||||
| #if CFG_TxContinuousMode | #if CFG_TxContinuousMode | ||||||
|         LMIC.txChnl = 0 |         LMIC.txChnl = 0 | ||||||
| #else | #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 | #endif | ||||||
|         LMIC.adrTxPow = adrTxPow; |         LMIC.adrTxPow = adrTxPow; | ||||||
|         // TODO(tmm@mcci.com) don't use EU directly, use a table. That |         // 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) { | ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels) { | ||||||
|         u1_t failed = 0; |         u1_t failed = 0; | ||||||
|  |         u2_t enableMap = (1 << nDefaultChannels) - 1; | ||||||
|  |  | ||||||
|         // Try each default channel with same DR |         // Try each default channel with same DR | ||||||
|         // If all fail try next lower datarate |         // If all fail try next lower datarate | ||||||
|         if (++LMIC.txChnl == /* NUM_DEFAULT_CHANNELS */ nDefaultChannels) |         if (LMIC.channelShuffleMap == 0) { | ||||||
|                 LMIC.txChnl = 0; |  | ||||||
|         if ((++LMIC.txCnt % nDefaultChannels) == 0) { |  | ||||||
|                 // Lower DR every nth try (having all default channels with same DR) |                 // 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; |                 // 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 |                 // the failed flag here. This will cause the outer caller to take the | ||||||
|                 // appropriate join path. Or add new LMICeulike_GetLowestJoinDR() |                 // appropriate join path. Or add new LMICeulike_GetLowestJoinDR() | ||||||
|                 // |                 // | ||||||
|  |  | ||||||
| // TODO(tmm@mcci.com) - see above; please remove regional dependency from this file. | // TODO(tmm@mcci.com) - see above; please remove regional dependency from this file. | ||||||
| #if CFG_region == LMIC_REGION_as923 | #if CFG_region == LMIC_REGION_as923 | ||||||
|                 // in the join of AS923 v1.1 or older, only DR2 is used. |                 // 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; |                 LMIC.datarate = AS923_DR_SF10; | ||||||
|                 failed = 1; |                 failed = 1; | ||||||
| #else | #else | ||||||
|                 if (LMIC.datarate == LORAWAN_DR0) |                 if (LMIC.datarate == LORAWAN_DR0) { | ||||||
|                         failed = 1; // we have tried all DR - signal EV_JOIN_FAILED |                         failed = 1; // we have tried all DR - signal EV_JOIN_FAILED | ||||||
|                 else { |                 } else { | ||||||
|                         LMICcore_setDrJoin(DRCHG_NOJACC, decDR((dr_t)LMIC.datarate)); |                         LMICcore_setDrJoin(DRCHG_NOJACC, decDR((dr_t)LMIC.datarate)); | ||||||
|                 } |                 } | ||||||
| #endif | #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; |         LMIC.opmode &= ~OP_NEXTCHNL; | ||||||
|  |  | ||||||
|         // Move txend to randomize synchronized concurrent joins. |         // Move txend to randomize synchronized concurrent joins. | ||||||
|         // Duty cycle is based on txend. |         // Duty cycle is based on txend. | ||||||
|         ostime_t const time = LMICbandplan_nextJoinTime(os_getTime()); |         ostime_t const time = LMICbandplan_nextJoinTime(os_getTime()); | ||||||
| @ -220,6 +227,27 @@ ostime_t LMICeulike_nextJoinState(uint8_t nDefaultChannels) { | |||||||
| } | } | ||||||
| #endif // !DISABLE_JOIN | #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) { | void LMICeulike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer) { | ||||||
|     os_copyMem( |     os_copyMem( | ||||||
|             pStateBuffer->channelFreq, |             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 | // there's a CFList on joins for EU-like plans | ||||||
| #define LMICbandplan_hasJoinCFlist()    (1) | #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()     \ | #define LMICbandplan_advanceBeaconChannel()     \ | ||||||
|         do { /* nothing */ } while (0) |         do { /* nothing */ } while (0) | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
| * Copyright (c) 2014-2016 IBM Corporation. | * Copyright (c) 2014-2016 IBM Corporation. | ||||||
| * Copyright (c) 2017, 2019 MCCI Corporation. | * Copyright (c) 2017, 2019-2021 MCCI Corporation. | ||||||
| * All rights reserved. | * All rights reserved. | ||||||
| * | * | ||||||
| *  Redistribution and use in source and binary forms, with or without | *  Redistribution and use in source and binary forms, with or without | ||||||
| @ -49,6 +49,14 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { | |||||||
|         ILLEGAL_RPS |         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)[] = { | static CONST_TABLE(u1_t, maxFrameLens)[] = { | ||||||
|         59+5, 59+5, 59+5, 123+5, 250+5, 250+5, 0, 250+5 |         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; |         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) { | 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. |         // zero the band bits in freq, just in case. | ||||||
|         freq &= ~3; |         freq &= ~3; | ||||||
|  |  | ||||||
|         if (chidx < NUM_DEFAULT_CHANNELS) { |         if (chidx < NUM_DEFAULT_CHANNELS) { | ||||||
|                 // can't disable a default channel. |  | ||||||
|                 if (freq == 0) |  | ||||||
|                         return 0; |  | ||||||
|                 // can't change a default channel. |  | ||||||
|                 else if (freq != (LMIC.channelFreq[chidx] & ~3)) |  | ||||||
|                 return 0; |                 return 0; | ||||||
|         } |         } | ||||||
|         bit_t fEnable = (freq != 0); |         bit_t fEnable = (freq != 0); | ||||||
| @ -173,28 +191,44 @@ u4_t LMICin866_convFreq(xref2cu1_t ptr) { | |||||||
|         return freq; |         return freq; | ||||||
| } | } | ||||||
|  |  | ||||||
| // return the next time, but also do channel hopping here | /// | ||||||
| // since there's no duty cycle limitation, and no dwell limitation, | /// \brief change the TX channel given the desired tx time. | ||||||
| // we simply loop through the channels sequentially. | /// | ||||||
|  | /// \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) { | 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++) { |         // scan all the enabled channels and make a mask of candidates | ||||||
|                 // Find next channel in given band |         availmask = 0; | ||||||
|                 u1_t chnl = LMIC.bands[band].lastchnl; |         for (u1_t chnl = 0; chnl < MAX_CHANNELS; ++chnl) { | ||||||
|                 for (u1_t ci = 0; ci<MAX_CHANNELS; ci++) { |                 // not enabled? | ||||||
|                         if ((chnl = (chnl + 1)) >= MAX_CHANNELS) |                 if ((LMIC.channelMap & (1 << chnl)) == 0) | ||||||
|                                 chnl -= MAX_CHANNELS; |                         continue; | ||||||
|                         if ((LMIC.channelMap & (1 << chnl)) != 0 &&  // channel enabled |                 // not feasible? | ||||||
|                                 (LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) != 0 && |                 if ((LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) == 0) | ||||||
|                                 band == (LMIC.channelFreq[chnl] & 0x3)) { // in selected band |                         continue; | ||||||
|                                 LMIC.txChnl = LMIC.bands[band].lastchnl = chnl; |                 availmask |= 1 << chnl; | ||||||
|                                 return now; |  | ||||||
|                         } |  | ||||||
|                 } |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 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; |         return now; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
| * Copyright (c) 2014-2016 IBM Corporation. | * Copyright (c) 2014-2016 IBM Corporation. | ||||||
| * Copyright (c) 2017, 2019 MCCI Corporation. | * Copyright (c) 2017, 2019-2021 MCCI Corporation. | ||||||
| * All rights reserved. | * All rights reserved. | ||||||
| * | * | ||||||
| *  Redistribution and use in source and binary forms, with or without | *  Redistribution and use in source and binary forms, with or without | ||||||
| @ -47,6 +47,14 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { | |||||||
|         ILLEGAL_RPS,                                    // [6] |         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)[] = { | static CONST_TABLE(u1_t, maxFrameLens)[] = { | ||||||
|         59+5, 59+5, 59+5, 123+5, 250+5, 250+5 |         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; |         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) { | 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. |         // zero the band bits in freq, just in case. | ||||||
|         freq &= ~3; |         freq &= ~3; | ||||||
|  |  | ||||||
|         if (chidx < NUM_DEFAULT_CHANNELS) { |         if (chidx < NUM_DEFAULT_CHANNELS) { | ||||||
|                 // can't disable a default channel. |  | ||||||
|                 if (freq == 0) |  | ||||||
|                         return 0; |  | ||||||
|                 // can't change a default channel. |                 // can't change a default channel. | ||||||
|                 else if (freq != (LMIC.channelFreq[chidx] & ~3)) |  | ||||||
|                 return 0; |                 return 0; | ||||||
|         } |         } | ||||||
|         bit_t fEnable = (freq != 0); |         bit_t fEnable = (freq != 0); | ||||||
| @ -184,28 +203,44 @@ u4_t LMICkr920_convFreq(xref2cu1_t ptr) { | |||||||
|         return freq; |         return freq; | ||||||
| } | } | ||||||
|  |  | ||||||
| // return the next time, but also do channel hopping here | /// | ||||||
| // since there's no duty cycle limitation, and no dwell limitation, | /// \brief change the TX channel given the desired tx time. | ||||||
| // we simply loop through the channels sequentially. | /// | ||||||
|  | /// \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) { | 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++) { |         // scan all the enabled channels and make a mask of candidates | ||||||
|                 // Find next channel in given band |         availmask = 0; | ||||||
|                 u1_t chnl = LMIC.bands[band].lastchnl; |         for (u1_t chnl = 0; chnl < MAX_CHANNELS; ++chnl) { | ||||||
|                 for (u1_t ci = 0; ci<MAX_CHANNELS; ci++) { |                 // not enabled? | ||||||
|                         if ((chnl = (chnl + 1)) >= MAX_CHANNELS) |                 if ((LMIC.channelMap & (1 << chnl)) == 0) | ||||||
|                                 chnl -= MAX_CHANNELS; |                         continue; | ||||||
|                         if ((LMIC.channelMap & (1 << chnl)) != 0 &&  // channel enabled |                 // not feasible? | ||||||
|                                 (LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) != 0 && |                 if ((LMIC.channelDrMap[chnl] & (1 << (LMIC.datarate & 0xF))) == 0) | ||||||
|                                 band == (LMIC.channelFreq[chnl] & 0x3)) { // in selected band |                         continue; | ||||||
|                                 LMIC.txChnl = LMIC.bands[band].lastchnl = chnl; |                 availmask |= 1 << chnl; | ||||||
|                                 return now; |  | ||||||
|                         } |  | ||||||
|                 } |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 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; |         return now; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
| * Copyright (c) 2014-2016 IBM Corporation. | * Copyright (c) 2014-2016 IBM Corporation. | ||||||
| * Copyright (c) 2017, 2019 MCCI Corporation. | * Copyright (c) 2017, 2019-2021 MCCI Corporation. | ||||||
| * All rights reserved. | * All rights reserved. | ||||||
| * | * | ||||||
| *  Redistribution and use in source and binary forms, with or without | *  Redistribution and use in source and binary forms, with or without | ||||||
| @ -55,6 +55,14 @@ CONST_TABLE(u1_t, _DR2RPS_CRC)[] = { | |||||||
|         ILLEGAL_RPS				// [14] |         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)[] = { | static CONST_TABLE(u1_t, maxFrameLens)[] = { | ||||||
|         19+5, 61+5, 133+5, 250+5, 250+5, 0, 0,0, |         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 |         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; |         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) { | 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); |         LMIC_API_PARAMETER(band); | ||||||
|  |  | ||||||
|         if (chidx < 72 || chidx >= 72 + MAX_XCHANNELS) |  | ||||||
|         return 0; // channels 0..71 are hardwired |         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 LMIC_disableChannel(u1_t channel) { | ||||||
|         bit_t result = 0; |         bit_t result = 0; | ||||||
|         if (channel < 72 + MAX_XCHANNELS) { |         if (channel < 72) { | ||||||
|                 if (ENABLED_CHANNEL(channel)) { |                 if (ENABLED_CHANNEL(channel)) { | ||||||
|                         result = 1; |                         result = 1; | ||||||
|                         if (IS_CHANNEL_125khz(channel)) |                         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 LMIC_enableChannel(u1_t channel) { | ||||||
|         bit_t result = 0; |         bit_t result = 0; | ||||||
|         if (channel < 72 + MAX_XCHANNELS) { |         if (channel < 72) { | ||||||
|                 if (!ENABLED_CHANNEL(channel)) { |                 if (!ENABLED_CHANNEL(channel)) { | ||||||
|                         result = 1; |                         result = 1; | ||||||
|                         if (IS_CHANNEL_125khz(channel)) |                         if (IS_CHANNEL_125khz(channel)) | ||||||
| @ -197,14 +218,8 @@ void LMICus915_updateTx(ostime_t txbeg) { | |||||||
|         } else { |         } else { | ||||||
|                 // at 500kHz bandwidth, we're allowed more power. |                 // at 500kHz bandwidth, we're allowed more power. | ||||||
|                 LMIC.txpow = 26; |                 LMIC.txpow = 26; | ||||||
|                 if (chnl < 64 + 8) { |  | ||||||
|                 LMIC.freq = US915_500kHz_UPFBASE + (chnl - 64)*US915_500kHz_UPFSTEP; |                 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 |         // Update global duty cycle stats | ||||||
|         if (LMIC.globalDutyRate != 0) { |         if (LMIC.globalDutyRate != 0) { | ||||||
|  | |||||||
| @ -36,36 +36,34 @@ | |||||||
| # error "LMICuslike_getFirst500kHzDR() not defined by bandplan" | # error "LMICuslike_getFirst500kHzDR() not defined by bandplan" | ||||||
| #endif | #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(count>0); | ||||||
|         ASSERT(start<end); |         ASSERT(start<end); | ||||||
|         ASSERT(count <= (end - start)); |         ASSERT(count <= (end - start)); | ||||||
|         // We used to pick a random channel once and then just increment. That is not per spec. |         ASSERT((start & 0xF) == 0); | ||||||
|         // Now we use a new random number each time, because they are not very expensive. |         uint16_t const mapStart = start >> 4; | ||||||
|         // Regarding the algo below, we cannot pick a number and scan until we hit an enabled channel. |         uint16_t const mapEntries = (end - start + 15) >> 4; | ||||||
|         // That would result in the first enabled channel following a set of disabled ones |  | ||||||
|         // being used more frequently than the other enabled channels. |  | ||||||
|  |  | ||||||
|         // Last used channel is in range. It is not a candidate, per spec. |         int candidate = start + LMIC_findNextChannel( | ||||||
|         uint lastTxChan = LMIC.txChnl; |                                 LMIC.channelShuffleMap + mapStart, | ||||||
|         if (start <= lastTxChan && lastTxChan<end && |                                 LMIC.channelMap + mapStart, | ||||||
|                 // Adjust count only if still enabled. Otherwise, no chance of selection. |                                 mapEntries, | ||||||
|                 ENABLED_CHANNEL(lastTxChan)) { |                                 LMIC.txChnl == 0xFF ? -1 : LMIC.txChnl | ||||||
|                 --count; |                                 ); | ||||||
|                 if (count == 0) { |  | ||||||
|                         return; // Only one active channel, so keep using it. |  | ||||||
|                 } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         uint nth = os_getRndU1() % count; |         if (candidate >= 0) | ||||||
|         for (u1_t chnl = start; chnl<end; chnl++) { |                 LMIC.txChnl = candidate; | ||||||
|                 // 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. |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -87,8 +85,11 @@ void LMICuslike_initDefaultChannels(bit_t fJoin) { | |||||||
|         for (u1_t i = 0; i<4; i++) |         for (u1_t i = 0; i<4; i++) | ||||||
|                 LMIC.channelMap[i] = 0xFFFF; |                 LMIC.channelMap[i] = 0xFFFF; | ||||||
|         LMIC.channelMap[4] = 0x00FF; |         LMIC.channelMap[4] = 0x00FF; | ||||||
|  |         os_clearMem(LMIC.channelShuffleMap, sizeof(LMIC.channelShuffleMap)); | ||||||
|         LMIC.activeChannels125khz = 64; |         LMIC.activeChannels125khz = 64; | ||||||
|         LMIC.activeChannels500khz = 8; |         LMIC.activeChannels500khz = 8; | ||||||
|  |         // choose a random channel. | ||||||
|  |         LMIC.txChnl = 0xFF; | ||||||
| } | } | ||||||
|  |  | ||||||
| // verify that a given setting is permitted | // verify that a given setting is permitted | ||||||
| @ -230,11 +231,10 @@ bit_t LMICuslike_isDataRateFeasible(dr_t dr) { | |||||||
| #if !defined(DISABLE_JOIN) | #if !defined(DISABLE_JOIN) | ||||||
| void LMICuslike_initJoinLoop(void) { | void LMICuslike_initJoinLoop(void) { | ||||||
|         // set an initial condition so that setNextChannel()'s preconds are met |         // 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 |         // 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 |         // the join. The join logic uses the current txChnl, | ||||||
|         // as the first join channel.  The join logic uses the current txChnl, |  | ||||||
|         // then changes after the rx window expires; so we need to set a valid |         // then changes after the rx window expires; so we need to set a valid | ||||||
|         // starting point. |         // starting point. | ||||||
|         setNextChannel(0, 64, LMIC.activeChannels125khz); |         setNextChannel(0, 64, LMIC.activeChannels125khz); | ||||||
| @ -277,10 +277,13 @@ ostime_t LMICuslike_nextJoinState(void) { | |||||||
|         if (LMIC.datarate != LMICuslike_getFirst500kHzDR()) { |         if (LMIC.datarate != LMICuslike_getFirst500kHzDR()) { | ||||||
|                 // assume that 500 kHz equiv of last 125 kHz channel |                 // assume that 500 kHz equiv of last 125 kHz channel | ||||||
|                 // is also enabled, and use it next. |                 // is also enabled, and use it next. | ||||||
|  |                 LMIC.txChnl_125kHz = LMIC.txChnl; | ||||||
|                 LMIC.txChnl = 64 + (LMIC.txChnl >> 3); |                 LMIC.txChnl = 64 + (LMIC.txChnl >> 3); | ||||||
|                 LMICcore_setDrJoin(DRCHG_SET, LMICuslike_getFirst500kHzDR()); |                 LMICcore_setDrJoin(DRCHG_SET, LMICuslike_getFirst500kHzDR()); | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|  |                 // restore invariant | ||||||
|  |                 LMIC.txChnl = LMIC.txChnl_125kHz; | ||||||
|                 setNextChannel(0, 64, LMIC.activeChannels125khz); |                 setNextChannel(0, 64, LMIC.activeChannels125khz); | ||||||
|  |  | ||||||
|                 // TODO(tmm@mcci.com) parameterize |                 // TODO(tmm@mcci.com) parameterize | ||||||
| @ -290,6 +293,7 @@ ostime_t LMICuslike_nextJoinState(void) { | |||||||
|                 } |                 } | ||||||
|                 LMICcore_setDrJoin(DRCHG_SET, dr); |                 LMICcore_setDrJoin(DRCHG_SET, dr); | ||||||
|         } |         } | ||||||
|  |         // tell the main loop that we've already selected a channel. | ||||||
|         LMIC.opmode &= ~OP_NEXTCHNL; |         LMIC.opmode &= ~OP_NEXTCHNL; | ||||||
|  |  | ||||||
|         // TODO(tmm@mcci.com): change delay to (0:1) secs + a known t0, but randomized; |         // 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 | #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) { | void LMICuslike_saveAdrState(lmic_saved_adr_state_t *pStateBuffer) { | ||||||
|         os_copyMem( |         os_copyMem( | ||||||
|                 pStateBuffer->channelMap, |                 pStateBuffer->channelMap, | ||||||
|  | |||||||
| @ -63,8 +63,14 @@ LMICuslike_isValidBeacon1(const uint8_t *d) { | |||||||
| // provide a default LMICbandplan_joinAcceptChannelClear() | // provide a default LMICbandplan_joinAcceptChannelClear() | ||||||
| #define LMICbandplan_joinAcceptChannelClear() do { } while (0) | #define LMICbandplan_joinAcceptChannelClear() do { } while (0) | ||||||
|  |  | ||||||
| // no CFList on joins for US-like plans | /// \brief there's a CFList on joins for US-like plans | ||||||
| #define LMICbandplan_hasJoinCFlist()    (0) | #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()     \ | #define LMICbandplan_advanceBeaconChannel()     \ | ||||||
|         do { LMIC.bcnChnl = (LMIC.bcnChnl+1) & 7; } while (0) |         do { LMIC.bcnChnl = (LMIC.bcnChnl+1) & 7; } while (0) | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| /* | /* | ||||||
|  * Copyright (c) 2014-2016 IBM Corporation. |  * Copyright (c) 2014-2016 IBM Corporation. | ||||||
|  * Copyritght (c) 2017 MCCI Corporation. |  * Copyright (c) 2017-2021 MCCI Corporation. | ||||||
|  * All rights reserved. |  * All rights reserved. | ||||||
|  * |  * | ||||||
|  *  Redistribution and use in source and binary forms, with or without |  *  Redistribution and use in source and binary forms, with or without | ||||||
| @ -445,6 +445,13 @@ enum { | |||||||
|     LEN_JA          = 17, |     LEN_JA          = 17, | ||||||
|     LEN_JAEXT       = 17+16 |     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 { | enum { | ||||||
|     // Data frame format |     // Data frame format | ||||||
|     OFF_DAT_HDR      = 0, |     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))) | #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 | // 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; } | 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 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 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 | // BEG: Keep in sync with lorabase.hpp | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user