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