使用PlutoSDR构建简易通信系统

使用 Pluto SDR 构建简易通信系统

作者

王路远

电子信息与通信学院 通信工程 1502 班

联系方式:e@wangluyuan.cc

同组人:李星

基本情况

我们利用 Pluto SDR 设备,在已有的示例程序 bpsk_demo 的基础上修改,实现了可靠的无线通信系统。两台 Pluto 中,一台作为发射机,另一台作为接收机,可在实际的无线信道中,传输任意长度的文字或文件。为了保证可靠传输,分别实现了简化的停止等待协议滑动窗口协议

操作系统:Windows 、 Ubuntu

(在 Windows 和 Ubuntu 设备上配置 Pluto 比较容易,按照说明文档操作就可以了。但是我们也尝试了许久在 macOS 上操作 Pluto,但没有成功。表现为:Matlab 下启动程序,报错 image not found。如果你成功在 macOS 上运行了相关程序,请联系我!)

overview

准备工作

在默认的库文件 /library/matlab/iio_sys_obj_matlab.m 中,函数 function ret = stepImpl(obj, varargin) 中实现了发射和接收数据,即

1
2
3
4
5
% Implement the data transmit flow
writeData(obj.libiio_data_in_dev, varargin{1});

% Implement the data capture flow
[~, data] = readData(obj.libiio_data_out_dev);

我们认为 Pluto 是拥有一个发送存储器和接收存储器,分别读、写这两个存储器完成发送和接收的功能。单个设备自发自收时,在一个函数里既发送又接收是正确的。但是我们现在要实现多台设备的通信,就需要指定发射机和接收机,也就是分别控制发送和接收。于是需要把 stepImpl 函数拆成两个函数:发送数据函数和接收数据函数:

1
2
3
4
5
6
7
8
9
10
11
12
%用来发送数据
function writeTxData(obj, varargin)
%......
writeData(obj.libiio_data_in_dev, varargin{1});
end
%用来接收数据
function ret = readRxData(obj)
varargout = cell(1, obj.out_ch_no + length(obj.iio_dev_cfg.mon_ch));
[~, data] = readData(obj.libiio_data_out_dev);
%......
ret=varargout;
end

在 bpsk_demo 的文件 /code/matlab/BPSK/transmitter/bpsk_tx_func.m 中,原作者设置了要发送的 128*4 = 512 bit 的数据(原注释有错,应是 bit 而不是 byte)。其中前 480 比特是有用信息(60 个字符),后 32 位用作循环冗余校验(CRC)。由于在其他地方设置了收发的数据长度而不好更改,这里我们就用了这 512 比特作为一个数据帧。为了支持任意长度的数据,需要把给定的不足 60 个字符的消息结尾补没有意义的空白字符填充到 60。

1
2
3
4
5
6
7
function txdata = bpsk_tx_func(msgStr)
%......
for k = length(msgStr)+1 :60
msgStr = [msgStr, char(0)];
end
%......
end

当发送长消息时,以 60 个字符一切割,在接受到之后只需要抹去最后的空白字符就可以恢复原始消息。

停止等待协议

我们实现的停止等待协议是这样的:

  • 帧序号有 0 和 1 两种,在两种之间跳变
  • 每帧的前 3 个字符用作帧序号和其他控制信息。即有效信息从 60 个字符减少到 57 个字符
  • 发送方每次发送一个帧,并开始计时
  • 接收方如果收到一个帧,且该帧的序号是自己期望的,则把收到的帧序号返回,并保存相应数据
  • 发送方如果没有到规定的超时时间(这里是 10 秒):
    • 持续监听返回值
    • 如果收到自己刚刚发送到的帧序号
      • 发送下一帧
    • 否则
      • 继续监听,直到超时
  • 发送方如果超时,重新传输刚才的帧

滑动窗口协议

我们实现的滑动窗口协议是这样的:

  • 发送方和接收方窗口大小均设置为 5
  • 为了避免序号回滚时引起歧义,序号的个数设置为窗口大小的 2 倍,即取 1- 10
  • 发送方维护发送窗口,在等待发送的数据数组上滚动
  • 接收方维护接收窗口,判断收到的数据帧号是否落在自己的窗口内,并酌情保存数据
  • 接收方发现有一段序号连续的数据后(顺序正常),滑动自己的窗口,并返回这些连续的数据中的最大序号
  • 发送方一次发完从上次发送的位置到窗口末尾中的所有数据,并开始计时
  • 发送方如果没有到规定的超时时间(这里是 10 秒)
    • 持续监听返回值
    • 如果收到的帧序号落在自己的发送窗口中,且与之前收到的帧序号不一样
      • 滑动窗口并开始发送下一串数据
    • 否则
      • 继续监听,直到超时
  • 发送方如果超时,重新传输窗口中的所有数据

