调制、均衡与纠错

0. 前言

USRP B200用的是AD9364芯片,有一路发射机和一路接收机可以同时工作。自然我们可以让USRP自发自收来测试数据的传输。

如图所示,我用电脑的麦克风采集音乐,音乐数据经过处理后通过USRP的发射机发射出去,然后被接收机的天线收到,再处理之后通过耳机播出去。如果整个链条不出问题,用户应该就能听到一首一模一样的音乐。

audio_loopback

但实际上整个链条容易出问题的地方相当多。为了解决这些问题,无数天才工程师们发明了很多办法来提高通信效率。本文会介绍数据的调制、信道的均衡、信道编码等概念和对应的实验结果。

1. 模拟调制

上古时代传递声音的通信方式都是模拟的。

人能听到的声音在20Hz~20kHz的频率范围,但说话的声音的频率更低,正常人说话也就几百Hz,钢琴的中央C是262Hz,只有女高音能唱到1kHz左右。所以我们可以很安全地在ADC采样以后把高频信号都滤掉,这里设定是滤掉5kHz以上的,得到一个窄带的声音信号。和后面数字调制用到的带宽相比,这真的很窄了。

FM(Frequency Modulation)的意思是用相对载波的频率偏移来携带信息。假设声音信号是 \(A(t)\),那么调制后的信号的瞬间频率是\(f_{carrier}+mA(t)\)。当\(A(t)\)达到最大值,也就是声音最响时,频偏也最大。在对讲机系统里最大频偏限制在3kHz~5kHz。

在GNURadio里先用NBFM (Narrow Band Frequency Modulation) 模块调制声音信号得到基带信号,再用USRP在射频频段发射出去。完整流图如下,是在官方Tutorial [1]的基础上稍微改一下,加上USRP收发机得到的。

窄带调频

这时通过麦克风说话,应该就能从耳机里听到声音了。然而背景会有很明显的啪啪声,就像老式电视机收不到信号时的那种噪声。这就是模拟调制系统最大的弱点,无法抵抗噪声,而且增加音量只会把噪声一起放大。

2. 数字调制

为了解决噪声的问题,现代通信系统都是数字调制的。就算有噪声,只要噪声没有大到让0变成1就不会有问题。然而还有其他问题导致接收机很难判断收到的到底是0还是1。

码间串扰(Inter-symbol Interference, ISI) 当脉冲在时域上扩展时,相邻符号的波形会重叠,导致接收端在抽样时刻受到前后符号的干扰。为了解决这个问题,我们引入一个升余弦滤波器 (Raised Cosine Filter)。它的时域响应的特点是是有在采样时刻 \(T_{0}\) 有非零值,在其他采样时刻 \(T_{n}, (n\neq 0)\)的响应都等于0,确保在下一个采样时刻不会干扰到下一个符号。但是代价是需要占用额外带宽,所以有个叫滚降系数的参数 (Roll-off factor),用来权衡频谱效率和时域扩展。 实际操作过程中这个滤波器的响应会分给接收端和发射端:两端各有一个平方根升余弦滤波器 (Root Raised Cosine Filter), 两个相乘就是完整的升余弦滤波器。

多径效应与信道均衡 除了直线传播,信号在房间里还可能有多种反射路径。各条路上的电磁波相互叠加,可能会增强或抵消有用的信号。在频域,某些频率成分会因相位相消而衰减,另一些则会因相位相同而增强。这使得信道在整个频带内的响应不均匀。为此我们用一个线性均衡器抵消多径效应,先估计出信道的频率响应,然后生成一个“相反”的滤波器,让扭曲的信号通过后能恢复成原来的样子。

载波同步 接收机和发射机的时钟不一定完全同步,如果有微小的频率差,星座图上的点看起来就会在单位圆上旋转。为此我们用一个Costas Loop,其原理就是一个PLL,提取出接收信号和本地参考信号的相位差,然后调整本地频率使它和接收到的信号同步。

假设我们用的是QPSK调制,最后实现出来的流图还是可以参考官方Tutorial [2]并把仿真用的信道换成实际的USRP收发机,把信号源换成麦克风输入。

3. 包

做完以上这些步骤,星座图看起来很干净了。 QPSK

