RegionCommon.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. /*!
  2. * \file RegionCommon.c
  3. *
  4. * \brief LoRa MAC common region implementation
  5. *
  6. * \copyright Revised BSD License, see section \ref LICENSE.
  7. *
  8. * \code
  9. * ______ _
  10. * / _____) _ | |
  11. * ( (____ _____ ____ _| |_ _____ ____| |__
  12. * \____ \| ___ | (_ _) ___ |/ ___) _ \
  13. * _____) ) ____| | | || |_| ____( (___| | | |
  14. * (______/|_____)_|_|_| \__)_____)\____)_| |_|
  15. * (C)2013-2017 Semtech
  16. *
  17. * ___ _____ _ ___ _ _____ ___ ___ ___ ___
  18. * / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __|
  19. * \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _|
  20. * |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___|
  21. * embedded.connectivity.solutions===============
  22. *
  23. * \endcode
  24. *
  25. * \author Miguel Luis ( Semtech )
  26. *
  27. * \author Gregory Cristian ( Semtech )
  28. *
  29. * \author Daniel Jaeckle ( STACKFORCE )
  30. */
  31. #include <math.h>
  32. #include "lora-radio.h"
  33. #include "utilities.h"
  34. #include "RegionCommon.h"
  35. #define BACKOFF_DC_1_HOUR 100
  36. #define BACKOFF_DC_10_HOURS 1000
  37. #define BACKOFF_DC_24_HOURS 10000
  38. static uint8_t CountChannels( uint16_t mask, uint8_t nbBits )
  39. {
  40. uint8_t nbActiveBits = 0;
  41. for( uint8_t j = 0; j < nbBits; j++ )
  42. {
  43. if( ( mask & ( 1 << j ) ) == ( 1 << j ) )
  44. {
  45. nbActiveBits++;
  46. }
  47. }
  48. return nbActiveBits;
  49. }
  50. uint16_t RegionCommonGetJoinDc( SysTime_t elapsedTime )
  51. {
  52. uint16_t dutyCycle = 0;
  53. if( elapsedTime.Seconds < 3600 )
  54. {
  55. dutyCycle = BACKOFF_DC_1_HOUR;
  56. }
  57. else if( elapsedTime.Seconds < ( 3600 + 36000 ) )
  58. {
  59. dutyCycle = BACKOFF_DC_10_HOURS;
  60. }
  61. else
  62. {
  63. dutyCycle = BACKOFF_DC_24_HOURS;
  64. }
  65. return dutyCycle;
  66. }
  67. bool RegionCommonChanVerifyDr( uint8_t nbChannels, uint16_t* channelsMask, int8_t dr, int8_t minDr, int8_t maxDr, ChannelParams_t* channels )
  68. {
  69. if( RegionCommonValueInRange( dr, minDr, maxDr ) == 0 )
  70. {
  71. return false;
  72. }
  73. for( uint8_t i = 0, k = 0; i < nbChannels; i += 16, k++ )
  74. {
  75. for( uint8_t j = 0; j < 16; j++ )
  76. {
  77. if( ( ( channelsMask[k] & ( 1 << j ) ) != 0 ) )
  78. {// Check datarate validity for enabled channels
  79. if( RegionCommonValueInRange( dr, ( channels[i + j].DrRange.Fields.Min & 0x0F ),
  80. ( channels[i + j].DrRange.Fields.Max & 0x0F ) ) == 1 )
  81. {
  82. // At least 1 channel has been found we can return OK.
  83. return true;
  84. }
  85. }
  86. }
  87. }
  88. return false;
  89. }
  90. uint8_t RegionCommonValueInRange( int8_t value, int8_t min, int8_t max )
  91. {
  92. if( ( value >= min ) && ( value <= max ) )
  93. {
  94. return 1;
  95. }
  96. return 0;
  97. }
  98. bool RegionCommonChanDisable( uint16_t* channelsMask, uint8_t id, uint8_t maxChannels )
  99. {
  100. uint8_t index = id / 16;
  101. if( ( index > ( maxChannels / 16 ) ) || ( id >= maxChannels ) )
  102. {
  103. return false;
  104. }
  105. // Deactivate channel
  106. channelsMask[index] &= ~( 1 << ( id % 16 ) );
  107. return true;
  108. }
  109. uint8_t RegionCommonCountChannels( uint16_t* channelsMask, uint8_t startIdx, uint8_t stopIdx )
  110. {
  111. uint8_t nbChannels = 0;
  112. if( channelsMask == NULL )
  113. {
  114. return 0;
  115. }
  116. for( uint8_t i = startIdx; i < stopIdx; i++ )
  117. {
  118. nbChannels += CountChannels( channelsMask[i], 16 );
  119. }
  120. return nbChannels;
  121. }
  122. void RegionCommonChanMaskCopy( uint16_t* channelsMaskDest, uint16_t* channelsMaskSrc, uint8_t len )
  123. {
  124. if( ( channelsMaskDest != NULL ) && ( channelsMaskSrc != NULL ) )
  125. {
  126. for( uint8_t i = 0; i < len; i++ )
  127. {
  128. channelsMaskDest[i] = channelsMaskSrc[i];
  129. }
  130. }
  131. }
  132. void RegionCommonSetBandTxDone( bool joined, Band_t* band, TimerTime_t lastTxDone )
  133. {
  134. if( joined == true )
  135. {
  136. band->LastTxDoneTime = lastTxDone;
  137. }
  138. else
  139. {
  140. band->LastTxDoneTime = lastTxDone;
  141. band->LastJoinTxDoneTime = lastTxDone;
  142. }
  143. }
  144. TimerTime_t RegionCommonUpdateBandTimeOff( bool joined, bool dutyCycle, Band_t* bands, uint8_t nbBands )
  145. {
  146. TimerTime_t nextTxDelay = TIMERTIME_T_MAX;
  147. // Update bands Time OFF
  148. for( uint8_t i = 0; i < nbBands; i++ )
  149. {
  150. if( joined == false )
  151. {
  152. TimerTime_t elapsedJoin = TimerGetElapsedTime( bands[i].LastJoinTxDoneTime );
  153. TimerTime_t elapsedTx = TimerGetElapsedTime( bands[i].LastTxDoneTime );
  154. TimerTime_t txDoneTime = MAX( elapsedJoin,
  155. ( dutyCycle == true ) ? elapsedTx : 0 );
  156. if( bands[i].TimeOff <= txDoneTime )
  157. {
  158. bands[i].TimeOff = 0;
  159. }
  160. if( bands[i].TimeOff != 0 )
  161. {
  162. nextTxDelay = MIN( bands[i].TimeOff - txDoneTime, nextTxDelay );
  163. }
  164. }
  165. else
  166. {
  167. if( dutyCycle == true )
  168. {
  169. TimerTime_t elapsed = TimerGetElapsedTime( bands[i].LastTxDoneTime );
  170. if( bands[i].TimeOff <= elapsed )
  171. {
  172. bands[i].TimeOff = 0;
  173. }
  174. if( bands[i].TimeOff != 0 )
  175. {
  176. nextTxDelay = MIN( bands[i].TimeOff - elapsed, nextTxDelay );
  177. }
  178. }
  179. else
  180. {
  181. nextTxDelay = 0;
  182. bands[i].TimeOff = 0;
  183. }
  184. }
  185. }
  186. return ( nextTxDelay == TIMERTIME_T_MAX ) ? 0 : nextTxDelay;
  187. }
  188. uint8_t RegionCommonParseLinkAdrReq( uint8_t* payload, RegionCommonLinkAdrParams_t* linkAdrParams )
  189. {
  190. uint8_t retIndex = 0;
  191. if( payload[0] == SRV_MAC_LINK_ADR_REQ )
  192. {
  193. // Parse datarate and tx power
  194. linkAdrParams->Datarate = payload[1];
  195. linkAdrParams->TxPower = linkAdrParams->Datarate & 0x0F;
  196. linkAdrParams->Datarate = ( linkAdrParams->Datarate >> 4 ) & 0x0F;
  197. // Parse ChMask
  198. linkAdrParams->ChMask = ( uint16_t )payload[2];
  199. linkAdrParams->ChMask |= ( uint16_t )payload[3] << 8;
  200. // Parse ChMaskCtrl and nbRep
  201. linkAdrParams->NbRep = payload[4];
  202. linkAdrParams->ChMaskCtrl = ( linkAdrParams->NbRep >> 4 ) & 0x07;
  203. linkAdrParams->NbRep &= 0x0F;
  204. // LinkAdrReq has 4 bytes length + 1 byte CMD
  205. retIndex = 5;
  206. }
  207. return retIndex;
  208. }
  209. uint8_t RegionCommonLinkAdrReqVerifyParams( RegionCommonLinkAdrReqVerifyParams_t* verifyParams, int8_t* dr, int8_t* txPow, uint8_t* nbRep )
  210. {
  211. uint8_t status = verifyParams->Status;
  212. int8_t datarate = verifyParams->Datarate;
  213. int8_t txPower = verifyParams->TxPower;
  214. int8_t nbRepetitions = verifyParams->NbRep;
  215. // Handle the case when ADR is off.
  216. if( verifyParams->AdrEnabled == false )
  217. {
  218. // When ADR is off, we are allowed to change the channels mask
  219. nbRepetitions = verifyParams->CurrentNbRep;
  220. datarate = verifyParams->CurrentDatarate;
  221. txPower = verifyParams->CurrentTxPower;
  222. }
  223. if( status != 0 )
  224. {
  225. // Verify datarate. The variable phyParam. Value contains the minimum allowed datarate.
  226. if( RegionCommonChanVerifyDr( verifyParams->NbChannels, verifyParams->ChannelsMask, datarate,
  227. verifyParams->MinDatarate, verifyParams->MaxDatarate, verifyParams->Channels ) == false )
  228. {
  229. status &= 0xFD; // Datarate KO
  230. }
  231. // Verify tx power
  232. if( RegionCommonValueInRange( txPower, verifyParams->MaxTxPower, verifyParams->MinTxPower ) == 0 )
  233. {
  234. // Verify if the maximum TX power is exceeded
  235. if( verifyParams->MaxTxPower > txPower )
  236. { // Apply maximum TX power. Accept TX power.
  237. txPower = verifyParams->MaxTxPower;
  238. }
  239. else
  240. {
  241. status &= 0xFB; // TxPower KO
  242. }
  243. }
  244. }
  245. // If the status is ok, verify the NbRep
  246. if( status == 0x07 )
  247. {
  248. if( nbRepetitions == 0 )
  249. { // Restore the default value according to the LoRaWAN specification
  250. nbRepetitions = 1;
  251. }
  252. }
  253. // Apply changes
  254. *dr = datarate;
  255. *txPow = txPower;
  256. *nbRep = nbRepetitions;
  257. return status;
  258. }
  259. double RegionCommonComputeSymbolTimeLoRa( uint8_t phyDr, uint32_t bandwidth )
  260. {
  261. return ( ( double )( 1 << phyDr ) / ( double )bandwidth ) * 1000;
  262. }
  263. double RegionCommonComputeSymbolTimeFsk( uint8_t phyDr )
  264. {
  265. return ( 8.0 / ( double )phyDr ); // 1 symbol equals 1 byte
  266. }
  267. void RegionCommonComputeRxWindowParameters( double tSymbol, uint8_t minRxSymbols, uint32_t rxError, uint32_t wakeUpTime, uint32_t* windowTimeout, int32_t* windowOffset )
  268. {
  269. *windowTimeout = MAX( ( uint32_t )ceil( ( ( 2 * minRxSymbols - 8 ) * tSymbol + 2 * rxError ) / tSymbol ), minRxSymbols ); // Computed number of symbols
  270. *windowOffset = ( int32_t )ceil( ( 4.0 * tSymbol ) - ( ( *windowTimeout * tSymbol ) / 2.0 ) - wakeUpTime );
  271. }
  272. int8_t RegionCommonComputeTxPower( int8_t txPowerIndex, float maxEirp, float antennaGain )
  273. {
  274. int8_t phyTxPower = 0;
  275. phyTxPower = ( int8_t )floor( ( maxEirp - ( txPowerIndex * 2U ) ) - antennaGain );
  276. return phyTxPower;
  277. }
  278. void RegionCommonCalcBackOff( RegionCommonCalcBackOffParams_t* calcBackOffParams )
  279. {
  280. uint8_t bandIdx = calcBackOffParams->Channels[calcBackOffParams->Channel].Band;
  281. uint16_t dutyCycle = calcBackOffParams->Bands[bandIdx].DCycle;
  282. uint16_t joinDutyCycle = 0;
  283. // Reset time-off to initial value.
  284. calcBackOffParams->Bands[bandIdx].TimeOff = 0;
  285. if( calcBackOffParams->Joined == false )
  286. {
  287. // Get the join duty cycle
  288. joinDutyCycle = RegionCommonGetJoinDc( calcBackOffParams->ElapsedTime );
  289. // Apply the most restricting duty cycle
  290. dutyCycle = MAX( dutyCycle, joinDutyCycle );
  291. // Reset the timeoff if the last frame was not a join request and when the duty cycle is not enabled
  292. if( ( calcBackOffParams->DutyCycleEnabled == false ) && ( calcBackOffParams->LastTxIsJoinRequest == false ) )
  293. {
  294. // This is the case when the duty cycle is off and the last uplink frame was not a join.
  295. // This could happen in case of a rejoin, e.g. in compliance test mode.
  296. // In this special case we have to set the time off to 0, since the join duty cycle shall only
  297. // be applied after the first join request.
  298. calcBackOffParams->Bands[bandIdx].TimeOff = 0;
  299. }
  300. else
  301. {
  302. // Apply band time-off.
  303. calcBackOffParams->Bands[bandIdx].TimeOff = calcBackOffParams->TxTimeOnAir * dutyCycle - calcBackOffParams->TxTimeOnAir;
  304. }
  305. }
  306. else
  307. {
  308. if( calcBackOffParams->DutyCycleEnabled == true )
  309. {
  310. calcBackOffParams->Bands[bandIdx].TimeOff = calcBackOffParams->TxTimeOnAir * dutyCycle - calcBackOffParams->TxTimeOnAir;
  311. }
  312. else
  313. {
  314. calcBackOffParams->Bands[bandIdx].TimeOff = 0;
  315. }
  316. }
  317. }
  318. void RegionCommonRxBeaconSetup( RegionCommonRxBeaconSetupParams_t* rxBeaconSetupParams )
  319. {
  320. bool rxContinuous = true;
  321. uint8_t datarate;
  322. // Set the radio into sleep mode
  323. Radio.Sleep( );
  324. // Setup frequency and payload length
  325. Radio.SetChannel( rxBeaconSetupParams->Frequency );
  326. Radio.SetMaxPayloadLength( MODEM_LORA, rxBeaconSetupParams->BeaconSize );
  327. // Check the RX continuous mode
  328. if( rxBeaconSetupParams->RxTime != 0 )
  329. {
  330. rxContinuous = false;
  331. }
  332. // Get region specific datarate
  333. datarate = rxBeaconSetupParams->Datarates[rxBeaconSetupParams->BeaconDatarate];
  334. // Setup radio
  335. Radio.SetRxConfig( MODEM_LORA, rxBeaconSetupParams->BeaconChannelBW, datarate,
  336. 1, 0, 10, rxBeaconSetupParams->SymbolTimeout, true, rxBeaconSetupParams->BeaconSize, false, 0, 0, false, rxContinuous );
  337. Radio.Rx( rxBeaconSetupParams->RxTime );
  338. }