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( &regionCommonRxBeaconSetup );
1034 
1035     // Store downlink datarate
1036     *outDr = CN470_BEACON_CHANNEL_DR;
1037 }
1038