Implement a secure ICS protocol targeting LoRa Node151 microcontroller for controlling irrigation.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

679 lines
24 KiB

  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 "radio.h"
  33. #include "utilities.h"
  34. #include "RegionCommon.h"
  35. #include "systime.h"
  36. #define BACKOFF_DC_1_HOUR 100
  37. #define BACKOFF_DC_10_HOURS 1000
  38. #define BACKOFF_DC_24_HOURS 10000
  39. #define BACKOFF_DUTY_CYCLE_1_HOUR_IN_S 3600
  40. #define BACKOFF_DUTY_CYCLE_10_HOURS_IN_S ( BACKOFF_DUTY_CYCLE_1_HOUR_IN_S + ( BACKOFF_DUTY_CYCLE_1_HOUR_IN_S * 10 ) )
  41. #define BACKOFF_DUTY_CYCLE_24_HOURS_IN_S ( BACKOFF_DUTY_CYCLE_10_HOURS_IN_S + ( BACKOFF_DUTY_CYCLE_1_HOUR_IN_S * 24 ) )
  42. #define BACKOFF_24_HOURS_IN_S ( BACKOFF_DUTY_CYCLE_1_HOUR_IN_S * 24 )
  43. #ifndef DUTY_CYCLE_TIME_PERIOD
  44. /*!
  45. * Default duty cycle observation time period
  46. *
  47. * \remark The ETSI observation time period is 1 hour (3600000 ms) but, the implemented algorithm may violate the
  48. * defined duty-cycle restrictions. In order to ensure that these restrictions never get violated we changed the
  49. * default duty cycle observation time period to 1/2 hour (1800000 ms).
  50. */
  51. #define DUTY_CYCLE_TIME_PERIOD 1800000
  52. #endif
  53. /*!
  54. * \brief Returns `N / D` rounded to the smallest integer value greater than or equal to `N / D`
  55. *
  56. * \warning when `D == 0`, the result is undefined
  57. *
  58. * \remark `N` and `D` can be signed or unsigned
  59. *
  60. * \param [IN] N the numerator, which can have any sign
  61. * \param [IN] D the denominator, which can have any sign
  62. * \retval N / D with any fractional part rounded to the smallest integer value greater than or equal to `N / D`
  63. */
  64. #define DIV_CEIL( N, D ) \
  65. ( \
  66. ( N > 0 ) ? \
  67. ( ( ( N ) + ( D ) - 1 ) / ( D ) ) : \
  68. ( ( N ) / ( D ) ) \
  69. )
  70. static uint16_t GetDutyCycle( Band_t* band, bool joined, SysTime_t elapsedTimeSinceStartup )
  71. {
  72. uint16_t dutyCycle = band->DCycle;
  73. if( joined == false )
  74. {
  75. uint16_t joinDutyCycle = BACKOFF_DC_24_HOURS;
  76. if( elapsedTimeSinceStartup.Seconds < BACKOFF_DUTY_CYCLE_1_HOUR_IN_S )
  77. {
  78. joinDutyCycle = BACKOFF_DC_1_HOUR;
  79. }
  80. else if( elapsedTimeSinceStartup.Seconds < BACKOFF_DUTY_CYCLE_10_HOURS_IN_S )
  81. {
  82. joinDutyCycle = BACKOFF_DC_10_HOURS;
  83. }
  84. else
  85. {
  86. joinDutyCycle = BACKOFF_DC_24_HOURS;
  87. }
  88. // Take the most restrictive duty cycle
  89. dutyCycle = MAX( dutyCycle, joinDutyCycle );
  90. }
  91. // Prevent value of 0
  92. if( dutyCycle == 0 )
  93. {
  94. dutyCycle = 1;
  95. }
  96. return dutyCycle;
  97. }
  98. static uint16_t SetMaxTimeCredits( Band_t* band, bool joined, SysTime_t elapsedTimeSinceStartup,
  99. bool dutyCycleEnabled, bool lastTxIsJoinRequest )
  100. {
  101. uint16_t dutyCycle = band->DCycle;
  102. TimerTime_t maxCredits = DUTY_CYCLE_TIME_PERIOD;
  103. TimerTime_t elapsedTime = SysTimeToMs( elapsedTimeSinceStartup );
  104. SysTime_t timeDiff = { 0 };
  105. // Get the band duty cycle. If not joined, the function either returns the join duty cycle
  106. // or the band duty cycle, whichever is more restrictive.
  107. dutyCycle = GetDutyCycle( band, joined, elapsedTimeSinceStartup );
  108. if( joined == false )
  109. {
  110. if( dutyCycle == BACKOFF_DC_1_HOUR )
  111. {
  112. maxCredits = DUTY_CYCLE_TIME_PERIOD;
  113. band->LastMaxCreditAssignTime = elapsedTime;
  114. }
  115. else if( dutyCycle == BACKOFF_DC_10_HOURS )
  116. {
  117. maxCredits = DUTY_CYCLE_TIME_PERIOD * 10;
  118. band->LastMaxCreditAssignTime = elapsedTime;
  119. }
  120. else
  121. {
  122. maxCredits = DUTY_CYCLE_TIME_PERIOD * 24;
  123. }
  124. timeDiff = SysTimeSub( elapsedTimeSinceStartup, SysTimeFromMs( band->LastMaxCreditAssignTime ) );
  125. // Verify if we have to assign the maximum credits in cases
  126. // of the preconditions have changed.
  127. if( ( ( dutyCycleEnabled == false ) && ( lastTxIsJoinRequest == false ) ) ||
  128. ( band->MaxTimeCredits != maxCredits ) ||
  129. ( timeDiff.Seconds >= BACKOFF_24_HOURS_IN_S ) )
  130. {
  131. band->TimeCredits = maxCredits;
  132. if( elapsedTimeSinceStartup.Seconds >= BACKOFF_DUTY_CYCLE_24_HOURS_IN_S )
  133. {
  134. timeDiff.Seconds = ( elapsedTimeSinceStartup.Seconds - BACKOFF_DUTY_CYCLE_24_HOURS_IN_S ) / BACKOFF_24_HOURS_IN_S;
  135. timeDiff.Seconds *= BACKOFF_24_HOURS_IN_S;
  136. timeDiff.Seconds += BACKOFF_DUTY_CYCLE_24_HOURS_IN_S;
  137. timeDiff.SubSeconds = 0;
  138. band->LastMaxCreditAssignTime = SysTimeToMs( timeDiff );
  139. }
  140. }
  141. }
  142. else
  143. {
  144. if( dutyCycleEnabled == false )
  145. {
  146. // Assign max credits when the duty cycle is disabled.
  147. band->TimeCredits = maxCredits;
  148. }
  149. }
  150. // Assign the max credits if its the first time
  151. if( band->LastBandUpdateTime == 0 )
  152. {
  153. band->TimeCredits = maxCredits;
  154. }
  155. // Setup the maximum allowed credits. We can assign them
  156. // safely all the time.
  157. band->MaxTimeCredits = maxCredits;
  158. return dutyCycle;
  159. }
  160. static uint16_t UpdateTimeCredits( Band_t* band, bool joined, bool dutyCycleEnabled,
  161. bool lastTxIsJoinRequest, SysTime_t elapsedTimeSinceStartup,
  162. TimerTime_t currentTime )
  163. {
  164. uint16_t dutyCycle = SetMaxTimeCredits( band, joined, elapsedTimeSinceStartup,
  165. dutyCycleEnabled, lastTxIsJoinRequest );
  166. if( joined == true )
  167. {
  168. // Apply a sliding window for the duty cycle with collection and speding
  169. // credits.
  170. band->TimeCredits += TimerGetElapsedTime( band->LastBandUpdateTime );
  171. }
  172. // Limit band credits to maximum
  173. if( band->TimeCredits > band->MaxTimeCredits )
  174. {
  175. band->TimeCredits = band->MaxTimeCredits;
  176. }
  177. // Synchronize update time
  178. band->LastBandUpdateTime = currentTime;
  179. return dutyCycle;
  180. }
  181. static uint8_t CountChannels( uint16_t mask, uint8_t nbBits )
  182. {
  183. uint8_t nbActiveBits = 0;
  184. for( uint8_t j = 0; j < nbBits; j++ )
  185. {
  186. if( ( mask & ( 1 << j ) ) == ( 1 << j ) )
  187. {
  188. nbActiveBits++;
  189. }
  190. }
  191. return nbActiveBits;
  192. }
  193. bool RegionCommonChanVerifyDr( uint8_t nbChannels, uint16_t* channelsMask, int8_t dr, int8_t minDr, int8_t maxDr, ChannelParams_t* channels )
  194. {
  195. if( RegionCommonValueInRange( dr, minDr, maxDr ) == 0 )
  196. {
  197. return false;
  198. }
  199. for( uint8_t i = 0, k = 0; i < nbChannels; i += 16, k++ )
  200. {
  201. for( uint8_t j = 0; j < 16; j++ )
  202. {
  203. if( ( ( channelsMask[k] & ( 1 << j ) ) != 0 ) )
  204. {// Check datarate validity for enabled channels
  205. if( RegionCommonValueInRange( dr, ( channels[i + j].DrRange.Fields.Min & 0x0F ),
  206. ( channels[i + j].DrRange.Fields.Max & 0x0F ) ) == 1 )
  207. {
  208. // At least 1 channel has been found we can return OK.
  209. return true;
  210. }
  211. }
  212. }
  213. }
  214. return false;
  215. }
  216. uint8_t RegionCommonValueInRange( int8_t value, int8_t min, int8_t max )
  217. {
  218. if( ( value >= min ) && ( value <= max ) )
  219. {
  220. return 1;
  221. }
  222. return 0;
  223. }
  224. bool RegionCommonChanDisable( uint16_t* channelsMask, uint8_t id, uint8_t maxChannels )
  225. {
  226. uint8_t index = id / 16;
  227. if( ( index > ( maxChannels / 16 ) ) || ( id >= maxChannels ) )
  228. {
  229. return false;
  230. }
  231. // Deactivate channel
  232. channelsMask[index] &= ~( 1 << ( id % 16 ) );
  233. return true;
  234. }
  235. uint8_t RegionCommonCountChannels( uint16_t* channelsMask, uint8_t startIdx, uint8_t stopIdx )
  236. {
  237. uint8_t nbChannels = 0;
  238. if( channelsMask == NULL )
  239. {
  240. return 0;
  241. }
  242. for( uint8_t i = startIdx; i < stopIdx; i++ )
  243. {
  244. nbChannels += CountChannels( channelsMask[i], 16 );
  245. }
  246. return nbChannels;
  247. }
  248. void RegionCommonChanMaskCopy( uint16_t* channelsMaskDest, uint16_t* channelsMaskSrc, uint8_t len )
  249. {
  250. if( ( channelsMaskDest != NULL ) && ( channelsMaskSrc != NULL ) )
  251. {
  252. for( uint8_t i = 0; i < len; i++ )
  253. {
  254. channelsMaskDest[i] = channelsMaskSrc[i];
  255. }
  256. }
  257. }
  258. void RegionCommonSetBandTxDone( Band_t* band, TimerTime_t lastTxAirTime, bool joined, SysTime_t elapsedTimeSinceStartup )
  259. {
  260. // Get the band duty cycle. If not joined, the function either returns the join duty cycle
  261. // or the band duty cycle, whichever is more restrictive.
  262. uint16_t dutyCycle = GetDutyCycle( band, joined, elapsedTimeSinceStartup );
  263. // Reduce with transmission time
  264. if( band->TimeCredits > ( lastTxAirTime * dutyCycle ) )
  265. {
  266. // Reduce time credits by the time of air
  267. band->TimeCredits -= ( lastTxAirTime * dutyCycle );
  268. }
  269. else
  270. {
  271. band->TimeCredits = 0;
  272. }
  273. }
  274. TimerTime_t RegionCommonUpdateBandTimeOff( bool joined, Band_t* bands,
  275. uint8_t nbBands, bool dutyCycleEnabled,
  276. bool lastTxIsJoinRequest, SysTime_t elapsedTimeSinceStartup,
  277. TimerTime_t expectedTimeOnAir )
  278. {
  279. TimerTime_t minTimeToWait = TIMERTIME_T_MAX;
  280. TimerTime_t currentTime = TimerGetCurrentTime( );
  281. TimerTime_t creditCosts = 0;
  282. uint16_t dutyCycle = 1;
  283. uint8_t validBands = 0;
  284. for( uint8_t i = 0; i < nbBands; i++ )
  285. {
  286. // Synchronization of bands and credits
  287. dutyCycle = UpdateTimeCredits( &bands[i], joined, dutyCycleEnabled,
  288. lastTxIsJoinRequest, elapsedTimeSinceStartup,
  289. currentTime );
  290. // Calculate the credit costs for the next transmission
  291. // with the duty cycle and the expected time on air
  292. creditCosts = expectedTimeOnAir * dutyCycle;
  293. // Check if the band is ready for transmission. Its ready,
  294. // when the duty cycle is off, or the TimeCredits of the band
  295. // is higher than the credit costs for the transmission.
  296. if( ( bands[i].TimeCredits > creditCosts ) ||
  297. ( ( dutyCycleEnabled == false ) && ( joined == true ) ) )
  298. {
  299. bands[i].ReadyForTransmission = true;
  300. // This band is a potential candidate for an
  301. // upcoming transmission, so increase the counter.
  302. validBands++;
  303. }
  304. else
  305. {
  306. // In this case, the band has not enough credits
  307. // for the next transmission.
  308. bands[i].ReadyForTransmission = false;
  309. if( bands[i].MaxTimeCredits > creditCosts )
  310. {
  311. // The band can only be taken into account, if the maximum credits
  312. // of the band are higher than the credit costs.
  313. // We calculate the minTimeToWait among the bands which are not
  314. // ready for transmission and which are potentially available
  315. // for a transmission in the future.
  316. minTimeToWait = MIN( minTimeToWait, ( creditCosts - bands[i].TimeCredits ) );
  317. // This band is a potential candidate for an
  318. // upcoming transmission (even if its time credits are not enough
  319. // at the moment), so increase the counter.
  320. validBands++;
  321. }
  322. // Apply a special calculation if the device is not joined.
  323. if( joined == false )
  324. {
  325. SysTime_t backoffTimeRange = {
  326. .Seconds = 0,
  327. .SubSeconds = 0,
  328. };
  329. // Get the backoff time range based on the duty cycle definition
  330. if( dutyCycle == BACKOFF_DC_1_HOUR )
  331. {
  332. backoffTimeRange.Seconds = BACKOFF_DUTY_CYCLE_1_HOUR_IN_S;
  333. }
  334. else if( dutyCycle == BACKOFF_DC_10_HOURS )
  335. {
  336. backoffTimeRange.Seconds = BACKOFF_DUTY_CYCLE_10_HOURS_IN_S;
  337. }
  338. else
  339. {
  340. backoffTimeRange.Seconds = BACKOFF_DUTY_CYCLE_24_HOURS_IN_S;
  341. }
  342. // Calculate the time to wait.
  343. if( elapsedTimeSinceStartup.Seconds > BACKOFF_DUTY_CYCLE_24_HOURS_IN_S )
  344. {
  345. backoffTimeRange.Seconds += BACKOFF_24_HOURS_IN_S * ( ( ( elapsedTimeSinceStartup.Seconds - BACKOFF_DUTY_CYCLE_24_HOURS_IN_S ) / BACKOFF_24_HOURS_IN_S ) + 1 );
  346. }
  347. // Calculate the time difference between now and the next range
  348. backoffTimeRange = SysTimeSub( backoffTimeRange, elapsedTimeSinceStartup );
  349. minTimeToWait = SysTimeToMs( backoffTimeRange );
  350. }
  351. }
  352. }
  353. if( validBands == 0 )
  354. {
  355. // There is no valid band available to handle a transmission
  356. // in the given DUTY_CYCLE_TIME_PERIOD.
  357. return TIMERTIME_T_MAX;
  358. }
  359. return minTimeToWait;
  360. }
  361. uint8_t RegionCommonParseLinkAdrReq( uint8_t* payload, RegionCommonLinkAdrParams_t* linkAdrParams )
  362. {
  363. uint8_t retIndex = 0;
  364. if( payload[0] == SRV_MAC_LINK_ADR_REQ )
  365. {
  366. // Parse datarate and tx power
  367. linkAdrParams->Datarate = payload[1];
  368. linkAdrParams->TxPower = linkAdrParams->Datarate & 0x0F;
  369. linkAdrParams->Datarate = ( linkAdrParams->Datarate >> 4 ) & 0x0F;
  370. // Parse ChMask
  371. linkAdrParams->ChMask = ( uint16_t )payload[2];
  372. linkAdrParams->ChMask |= ( uint16_t )payload[3] << 8;
  373. // Parse ChMaskCtrl and nbRep
  374. linkAdrParams->NbRep = payload[4];
  375. linkAdrParams->ChMaskCtrl = ( linkAdrParams->NbRep >> 4 ) & 0x07;
  376. linkAdrParams->NbRep &= 0x0F;
  377. // LinkAdrReq has 4 bytes length + 1 byte CMD
  378. retIndex = 5;
  379. }
  380. return retIndex;
  381. }
  382. uint8_t RegionCommonLinkAdrReqVerifyParams( RegionCommonLinkAdrReqVerifyParams_t* verifyParams, int8_t* dr, int8_t* txPow, uint8_t* nbRep )
  383. {
  384. uint8_t status = verifyParams->Status;
  385. int8_t datarate = verifyParams->Datarate;
  386. int8_t txPower = verifyParams->TxPower;
  387. int8_t nbRepetitions = verifyParams->NbRep;
  388. // Handle the case when ADR is off.
  389. if( verifyParams->AdrEnabled == false )
  390. {
  391. // When ADR is off, we are allowed to change the channels mask
  392. nbRepetitions = verifyParams->CurrentNbRep;
  393. datarate = verifyParams->CurrentDatarate;
  394. txPower = verifyParams->CurrentTxPower;
  395. }
  396. if( status != 0 )
  397. {
  398. // Verify datarate. The variable phyParam. Value contains the minimum allowed datarate.
  399. if( datarate == 0x0F )
  400. { // 0xF means that the device MUST ignore that field, and keep the current parameter value.
  401. datarate = verifyParams->CurrentDatarate;
  402. }
  403. else if( RegionCommonChanVerifyDr( verifyParams->NbChannels, verifyParams->ChannelsMask, datarate,
  404. verifyParams->MinDatarate, verifyParams->MaxDatarate, verifyParams->Channels ) == false )
  405. {
  406. status &= 0xFD; // Datarate KO
  407. }
  408. // Verify tx power
  409. if( txPower == 0x0F )
  410. { // 0xF means that the device MUST ignore that field, and keep the current parameter value.
  411. txPower = verifyParams->CurrentTxPower;
  412. }
  413. else if( RegionCommonValueInRange( txPower, verifyParams->MaxTxPower, verifyParams->MinTxPower ) == 0 )
  414. {
  415. // Verify if the maximum TX power is exceeded
  416. if( verifyParams->MaxTxPower > txPower )
  417. { // Apply maximum TX power. Accept TX power.
  418. txPower = verifyParams->MaxTxPower;
  419. }
  420. else
  421. {
  422. status &= 0xFB; // TxPower KO
  423. }
  424. }
  425. }
  426. // If the status is ok, verify the NbRep
  427. if( status == 0x07 )
  428. {
  429. if( nbRepetitions == 0 )
  430. { // Set nbRep to the default value of 1.
  431. nbRepetitions = 1;
  432. }
  433. }
  434. // Apply changes
  435. *dr = datarate;
  436. *txPow = txPower;
  437. *nbRep = nbRepetitions;
  438. return status;
  439. }
  440. uint32_t RegionCommonComputeSymbolTimeLoRa( uint8_t phyDr, uint32_t bandwidthInHz )
  441. {
  442. return ( 1 << phyDr ) * 1000000 / bandwidthInHz;
  443. }
  444. uint32_t RegionCommonComputeSymbolTimeFsk( uint8_t phyDrInKbps )
  445. {
  446. return 8000 / ( uint32_t )phyDrInKbps; // 1 symbol equals 1 byte
  447. }
  448. void RegionCommonComputeRxWindowParameters( uint32_t tSymbolInUs, uint8_t minRxSymbols, uint32_t rxErrorInMs, uint32_t wakeUpTimeInMs, uint32_t* windowTimeoutInSymbols, int32_t* windowOffsetInMs )
  449. {
  450. *windowTimeoutInSymbols = MAX( DIV_CEIL( ( ( 2 * minRxSymbols - 8 ) * tSymbolInUs + 2 * ( rxErrorInMs * 1000 ) ), tSymbolInUs ), minRxSymbols ); // Computed number of symbols
  451. *windowOffsetInMs = ( int32_t )DIV_CEIL( ( int32_t )( 4 * tSymbolInUs ) -
  452. ( int32_t )DIV_CEIL( ( *windowTimeoutInSymbols * tSymbolInUs ), 2 ) -
  453. ( int32_t )( wakeUpTimeInMs * 1000 ), 1000 );
  454. }
  455. int8_t RegionCommonComputeTxPower( int8_t txPowerIndex, float maxEirp, float antennaGain )
  456. {
  457. int8_t phyTxPower = 0;
  458. phyTxPower = ( int8_t )floor( ( maxEirp - ( txPowerIndex * 2U ) ) - antennaGain );
  459. return phyTxPower;
  460. }
  461. void RegionCommonRxBeaconSetup( RegionCommonRxBeaconSetupParams_t* rxBeaconSetupParams )
  462. {
  463. bool rxContinuous = true;
  464. uint8_t datarate;
  465. // Set the radio into sleep mode
  466. Radio.Sleep( );
  467. // Setup frequency and payload length
  468. Radio.SetChannel( rxBeaconSetupParams->Frequency );
  469. Radio.SetMaxPayloadLength( MODEM_LORA, rxBeaconSetupParams->BeaconSize );
  470. // Check the RX continuous mode
  471. if( rxBeaconSetupParams->RxTime != 0 )
  472. {
  473. rxContinuous = false;
  474. }
  475. // Get region specific datarate
  476. datarate = rxBeaconSetupParams->Datarates[rxBeaconSetupParams->BeaconDatarate];
  477. // Setup radio
  478. Radio.SetRxConfig( MODEM_LORA, rxBeaconSetupParams->BeaconChannelBW, datarate,
  479. 1, 0, 10, rxBeaconSetupParams->SymbolTimeout, true, rxBeaconSetupParams->BeaconSize, false, 0, 0, false, rxContinuous );
  480. Radio.Rx( rxBeaconSetupParams->RxTime );
  481. }
  482. void RegionCommonCountNbOfEnabledChannels( RegionCommonCountNbOfEnabledChannelsParams_t* countNbOfEnabledChannelsParams,
  483. uint8_t* enabledChannels, uint8_t* nbEnabledChannels, uint8_t* nbRestrictedChannels )
  484. {
  485. uint8_t nbChannelCount = 0;
  486. uint8_t nbRestrictedChannelsCount = 0;
  487. for( uint8_t i = 0, k = 0; i < countNbOfEnabledChannelsParams->MaxNbChannels; i += 16, k++ )
  488. {
  489. for( uint8_t j = 0; j < 16; j++ )
  490. {
  491. if( ( countNbOfEnabledChannelsParams->ChannelsMask[k] & ( 1 << j ) ) != 0 )
  492. {
  493. if( countNbOfEnabledChannelsParams->Channels[i + j].Frequency == 0 )
  494. { // Check if the channel is enabled
  495. continue;
  496. }
  497. if( ( countNbOfEnabledChannelsParams->Joined == false ) &&
  498. ( countNbOfEnabledChannelsParams->JoinChannels != NULL ) )
  499. {
  500. if( ( countNbOfEnabledChannelsParams->JoinChannels[k] & ( 1 << j ) ) == 0 )
  501. {
  502. continue;
  503. }
  504. }
  505. if( RegionCommonValueInRange( countNbOfEnabledChannelsParams->Datarate,
  506. countNbOfEnabledChannelsParams->Channels[i + j].DrRange.Fields.Min,
  507. countNbOfEnabledChannelsParams->Channels[i + j].DrRange.Fields.Max ) == false )
  508. { // Check if the current channel selection supports the given datarate
  509. continue;
  510. }
  511. if( countNbOfEnabledChannelsParams->Bands[countNbOfEnabledChannelsParams->Channels[i + j].Band].ReadyForTransmission == false )
  512. { // Check if the band is available for transmission
  513. nbRestrictedChannelsCount++;
  514. continue;
  515. }
  516. enabledChannels[nbChannelCount++] = i + j;
  517. }
  518. }
  519. }
  520. *nbEnabledChannels = nbChannelCount;
  521. *nbRestrictedChannels = nbRestrictedChannelsCount;
  522. }
  523. LoRaMacStatus_t RegionCommonIdentifyChannels( RegionCommonIdentifyChannelsParam_t* identifyChannelsParam,
  524. TimerTime_t* aggregatedTimeOff, uint8_t* enabledChannels,
  525. uint8_t* nbEnabledChannels, uint8_t* nbRestrictedChannels,
  526. TimerTime_t* nextTxDelay )
  527. {
  528. TimerTime_t elapsed = TimerGetElapsedTime( identifyChannelsParam->LastAggrTx );
  529. *nextTxDelay = identifyChannelsParam->AggrTimeOff - elapsed;
  530. *nbRestrictedChannels = 1;
  531. *nbEnabledChannels = 0;
  532. if( ( identifyChannelsParam->LastAggrTx == 0 ) ||
  533. ( identifyChannelsParam->AggrTimeOff <= elapsed ) )
  534. {
  535. // Reset Aggregated time off
  536. *aggregatedTimeOff = 0;
  537. // Update bands Time OFF
  538. *nextTxDelay = RegionCommonUpdateBandTimeOff( identifyChannelsParam->CountNbOfEnabledChannelsParam->Joined,
  539. identifyChannelsParam->CountNbOfEnabledChannelsParam->Bands,
  540. identifyChannelsParam->MaxBands,
  541. identifyChannelsParam->DutyCycleEnabled,
  542. identifyChannelsParam->LastTxIsJoinRequest,
  543. identifyChannelsParam->ElapsedTimeSinceStartUp,
  544. identifyChannelsParam->ExpectedTimeOnAir );
  545. RegionCommonCountNbOfEnabledChannels( identifyChannelsParam->CountNbOfEnabledChannelsParam, enabledChannels,
  546. nbEnabledChannels, nbRestrictedChannels );
  547. }
  548. if( *nbEnabledChannels > 0 )
  549. {
  550. *nextTxDelay = 0;
  551. return LORAMAC_STATUS_OK;
  552. }
  553. else if( *nbRestrictedChannels > 0 )
  554. {
  555. return LORAMAC_STATUS_DUTYCYCLE_RESTRICTED;
  556. }
  557. else
  558. {
  559. return LORAMAC_STATUS_NO_CHANNEL_FOUND;
  560. }
  561. }
  562. int8_t RegionCommonGetNextLowerTxDr( RegionCommonGetNextLowerTxDrParams_t *params )
  563. {
  564. int8_t drLocal = params->CurrentDr;
  565. if( params->CurrentDr == params->MinDr )
  566. {
  567. return params->MinDr;
  568. }
  569. else
  570. {
  571. do
  572. {
  573. drLocal = ( drLocal - 1 );
  574. } while( ( drLocal != params->MinDr ) &&
  575. ( RegionCommonChanVerifyDr( params->NbChannels, params->ChannelsMask, drLocal, params->MinDr, params->MaxDr, params->Channels ) == false ) );
  576. return drLocal;
  577. }
  578. }
  579. int8_t RegionCommonLimitTxPower( int8_t txPower, int8_t maxBandTxPower )
  580. {
  581. // Limit tx power to the band max
  582. return MAX( txPower, maxBandTxPower );
  583. }
  584. uint32_t RegionCommonGetBandwidth( uint32_t drIndex, const uint32_t* bandwidths )
  585. {
  586. switch( bandwidths[drIndex] )
  587. {
  588. default:
  589. case 125000:
  590. return 0;
  591. case 250000:
  592. return 1;
  593. case 500000:
  594. return 2;
  595. }
  596. }