但还是有问题。麦克风采集到的信号是32位浮点数,但发射机传送数据的时候是一个比特一个比特传的。接收机收到的时候不知道哪个是第一个比特,根本没办法恢复出原来的数据。所以我们必须要引入”Packet”的机制。每次发真正的数据之前,先发一些前导数据,告诉接收机准备好收数据。

在做实验的时候我可以自定义协议,而不需要遵循Wi-Fi或蓝牙这样的标准。我在GNURadio的Default Header Format的基础上自定义了一个Packet的格式,

 | access code (8b) | hdr (32b) | payload |

其中,access code是一段8比特的固定的序列,可以自己随便选,比如10100101就可以,目的是让当接收机收到这个序列时就知道一个包要开始了。接下来的32比特是包的长度信息,16位的整数重复两遍。最后是真正要发送的数据。

计算一下,我把麦克风采样率设置成10kHz,每秒会生成10000个32位浮点数也就是40000字节。粗暴压缩一下数据量,一个采样点不需要32位浮点数那么精确,8位就够了,这样每秒是10000字节。

我把它们每320字节分为一组,每秒会有31.25个包。每个包会有5字节(1字节access code + 4字节长度信息)的额外开销,最后实际需要传输的数据量是325 * 31.25 * 8 = 81250bps。QPSK每两个比特是一个符号,每个符号采样4次,最终USRP工作时的采样率是162.5kHz。

具体实现方法还是参考官方Tutorial [3], 利用GNURadio的tag机制在包开始的地方打上标签,包含长度信息,告诉下游的模块什么时候开始分析。注意:tag不是实际在信道里传输的数据,只是GNURadio在处理信号时用来同步上下游模块的一个机制。

最后的效果是这样(qpsk_audio.grc),能流畅地听到音乐,也没有明显噪音了,但是偶尔还是会有个别比特出错,会有沙沙声。还有另一个BPSK调制的版本 (bpsk_audio.grc),因为调制更简单,所以比QPSK误码率更低一点。

4. 纠错码

尽管接收机做了各种处理,但是收到的比特仍然有可能出错,这时就需要纠错码(Forward Error Control, FEC)了,多传一点冗余比特以便能检测到错误并改正。

最简单粗暴的是重复码 (Repitition Code),即同一个比特发送好几遍,比如3遍,少数服从多数。

学校里还学过一个汉明码 (Hamming Code),利用奇偶校验来判断有没有出错。常用的 (7,4) 汉明码7个比特一组,其中4个是数据,3个是校验位,它能纠正一个比特的错误或发现两个比特的错误。

高级一点的还有卷积码 (Convolution Code)、LPDC码、POLAR码等。Wi-Fi联盟在11a/b/g中都把卷积码作为标准,在11n和11ac中引入LDPC码作为可选feature,并在11ax开始淘汰了卷积码,把LDPC码作为标准。

最后,加上FEC的完整流图在这里(qpsk_audio_fec.grc)。我用的是(2,1,7)卷积码,编码效率是1/2。用GNURadio自带的BER模块测量误码率,用每包320字节的数据作为参考,结果显示,用了卷积码之后整体误码率下降到\(10^{-2.8}\),而不加FEC的时候有\(10^{-2.2}\)

qpsk_ber

5. 总结

继上一篇解析ADS-B之后,我们又实现了一个稍微复杂一点的自打自收的系统,复习了通信原理这门课的很多知识,还顺便摸清了GNURadio的各个常用功能。

然而,到FEC为止,我的流图已经乱得放不下了,我也能感觉到GNURadio开始变得繁琐了,尤其是各种bit和symbol之间的转换,不停地pack和unpack。

于是,下一篇迷你蓝牙综测仪,我会用USRP解析市面上的蓝牙设备发出的蓝牙信号,介绍蓝牙测试的指标,并在GNURadio里用自定义的Python模块进行解析。


参考资料

GNU Radio

  1. GNU Radio Tutorial - NBFM

  2. GNU Radio Tutorial - QPSK Mod and Demod

  3. GNU Radio Tutorial - Packet Communications

NBFM

  1. Continuous Tone Coded Squelch System

  2. Narrow Band FM in Amateru Radio

  3. Narrowband FM with a Single Frequency Input

Pulse Shaping

  1. Root Raised Cosine Filter

Equalization

  1. Constant Modulus Algorithm