sketch

(这是我们在讨论两种协议时打的草稿,当时忘记带草稿纸,只好用抽纸应急:P)

传输文件

一旦可靠的传输文字的系统构建完成,传输文件遍不再是问题。我们使用 Base64 编码,将二进制文件编码成字符串,就可以使用之前的通信系统来传输任意的文件了。

需要注意的是,Base64 编码的字符集中不包含空白字符,因此不会与截取末尾空白字符的操作方法产生冲突。

效果

发送方将下图成功发送给接收方,该图片大小约为 3 kB。

logo

发送过程中,控制台打印如下消息:

console1

从上图可以看到,发送方先收到 4, 后收到 9,于是发送窗口滑动,一次性发送五条消息。

console2

从上图可以看到,由于全部消息已经发完,即使收到的序号发生改变,窗口也不再继续滑动。最后双方挥手再见,退出程序。平均发送速度为 49.8558 bps。

不足

因为时间比较仓促(我要赴美国参加冬令营活动,李星即将去做手术),我们的程序还有一些不足。其中最主要的一点是发送数据的速度比较缓慢。我们分析的原因主要是,Pluto 每次接收数据都需要一定的时间,而发送的速度很快。有可能发送只用了一瞬间,而恰好这段时间接收方还在读取上次的数据,就错过了消息,导致丢包率非常严重,每次都不得不等待超时重传。我们对发送方发送的速度做了延迟,即同样的内容延长发送时间,效果有所改善,但仍不尽人意,两者不能进行很好的同步。目前我们还没找到比较好的解决方案。

当然,改善发送速率也可以降低超时时间(现在的 10 秒是为了方便观察实验现象);采用多进制的调制方式,如 4PSK、16QAM 等;增大包体积(因为误码率不算太高,而丢包率很高)还有增大滑动窗口大小等。

遇到的困难

  • 在 macOS 上程序一直报错,还没有解决。最后我们使用了 Windows 和 Ubuntu 操作系统。
  • 公开的资料太少,上手不容易。不过在和同学讨论后得到解决,在写协议逻辑的时候还是很愉快的。
  • 发送方频繁接收到自己刚刚发送的数据,而很少收到接收方回复的消息。也就是应该接受的消息完全被淹没在自己发送的消息里了。为了确定是干扰导致的,我们先用同轴线将两台 Pluto 的 Rx 和 Tx 分别连在了一起。有线的状态下,电磁波基本都被屏蔽在导线内,这个问题果然不存在了。确定问题的原因后,我们修改了发送和接收所用的载波频率,发送和接收就不再互相干扰了。)

wire

总结和收获

做这样一件事情确实比较有挑战性。主要原因是网上公开的资料太少,无论是官方 Wiki 还是厂家提供的资料,帮助都比较小。再加上之前也没有使用过同类的 SDR 产品,开始确实比较懵。不过后来在自习阅读了厂家提供的示例代码,又和同学讨论过后,逐渐就能开始编写逻辑了。

我和李星同学大概花了一天半的时间来实际的编写逻辑代码,时间确实比较紧张,不过收获也很多。从头到位设计可靠传输的协议实在太有意思了。事实上我们在写代码的时候完全没有翻看计算机网络的数据,也基本没有查找什么资料。因为时间过的比较久了,不少协议的细节我们都记不清楚了,于是就自己思考传输的过程中会出现什么差错,讨论解决这些问题的办法。有的时候会突然恍然大悟,想明白为什么前辈要这样设计!通过这次实验,熟悉了 SDR 设备、对通信原理中的理论知识有了一个落地,更是大大增强了对计算机网络的理解。

期待以后还能有机会用 Pluto SDR 做一些更复杂、更有意思的东西。

其他

本项目的完整代码托管在了 GitHub 上:https://github.com/BeBeBerr/Pluto-Network

此文章发布在了我的 Blog 上,欢迎查看

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×