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