对eNodeB的一些配置以及代码结构进行说明,如下:
一,eNodeB配置结构
|| 控制进程(传递eNB配置命令)|| ---》 || eNB按照配置进程的配置命令工作 ||
1, 在一个Tab窗口运行LTE_fdd_eNodeB进程(启动eNodeB工作进程)
2, 在另一个Tab窗口运行控制进程,可使用连接工具(Telent,nc等)连接LTE_fdd_eNodeB进程(端口固定为30000,以Telnet连接本机示例,使用命令为Telnet 127.0.0.1 30000)来完成配置操作,包括启动,终止LTE_fdd_eNodeB进程中的基站操作,读取,设置基站操作中需要的参数;
二,代码重要实体层说明
2.1 Interface层
eNB提供的接口,处理来自外部的配置命令;
2.2 MsgQ实体
提供统一的消息管理实体,对于各个协议层的消息进行处理,接收,存储,发送;
2.3 Radio层
1, Radio的Start函数创建线程(radio_thread)来处理数据帧收发;在线程函数中会调用Phy层实体的radio_interface接口来处理上,下行数据,在radio_interface中会进一步地包含上,下行处理,在处理上下行数据中,当对user data进行处理时,会用到dl_sched_mutx锁:
Dl_sched_mutex_lock()
Dl_sched_mutex_unlock()
2.4 Phy层
Start函数中会定义对于Mac数据处理的接口,在进而处理下行调度类型数据时,会应用线程锁:
boost::mutex::scoped_lock lock(dl_sched_mutex)
2.5 Rb协议实体
1,Msg会包含Rb的指针,指向特定的Rb
1, 一个UE只会定义一定数据的RB实体(最多3个SRB,8个DRB),每个RB中会为每个协子层定义一个消息队列(如,RRC,PDCP,RLC,MAC);
Rb_Id的填写在gw协议实体中完成,目前代码中作了简化处理,在gw层只会填写id=1:gw将rb_id会被填写进消息中,并一直在包含在后续pdb流中各个的协议实体间传递:
Gw->pdcp->rlc->mac
2, 当前处理协议子层m从传递的消息中查询到RB后,从RB中取出对应当前处理协议子层m队列中的消息数据,按照当前处理协议子层m的处理机制进行处理,然后再次处理完成后的消息数据单元(下一相邻子层的SDU)存储到RB定义的对应到下一协议子层n的消息数据队列中;并构造消息头传递到协议子层定义的与下一相邻子层n通信的m_n_olmq中下一相邻协议子层n接收到来m的消息后,会触发hand_m_msg的操作,也是处理消息头,取SDU,处理完后再构造下一层的SDU,再向下一协议层发送消息触发下一层的处理,例如,rlc处理完本协议层的操作后向mac层发送消息操作:
LTE_fdd_enb_msg::send(rlc_mac_olmq, **)
3, 直到消息到了mac层,处理方式有不同:
1, rb_id需要转换为mac_id,处理方式有不同:
sdb_ready->rb->get_rb_id()
2, 上层发下来的SDB数据处理后,只是会存储在scheduler队列中,如下:
Add_to_dl_sched_queue
不会主动向phy层发送消息,phy层会基于phy层的机制来触发mac层的调度机制,进而从调度队列中依次取出PDU进行发送;
2.6 User实体
管理用户的参数属性;例如标识,能力,承载等;
2.7 安全机制
Eia算法
33220中KDF算法如下:
Derivedkey = HAMC-SHA-256(Key,S)
在Security中使用sha2_hmac实现:
在代码中好像没有对up面生成的key使用,而且只是保存在Hss中,有些疑问:
用户面的key使用终点是基站,应该保存基站侧才合适;
三,实体之间交互
3.1 Radio与Phy实体之间交互
Radio_thread->radio_thread_func->radio_interface(此函数是由Phy层提供,并且此处是通过while(循环一直调用))->process_dl/process_ul
其中,UL发送到MAC的帧;DL调用radio中的send(调用uhd的tx-stream)进行发送
Phy类中定义的成员boost::interprocess:message_queue*phy_mac_olmq用来承载Phy给MAC层传递的消息(包括各种类型物理信道(PRACH,PUSCH,PUCCH)的解码后的内容以及一种全称为read_to_send的消息),此处重点说明一下ready_to_send消息,即Phy层处理完了上行或者下行子帧的发送,需要告知MAC层其已经等待好处理下一子帧;MAC层通过读取phy_mac_olmq进行获取发送的消息;当获取到消息类型为read_to_send时,则会执行调度操作,主要为配置子帧资源;
3.2 MessageQueue举例
上层通过send函数向该queue中发送message,主要操作过程如下:
a) Phy层实例化对象:mac_comm_msgq=newLTE_fdd_end_msgq(“mac_phy_olmq”,cb);
b) Mac层定义了boost对象mac_phy_olmq,再向该对象队列中发送消息:
1) mac_phy_olmq->send(&msg_sizeof(msg),0)
2) LTE_fdd_enb_msg::send(mac_phy_olmq;
Phy层在实例化mac_comm_msgq对象时,会调用回调函数:
Messageq->receive_thread->callback(即handle_mac_message,并且callback直到接收到Interface发送的Stop参数的消息才终止处理)->handle_dl_schedulehandle_ul_schedule 其中:
Handle_dl_schedule处理下行调度相关操作,主要是将MAC中Scheduler函数中已经组装好的帧拷贝到具体的位置;并提供给后续process_dl函数中封装user_data时使用,有以下代码操作:
memcpy(&pdcch.alloc[pdcch.N_alloc],&dl_schedule[subfn].ul_allocations.alloc[i],
handle_ul_schedule的目的是为了封装phich。因为phich的封装需要基于MAC层的调度信息。有以下Comment:
for(i=0; i<ul_schedule[ul_subframe.num].decodes.N_alloc; i++)
{
// Determine PHICH indecies
3.3 LTE_fdd_enb_msgq对象Msgq与boost对象olmq
层m为与相邻n层交互,需要定义一个m_n_olmq,并向m_n_olmq发送消息数据;n层会定义一个相应的m_comm_mesq(实例化的时候,其string名称即为m_n_olmq),这样n层在m_comm_mesq中即可获取来自m层的消息数据:
具体操作如下实例:
在协议层mac层中实例化boost对象:
Mac_phy_olmq = newboost::interprocess::message_queue(boost::interprocess:open_only *
在Phy层中msgq定义如下:
Mac_comm_msgq= new LTE_fdd_enb_msgq(“mac_phy_olmq”,cb);
在后续msgq(专用类)的处理操作receive_thread中,通过msgq的string name对应到”mac_phy_olmq”这个boost:
Boost::interprocess::message_queue mq(boost::interprocess::open_only,msgq->msgq_name.c_str());
未完待续。。。欢迎各位也在关注这个项目的朋友指点交流~