周报:1.3-1.9
一、PSK系统进一步研究
在上周实验的基础上继续分析框图,首先将信道影响全部去除,噪声(noise voltage)和频偏(frequency offset)都设为0。
用于模拟发送器和接收器采样时钟之间不同速率的采样定时偏移epsilon设为1.0。 epsilon为1.0表示没有区别。
taps表示模拟多径延迟分布的 FIR 滤波器的抽头。 默认值为 1+1j 意味着单击一次,因此没有多径。此系统多径和单径都能正常解码。
发现当去掉Costas Loop
模块(用于恢复频偏和相偏的模块)之后系统仍然能够正常的运行,当去掉均衡模块:CMA Equalizer
后系统不能正常运行。
考虑到OFDM系统在物理层实际上就是psk加上一些子载波映射,IFFT等等,以后可以考虑在此系统的基础上进行改进。
为了验证此系统能不能真正用,把系统中的信道换成真实信道,将信道换成USRP后解码出的星座图不是很清晰,存在误码,无法准确找到同步的延时时间。不知道与USRP的采样率有没有关系。当USRP的采样率到达1M时,GNU Radio一直显示U,程序卡死,加入了Throttle也是如此。事实证明产生此现象的原因是因为用到了一个0.2MHz带宽的滤波器,而USRP的采样率只有32k,这时候会有一个warning,将USRP的采样率改为500KHz,问题解决,得到的星座图还比较清晰,但是依然不能同步。
( “U” = underrun(PC 无法快速的提供数据 - PC not providing data quickly enough))
想到的几种解决方法:
1. matlab程序选择比较的比特数缩短,比如收发只要连续20个一样就认为同步上了,然后再计算误码率。
方法1的实验结果:测试多次发现延时时间一直在变,范围在5000-6000之间,但是移位之后,误码率还是很高。可能的原因是USRP每次发送的时间间隔是不同的,因此每次同步需要的时间延时不一样。
2. 通过观察用USRP发送时接收端的星座图,然后调整仿真信道的参数,让其与USRP的星座图相接近。再从接收端调整Polyphase Clock Sync
模块,CMA Equalizer
模块,Costas Loop
模块的参数,通过调整参数使星座图效果更好。
方法2的实验结果:当增大噪声至 0.5 时,系统基本不受影响,依旧能解调出数据。当增大时间偏移到 1.001 时,依旧没有误码。在此基础上,将频偏调至0.042左右,出现大量误码。将时间偏移移回0重新测试,依旧没有误码。可见此系统受频率搬移的影响更大,因为这时候星座图在发生旋转。由于使用USRP的系统的星座图与仿真中的星座图加入一定的噪声相近似,在仿真中加入噪声后依旧能同步,而在USRP上则不能实现同步…至此此系统暂时告一段落。
二、GNURADIO中的.grc文件和.py文件
首先是grc的gui界面和其代码的关系
.grc文件主要包括options,blocks,connections,metadata.其中:
options定义整个框图的一些参数,全都是定义好的,只要改文件名。
blocks定义
其他的块都在blocks模块中,包括variable等等。
block模块下又包括name,id,parameters,states。
假如定义一个variable模块叫samp_rate, 这个block的name是samp_rate,ID是variable。
connection用于将不同的模块对应的端口连接起来。
metadata是gnuradio中的数据源,正常情况下都是file_format: 1。
.py文件
GNU Radio GRC流图生成的Python脚本
当我们点击GRC中工具栏中的“Generate”代码生成按钮时,在“工作空间”会提示你,生成了一个文件名字为“xxx.py”的python脚本。
在命令行输入python3 xxx.py的效果和在GRC中点击“Generate”代码作用一样。
其构造函数主要也包括Variables,Blocks,Connections(这些是固定要有的)
总体结构1
#!/usr/bin/env python3 //表示用python3来执行脚本
1
from gnuradio import gr //python调库
1
#!/usr/bin/env python3 //表示用python3来执行脚本
1
2class test(gr.top_block, Qt.QWidget):
该行定义了一个名称为与该Python脚本名称相同的类,这个类是继承于gr.top_block类。test类是本流图程序的主体1
2def __init__(self):
在test类中只有一个构造函数“init()”,调用了父类中的构造函数(gr.top_block.__init__(self))。1
2
3if __name__ == '__main__'
name 是当前模块名,当模块被直接运行时模块名为 main 。这句话的意思就是,当模块被直接运行时,if 以下代码块将被运行,当模块是被导入时,代码块不被运行。
python中两个_代表私有类型,
构造函数中的Variables,Blocks,Connections1
2self.samp_rate = samp_rate = 32000
定义variable1
2
3self.connect((self.analog_sig_source_x_0, 0), (self.audio_sink_0, 0))
self.connect((self.analog_sig_source_x_1, 0), (self.audio_sink_0, 1))
这种就是用于连接模块的函数,将不同的端口连接起来
三、OFDM系统的搭建
1. 尝试从wifi的grc提取出物理层的部分,但是由于WIFI Frame Equalizer
将FFT之后的解调,解码,以及链路层的成帧等处理放在了一起,无法在不改代码的情况下提取出解码后的数据。
Wifi Mapper输出中的tag: encoding(0), psdu_len(128), packet_len(2112).
tag遵循键值相对应的原则, encoding是key,0是value。
tag object
模块,定义一个tag类型的变量。
Wifi mapper
模块输出的数据0,1;
经过加入Tagged Stream Mux
模块和最后在Wifi Frame Equalizer
模块输出的数据,都是实部为-1,1;虚部是0.
直接用USRP产生方波对wifi系统进行移频,接收端接不到信息。
2. 消息传递机制
Message Strobe
模块:两个参数Message PMT(PMT类型)和Period(ms)(long类型).
Message PMT作为PMT要发送的消息,如:1
pmt.cons(pmt.PMT_NIL, pmt.make_u8vector(16, 0xFF))
pmt.PMT_T和pmt.PMT_F是布尔类型的变量分别表示true和false。PMT_NIL就类似于NULL或者None类型的变量并且常用于默认参数和返回值,通常表示发生了某个错误。这里用PMT_NIL是因为PDU的元数据部分是空的。u8vector表示8位,也就是一个字节。这个语句输入就表示创建16字节的1。
1 | pmt.intern("TEST") //表示发送字符TEST |
代码将这两个参数都声明为私有,然后通过set_msg和set_period函数为这两个参数赋值,这样起到了对数据的保护作用。通过start()和stop()两个函数实现定时。start()函数又调用了run()函数计时,run()函数用到了boost::posix_time
函数进行系统计时。
Options的选择包括QT GUI, No GUI, Hier Block, Hier Block(QT GUI)
QT GUI可以使用GUI界面,也就是一些星座图,数据的输出; No GUI不能用GUI的模块,否则极有可能卡死,其输出在terminal中.
PDU to Tagged Stream
模块:
将PMT数据类型转换为byte类型,数据流以数据包包长为tag进行标注
Message Debug
模块对数据进行输出
print端口输出message作为数据包的key, print_pdu端口输出message作为数据包的value.
3. 向Vector Source中加标签
用到Tag Object
模块,offset表示偏移,变量的数据类型都是pmt; Vector Source中定义tag要输入tag的ID号.
4.OFDM例程
OFDM_loopback模块
其grc类似于上周的PSK,也是仿真系统,多加了一个OFDM Transmitter和OFDM Receiver,预计其结果也是仿真时加一个延时能同步,通过USRP不能同步,因此如何实现能通过同步头来同步数据很重要.
Occupied Carriers
:数据子载波; Pilot Carriers
:导频子载波
数据头的调制方式:BPSK; 负载的调制方式:QPSK;
不能同步只能仿真没啥意义,就没有接USRP测试。
tx_OFDM模块的grc:
Stream CRC32模块: 产生CRC
Protocol Formatter模块: 从Tagged Stream
中创建数据包头, 然后将数据包头打包为1bit/byte, 以便后续进行BPSK。 将数据有效载荷打包为2bit/byte, 以便后续进行QPSK。
Tag Gate: 根据标签的Key判断是否允许标签通过。
Sync Word 1
和Sync Word 2
是两个用于同步的前导码, 第一个同步码用于频率偏移和时序估计,
第二个同步码用于粗略的频率偏移和信道估计.
此框图通过Tag Debug
产生的数据为:
rx_OFDM模块的grc:
这个模块加入了前导码做同步.
此块收集发送到所有输入端口上的所有标记,并且显示在terminal中, 有点类似于Message Debug
, Name标识Tag Debug
的名称,终端中首先输出. Key Filter
表示过滤器,可以选择自己想要的标签.Display
表示要不要把数据显示在终端.
Schmidl & Cox OFDM synch模块模块:进行频率同步
Frequency Mod做相位增加, 瞬时相位增加与灵敏度和输入幅度成正比。
输入 0 (in端口)采用连续传输样本(项目)。
输入 1 (Header/Payload Demux
端口)是触发信号的可选输入(标记数据包的开头),当这个端口的输入非0时,标识数据包的开头。当检测到触发信号后,将header_len项复制从out_hdr端口输出;该块然后停止,直到它在消息端口 header_data
上接收到消息, 消息是PMT类型的; (这个部分由Header Stream经过FFT,信道估计(OFDM Channel Estimation),帧均衡(OFDM Frame Equalizer),并串转换(OFDM Seriallizer),星座映射,包头解析器(Packet Header Parser)后输出)然后首先将所有的键值对复制到payload。读取length_tag_key中指定的key对应的值,作为payload长度。 然后将有payload的值和作为标签的标头数据一起复制到输出口out_payload
。
帧均衡(OFDM Frame Equalizer):
用到了digital中的ofdm_equalizer_simpledef()函数。
这个函数中有一个std::vector<std::vector<int>>
,定义一个动态二维数组。
包头解析器(Packet Header Parser)的作用:
将标头元数据作为 PMT 发布。 从某种意义上说,这是 Packet Header Generator 的逆块。 不同的是,解析后的头部不是作为流输出,而是作为PMT字典输出到id为“header_data”的消息端口。
symbol和item的区别的一个例子:具有 48 个子载波的 OFDM,使用长度为 64 的 IFFT调制,以及 16 个样本的循环前缀长度。 在这种情况下,itemsize 是 sizeof(gr_complex),因为正在接收complex样本。 一个 OFDM 符号有 64 个样本,因此 items_per_symbol 设置为 64,guard_interval 设置为 16(guard_interval是每个symbol丢弃的长度)。 报头长度以 OFDM 符号的数量指定。 因为我们要处理完整的 OFDM 符号,所以我们将 output_symbols 设置为 true。
对于单载波调制,比如PSK来说, items和sample等价,items_per_symbol实际上等价于sample_per_symbol.
如果报头解调失败,报头必须发送一个值为 pmt::PMT_F 的 PMT。 状态被重置,标题被忽略。
Stream CRC32
生成数据的CRC或者检查数据的CRC, 在接收端就是检查数据的CRC。
此框图通过Tag Debug
产生的数据为:
实验
利用tx_OFDM模块和rx_OFDM模块进行了实验,其中tx_OFDM使用台式机, rx_OFDM使用笔记本.USRP中心频率设为2.4GHz, 采样频率设为300KHz, tx增益为30, rx增益为50;发送终端一直输出U,输出端输出几个数据就变成O了, 有待进一步的调试。
当把tx_OFDM模块和rx_OFDM模块放在一个框图中不能正常进行传输, 接收端口的灯一直不亮, 原因是发送端口和接收端口的信息速率不匹配.终端一直输出U或者O.
“U”表示PC无法快速的提供数据; “O”表示PC无法同步地去接收USRP上的数据.
**注意:如果出现报错 gr::log:INFO: packet_headerparser_b0 - Detected an invalid packet at item
可以参考:**
链接
总结:这个例程产生数据时加入了数据头, 同步等操作, 是一套比较完整的OFDM通信系统框图, 没有加入MAC层的内容, 而且所有的模块都能在Wiki中查到。 但是现阶段此例程中通过tag实现的一些标注还没有完全理解, 有待进一步的研究。 此外,发送端口和接收端口的信息速率不匹配, 发送终端一直输出U,输出端输出几个数据就变成O了, 有待进一步的调试。