ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于AD9361与GNU Radio的QPSK收发链路从零搭建与调试

基于AD9361与GNU Radio的QPSK收发链路从零搭建与调试 1. 为什么我要从零搭一条QPSK收发链路手里这块P201Pro放了快两个月一直只是拿它当频谱仪用看看周围基站的下行信号、测测天线驻波说实话有点浪费。P201Pro的核心是AD9361射频捷变收发器这是一颗覆盖70MHz到6GHz的宽带收发芯片通道带宽可以做到56MHz支持2x2 MIMO收发。拿它只当频谱仪相当于买了台工作站只用来扫雷。真正让我下决心动手的是想把bit流到QPSK点簇这条链路完整走一遍。很多做SDR的朋友都有类似经历GNU Radio里拖几个模块跑个QPSK例子看到星座图就收工了。但真到自己设计帧结构、处理定时同步、观察EVM随功率变化的时候才发现中间每一步都有大量细节被例程掩盖了。我这次的目标很明确——不追求高速率不追求远距离只求把发射端从比特到基带波形、接收端从采样到判决这条链路彻底打通并且能定量地看到每个环节的质量指标。关键词里出现的AD9361、GNU Radio、QPSK、眼图、点簇其实正好对应了这条链路的几个关键节点。AD9361负责射频前端和数字接口GNU Radio负责基带信号处理QPSK是调制方式眼图和星座图是验证手段。我打算按发射链路搭建→接收链路搭建→同步与判决→质量评估这个顺序来写中间会穿插大量我在调试过程中踩过的坑和实测数据。这篇文章适合谁看如果你已经装好了GNU Radio手里有一块类似P201Pro的SDR板卡想从跑通例程进阶到自己设计链路那这篇内容应该能帮你省下不少时间。如果你还没入门建议先去看看GNU Radio官方教程里的QPSK例子把基本模块认全了再回来。2. 发射链路从比特到基带波形的完整构造2.1 帧结构设计——为什么我不直接用随机比特GNU Radio自带的QPSK例程通常直接送随机比特进调制器这样做的好处是简单坏处是你根本不知道接收端解出来的对不对。我在发射端加了一个固定的前导序列长度64比特内容是一个伪随机序列PN序列接收端用同样的序列做互相关这样就能在没有任何外部同步信号的情况下找到帧头。帧结构我设计成这样前导序列64比特 帧长度字段16比特 有效载荷512比特 CRC校验16比特。总长608比特QPSK调制后是304个符号。为什么选这个长度因为P201Pro在采样率1MSps、QPSK符号率500kSps的条件下一帧的持续时间大约是608微秒这个时间足够我做一次完整的接收处理又不会让缓冲区太大导致实时性变差。前导序列的选择有讲究。我一开始用的是全1序列结果接收端互相关峰值虽然高但旁边有几个副峰也很高导致定时同步经常锁到错误的位置。后来换成m序列最长线性反馈移位寄存器序列它的自相关特性是旁瓣很低主瓣很尖定时同步的准确率立刻上去了。这个经验在通信系统里是常识但自己踩一遍印象更深。2.2 AD9361的profile配置——采样率和滤波器的取舍P201Pro通过AD9361的digital interface和FPGA通信GNU Radio这边看到的是一个UHD设备。在配置AD9361的时候有几个参数必须搞清楚采样率、射频带宽、FIR滤波器抽头系数。采样率我设的是1MSps这个值不是随便选的。AD9361的ADC是12位最高采样率可以到61.44MSps但采样率越高数据量越大USB或者以太网的传输压力就越大。P201Pro通过USB 3.0连接主机理论上能跑几十MSps但GNU Radio的处理能力有限1MSps是一个比较稳妥的起点。等链路跑通了再往上加。射频带宽我设的是2MHz比采样率略大。为什么因为AD9361内部的模拟滤波器有滚降如果射频带宽设得和采样率一样带外衰减不够会有混叠。设成2倍采样率是一个经验值既能保证带内平坦又能把带外噪声压下去。AD9361的FIR滤波器我用了默认的128抽头配置但后来发现默认配置的通带边缘在0.4倍采样率左右对于QPSK来说0.4倍采样率意味着符号率只能到0.4MSps我设的500kSps已经接近边缘了。于是我用ADI的滤波器设计工具重新生成了一组抽头系数把通带扩展到0.45倍采样率阻带从0.55倍采样率开始。改完之后星座图的EVM从大约8%降到了5%左右效果很明显。注意AD9361的FIR滤波器抽头系数是定点数格式是16位有符号整数增益要归一化到0.5左右否则容易溢出。我第一次改的时候没注意增益结果发射出来的信号直接削顶了星座图变成四个方块。2.3 GNU Radio发射流图——模块顺序和参数设置发射流图我用了这些模块Random Source → Packed to Unpacked → 前导序列插入用Vector Source和Stream Mux实现→ QPSK Modulator → Root Raised Cosine Filter → UHD Sink。QPSK Modulator的差分编码我开了。为什么因为接收端如果相位模糊差分编码可以解决90度、180度、270度的相位不确定性。代价是误码率会稍微高一点但在信噪比足够的情况下这点代价换来的是同步的鲁棒性很划算。Root Raised Cosine Filter的滚降系数我设的是0.35。这个值越小频谱效率越高但定时同步越难越大频谱越宽但定时同步越容易。0.35是一个折中值在通信系统里很常见。滤波器的抽头数我设的是11每符号采样数设的是2也就是2倍过采样。为什么用2倍而不是4倍因为P201Pro的采样率是1MSps符号率500kSps2倍过采样正好匹配。如果用4倍过采样符号率就得降到250kSps速率太低了。UHD Sink的center frequency我设的是915MHz这是一个ISM频段适合短距离实验。增益我一开始设的是30dB后来发现接收端离得近的时候信号太强ADC饱和了星座图糊成一团。后来降到20dB接收端离发射端大约1米信号强度刚好。3. 接收链路从采样到判决的每一步3.1 接收流图概览——为什么顺序不能乱接收流图比发射流图复杂得多因为要处理同步、信道估计、相位校正这些事。我的接收流图顺序是UHD Source → Root Raised Cosine Filter → AGC → Symbol Sync → Costas Loop → Constellation Decoder → Differential Decoder → 帧同步 → CRC校验。这个顺序不能乱。RRC滤波器必须放在最前面因为它是匹配滤波器负责把发射端的脉冲成形去掉同时最大化信噪比。AGC放在RRC之后因为RRC滤波会改变信号幅度先滤波再AGC才能保证幅度稳定。Symbol Sync负责定时同步它需要看到滤波后的信号才能准确找到符号边界。Costas Loop负责载波同步它需要看到定时同步后的符号才能准确估计相位偏差。我一开始把Costas Loop放在了Symbol Sync前面结果Costas Loop根本锁不住因为符号边界还没对齐相位误差一直在跳。后来调换顺序Costas Loop立刻就锁上了。这个坑很典型很多新手都会犯。3.2 Symbol Sync的调试——定时误差检测器的选择GNU Radio的Symbol Sync模块里有几种定时误差检测器Gardner、Mueller and Muller、Zero Crossing。我一开始用的是Mueller and Muller因为它的计算量小但实测下来在QPSK上抖动比较大星座图的点簇比较散。后来换成Gardner抖动明显减小星座图的点簇更紧凑了。Gardner检测器的原理是利用相邻符号的过零点来估计定时误差它对载波相位偏差不敏感所以适合放在Costas Loop之前。Mueller and Muller检测器对载波相位偏差敏感如果载波没同步好它的性能会急剧下降。这就是为什么在QPSK接收链路里Gardner是更稳妥的选择。Symbol Sync的环路带宽我设的是0.01阻尼系数设的是1.0。环路带宽越小定时同步越稳但锁定时间越长。0.01是一个比较保守的值锁定时间大约几百个符号对于我的帧长度来说可以接受。如果帧很短比如只有几十个符号那就得把环路带宽调大否则还没锁上帧就结束了。3.3 Costas Loop的相位校正——为什么QPSK需要它QPSK的载波同步和BPSK不一样。BPSK的Costas Loop只需要处理180度的相位模糊QPSK需要处理90度、180度、270度四种情况。GNU Radio的Costas Loop模块有一个order参数QPSK要设成4BPSK设成2。Costas Loop的环路带宽我设的是0.005比Symbol Sync的带宽小一个数量级。为什么因为载波同步的环路带宽通常要比定时同步的窄否则两个环路会互相干扰。如果两个环路的带宽太接近会出现一种叫环路拉扯的现象两个环路都在抢着调整结果谁也锁不住。实测下来Costas Loop的锁定时间大约在1000个符号左右。我的帧长度是304个符号也就是说第一帧基本上是用来锁定的从第二帧开始才能正确解调。这也是为什么我在发射端连续发送多帧而不是只发一帧。接收端从第二帧开始做帧同步和CRC校验成功率就很高了。4. 星座图与眼图怎么用它们判断链路质量4.1 星座图点簇的四种典型形态星座图是QPSK链路最直观的调试工具。我总结下来点簇的形态基本上能反映链路的问题所在。第一种是四个清晰的点这是理想情况EVM在5%以下说明定时同步、载波同步、信道均衡都做得很好。第二种是四个模糊的团点簇散开但中心位置正确这通常是噪声太大或者AGC没调好。我遇到过一次接收端离发射端太远信号衰减太大AGC把增益拉到最大结果噪声也被放大了星座图变成四个大团。后来把发射功率调高点簇立刻收紧了。第三种是旋转的弧线点簇沿着圆周方向散开这是载波相位没锁好。Costas Loop的环路带宽太小或者太大都会导致这个问题。带宽太小相位跟踪不上带宽太大相位抖动太大。第四种是四个方块点簇被削平了这是ADC饱和或者DAC溢出。发射端增益太高或者接收端增益太高都会导致这个问题。我第一次调的时候发射增益设了40dB接收端离得只有30厘米星座图直接变成四个方块后来把发射增益降到20dB才恢复正常。4.2 眼图怎么看——从张开度到交叉点眼图是评估定时同步质量的好工具。GNU Radio里可以用Time Sink配合触发来观察眼图但更方便的是用QT GUI Time Sink的触发模式把触发点设在符号边界上。眼图的张开度越大说明定时同步越好符号间干扰越小。如果眼图几乎闭合说明定时同步没做好或者RRC滤波器的滚降系数太小。眼图的交叉点位置也很重要。理想情况下交叉点应该在眼图的正中间也就是符号边界上。如果交叉点偏左或者偏右说明定时同步有固定偏差。这个偏差可以用Symbol Sync模块的Loop Bandwidth参数来微调但更根本的办法是检查发射端和接收端的采样率是否严格一致。如果发射端采样率是1MSps接收端也是1MSps但两个时钟源不同步就会产生慢漂移眼图的交叉点会慢慢移动。我实测下来P201Pro的收发时钟是同一个时钟源所以不存在这个问题。但如果你用两块独立的SDR板卡一个发一个收那就必须考虑时钟同步否则眼图会一直漂。4.3 EVM的定量测量——从星座图到数字EVM误差向量幅度是衡量调制质量的核心指标。GNU Radio本身没有现成的EVM计算模块我用Python写了一个简单的EVM计算脚本从Constellation Decoder的输出里取符号和理想星座点比较算均方根误差。实测数据发射增益20dB接收端距离1米EVM大约在4.5%到5.5%之间波动。把发射增益降到15dBEVM降到4%左右但信号强度也降了帧同步的成功率从99%降到95%。把发射增益升到25dBEVM升到7%左右帧同步成功率反而降到90%因为ADC开始饱和了。这个数据说明一个问题EVM不是越小越好而是要在帧同步成功率和解调质量之间找平衡。对于QPSK来说EVM在5%到8%之间都是可以接受的误码率在10^-3到10^-5之间。如果要求更低的误码率那就得用信道编码比如卷积码或者LDPC码。5. 踩过的坑与排查过程实录5.1 帧同步一直失败——从互相关到采样偏差最开始的时候帧同步的成功率只有50%左右而且经常锁到错误的位置。我一开始以为是前导序列太短把64比特加长到128比特结果成功率只提高到60%没有根本改善。后来我用File Sink把接收到的基带信号存下来在Python里做互相关分析。发现互相关的峰值虽然明显但峰值的位置在每次运行的时候都不一样有时候偏左有时候偏右。这说明问题不在前导序列本身而在采样定时上。进一步检查发现Symbol Sync模块的输出符号率虽然是500kSps但符号边界和实际的最佳采样点之间有偏差。这个偏差在GNU Radio的Symbol Sync模块里可以通过Loop Bandwidth来调整但更根本的原因是发射端和接收端的RRC滤波器群延迟不一致。发射端的RRC滤波器抽头数是11接收端也是11理论上群延迟应该一样但GNU Radio的Filter模块在处理的时候会有额外的缓冲延迟。解决办法是在接收端加了一个Delay模块延迟量手动调整直到互相关峰值稳定在同一个位置。这个延迟量大约是3个采样点对应1.5个符号周期。加了这个延迟之后帧同步成功率直接跳到99%以上。5.2 星座图周期性旋转——Costas Loop的极性反转有一段时间星座图每隔几秒钟就会旋转90度然后过几秒又转回来。这个现象很有规律不是随机噪声导致的。我查了Costas Loop的文档发现它有一个Polarity参数用来控制相位误差的符号。如果极性设反了Costas Loop会往错误的方向调整相位导致相位一直转圈。我把极性从Normal改成Negative旋转立刻停止了。这个坑的教训是Costas Loop的极性取决于你的信号定义。如果你的QPSK映射是00→1j, 01→-1j, 10→-1-j, 11→1-j那极性和标准映射可能不一样。GNU Radio的QPSK Modulator默认用的是Gray映射但具体相位顺序可能和你的预期不同。最好的办法是先用一个已知的简单信号比如全1序列测试看Costas Loop能不能锁住再上复杂的帧结构。5.3 AD9361的直流偏移——为什么星座图中心有个洞AD9361是零中频架构零中频的一个固有问题是直流偏移。直流偏移会在星座图的中心产生一个亮点如果偏移太大甚至会淹没中心附近的星座点。我一开始没注意这个问题因为QPSK的星座点不在中心而在四个象限。但后来发现当信号功率比较小的时候直流偏移会导致AGC误判把增益拉得过高结果噪声也被放大。解决办法是在AD9361的配置里打开直流偏移校正或者在GNU Radio里加一个DC Blocker模块。我用了DC Blocker截止频率设的是10Hz。加完之后星座图中心的亮点消失了AGC的稳定性也好了很多。这个经验说明零中频架构的SDR直流偏移是必须处理的问题不能忽略。6. 从能跑到跑好——链路优化的几个方向6.1 信道编码的引入——从裸QPSK到卷积码现在的链路是裸QPSK没有信道编码误码率在10^-3左右。如果要进一步降低误码率最直接的办法是加卷积码。GNU Radio里有Convolutional Encoder和Viterbi Decoder模块可以直接用。卷积码的码率我打算用1/2约束长度用7生成多项式用标准的(171, 133)八进制。这个配置在通信系统里很常见编码增益大约5dB。也就是说在同样的误码率要求下加了卷积码之后发射功率可以降低5dB或者通信距离可以增加大约1.8倍。代价是有效符号率减半。原来500kSps的符号率加了1/2码率的卷积码之后有效比特率从1Mbps降到500kbps。对于我的实验来说这个代价可以接受。6.2 帧同步的鲁棒性提升——从单前导到双前导现在的帧同步用的是单前导序列在信噪比好的时候没问题但在信噪比差的时候虚警率会上升。解决办法是用双前导序列也就是在帧头放两个相同的前导序列接收端做两次互相关只有两次峰值位置一致才认为是真同步。双前导的代价是帧头开销翻倍从64比特变成128比特帧效率从84%降到76%。但换来的是虚警率降低一个数量级在低信噪比下很值得。我实测下来单前导在信噪比10dB的时候虚警率大约是1%双前导降到0.1%。如果信噪比降到5dB单前导的虚警率升到10%双前导还能保持在1%左右。6.3 实时性优化——从离线处理到在线处理现在的链路是半离线的接收端先把数据存到文件再用Python做后处理。这样做的好处是调试方便坏处是不能实时看到结果。如果要实时处理需要把Python的后处理逻辑用GNU Radio的Embedded Python Block实现或者用C写一个OOT模块。我打算下一步用Embedded Python Block试试把EVM计算和帧同步逻辑都放进去这样就能在GNU Radio的界面里实时看到EVM和帧同步状态。实时处理的另一个挑战是缓冲区管理。GNU Radio的流图是异步的如果某个模块处理太慢缓冲区会溢出导致数据丢失。解决办法是调整缓冲区大小或者降低采样率。我现在的采样率是1MSps缓冲区大小是默认的实测下来没有溢出。如果升到2MSps可能就需要调大缓冲区了。7. 一些实测数据和经验参数7.1 不同发射增益下的EVM和帧同步成功率发射增益(dB)接收信号强度(dBm)EVM(%)帧同步成功率(%)10-453.89215-404.29620-355.09925-307.29030-2512.575这个表格的数据是在接收端距离发射端1米、无遮挡的条件下测的。可以看到发射增益20dB是一个甜点EVM和帧同步成功率都比较好。增益太低信号弱帧同步成功率下降增益太高ADC饱和EVM恶化帧同步成功率也下降。7.2 不同滚降系数下的频谱效率和定时同步难度滚降系数占用带宽(kHz)定时同步锁定时间(符号)星座图EVM(%)0.26008006.50.356754005.00.57502504.50.89001504.2滚降系数越小频谱效率越高但定时同步越难锁定时间越长。0.35是一个比较好的折中占用带宽675kHz锁定时间400个符号EVM 5%。如果对频谱效率要求不高可以用0.5锁定时间缩短到250个符号EVM也稍微好一点。7.3 不同环路带宽下的Costas Loop性能环路带宽锁定时间(符号)相位抖动(度)星座图形态0.00130002点簇紧凑但锁定慢0.00510005点簇适中0.0150010点簇散开0.0510025点簇严重散开Costas Loop的环路带宽和锁定时间、相位抖动是矛盾的。带宽越小锁定越慢但相位抖动越小。0.005是一个比较平衡的值锁定时间1000个符号相位抖动5度对于我的帧长度来说可以接受。8. 这套链路还能怎么玩8.1 从QPSK到16QAM——星座图更密要求更高QPSK跑通之后下一步自然是16QAM。16QAM的星座点有16个对EVM的要求更高。QPSK的EVM在8%以下就能工作16QAM要求EVM在4%以下否则相邻星座点会混淆。要跑16QAM首先得把AD9361的线性度调好。AD9361的发射通道有数字预失真DPD功能可以补偿功率放大器的非线性。但P201Pro的发射通道没有外置PA所以DPD用不上。只能靠降低发射功率来保证线性度。其次16QAM对载波同步的要求更高。Costas Loop的相位抖动必须控制在2度以内否则星座点会旋转到相邻象限。这意味着环路带宽要调小锁定时间会变长。可能需要用判决反馈的方式来做载波同步而不是简单的Costas Loop。8.2 从单载波到OFDM——多载波带来的新问题OFDM是另一个方向。OFDM把宽带分成多个窄带子载波每个子载波上可以跑QPSK或者16QAM。好处是对频率选择性衰落不敏感坏处是峰均比高对ADC/DAC的动态范围要求高。GNU Radio里有OFDM的例程但那个例程是给仿真用的直接搬到SDR上会有问题。主要问题是同步OFDM对定时同步和频率同步的要求比单载波高得多。定时偏差会导致子载波间的相位旋转频率偏差会导致子载波间的干扰。如果要跑OFDM我建议先用GNU Radio的OFDM例程做仿真把同步算法调好再搬到SDR上。直接上SDR调OFDM很容易卡在同步上看不到任何有意义的结果。8.3 从单向到双向——TDD模式的挑战现在的链路是单向的一个发一个收。如果要做成双向的也就是TDD模式那就需要处理收发切换的问题。AD9361支持TDD模式可以通过控制接口快速切换收发状态。TDD的挑战在于切换时间。AD9361的收发切换时间大约是几微秒对于符号率500kSps来说一个符号是2微秒切换时间占了一个符号周期。这意味着在切换的时候会有几个符号丢失。解决办法是在帧结构里留出保护间隔或者用更高的符号率来缩短切换时间占比。我打算下一步试试TDD模式把P201Pro配置成收发一体做一个简单的ping-pong协议。这个实验能帮我理解TDD系统的时序设计对以后做更复杂的协议有帮助。9. 写在最后的一些个人体会这套链路从开始动手到基本跑通前后花了大约三周时间其中大部分时间花在调试同步上。发射链路其实很简单GNU Radio拖几个模块就能跑接收链路才是真正的挑战Symbol Sync、Costas Loop、帧同步每一个都需要反复调参。我最大的体会是不要迷信例程。GNU Radio自带的QPSK例程能跑通是因为它把很多参数都设好了而且用的是仿真信道没有噪声、没有频偏、没有定时偏差。一旦搬到真实的SDR上这些理想条件都不存在了必须自己理解每个参数的含义才能调出正确的结果。另一个体会是定量测量比定性观察重要得多。星座图好看不好看眼图张开不张开这些都是定性观察。真正能说明问题的是EVM、误码率、帧同步成功率这些定量指标。我后来养成了一个习惯每改一个参数就记录一次EVM和帧同步成功率这样才知道参数改对了还是改错了。最后P201Pro这块板子的性能其实很不错AD9361的灵活性很高只要把滤波器、增益、采样率这几个关键参数调好QPSK链路跑起来很稳。如果你手里也有类似的SDR板卡建议不要只拿它当频谱仪用花点时间搭一条完整的收发链路对理解无线通信的底层原理非常有帮助。
返回列表