使用 Pluto SDR 构建简易通信系统
作者
王路远
电子信息与通信学院 通信工程 1502 班
联系方式:e@wangluyuan.cc
同组人:李星
基本情况
我们利用 Pluto SDR 设备,在已有的示例程序 bpsk_demo 的基础上修改,实现了可靠的无线通信系统。两台 Pluto 中,一台作为发射机,另一台作为接收机,可在实际的无线信道中,传输任意长度的文字或文件。为了保证可靠传输,分别实现了简化的停止等待协议和滑动窗口协议。
操作系统:Windows 、 Ubuntu
(在 Windows 和 Ubuntu 设备上配置 Pluto 比较容易,按照说明文档操作就可以了。但是我们也尝试了许久在 macOS 上操作 Pluto,但没有成功。表现为:Matlab 下启动程序,报错 image not found
。如果你成功在 macOS 上运行了相关程序,请联系我!)
准备工作
在默认的库文件 /library/matlab/iio_sys_obj_matlab.m
中,函数 function ret = stepImpl(obj, varargin)
中实现了发射和接收数据,即
1 | % Implement the data transmit flow |
我们认为 Pluto 是拥有一个发送存储器和接收存储器,分别读、写这两个存储器完成发送和接收的功能。单个设备自发自收时,在一个函数里既发送又接收是正确的。但是我们现在要实现多台设备的通信,就需要指定发射机和接收机,也就是分别控制发送和接收。于是需要把 stepImpl
函数拆成两个函数:发送数据函数和接收数据函数:
1 | %用来发送数据 |
在 bpsk_demo 的文件 /code/matlab/BPSK/transmitter/bpsk_tx_func.m
中,原作者设置了要发送的 128*4 = 512 bit 的数据(原注释有错,应是 bit 而不是 byte)。其中前 480 比特是有用信息(60 个字符),后 32 位用作循环冗余校验(CRC)。由于在其他地方设置了收发的数据长度而不好更改,这里我们就用了这 512 比特作为一个数据帧。为了支持任意长度的数据,需要把给定的不足 60 个字符的消息结尾补没有意义的空白字符填充到 60。
1 | function txdata = bpsk_tx_func(msgStr) |
当发送长消息时,以 60 个字符一切割,在接受到之后只需要抹去最后的空白字符就可以恢复原始消息。
停止等待协议
我们实现的停止等待协议是这样的:
- 帧序号有 0 和 1 两种,在两种之间跳变
- 每帧的前 3 个字符用作帧序号和其他控制信息。即有效信息从 60 个字符减少到 57 个字符
- 发送方每次发送一个帧,并开始计时
- 接收方如果收到一个帧,且该帧的序号是自己期望的,则把收到的帧序号返回,并保存相应数据
- 发送方如果没有到规定的超时时间(这里是 10 秒):
- 持续监听返回值
- 如果收到自己刚刚发送到的帧序号
- 发送下一帧
- 否则
- 继续监听,直到超时
- 发送方如果超时,重新传输刚才的帧
滑动窗口协议
我们实现的滑动窗口协议是这样的:
- 发送方和接收方窗口大小均设置为 5
- 为了避免序号回滚时引起歧义,序号的个数设置为窗口大小的 2 倍,即取 1- 10
- 发送方维护发送窗口,在等待发送的数据数组上滚动
- 接收方维护接收窗口,判断收到的数据帧号是否落在自己的窗口内,并酌情保存数据
- 接收方发现有一段序号连续的数据后(顺序正常),滑动自己的窗口,并返回这些连续的数据中的最大序号
- 发送方一次发完从上次发送的位置到窗口末尾中的所有数据,并开始计时
- 发送方如果没有到规定的超时时间(这里是 10 秒)
- 持续监听返回值
- 如果收到的帧序号落在自己的发送窗口中,且与之前收到的帧序号不一样
- 滑动窗口并开始发送下一串数据
- 否则
- 继续监听,直到超时
- 发送方如果超时,重新传输窗口中的所有数据
(这是我们在讨论两种协议时打的草稿,当时忘记带草稿纸,只好用抽纸应急:P)
传输文件
一旦可靠的传输文字的系统构建完成,传输文件遍不再是问题。我们使用 Base64 编码,将二进制文件编码成字符串,就可以使用之前的通信系统来传输任意的文件了。
需要注意的是,Base64 编码的字符集中不包含空白字符,因此不会与截取末尾空白字符的操作方法产生冲突。
效果
发送方将下图成功发送给接收方,该图片大小约为 3 kB。
发送过程中,控制台打印如下消息:
从上图可以看到,发送方先收到 4, 后收到 9,于是发送窗口滑动,一次性发送五条消息。
从上图可以看到,由于全部消息已经发完,即使收到的序号发生改变,窗口也不再继续滑动。最后双方挥手再见,退出程序。平均发送速度为 49.8558 bps。
不足
因为时间比较仓促(我要赴美国参加冬令营活动,李星即将去做手术),我们的程序还有一些不足。其中最主要的一点是发送数据的速度比较缓慢。我们分析的原因主要是,Pluto 每次接收数据都需要一定的时间,而发送的速度很快。有可能发送只用了一瞬间,而恰好这段时间接收方还在读取上次的数据,就错过了消息,导致丢包率非常严重,每次都不得不等待超时重传。我们对发送方发送的速度做了延迟,即同样的内容延长发送时间,效果有所改善,但仍不尽人意,两者不能进行很好的同步。目前我们还没找到比较好的解决方案。
当然,改善发送速率也可以降低超时时间(现在的 10 秒是为了方便观察实验现象);采用多进制的调制方式,如 4PSK、16QAM 等;增大包体积(因为误码率不算太高,而丢包率很高)还有增大滑动窗口大小等。
遇到的困难
- 在 macOS 上程序一直报错,还没有解决。最后我们使用了 Windows 和 Ubuntu 操作系统。
- 公开的资料太少,上手不容易。不过在和同学讨论后得到解决,在写协议逻辑的时候还是很愉快的。
- 发送方频繁接收到自己刚刚发送的数据,而很少收到接收方回复的消息。也就是应该接受的消息完全被淹没在自己发送的消息里了。为了确定是干扰导致的,我们先用同轴线将两台 Pluto 的 Rx 和 Tx 分别连在了一起。有线的状态下,电磁波基本都被屏蔽在导线内,这个问题果然不存在了。确定问题的原因后,我们修改了发送和接收所用的载波频率,发送和接收就不再互相干扰了。)
总结和收获
做这样一件事情确实比较有挑战性。主要原因是网上公开的资料太少,无论是官方 Wiki 还是厂家提供的资料,帮助都比较小。再加上之前也没有使用过同类的 SDR 产品,开始确实比较懵。不过后来在自习阅读了厂家提供的示例代码,又和同学讨论过后,逐渐就能开始编写逻辑了。
我和李星同学大概花了一天半的时间来实际的编写逻辑代码,时间确实比较紧张,不过收获也很多。从头到位设计可靠传输的协议实在太有意思了。事实上我们在写代码的时候完全没有翻看计算机网络的数据,也基本没有查找什么资料。因为时间过的比较久了,不少协议的细节我们都记不清楚了,于是就自己思考传输的过程中会出现什么差错,讨论解决这些问题的办法。有的时候会突然恍然大悟,想明白为什么前辈要这样设计!通过这次实验,熟悉了 SDR 设备、对通信原理中的理论知识有了一个落地,更是大大增强了对计算机网络的理解。
期待以后还能有机会用 Pluto SDR 做一些更复杂、更有意思的东西。
其他
本项目的完整代码托管在了 GitHub 上:https://github.com/BeBeBerr/Pluto-Network
此文章发布在了我的 Blog 上,欢迎查看。