赞
踩
本文将从频率、时间这两个资源的角度,来剖析LoRaWAN A类/C类终端的信道选择算法与代码实现示例。
不同的地区的频段,有不同的规范要求,算法有所差异,本文将以中国区的470M频段为例进行拆解,其他区域的频段以此类推。
不同的终端类型,算法也算法有所差异,本文将以ClassA与Class C为例,而Class B相对比较复杂,将单独讨论。
Table of Contents
第二章 RegionCN470上行发送的信道/频率资源调度算法
第三章 RegionCN470下行接收的信道/频率资源调度算法
是指按电磁波波长(或频率)连续排列的整个电磁波族。
频段:无线通信中使用的频段只是电磁波频段中很小的一部分,定义了无线电波的频率范围。
LoRaWAN代码目前支持的频段
* - #define REGION_EU433
* - #define REGION_CN470
* - #define REGION_CN779
* - #define REGION_IN865
* - #define REGION_EU868
* - #define REGION_AU915
* - #define REGION_US915
* - #define REGION_US915_HYBRID
* - #define REGION_KR920
* - #define REGION_AS923
EU: 欧洲; CN: 中国;IN:印度;AU:澳大利亚;US: 美国;
频段是一段频率范围,跨越的范围有几十兆,甚至上百兆。对于LoRa而言,一次数据传输通常只需要125K或250K或500K的频谱带宽。因此需要把整个频段的频谱进一步的平均切分。有点类似整个马路的车道:
每个切分的一小段称为载波带宽
在LTE里面,每个切分的一小段载波带宽,又称为子载波;
在LoRa里,没有子载波的概念,每个切分的一小段载波带宽,被称为一个独立的信道(channel)
LoRa的调制解调,就是对125K或250K或500K这么一小段的载波带宽进行的。
上行发送信道:96个信道,信道带宽:200K, 总带宽=96*0.2K = 19.2M
下行接收信道:48个信道,信道带宽:200K, 总带宽=48*0.2K = 9.6M
接收窗口Rx1和RX2的信道都来下行接收信道。
至于Rx1和Rx2窗口选择哪个具体的信道,由下行信道的算法所决定。
代码实现参考:
- /*!
- * Defines the first channel for RX window 1 for CN470 band
- */
- #define CN470_FIRST_RX1_CHANNEL ( (uint32_t) 500300000 )
-
- /*!
- * Defines the last channel for RX window 1 for CN470 band
- */
- #define CN470_LAST_RX1_CHANNEL ( (uint32_t) 509700000 )
-
- /*!
- * Defines the step width of the channels for RX window 1
- */
- #define CN470_STEPWIDTH_RX1_CHANNEL ( (uint32_t) 200000 )
-
- /*!
- * Second reception window channel frequency definition.
- */
- #define CN470_RX_WND_2_FREQ ( (uint32_t)505300000 )
-
-
- /*!
- * LoRaMac maximum number of channels
- */
- #define CN470_MAX_NB_CHANNELS 96
-
-
- /*!
- * LoRaMAC channel definition
- */
- typedef struct sChannelParams
- {
- /*!
- * Frequency in Hz
- */
- uint32_t Frequency;
- /*!
- * Alternative frequency for RX window 1
- */
- uint32_t Rx1Frequency;
- /*!
- * Data rate definition
- */
- DrRange_t DrRange;
- /*!
- * Band index
- */
- uint8_t Band;
- }ChannelParams_t;
-
- /*!
- * LoRaMAC receive window 2 channel parameters
- */
- typedef struct sRx2ChannelParams
- {
- /*!
- * Frequency in Hz
- */
- uint32_t Frequency;
- /*!
- * Data rate
- *
- * LoRaWAN Regional Parameters V1.0.2rB
- *
- * The allowed ranges are region specific. Please refer to \ref DR_0 to \ref DR_15 for details.
- */
- uint8_t Datarate;
- }Rx2ChannelParams_t;
-
-
-
-
- // Global attributes
- /*!
- * LoRaMAC channels
- */
- static ChannelParams_t Channels[CN470_MAX_NB_CHANNELS];
-
- /*!
- * LoRaMac bands
- */
- static Band_t Bands[CN470_MAX_NB_BANDS] =
- {
- CN470_BAND0
- };
-
- /*!
- * LoRaMac channels mask
- */
- static uint16_t ChannelsMask[CHANNELS_MASK_SIZE]; //通过mask,可以中96个信道中选择部分信道
-
- /*!
- * LoRaMac channels default mask
- */
- static uint16_t ChannelsDefaultMask[CHANNELS_MASK_SIZE];
-
-
- //上行信道号与上行发送频率之间的映射关系:按顺序一一映射
- void RegionCN470InitDefaults( InitType_t type )
- {
- //.........................................
- // 125 kHz channels
- for( i = 0; i < CN470_MAX_NB_CHANNELS; i++ )
- {
- Channels[i].Frequency = 470300000 + i * 200000;
- Channels[i].DrRange.Value = ( DR_5 << 4 ) | DR_0;
- Channels[i].Band = 0;
- }
-
- // Initialize the channels default mask
- // 使用所有的96个信道
- ChannelsDefaultMask[0] = 0xFFFF;
- ChannelsDefaultMask[1] = 0xFFFF;
- ChannelsDefaultMask[2] = 0xFFFF;
- ChannelsDefaultMask[3] = 0xFFFF;
- ChannelsDefaultMask[4] = 0xFFFF;
- ChannelsDefaultMask[5] = 0xFFFF;
- }
-
- //下行信道号与下行发送频率之间的映射关系:按顺序一一映射
- //这里采用的是动态计算还是一次性初始化
- bool RegionCN470RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
- {
-
- if( rxConfig->RxSlot == RX_SLOT_WIN_1 )
- {
- // Apply window 1 frequency
- frequency = CN470_FIRST_RX1_CHANNEL + ( rxConfig->Channel % 48 ) * CN470_STEPWIDTH_RX1_CHANNEL;
- }
- }
对上述代码需要进一步解读的是:信道号与频率的映射关系
(1)发送信道号与频率的映射关系:是在程序初始时,进行映射的。
(2)接收信道号与频率的映射关系:
接收窗口Rx1:是在每次RxConfg的时候,进行映射的。
bool RegionCN470RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
{
}
接收窗口Rx2:是动态参数配置
static void OnRxWindow2TimerEvent( void )
{
RxWindow2Config.Channel = Channel;
RxWindow2Config.Frequency = LoRaMacParams.Rx2Channel.Frequency; //读取参数设置。
RegionRxConfig( LoRaMacRegion, &RxWindow2Config, ....)
}
每一次数据发送,LoWAN MAC都会从96个最大信道数,随机的选择一个可用的信道,而不是采用固定信道:
代码示例:
- LoRaMacStatus_t RegionCN470NextChannel(...)
- {
-
- // We found a valid channel
- *channel = enabledChannels[randr( 0, nbEnabledChannels - 1 )];
-
- }
nbEnabledChannels:是支持的最大时机channel数目
randr( 0, nbEnabledChannels - 1):在0和nbEnabledChannels - 1产生一个随机数,通过该随机数的下标,从enabledChannels【】数组中获取实际的信道号 。
RegionCN470 FDD模式,上下行的频率是分开的。
由于上行发送最大支持96个信道,下行接收只支持48个信道,因此无法采用1对1的映射。
RegionCN470采用的是多对1的映射关系:接收信号号=发送信道号%48.
当LoRa的网关接收到终端的数据,并获取该数据对应的信道后,通过上述同样的映射关系,选择下行的发送信道。
确保终端的接收与网关的发送是在相同的信道上。
- bool RegionCN470RxConfig( RxConfigParams_t* rxConfig, int8_t* datarate )
- {
- //...................................
-
- if( rxConfig->RxSlot == RX_SLOT_WIN_1 )
- {
- // Apply window 1 frequency
- rx_channel = rxConfig->Channel % 48;
- frequency = CN470_FIRST_RX1_CHANNEL + rx_channel * CN470_STEPWIDTH_RX1_CHANNEL;
- }
- }
注意:
多对一的映射关系不会引发错乱,只有一对多的关系,才会导致错乱,才会导致终端接收与网关发送的信道不一致。
终端的接收窗口Rx2的接收信道与发送信道号无关。采用两种机制
代码示例:
- static void OnRxWindow2TimerEvent( void )
- {
- RxWindow2Config.Frequency = LoRaMacParams.Rx2Channel.Frequency;
- }
-
上述参数LoRaMacParams.Rx2Channel.Frequency是实现线下预约好的,作为默认值,也可以通过下发配置命令修改该默认值。
至此:探讨了LoRa终端在接收和发送数据时,发送和接收信道的选择,即发送和接收数据时,载波频率的选择。
但每个LoRa终端,并非在任何时间,都可以占用信道发送数据,这里受限于发送占空比(消息类型、占空比 duty cycle的设定)
也并非在任何时候,都可以通过信道接收数据,这里这里所限于终端的工作模式(种类类型、接收窗口)
在这种情况下,不受空口占用信道占空比的限制,可以在任意时刻发送数据。
发送数据的周期只收到如下参数的影响:
- #define CN470_DUTY_CYCLE_ENABLED 0
-
-
- /*!
- * Defines the application data transmission duty cycle. 5s, value in [ms].
- */
- #define APP_TX_DUTYCYCLE 5000
-
- /*!
- * Defines a random delay for application data transmission duty cycle. 1s,
- * value in [ms].
- */
- #define APP_TX_DUTYCYCLE_RND 1000
-
-
- // Schedule next packet transmission
- TxDutyCycleTime = APP_TX_DUTYCYCLE + randr( 0, APP_TX_DUTYCYCLE_RND );
-
TxDutyCycleTime定义了下次发送数据的时间,叠加了周期性与随机性双重特征。
当应用层有数据发送时,LoRaWAN的发送调度器必须确保:发送数据占用空口信道的时间满足占空比的要求。
在上图中,在T0和T2之间的任何时间是不允许发送数据的,比如T1,因为这段时间,不满足发送占空比的要求。
只有当前时间大于T2, 比如T3, 才能允许发送下一个数据。
这里的关键是如何求发送间隔周期?
发送间隔周期T = 数据帧在空口发送时间 /占空比。
举例:
空口发送时间1.2s, 占空比100%(或者变成1), 最短发送周期=1.2s
空口发送时间1.2s, 占空比1%(或者表达成100), 最短发送周期=1.2s * 100 =》120s =》2分钟。
空口发送时间1.2s, 占空比0.1%(或者表达成1000), 最短发送周期=1.2s * 1000 =》1200s =》20分钟。
代码示例:
- #define CN470_DUTY_CYCLE_ENABLED 1
-
-
- /*!
- * Band 0 definition
- * { DutyCycle, TxMaxPower, LastJoinTxDoneTime, LastTxDoneTime, TimeOff }
- */
- #define CN470_BAND0 { 1, CN470_MAX_TX_POWER, 0, 0, 0 } // 占空比:1/1 = 100.0 %
-
-
- void RegionCommonCalcBackOff( RegionCommonCalcBackOffParams_t* calcBackOffParams )
- {
-
- if( calcBackOffParams->DutyCycleEnabled == true ) //发送占空比使能
- {
- //计算还剩多少时间,允许下次发送数据
- calcBackOffParams->Bands[bandIdx].TimeOff = calcBackOffParams->TxTimeOnAir * dutyCycle - calcBackOffParams->TxTimeOnAir;
- }
- else //发送占空比没有使能
- {
- //立即发送数据,无时间延时:TimeOff = 0
- calcBackOffParams->Bands[bandIdx].TimeOff = 0;
- }
-
- }
(1)Join消息的可变占空比
为了防止join request失败或无响应后,终端无休止的发送Jion request。
针对request成功之前的Join消息请求,LoRaWAN MAC设计了强制性的占空比使能机制。
且随着时间的推移,占空比的比值不是固定的,需要分等级性的变化,时间越长,占空比的比值越小。
定义可变占空比的代码案例:
-
- #define BACKOFF_DC_1_HOUR 100 //占空比=1%
- #define BACKOFF_DC_10_HOURS 1000 //占空比=0.1%
- #define BACKOFF_DC_24_HOURS 10000 //占空比=0.01%
-
- uint16_t RegionCommonGetJoinDc( TimerTime_t elapsedTime )
- {
- uint16_t dutyCycle = 0;
-
- if( elapsedTime < 3600000 )
- {
- dutyCycle = BACKOFF_DC_1_HOUR;
- }
- else if( elapsedTime < ( 3600000 + 36000000 ) )
- {
- dutyCycle = BACKOFF_DC_10_HOURS;
- }
- else
- {
- dutyCycle = BACKOFF_DC_24_HOURS;
- }
- return dutyCycle;
- }
(2)通过占空比和join消息空口的发送时间求下次发送的剩余时间time_off
- void RegionCommonCalcBackOff( RegionCommonCalcBackOffParams_t* calcBackOffParams )
- {
-
- if( calcBackOffParams->Joined == false )
- {
- // Get the join duty cycle: 100, 1000, 10000
- joinDutyCycle = RegionCommonGetJoinDc( calcBackOffParams->ElapsedTime );
-
- // Apply band time-off.
- calcBackOffParams->Bands[bandIdx].TimeOff = calcBackOffParams->TxTimeOnAir * (dutyCycle - 1);
-
- }
-
- }
因此,如果Join request消息的空口发送时间是1.5s,
则Join request失败或超时重新请求的最小周期为:1.5s * 100 = 1500s => 2.5分钟。
(1)Join accept之前
(2)Join accept之后:
(1)Join accept之前
(2)Join accept之后:
- static void OnRadioTxDone( void )
- {
- GetPhyParams_t getPhy;
- PhyParam_t phyParam;
- SetBandTxDoneParams_t txDone;
- TimerTime_t curTime = TimerGetCurrentTime( );
-
- if( LoRaMacDeviceClass != CLASS_C )
- {
- Radio.Sleep( ); //Class A终端,发送完成后,直接进入休眠状态
- }
- else
- {
- OpenContinuousRx2Window( ); //Class A终端,发送完成后, 带宽Rx2接收窗口
- }
-
- // Setup timers
- if( IsRxWindowsEnabled == true )
- {
- TimerSetValue( &RxWindowTimer1, RxWindow1Delay ); //1s或5s之后打开Rx1窗口
- TimerStart( &RxWindowTimer1 );
-
- if( LoRaMacDeviceClass != CLASS_C )
- {
- TimerSetValue( &RxWindowTimer2, RxWindow2Delay ); //2s或6s之后打开Rx2窗口
- TimerStart( &RxWindowTimer2 );
- }
-
- if( ( LoRaMacDeviceClass == CLASS_C ) || ( NodeAckRequested == true ) )
- {
- getPhy.Attribute = PHY_ACK_TIMEOUT;
- phyParam = RegionGetPhyParam( LoRaMacRegion, &getPhy );
- TimerSetValue( &AckTimeoutTimer, RxWindow2Delay + phyParam.Value );
- TimerStart( &AckTimeoutTimer ); //对于需要应答的消息,额外等待2s
- }
- }
- //...................
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。