1 /*!
2 * \file RegionCN470.c
3 *
4 * \brief Region implementation for CN470
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 "radio.h"
32 #include "RegionCommon.h"
33 #include "RegionCN470.h"
34 #include "RegionCN470A20.h"
35 #include "RegionCN470B20.h"
36 #include "RegionCN470A26.h"
37 #include "RegionCN470B26.h"
38
39 // Definitions
40 #define CHANNELS_MASK_SIZE 6
41
42
43 #ifndef REGION_CN470_DEFAULT_CHANNEL_PLAN
44 #define REGION_CN470_DEFAULT_CHANNEL_PLAN CHANNEL_PLAN_20MHZ_TYPE_A
45 #endif
46
47 #ifndef REGION_CN470_DEFAULT_RX_WND_2_FREQ
48 #define REGION_CN470_DEFAULT_RX_WND_2_FREQ CN470_A20_RX_WND_2_FREQ_ABP
49 #endif
50
51
52 ChannelParams_t CommonJoinChannels[] = CN470_COMMON_JOIN_CHANNELS;
53
54 /*!
55 * Definition of the regional channel plan.
56 */
57 typedef struct sRegionCN470ChannelPlanCtx
58 {
59 /*!
60 * Size of the channels mask. Must be smaller
61 * or equal than CHANNELS_MASK_SIZE.
62 */
63 uint8_t ChannelsMaskSize;
64 /*!
65 * Number of elements in the join accept list.
66 */
67 uint8_t JoinAcceptListSize;
68 /*!
69 * Number of available channels for beaconing.
70 */
71 uint8_t NbBeaconChannels;
72 /*!
73 * Number of available channels for ping slots.
74 */
75 uint8_t NbPingSlotChannels;
76 /*!
77 * \brief Calculation of the beacon frequency.
78 *
79 * \param [IN] channel The Beacon channel number.
80 *
81 * \param [IN] joinChannelIndex The join channel index.
82 *
83 * \param [IN] isPingSlot Set to true, if its a ping slot.
84 *
85 * \retval Returns the beacon frequency.
86 */
87 uint32_t ( *GetDownlinkFrequency )( uint8_t channel, uint8_t joinChannelIndex, bool isPingSlot );
88 /*!
89 * \brief Performs the update of the channelsMask based on the input parameters.
90 *
91 * \param [IN] joinChannelIndex The join channel index.
92 *
93 * \retval Returns the offset for the given join channel.
94 */
95 uint8_t ( *GetBeaconChannelOffset )( uint8_t joinChannelIndex );
96 /*!
97 * \brief Performs the update of the channelsMask based on the input parameters.
98 *
99 * \param [IN] channelsMask A pointer to the channels mask.
100 *
101 * \param [IN] chMaskCntl The value of the chMaskCntl field of the LinkAdrReq.
102 *
103 * \param [IN] chanMask The value of the chanMask field of the LinkAdrReq.
104 *
105 * \param [IN] channels A pointer to the available channels.
106 *
107 * \retval Status of the operation. Return 0x07 if the channels mask is valid.
108 */
109 uint8_t ( *LinkAdrChMaskUpdate )( uint16_t* channelsMask, uint8_t chMaskCntl,
110 uint16_t chanMask, ChannelParams_t* channels );
111 /*!
112 * \brief Verifies if the frequency provided is valid.
113 *
114 * \param [IN] frequency The frequency to verify.
115 *
116 * \retval Returns true, if the frequency is valid.
117 */
118 bool ( *VerifyRfFreq )( uint32_t frequency );
119 /*!
120 * \brief Initializes all channels, datarates, frequencies and bands.
121 *
122 * \param [IN] channels A pointer to the available channels.
123 */
124 void ( *InitializeChannels )( ChannelParams_t* channels );
125 /*!
126 * \brief Initializes the channels mask and the channels default mask.
127 *
128 * \param [IN] channelsDefaultMask A pointer to the channels default mask.
129 */
130 void ( *InitializeChannelsMask )( uint16_t* channelsDefaultMask );
131 /*!
132 * \brief Computes the frequency for the RX1 window.
133 *
134 * \param [IN] channel The channel utilized currently.
135 *
136 * \retval Returns the frequency which shall be used.
137 */
138 uint32_t ( *GetRx1Frequency )( uint8_t channel );
139 /*!
140 * \brief Computes the frequency for the RX2 window.
141 *
142 * \param [IN] joinChannelIndex The join channel index.
143 *
144 * \param [IN] isOtaaDevice Set to true, if the device is an OTAA device.
145 *
146 * \retval Returns the frequency which shall be used.
147 */
148 uint32_t ( *GetRx2Frequency )( uint8_t joinChannelIndex, bool isOtaaDevice );
149 }RegionCN470ChannelPlanCtx_t;
150
151 /*
152 * Non-volatile module context.
153 */
154 static RegionNvmDataGroup1_t* RegionNvmGroup1;
155 static RegionNvmDataGroup2_t* RegionNvmGroup2;
156 static Band_t* RegionBands;
157
158 /*
159 * Context for the current channel plan.
160 */
161 static RegionCN470ChannelPlanCtx_t ChannelPlanCtx;
162
163 // Static functions
ApplyChannelPlanConfig(RegionCN470ChannelPlan_t channelPlan,RegionCN470ChannelPlanCtx_t * ctx)164 static void ApplyChannelPlanConfig( RegionCN470ChannelPlan_t channelPlan, RegionCN470ChannelPlanCtx_t* ctx )
165 {
166 switch( channelPlan )
167 {
168 case CHANNEL_PLAN_20MHZ_TYPE_A:
169 {
170 ctx->ChannelsMaskSize = CN470_A20_CHANNELS_MASK_SIZE;
171 ctx->JoinAcceptListSize = CN470_A20_JOIN_ACCEPT_LIST_SIZE;
172 ctx->NbBeaconChannels = CN470_A20_BEACON_NB_CHANNELS;
173 ctx->NbPingSlotChannels = CN470_A20_PING_SLOT_NB_CHANNELS;
174 ctx->GetDownlinkFrequency = RegionCN470A20GetDownlinkFrequency;
175 ctx->GetBeaconChannelOffset = RegionCN470A20GetBeaconChannelOffset;
176 ctx->LinkAdrChMaskUpdate = RegionCN470A20LinkAdrChMaskUpdate;
177 ctx->VerifyRfFreq = RegionCN470A20VerifyRfFreq;
178 ctx->InitializeChannels = RegionCN470A20InitializeChannels;
179 ctx->InitializeChannelsMask = RegionCN470A20InitializeChannelsMask;
180 ctx->GetRx1Frequency = RegionCN470A20GetRx1Frequency;
181 ctx->GetRx2Frequency = RegionCN470A20GetRx2Frequency;
182 break;
183 }
184 case CHANNEL_PLAN_20MHZ_TYPE_B:
185 {
186 ctx->ChannelsMaskSize = CN470_B20_CHANNELS_MASK_SIZE;
187 ctx->JoinAcceptListSize = CN470_B20_JOIN_ACCEPT_LIST_SIZE;
188 ctx->NbBeaconChannels = CN470_B20_BEACON_NB_CHANNELS;
189 ctx->NbPingSlotChannels = CN470_B20_PING_SLOT_NB_CHANNELS;
190 ctx->GetDownlinkFrequency = RegionCN470B20GetDownlinkFrequency;
191 ctx->GetBeaconChannelOffset = RegionCN470B20GetBeaconChannelOffset;
192 ctx->LinkAdrChMaskUpdate = RegionCN470B20LinkAdrChMaskUpdate;
193 ctx->VerifyRfFreq = RegionCN470B20VerifyRfFreq;
194 ctx->InitializeChannels = RegionCN470B20InitializeChannels;
195 ctx->InitializeChannelsMask = RegionCN470B20InitializeChannelsMask;
196 ctx->GetRx1Frequency = RegionCN470B20GetRx1Frequency;
197 ctx->GetRx2Frequency = RegionCN470B20GetRx2Frequency;
198 break;
199 }
200 case CHANNEL_PLAN_26MHZ_TYPE_A:
201 {
202 ctx->ChannelsMaskSize = CN470_A26_CHANNELS_MASK_SIZE;
203 ctx->JoinAcceptListSize = CN470_A26_JOIN_ACCEPT_LIST_SIZE;
204 ctx->NbBeaconChannels = CN470_A26_BEACON_NB_CHANNELS;
205 ctx->NbPingSlotChannels = CN470_A26_PING_SLOT_NB_CHANNELS;
206 ctx->GetDownlinkFrequency = RegionCN470A26GetDownlinkFrequency;
207 ctx->GetBeaconChannelOffset = RegionCN470A26GetBeaconChannelOffset;
208 ctx->LinkAdrChMaskUpdate = RegionCN470A26LinkAdrChMaskUpdate;
209 ctx->VerifyRfFreq = RegionCN470A26VerifyRfFreq;
210 ctx->InitializeChannels = RegionCN470A26InitializeChannels;
211 ctx->InitializeChannelsMask = RegionCN470A26InitializeChannelsMask;
212 ctx->GetRx1Frequency = RegionCN470A26GetRx1Frequency;
213 ctx->GetRx2Frequency = RegionCN470A26GetRx2Frequency;
214 break;
215 }
216 case CHANNEL_PLAN_26MHZ_TYPE_B:
217 {
218 ctx->ChannelsMaskSize = CN470_B26_CHANNELS_MASK_SIZE;
219 ctx->JoinAcceptListSize = CN470_B26_JOIN_ACCEPT_LIST_SIZE;
220 ctx->NbBeaconChannels = CN470_B26_BEACON_NB_CHANNELS;
221 ctx->NbPingSlotChannels = CN470_B26_PING_SLOT_NB_CHANNELS;
222 ctx->GetDownlinkFrequency = RegionCN470B26GetDownlinkFrequency;
223 ctx->GetBeaconChannelOffset = RegionCN470B26GetBeaconChannelOffset;
224 ctx->LinkAdrChMaskUpdate = RegionCN470B26LinkAdrChMaskUpdate;
225 ctx->VerifyRfFreq = RegionCN470B26VerifyRfFreq;
226 ctx->InitializeChannels = RegionCN470B26InitializeChannels;
227 ctx->InitializeChannelsMask = RegionCN470B26InitializeChannelsMask;
228 ctx->GetRx1Frequency = RegionCN470B26GetRx1Frequency;
229 ctx->GetRx2Frequency = RegionCN470B26GetRx2Frequency;
230 break;
231 }
232 default:
233 {
234 // Apply CHANNEL_PLAN_20MHZ_TYPE_A
235 ctx->ChannelsMaskSize = CN470_A20_CHANNELS_MASK_SIZE;
236 ctx->JoinAcceptListSize = CN470_A20_JOIN_ACCEPT_LIST_SIZE;
237 ctx->NbBeaconChannels = CN470_A20_BEACON_NB_CHANNELS;
238 ctx->NbPingSlotChannels = CN470_A20_PING_SLOT_NB_CHANNELS;
239 ctx->GetDownlinkFrequency = RegionCN470A20GetDownlinkFrequency;
240 ctx->GetBeaconChannelOffset = RegionCN470A20GetBeaconChannelOffset;
241 ctx->LinkAdrChMaskUpdate = RegionCN470A20LinkAdrChMaskUpdate;
242 ctx->VerifyRfFreq = RegionCN470A20VerifyRfFreq;
243 ctx->InitializeChannels = RegionCN470A20InitializeChannels;
244 ctx->InitializeChannelsMask = RegionCN470A20InitializeChannelsMask;
245 ctx->GetRx1Frequency = RegionCN470A20GetRx1Frequency;
246 ctx->GetRx2Frequency = RegionCN470A20GetRx2Frequency;
247 break;
248 }
249 }
250 }
251
IdentifyChannelPlan(uint8_t joinChannel)252 static RegionCN470ChannelPlan_t IdentifyChannelPlan( uint8_t joinChannel )
253 {
254 RegionCN470ChannelPlan_t channelPlan = CHANNEL_PLAN_UNKNOWN;
255
256 if( joinChannel <= 7 )
257 {
258 channelPlan = CHANNEL_PLAN_20MHZ_TYPE_A;
259 }
260 else if ( ( joinChannel <= 9 ) && ( joinChannel >= 8 ) )
261 {
262 channelPlan = CHANNEL_PLAN_20MHZ_TYPE_B;
263 }
264 else if ( ( joinChannel <= 14 ) && ( joinChannel >= 10 ) )
265 {
266 channelPlan = CHANNEL_PLAN_26MHZ_TYPE_A;
267 }
268 else if( ( joinChannel <= 19 ) && ( joinChannel >= 15 ) )
269 {
270 channelPlan = CHANNEL_PLAN_26MHZ_TYPE_B;
271 }
272 return channelPlan;
273 }
274
VerifyRfFreq(uint32_t frequency)275 static bool VerifyRfFreq( uint32_t frequency )
276 {
277 // Check radio driver support
278 if( Radio.CheckRfFrequency( frequency ) == false )
279 {
280 return false;
281 }
282
283 return ChannelPlanCtx.VerifyRfFreq( frequency );
284 }
285
GetTimeOnAir(int8_t datarate,uint16_t pktLen)286 static TimerTime_t GetTimeOnAir( int8_t datarate, uint16_t pktLen )
287 {
288 int8_t phyDr = DataratesCN470[datarate];
289 uint32_t bandwidth = RegionCommonGetBandwidth( datarate, BandwidthsCN470 );
290
291 return Radio.TimeOnAir( MODEM_LORA, bandwidth, phyDr, 1, 8, false, pktLen, true );
292 }
293
RegionCN470GetPhyParam(GetPhyParams_t * getPhy)294 PhyParam_t RegionCN470GetPhyParam( GetPhyParams_t* getPhy )
295 {
296 PhyParam_t phyParam = { 0 };
297
298 switch( getPhy->Attribute )
299 {
300 case PHY_MIN_RX_DR:
301 {
302 phyParam.Value = CN470_RX_MIN_DATARATE;
303 break;
304 }
305 case PHY_MIN_TX_DR:
306 {
307 phyParam.Value = CN470_TX_MIN_DATARATE;
308 break;
309 }
310 case PHY_DEF_TX_DR:
311 {
312 phyParam.Value = CN470_DEFAULT_DATARATE;
313 break;
314 }
315 case PHY_NEXT_LOWER_TX_DR:
316 {
317 RegionCommonGetNextLowerTxDrParams_t nextLowerTxDrParams =
318 {
319 .CurrentDr = getPhy->Datarate,
320 .MaxDr = ( int8_t )CN470_TX_MAX_DATARATE,
321 .MinDr = ( int8_t )CN470_TX_MIN_DATARATE,
322 .NbChannels = CN470_MAX_NB_CHANNELS,
323 .ChannelsMask = RegionNvmGroup2->ChannelsMask,
324 .Channels = RegionNvmGroup2->Channels,
325 };
326 phyParam.Value = RegionCommonGetNextLowerTxDr( &nextLowerTxDrParams );
327 break;
328 }
329 case PHY_MAX_TX_POWER:
330 {
331 phyParam.Value = CN470_MAX_TX_POWER;
332 break;
333 }
334 case PHY_DEF_TX_POWER:
335 {
336 phyParam.Value = CN470_DEFAULT_TX_POWER;
337 break;
338 }
339 case PHY_DEF_ADR_ACK_LIMIT:
340 {
341 phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_LIMIT;
342 break;
343 }
344 case PHY_DEF_ADR_ACK_DELAY:
345 {
346 phyParam.Value = REGION_COMMON_DEFAULT_ADR_ACK_DELAY;
347 break;
348 }
349 case PHY_MAX_PAYLOAD:
350 {
351 phyParam.Value = MaxPayloadOfDatarateCN470[getPhy->Datarate];
352 break;
353 }
354 case PHY_DUTY_CYCLE:
355 {
356 phyParam.Value = CN470_DUTY_CYCLE_ENABLED;
357 break;
358 }
359 case PHY_MAX_RX_WINDOW:
360 {
361 phyParam.Value = CN470_MAX_RX_WINDOW;
362 break;
363 }
364 case PHY_RECEIVE_DELAY1:
365 {
366 phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY1;
367 break;
368 }
369 case PHY_RECEIVE_DELAY2:
370 {
371 phyParam.Value = REGION_COMMON_DEFAULT_RECEIVE_DELAY2;
372 break;
373 }
374 case PHY_JOIN_ACCEPT_DELAY1:
375 {
376 phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY1;
377 break;
378 }
379 case PHY_JOIN_ACCEPT_DELAY2:
380 {
381 phyParam.Value = REGION_COMMON_DEFAULT_JOIN_ACCEPT_DELAY2;
382 break;
383 }
384 case PHY_RETRANSMIT_TIMEOUT:
385 {
386 phyParam.Value = ( REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT + randr( -REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND, REGION_COMMON_DEFAULT_RETRANSMIT_TIMEOUT_RND ) );
387 break;
388 }
389 case PHY_DEF_DR1_OFFSET:
390 {
391 phyParam.Value = REGION_COMMON_DEFAULT_RX1_DR_OFFSET;
392 break;
393 }
394 case PHY_DEF_RX2_FREQUENCY:
395 {
396 phyParam.Value = REGION_CN470_DEFAULT_RX_WND_2_FREQ;
397
398 if( RegionNvmGroup2->ChannelPlan != CHANNEL_PLAN_UNKNOWN )
399 {
400 phyParam.Value = ChannelPlanCtx.GetRx2Frequency( RegionNvmGroup2->CommonJoinChannelIndex, RegionNvmGroup2->IsOtaaDevice );
401 }
402 break;
403 }
404 case PHY_DEF_RX2_DR:
405 {
406 phyParam.Value = CN470_RX_WND_2_DR;
407 break;
408 }
409 case PHY_CHANNELS_MASK:
410 {
411 phyParam.ChannelsMask = RegionNvmGroup2->ChannelsMask;
412 break;
413 }
414 case PHY_CHANNELS_DEFAULT_MASK:
415 {
416 phyParam.ChannelsMask = RegionNvmGroup2->ChannelsDefaultMask;
417 break;
418 }
419 case PHY_MAX_NB_CHANNELS:
420 {
421 phyParam.Value = CN470_MAX_NB_CHANNELS;
422 break;
423 }
424 case PHY_CHANNELS:
425 {
426 phyParam.Channels = RegionNvmGroup2->Channels;
427 break;
428 }
429 case PHY_DEF_UPLINK_DWELL_TIME:
430 {
431 phyParam.Value = CN470_DEFAULT_UPLINK_DWELL_TIME;
432 break;
433 }
434 case PHY_DEF_DOWNLINK_DWELL_TIME:
435 {
436 phyParam.Value = REGION_COMMON_DEFAULT_DOWNLINK_DWELL_TIME;
437 break;
438 }
439 case PHY_DEF_MAX_EIRP:
440 {
441 phyParam.fValue = CN470_DEFAULT_MAX_EIRP;
442 break;
443 }
444 case PHY_DEF_ANTENNA_GAIN:
445 {
446 phyParam.fValue = CN470_DEFAULT_ANTENNA_GAIN;
447 break;
448 }
449 case PHY_BEACON_CHANNEL_FREQ:
450 {
451 phyParam.Value = REGION_CN470_DEFAULT_RX_WND_2_FREQ;
452
453 // Implementation depending on the join channel
454 if( RegionNvmGroup2->ChannelPlan != CHANNEL_PLAN_UNKNOWN )
455 {
456 phyParam.Value = ChannelPlanCtx.GetDownlinkFrequency( getPhy->Channel,
457 RegionNvmGroup2->CommonJoinChannelIndex,
458 false );
459 }
460 break;
461 }
462 case PHY_BEACON_FORMAT:
463 {
464 phyParam.BeaconFormat.BeaconSize = CN470_BEACON_SIZE;
465 phyParam.BeaconFormat.Rfu1Size = CN470_RFU1_SIZE;
466 phyParam.BeaconFormat.Rfu2Size = CN470_RFU2_SIZE;
467 break;
468 }
469 case PHY_BEACON_CHANNEL_DR:
470 {
471 phyParam.Value = CN470_BEACON_CHANNEL_DR;
472 break;
473 }
474 case PHY_BEACON_NB_CHANNELS:
475 {
476 // Implementation depending on the join channel
477 if( RegionNvmGroup2->ChannelPlan != CHANNEL_PLAN_UNKNOWN )
478 {
479 phyParam.Value = ChannelPlanCtx.NbBeaconChannels;
480 }
481 break;
482 }
483 case PHY_BEACON_CHANNEL_OFFSET:
484 {
485 // Implementation depending on the join channel
486 if( RegionNvmGroup2->ChannelPlan != CHANNEL_PLAN_UNKNOWN )
487 {
488 phyParam.Value = ChannelPlanCtx.GetBeaconChannelOffset( RegionNvmGroup2->CommonJoinChannelIndex );
489 }
490 break;
491 }
492 case PHY_PING_SLOT_CHANNEL_FREQ:
493 {
494 phyParam.Value = REGION_CN470_DEFAULT_RX_WND_2_FREQ;
495
496 // Implementation depending on the join channel
497 if( RegionNvmGroup2->ChannelPlan != CHANNEL_PLAN_UNKNOWN )
498 {
499 phyParam.Value = ChannelPlanCtx.GetDownlinkFrequency( getPhy->Channel,
500 RegionNvmGroup2->CommonJoinChannelIndex,
501 true );
502 }
503 break;
504 }
505 case PHY_PING_SLOT_CHANNEL_DR:
506 {
507 phyParam.Value = CN470_PING_SLOT_CHANNEL_DR;
508 break;
509 }
510 case PHY_PING_SLOT_NB_CHANNELS:
511 {
512 // Implementation depending on the join channel
513 if( RegionNvmGroup2->ChannelPlan != CHANNEL_PLAN_UNKNOWN )
514 {
515 phyParam.Value = ChannelPlanCtx.NbPingSlotChannels;
516 }
517 break;
518 }
519 case PHY_SF_FROM_DR:
520 {
521 phyParam.Value = DataratesCN470[getPhy->Datarate];
522 break;
523 }
524 case PHY_BW_FROM_DR:
525 {
526 phyParam.Value = RegionCommonGetBandwidth( getPhy->Datarate, BandwidthsCN470 );
527 break;
528 }
529 default:
530 {
531 break;
532 }
533 }
534
535 return phyParam;
536 }
537
RegionCN470SetBandTxDone(SetBandTxDoneParams_t * txDone)538 void RegionCN470SetBandTxDone( SetBandTxDoneParams_t* txDone )
539 {
540 RegionCommonSetBandTxDone( &RegionBands[RegionNvmGroup2->Channels[txDone->Channel].Band],
541 txDone->LastTxAirTime, txDone->Joined, txDone->ElapsedTimeSinceStartUp );
542 }
543
RegionCN470InitDefaults(InitDefaultsParams_t * params)544 void RegionCN470InitDefaults( InitDefaultsParams_t* params )
545 {
546 Band_t bands[CN470_MAX_NB_BANDS] =
547 {
548 CN470_BAND0
549 };
550
551 switch( params->Type )
552 {
553 case INIT_TYPE_DEFAULTS:
554 {
555 if( ( params->NvmGroup1 == NULL ) || ( params->NvmGroup2 == NULL ) )
556 {
557 return;
558 }
559
560 RegionNvmGroup1 = (RegionNvmDataGroup1_t*) params->NvmGroup1;
561 RegionNvmGroup2 = (RegionNvmDataGroup2_t*) params->NvmGroup2;
562 RegionBands = (Band_t*) params->Bands;
563
564 // Default bands
565 memcpy1( ( uint8_t* )RegionBands, ( uint8_t* )bands, sizeof( Band_t ) * CN470_MAX_NB_BANDS );
566
567 // 125 kHz channels
568 RegionNvmGroup2->ChannelPlan = REGION_CN470_DEFAULT_CHANNEL_PLAN;
569 RegionNvmGroup2->CommonJoinChannelIndex = 0;
570 RegionNvmGroup2->IsOtaaDevice = false;
571
572 // Apply the channel plan configuration
573 ApplyChannelPlanConfig( RegionNvmGroup2->ChannelPlan, &ChannelPlanCtx );
574
575 // Default channels
576 ChannelPlanCtx.InitializeChannels( RegionNvmGroup2->Channels );
577
578 // Default ChannelsMask
579 ChannelPlanCtx.InitializeChannelsMask( RegionNvmGroup2->ChannelsDefaultMask );
580
581 // Copy channels default mask
582 RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE );
583
584 // Copy into channels mask remaining
585 RegionCommonChanMaskCopy( RegionNvmGroup1->ChannelsMaskRemaining, RegionNvmGroup2->ChannelsMask, CHANNELS_MASK_SIZE );
586 break;
587 }
588 case INIT_TYPE_RESET_TO_DEFAULT_CHANNELS:
589 {
590 // Intentional fallthrough
591 }
592 case INIT_TYPE_ACTIVATE_DEFAULT_CHANNELS:
593 {
594 // Restore channels default mask
595 RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, RegionNvmGroup2->ChannelsDefaultMask, CHANNELS_MASK_SIZE );
596
597 for( uint8_t i = 0; i < CHANNELS_MASK_SIZE; i++ )
598 { // Copy-And the channels mask
599 RegionNvmGroup1->ChannelsMaskRemaining[i] &= RegionNvmGroup2->ChannelsMask[i];
600 }
601 break;
602 }
603 default:
604 {
605 break;
606 }
607 }
608 }
609
RegionCN470Verify(VerifyParams_t * verify,PhyAttribute_t phyAttribute)610 bool RegionCN470Verify( VerifyParams_t* verify, PhyAttribute_t phyAttribute )
611 {
612 switch( phyAttribute )
613 {
614 case PHY_FREQUENCY:
615 {
616 return VerifyRfFreq( verify->Frequency );
617 }
618 case PHY_TX_DR:
619 case PHY_DEF_TX_DR:
620 {
621 return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN470_TX_MIN_DATARATE, CN470_TX_MAX_DATARATE );
622 }
623 case PHY_RX_DR:
624 {
625 return RegionCommonValueInRange( verify->DatarateParams.Datarate, CN470_RX_MIN_DATARATE, CN470_RX_MAX_DATARATE );
626 }
627 case PHY_DEF_TX_POWER:
628 case PHY_TX_POWER:
629 {
630 // Remark: switched min and max!
631 return RegionCommonValueInRange( verify->TxPower, CN470_MAX_TX_POWER, CN470_MIN_TX_POWER );
632 }
633 case PHY_DUTY_CYCLE:
634 {
635 return CN470_DUTY_CYCLE_ENABLED;
636 }
637 default:
638 return false;
639 }
640 }
641
RegionCN470ApplyCFList(ApplyCFListParams_t * applyCFList)642 void RegionCN470ApplyCFList( ApplyCFListParams_t* applyCFList )
643 {
644 // Setup the channel plan based on the join channel
645 RegionNvmGroup2->CommonJoinChannelIndex = applyCFList->JoinChannel;
646 RegionNvmGroup2->IsOtaaDevice = true;
647 RegionNvmGroup2->ChannelPlan = IdentifyChannelPlan( RegionNvmGroup2->CommonJoinChannelIndex );
648
649 if( RegionNvmGroup2->ChannelPlan == CHANNEL_PLAN_UNKNOWN )
650 {
651 // Invalid channel plan, fallback to default
652 RegionNvmGroup2->ChannelPlan = REGION_CN470_DEFAULT_CHANNEL_PLAN;
653 }
654 // Apply the configuration for the channel plan
655 ApplyChannelPlanConfig( RegionNvmGroup2->ChannelPlan, &ChannelPlanCtx );
656
657 // Size of the optional CF list must be 16 byte
658 if( applyCFList->Size != 16 )
659 {
660 return;
661 }
662
663 // Last byte CFListType must be 0x01 to indicate the CFList contains a series of ChMask fields
664 if( applyCFList->Payload[15] != 0x01 )
665 {
666 return;
667 }
668
669 // ChMask0 - ChMask5 must be set (every ChMask has 16 bit)
670 for( uint8_t chMaskItr = 0, cntPayload = 0; chMaskItr < ChannelPlanCtx.JoinAcceptListSize; chMaskItr++, cntPayload+=2 )
671 {
672 RegionNvmGroup2->ChannelsMask[chMaskItr] = (uint16_t) (0x00FF & applyCFList->Payload[cntPayload]);
673 RegionNvmGroup2->ChannelsMask[chMaskItr] |= (uint16_t) (applyCFList->Payload[cntPayload+1] << 8);
674
675 // Set the channel mask to the remaining
676 RegionNvmGroup1->ChannelsMaskRemaining[chMaskItr] &= RegionNvmGroup2->ChannelsMask[chMaskItr];
677 }
678 }
679
RegionCN470ChanMaskSet(ChanMaskSetParams_t * chanMaskSet)680 bool RegionCN470ChanMaskSet( ChanMaskSetParams_t* chanMaskSet )
681 {
682 switch( chanMaskSet->ChannelsMaskType )
683 {
684 case CHANNELS_MASK:
685 {
686 RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE );
687
688 for( uint8_t i = 0; i < CHANNELS_MASK_SIZE; i++ )
689 { // Copy-And the channels mask
690 RegionNvmGroup1->ChannelsMaskRemaining[i] &= RegionNvmGroup2->ChannelsMask[i];
691 }
692 break;
693 }
694 case CHANNELS_DEFAULT_MASK:
695 {
696 RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsDefaultMask, chanMaskSet->ChannelsMaskIn, CHANNELS_MASK_SIZE );
697 break;
698 }
699 default:
700 return false;
701 }
702 return true;
703 }
704
RegionCN470ComputeRxWindowParameters(int8_t datarate,uint8_t minRxSymbols,uint32_t rxError,RxConfigParams_t * rxConfigParams)705 void RegionCN470ComputeRxWindowParameters( int8_t datarate, uint8_t minRxSymbols, uint32_t rxError, RxConfigParams_t *rxConfigParams )
706 {
707 uint32_t tSymbolInUs = 0;
708
709 // Get the datarate, perform a boundary check
710 rxConfigParams->Datarate = MIN( datarate, CN470_RX_MAX_DATARATE );
711 rxConfigParams->Bandwidth = RegionCommonGetBandwidth( rxConfigParams->Datarate, BandwidthsCN470 );
712
713 tSymbolInUs = RegionCommonComputeSymbolTimeLoRa( DataratesCN470[rxConfigParams->Datarate], BandwidthsCN470[rxConfigParams->Datarate] );
714
715 RegionCommonComputeRxWindowParameters( tSymbolInUs, minRxSymbols, rxError, Radio.GetWakeupTime( ), &rxConfigParams->WindowTimeout, &rxConfigParams->WindowOffset );
716 }
717
RegionCN470RxConfig(RxConfigParams_t * rxConfig,int8_t * datarate)718 bool RegionCN470RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
719 {
720 int8_t dr = rxConfig->Datarate;
721 int8_t phyDr = 0;
722 uint32_t frequency = rxConfig->Frequency;
723
724 if( Radio.GetStatus( ) != RF_IDLE )
725 {
726 return false;
727 }
728
729 // The RX configuration depends on whether the device has joined or not.
730 if( rxConfig->NetworkActivation != ACTIVATION_TYPE_NONE )
731 {
732 // Update the downlink frequency in case of RX_SLOT_WIN_1 or RX_SLOT_WIN_2.
733 // Keep the frequency for all other cases.
734 if( rxConfig->RxSlot == RX_SLOT_WIN_1 )
735 {
736 // Apply window 1 frequency
737 frequency = ChannelPlanCtx.GetRx1Frequency( rxConfig->Channel );
738 }
739 else if( rxConfig->RxSlot == RX_SLOT_WIN_2 )
740 {
741 // Apply window 2 frequency
742 frequency = ChannelPlanCtx.GetRx2Frequency( RegionNvmGroup2->CommonJoinChannelIndex, RegionNvmGroup2->IsOtaaDevice );
743 }
744 }
745 else
746 {
747 // In this case, only RX_SLOT_WIN_1 and RX_SLOT_WIN_2 is possible. There is
748 // no need to verify it. The end device is not joined and is an OTAA device.
749 frequency = CommonJoinChannels[rxConfig->Channel].Rx1Frequency;
750 }
751
752 // Read the physical datarate from the datarates table
753 phyDr = DataratesCN470[dr];
754
755 Radio.SetChannel( frequency );
756
757 // Radio configuration
758 Radio.SetRxConfig( MODEM_LORA, rxConfig->Bandwidth, phyDr, 1, 0, 8, rxConfig->WindowTimeout, false, 0, false, 0, 0, true, rxConfig->RxContinuous );
759
760 Radio.SetMaxPayloadLength( MODEM_LORA, MaxPayloadOfDatarateCN470[dr] + LORAMAC_FRAME_PAYLOAD_OVERHEAD_SIZE );
761
762 *datarate = (uint8_t) dr;
763 return true;
764 }
765
RegionCN470TxConfig(TxConfigParams_t * txConfig,int8_t * txPower,TimerTime_t * txTimeOnAir)766 bool RegionCN470TxConfig( TxConfigParams_t* txConfig, int8_t* txPower, TimerTime_t* txTimeOnAir )
767 {
768 RadioModems_t modem;
769 int8_t phyDr = DataratesCN470[txConfig->Datarate];
770 int8_t txPowerLimited = RegionCommonLimitTxPower( txConfig->TxPower, RegionBands[RegionNvmGroup2->Channels[txConfig->Channel].Band].TxMaxPower );
771 uint32_t bandwidth = RegionCommonGetBandwidth( txConfig->Datarate, BandwidthsCN470 );
772 int8_t phyTxPower = 0;
773
774 // Calculate physical TX power
775 phyTxPower = RegionCommonComputeTxPower( txPowerLimited, txConfig->MaxEirp, txConfig->AntennaGain );
776
777 // Setup the radio frequency
778 Radio.SetChannel( RegionNvmGroup2->Channels[txConfig->Channel].Frequency );
779
780 if( txConfig->Datarate == DR_7 )
781 { // High Speed FSK channel
782 modem = MODEM_FSK;
783 Radio.SetTxConfig( modem, phyTxPower, 25000, bandwidth, phyDr * 1000, 0, 5, false, true, 0, 0, false, 4000 );
784 }
785 else
786 {
787 modem = MODEM_LORA;
788 Radio.SetTxConfig( modem, phyTxPower, 0, bandwidth, phyDr, 1, 8, false, true, 0, 0, false, 4000 );
789 }
790
791 // Setup maximum payload length of the radio driver
792 Radio.SetMaxPayloadLength( modem, txConfig->PktLen );
793 // Update time-on-air
794 *txTimeOnAir = GetTimeOnAir( txConfig->Datarate, txConfig->PktLen );
795
796 *txPower = txPowerLimited;
797
798 return true;
799 }
800
RegionCN470LinkAdrReq(LinkAdrReqParams_t * linkAdrReq,int8_t * drOut,int8_t * txPowOut,uint8_t * nbRepOut,uint8_t * nbBytesParsed)801 uint8_t RegionCN470LinkAdrReq( LinkAdrReqParams_t* linkAdrReq, int8_t* drOut, int8_t* txPowOut, uint8_t* nbRepOut, uint8_t* nbBytesParsed )
802 {
803 uint8_t status = 0x07;
804 RegionCommonLinkAdrParams_t linkAdrParams = { 0 };
805 uint8_t nextIndex = 0;
806 uint8_t bytesProcessed = 0;
807 uint16_t channelsMask[CHANNELS_MASK_SIZE] = { 0, 0, 0, 0, 0, 0 };
808 GetPhyParams_t getPhy;
809 PhyParam_t phyParam;
810 RegionCommonLinkAdrReqVerifyParams_t linkAdrVerifyParams;
811
812 // Initialize local copy of channels mask
813 RegionCommonChanMaskCopy( channelsMask, RegionNvmGroup2->ChannelsMask, CHANNELS_MASK_SIZE );
814
815 while( bytesProcessed < linkAdrReq->PayloadSize )
816 {
817 // Get ADR request parameters
818 nextIndex = RegionCommonParseLinkAdrReq( &( linkAdrReq->Payload[bytesProcessed] ), &linkAdrParams );
819
820 if( nextIndex == 0 )
821 break; // break loop, since no more request has been found
822
823 // Update bytes processed
824 bytesProcessed += nextIndex;
825
826 // Update the channel plan
827 status = ChannelPlanCtx.LinkAdrChMaskUpdate( channelsMask, linkAdrParams.ChMaskCtrl,
828 linkAdrParams.ChMask, RegionNvmGroup2->Channels );
829 }
830
831 // Make sure at least one channel is active
832 if( RegionCommonCountChannels( channelsMask, 0, ChannelPlanCtx.ChannelsMaskSize ) == 0 )
833 {
834 status &= 0xFE; // Channel mask KO
835 }
836
837 // Get the minimum possible datarate
838 getPhy.Attribute = PHY_MIN_TX_DR;
839 getPhy.UplinkDwellTime = linkAdrReq->UplinkDwellTime;
840 phyParam = RegionCN470GetPhyParam( &getPhy );
841
842 linkAdrVerifyParams.Status = status;
843 linkAdrVerifyParams.AdrEnabled = linkAdrReq->AdrEnabled;
844 linkAdrVerifyParams.Datarate = linkAdrParams.Datarate;
845 linkAdrVerifyParams.TxPower = linkAdrParams.TxPower;
846 linkAdrVerifyParams.NbRep = linkAdrParams.NbRep;
847 linkAdrVerifyParams.CurrentDatarate = linkAdrReq->CurrentDatarate;
848 linkAdrVerifyParams.CurrentTxPower = linkAdrReq->CurrentTxPower;
849 linkAdrVerifyParams.CurrentNbRep = linkAdrReq->CurrentNbRep;
850 linkAdrVerifyParams.NbChannels = CN470_MAX_NB_CHANNELS;
851 linkAdrVerifyParams.ChannelsMask = channelsMask;
852 linkAdrVerifyParams.MinDatarate = ( int8_t )phyParam.Value;
853 linkAdrVerifyParams.MaxDatarate = CN470_TX_MAX_DATARATE;
854 linkAdrVerifyParams.Channels = RegionNvmGroup2->Channels;
855 linkAdrVerifyParams.MinTxPower = CN470_MIN_TX_POWER;
856 linkAdrVerifyParams.MaxTxPower = CN470_MAX_TX_POWER;
857 linkAdrVerifyParams.Version = linkAdrReq->Version;
858
859 // Verify the parameters and update, if necessary
860 status = RegionCommonLinkAdrReqVerifyParams( &linkAdrVerifyParams, &linkAdrParams.Datarate, &linkAdrParams.TxPower, &linkAdrParams.NbRep );
861
862 // Update channelsMask if everything is correct
863 if( status == 0x07 )
864 {
865 // Copy Mask
866 RegionCommonChanMaskCopy( RegionNvmGroup2->ChannelsMask, channelsMask, CHANNELS_MASK_SIZE );
867
868 RegionNvmGroup1->ChannelsMaskRemaining[0] &= RegionNvmGroup2->ChannelsMask[0];
869 RegionNvmGroup1->ChannelsMaskRemaining[1] &= RegionNvmGroup2->ChannelsMask[1];
870 RegionNvmGroup1->ChannelsMaskRemaining[2] &= RegionNvmGroup2->ChannelsMask[2];
871 RegionNvmGroup1->ChannelsMaskRemaining[3] &= RegionNvmGroup2->ChannelsMask[3];
872 RegionNvmGroup1->ChannelsMaskRemaining[4] = RegionNvmGroup2->ChannelsMask[4];
873 RegionNvmGroup1->ChannelsMaskRemaining[5] = RegionNvmGroup2->ChannelsMask[5];
874 }
875
876 // Update status variables
877 *drOut = linkAdrParams.Datarate;
878 *txPowOut = linkAdrParams.TxPower;
879 *nbRepOut = linkAdrParams.NbRep;
880 *nbBytesParsed = bytesProcessed;
881
882 return status;
883 }
884
RegionCN470RxParamSetupReq(RxParamSetupReqParams_t * rxParamSetupReq)885 uint8_t RegionCN470RxParamSetupReq( RxParamSetupReqParams_t* rxParamSetupReq )
886 {
887 uint8_t status = 0x07;
888
889 // Verify radio frequency
890 if( VerifyRfFreq( rxParamSetupReq->Frequency ) == false )
891 {
892 status &= 0xFE; // Channel frequency KO
893 }
894
895 // Verify datarate
896 if( RegionCommonValueInRange( rxParamSetupReq->Datarate, CN470_RX_MIN_DATARATE, CN470_RX_MAX_DATARATE ) == false )
897 {
898 status &= 0xFD; // Datarate KO
899 }
900
901 // Verify datarate offset
902 if( RegionCommonValueInRange( rxParamSetupReq->DrOffset, CN470_MIN_RX1_DR_OFFSET, CN470_MAX_RX1_DR_OFFSET ) == false )
903 {
904 status &= 0xFB; // Rx1DrOffset range KO
905 }
906
907 return status;
908 }
909
RegionCN470NewChannelReq(NewChannelReqParams_t * newChannelReq)910 int8_t RegionCN470NewChannelReq( NewChannelReqParams_t* newChannelReq )
911 {
912 // Do not accept the request
913 return -1;
914 }
915
RegionCN470TxParamSetupReq(TxParamSetupReqParams_t * txParamSetupReq)916 int8_t RegionCN470TxParamSetupReq( TxParamSetupReqParams_t* txParamSetupReq )
917 {
918 // Do not accept the request
919 return -1;
920 }
921
RegionCN470DlChannelReq(DlChannelReqParams_t * dlChannelReq)922 int8_t RegionCN470DlChannelReq( DlChannelReqParams_t* dlChannelReq )
923 {
924 // Do not accept the request
925 return -1;
926 }
927
RegionCN470AlternateDr(int8_t currentDr,AlternateDrType_t type)928 int8_t RegionCN470AlternateDr( int8_t currentDr, AlternateDrType_t type )
929 {
930 return currentDr;
931 }
932
RegionCN470NextChannel(NextChanParams_t * nextChanParams,uint8_t * channel,TimerTime_t * time,TimerTime_t * aggregatedTimeOff)933 LoRaMacStatus_t RegionCN470NextChannel( NextChanParams_t* nextChanParams, uint8_t* channel, TimerTime_t* time, TimerTime_t* aggregatedTimeOff )
934 {
935 uint8_t nbEnabledChannels = 0;
936 uint8_t nbRestrictedChannels = 0;
937 uint8_t enabledChannels[CN470_MAX_NB_CHANNELS] = { 0 };
938 uint16_t joinChannelsMask[2] = CN470_JOIN_CHANNELS;
939 RegionCommonIdentifyChannelsParam_t identifyChannelsParam;
940 RegionCommonCountNbOfEnabledChannelsParams_t countChannelsParams;
941 LoRaMacStatus_t status = LORAMAC_STATUS_NO_CHANNEL_FOUND;
942
943 // Count 125kHz channels
944 if( RegionCommonCountChannels( RegionNvmGroup1->ChannelsMaskRemaining, 0, ChannelPlanCtx.ChannelsMaskSize ) == 0 )
945 { // Reactivate default channels
946 RegionNvmGroup2->ChannelsMask[0] = 0xFFFF;
947 RegionNvmGroup2->ChannelsMask[1] = 0xFFFF;
948 RegionNvmGroup2->ChannelsMask[2] = 0xFFFF;
949 RegionNvmGroup2->ChannelsMask[3] = 0xFFFF;
950 RegionNvmGroup2->ChannelsMask[4] = 0xFFFF;
951 RegionNvmGroup2->ChannelsMask[5] = 0xFFFF;
952 RegionCommonChanMaskCopy( RegionNvmGroup1->ChannelsMaskRemaining, RegionNvmGroup2->ChannelsMask, ChannelPlanCtx.ChannelsMaskSize );
953 }
954
955 // Search how many channels are enabled
956 countChannelsParams.Joined = nextChanParams->Joined;
957 countChannelsParams.Datarate = nextChanParams->Datarate;
958 countChannelsParams.ChannelsMask = RegionNvmGroup1->ChannelsMaskRemaining;
959 countChannelsParams.Channels = RegionNvmGroup2->Channels;
960 countChannelsParams.Bands = RegionBands;
961 countChannelsParams.MaxNbChannels = CN470_MAX_NB_CHANNELS;
962 countChannelsParams.JoinChannels = NULL;
963
964 // Apply a different channel selection if the device is not joined yet
965 // In this case the device shall not follow the individual channel plans for the
966 // different type, but instead shall follow the common join channel plan.
967 if( countChannelsParams.Joined == false )
968 {
969 countChannelsParams.ChannelsMask = joinChannelsMask;
970 countChannelsParams.Channels = CommonJoinChannels;
971 countChannelsParams.MaxNbChannels = CN470_COMMON_JOIN_CHANNELS_SIZE;
972 countChannelsParams.JoinChannels = joinChannelsMask;
973 }
974
975 identifyChannelsParam.AggrTimeOff = nextChanParams->AggrTimeOff;
976 identifyChannelsParam.LastAggrTx = nextChanParams->LastAggrTx;
977 identifyChannelsParam.DutyCycleEnabled = nextChanParams->DutyCycleEnabled;
978 identifyChannelsParam.MaxBands = CN470_MAX_NB_BANDS;
979
980 identifyChannelsParam.ElapsedTimeSinceStartUp = nextChanParams->ElapsedTimeSinceStartUp;
981 identifyChannelsParam.LastTxIsJoinRequest = nextChanParams->LastTxIsJoinRequest;
982 identifyChannelsParam.ExpectedTimeOnAir = GetTimeOnAir( nextChanParams->Datarate, nextChanParams->PktLen );
983
984 identifyChannelsParam.CountNbOfEnabledChannelsParam = &countChannelsParams;
985
986 status = RegionCommonIdentifyChannels( &identifyChannelsParam, aggregatedTimeOff, enabledChannels,
987 &nbEnabledChannels, &nbRestrictedChannels, time );
988
989 if( status == LORAMAC_STATUS_OK )
990 {
991 // We found a valid channel. Selection is random.
992 *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
993
994 // Disable the channel in the mask
995 RegionCommonChanDisable( RegionNvmGroup1->ChannelsMaskRemaining, *channel, ChannelPlanCtx.ChannelsMaskSize );
996 }
997 return status;
998 }
999
RegionCN470ChannelAdd(ChannelAddParams_t * channelAdd)1000 LoRaMacStatus_t RegionCN470ChannelAdd( ChannelAddParams_t* channelAdd )
1001 {
1002 return LORAMAC_STATUS_PARAMETER_INVALID;
1003 }
1004
RegionCN470ChannelsRemove(ChannelRemoveParams_t * channelRemove)1005 bool RegionCN470ChannelsRemove( ChannelRemoveParams_t* channelRemove )
1006 {
1007 return LORAMAC_STATUS_PARAMETER_INVALID;
1008 }
1009
RegionCN470ApplyDrOffset(uint8_t downlinkDwellTime,int8_t dr,int8_t drOffset)1010 uint8_t RegionCN470ApplyDrOffset( uint8_t downlinkDwellTime, int8_t dr, int8_t drOffset )
1011 {
1012 int8_t datarate = DatarateOffsetsCN470[dr][drOffset];
1013
1014 if( datarate < 0 )
1015 {
1016 datarate = DR_0;
1017 }
1018 return datarate;
1019 }
1020
RegionCN470RxBeaconSetup(RxBeaconSetup_t * rxBeaconSetup,uint8_t * outDr)1021 void RegionCN470RxBeaconSetup( RxBeaconSetup_t* rxBeaconSetup, uint8_t* outDr )
1022 {
1023 RegionCommonRxBeaconSetupParams_t regionCommonRxBeaconSetup;
1024
1025 regionCommonRxBeaconSetup.Datarates = DataratesCN470;
1026 regionCommonRxBeaconSetup.Frequency = rxBeaconSetup->Frequency;
1027 regionCommonRxBeaconSetup.BeaconSize = CN470_BEACON_SIZE;
1028 regionCommonRxBeaconSetup.BeaconDatarate = CN470_BEACON_CHANNEL_DR;
1029 regionCommonRxBeaconSetup.BeaconChannelBW = CN470_BEACON_CHANNEL_BW;
1030 regionCommonRxBeaconSetup.RxTime = rxBeaconSetup->RxTime;
1031 regionCommonRxBeaconSetup.SymbolTimeout = rxBeaconSetup->SymbolTimeout;
1032
1033 RegionCommonRxBeaconSetup( ®ionCommonRxBeaconSetup );
1034
1035 // Store downlink datarate
1036 *outDr = CN470_BEACON_CHANNEL_DR;
1037 }
1038