基于P201Pro与AD9361的QPSK收发链路搭建与调试实录
1. 为什么我要折腾这条链路去年年底接手一个短距离无线数据回传的小项目需求说起来简单把一路几百 kbps 的比特流通过空口发出去再在另一台设备上原样收回来误码率控制在可接受范围内。预算有限不可能上商用综测仪那套东西于是很自然地落到 SDR 方案上。手头正好有一块 P201Pro核心是 AD9361 射频捷变收发器配合 GNU Radio 做基带处理这套组合在圈子里已经相当成熟资料也多踩坑的时候不至于孤立无援。这篇文章不是教程更像是我这几个月从零搭链路、调通 QPSK 收发、再到看着星座图上那些点簇慢慢收敛的阶段性记录。里面会涉及 P201Pro 的配置、GNU Radio 里 QPSK 调制解调链的搭建、AD9361 初始化时遇到的那个经典问题0x247 寄存器读出 0x80、CP OVRG 拉高、RX PLL 不锁定以及眼图和星座图怎么用来判断链路健康度。如果你也在用 Pluto SDR 或者类似的 AD9361 平台做 QPSK 收发这篇东西应该能帮你省下不少翻手册和试错的时间。适合谁看刚接触 SDR 收发链路、手里有 P201Pro 或 Pluto 这类设备、想跑通一个完整 QPSK 链路的同学。不需要你精通通信原理但至少得知道 I/Q 是什么、FFT 大概在干嘛。我会尽量把每个参数选择背后的理由讲清楚而不是甩一堆配置让你抄。2. 整体链路设计与方案选型2.1 为什么是 QPSK 而不是其他调制方式选 QPSK 作为切入点理由很实际。第一它足够简单星座图上就四个点I/Q 两路各取 ±1映射关系一目了然调试的时候眼睛盯着星座图就能判断个大概。第二它足够有代表性QPSK 是很多高阶调制的基础把 QPSK 的收发链路跑通了后面上 16QAM、64QAM 只是映射和判决门限的差别框架不用动。第三它对相位噪声和频偏的容忍度比高阶调制好得多在 SDR 这种本振质量参差不齐的平台上QPSK 更容易先跑出个能看的结果给你正反馈。相比之下如果你一上来就搞 16QAM 甚至 64QAM星座图点簇挤在一起频偏稍微大一点就糊成一团你根本分不清是调制的问题、同步的问题还是射频前端的问题。QPSK 四个点分得开哪个环节出问题相对好定位。这是我个人的经验先易后难别跟自己过不去。2.2 P201Pro 与 AD9361 的定位P201Pro 这块板子本质上是一个基于 AD9361 的宽带收发平台。AD9361 是一颗集成度很高的射频捷变收发器覆盖 70 MHz 到 6 GHz通道带宽可配置内置 12 位 ADC/DAC、可编程增益、数字滤波器、AGC 等一大堆东西。它的好处是把射频前端和数字基带接口都集成在一颗芯片里你只需要通过 SPI 配置寄存器通过 LVDS/CMOS 接口收发 I/Q 数据就行。但集成度高也意味着配置复杂。AD9361 有上千个寄存器初始化序列如果不对芯片根本不出数据或者出数据但 PLL 没锁、增益失控。我遇到的那个 0x247 寄存器读出 0x80 的问题就是初始化阶段典型的坑后面会专门讲。选 P201Pro 而不是直接买 ADI 官方评估板主要是成本和可得性。P201Pro 在社区里的资料还算丰富GNU Radio 的 gr-iio 模块能直接跟它对接省去了自己写驱动的时间。Pluto SDR 也是类似思路但 P201Pro 的射频指标和接口灵活性对我来说更合适。2.3 GNU Radio 在链路中的角色GNU Radio 在这条链路里干的是基带处理的活。发射侧比特流进来到星座映射、脉冲成型、上采样然后通过 IIO 接口把 I/Q 样本推给 AD9361。接收侧反过来从 AD9361 拉 I/Q 样本做下采样、匹配滤波、定时同步、载波同步、判决最后还原出比特流。用 GNU Radio 而不是自己写 C/Python 从头撸核心原因是它把通信链路里那些标准模块都做好了你拖拖拽拽就能搭出一个能跑的流图调试的时候还能实时看星座图、眼图、频谱。对于我这种主要目的是验证链路、而不是研究算法实现的人来说GNU Radio 是效率最高的选择。当然如果你想深入理解每个模块内部的算法那还是得去看源码GNU Radio 的模块源码质量参差不齐有些写得挺清楚有些就比较潦草。2.4 链路整体框图与数据流整条链路的数据流是这样的发射端 PC 上跑一个 GNU Radio 流图生成随机比特经过 QPSK 调制、根升余弦脉冲成型、上采样到 AD9361 需要的采样率然后通过 gr-iio 的 FMCOMMS2/Pluto sink 模块送到 P201Pro。P201Pro 把基带 I/Q 上变频到射频频段发出去。接收端另一块 P201Pro 把射频信号下变频回基带 I/Q通过 gr-iio 的 source 模块送进接收流图经过匹配滤波、同步、判决还原比特最后跟发射端的原始比特对比算误码率。这里有个关键点发射和接收的采样率必须匹配而且要考虑 AD9361 的接口速率限制。AD9361 的采样率配置是通过时钟链算出来的涉及到参考时钟、BBPLL、ADC/DAC 时钟分频等一堆参数。如果采样率设得不对要么数据流断断续续要么直接报 overflow/underflow。我一开始就是采样率没算对GNU Radio 里一直报 “L”调了半天才发现是 AD9361 的采样率跟流图里设的不一致。3. 核心细节解析与实操要点3.1 AD9361 初始化那个让人头大的 0x247 寄存器先讲这个因为这是我最开始卡了最久的地方。AD9361 上电后需要一套初始化序列通常是通过 SPI 写一堆寄存器把时钟、PLL、增益、滤波器、接口模式都配好。我用的是社区里流传的一套初始化脚本按理说应该能跑通但实际执行到某一步的时候读 0x247 寄存器一直返回 0x80同时 CP OVRGCharge Pump Overflow被置高RX PLL 死活不锁定。0x247 这个寄存器在 AD9361 手册里是跟 RX 电荷泵和 VCO 校准相关的。读出 0x80 意味着某一位被置起来了结合 CP OVRG 拉高基本可以判断是电荷泵电流设置或者 VCO 校准没通过。我翻了好几遍 AD9361 芯片手册的 Clock and PLL 那一章又对照了 ADI 官方给的初始化流程发现问题出在两个地方。第一参考时钟的频率跟我实际板子上的晶振对不上。初始化脚本里默认参考时钟是 40 MHz但 P201Pro 上实际用的是 40 MHz 没错可我的脚本里 BBPLL 的分频参数是按另一个频率算的导致 BBPLL 输出频率偏离进而影响 ADC/DAC 时钟和 PLL 的参考。这个错误很隐蔽因为 BBPLL 本身可能锁了但下游的 RX PLL 参考频率不对就锁不上。第二VCO 校准的等待时间不够。AD9361 的 VCO 校准需要一定时间初始化序列里如果写完校准触发寄存器之后立刻去读状态很可能读到还没校准完的状态误判为失败。我在校准触发和状态查询之间加了 20 ms 的延时问题就解决了。具体操作上我是这么改的先确认板子上的参考时钟频率然后根据目标采样率反推 BBPLL 和 ADC/DAC 的分频参数。AD9361 的采样率计算公式大致是ADC 采样率 BBPLL 频率 / ADC 分频系数而 BBPLL 频率 参考时钟 × BBPLL 倍频系数。你要先定 ADC 采样率再选 BBPLL 频率最后算倍频系数。这里有个约束BBPLL 频率必须在 715 MHz 到 1430 MHz 之间超出范围芯片不工作。注意AD9361 的初始化序列不是写完就完事每一步写完最好都回读确认。尤其是 PLL 相关的寄存器回读能帮你快速定位是哪一步没生效。3.2 GNU Radio 里 QPSK 调制链的搭建发射链的核心模块就几个Random Source 生成比特Pack K bits 把比特打包成字节QPSK 每个符号 2 比特所以这里 K2Constellation Modulator 做 QPSK 映射Root Raised Cosine Filter 做脉冲成型然后进 IIO sink。这里有几个参数需要仔细选。首先是脉冲成型滤波器的滚降系数excess bandwidth我用的 0.35。这个值越小频谱效率越高但时域拖尾越长对定时同步要求越高越大则相反。0.35 是个比较折中的选择既不会让频谱太宽也不会让同步太难。其次是每符号采样数samples per symbol我设的是 4。这个值决定了上采样倍数也影响后续定时同步的精度。设太小定时同步误差大设太大计算量大而且 AD9361 的采样率可能跟不上。4 是一个常见的选择兼顾精度和效率。Constellation Modulator 的星座点我直接用 GNU Radio 自带的 QPSK 星座点是 (11j)、(-11j)、(-1-1j)、(1-1j)归一化之后幅度是 1。差分编码我开了因为 SDR 链路里相位模糊是个常见问题差分编码能帮你解决 90 度相位模糊代价是误码率稍微高一点但工程上很划算。3.3 接收链的同步与判决接收链比发射链复杂得多核心难点在同步。我用的结构是IIO source 进来先做 AGC如果 AD9361 的硬件 AGC 没开的话然后匹配滤波跟发射端一样的 RRC 滤波器接着是定时同步用 GNU Radio 的 Symbol Sync 模块再是载波同步Costas Loop最后是星座判决和差分解码。定时同步我用的是 Gardner 算法GNU Radio 的 Symbol Sync 模块里可以选。Gardner 对 QPSK 这种调制方式效果不错而且实现简单。关键是环路带宽要选对太宽了跟踪快但噪声大太窄了跟踪慢但稳。我一开始设的环路带宽是 0.01归一化结果发现跟踪速度太慢频偏稍微大一点就跟不上。后来调到 0.05效果好很多。这个值跟你的频偏大小和符号率都有关系需要根据实际情况调。载波同步用 Costas Loop环路带宽也是关键参数。QPSK 的 Costas Loop 有个四象限判决的问题GNU Radio 的模块已经处理好了。我用的环路带宽是 0.01配合定时同步的 0.05整体能稳定工作。这里有个经验载波同步的环路带宽一般要比定时同步的窄因为载波频偏通常变化更慢窄带宽能更好地抑制噪声。判决就是找离接收符号最近的星座点GNU Radio 的 Constellation Decoder 模块直接干这个。差分解码在判决之后做把相位模糊消掉。3.4 眼图与星座图链路健康的晴雨表调试的时候眼图和星座图是我看得最多的两个东西。星座图告诉你符号判决的质量眼图告诉你定时同步的质量。星座图上如果四个点簇又小又圆说明链路很健康信噪比高频偏和相位噪声都控制得好。如果点簇变成了四个椭圆说明 I/Q 两路增益不平衡或者有相位偏差。如果点簇旋转说明有残余频偏。如果点簇糊成一团那可能是同步没锁、信噪比太低或者射频前端饱和了。眼图上眼睛张得越开定时同步越好。如果眼睛闭合说明定时误差大或者码间干扰严重。我调定时同步的时候就是盯着眼图看慢慢调环路带宽和阻尼系数直到眼睛张到最大。提示眼图和星座图要结合起来看。星座图好但眼图差可能是判决门限的问题眼图好但星座图差可能是载波同步的问题。4. 实操过程与核心环节实现4.1 硬件连接与基础环境准备硬件上两块 P201Pro 分别接发射和接收天线通过 USB 接到同一台 PC 上或者两台 PC看你的算力。我用的是同一台 PC因为 GNU Radio 流图跑起来对 CPU 占用不算太高只要采样率不设得太夸张一台机器同时跑收发没问题。天线我用的是普通的 2.4 GHz 全向天线因为 AD9361 覆盖这个频段而且 2.4 GHz 的器件便宜好买。软件环境Ubuntu 20.04GNU Radio 3.10gr-iio 模块libiio 库。安装过程这里不展开社区里教程很多。需要注意的是 gr-iio 的版本要跟 GNU Radio 匹配不然流图加载的时候会报模块找不到。我一开始就是 gr-iio 版本太老跟 GNU Radio 3.10 不兼容后来重新编译了 gr-iio 才搞定。4.2 AD9361 采样率与时钟配置的详细计算这部分是重点因为采样率配不对后面全白搭。我的目标是符号率 100 ksymbol/sQPSK 每个符号 2 比特所以比特率是 200 kbps。每符号采样数设 4所以基带采样率是 400 kHz。但 AD9361 的 ADC/DAC 采样率不能这么低它有个最低限制而且接口速率也有要求。我实际设的 ADC 采样率是 3.84 MHz然后在 GNU Radio 里做数字下变频和抽取把 3.84 MHz 降到 400 kHz。AD9361 的采样率计算假设参考时钟 40 MHzBBPLL 倍频系数设为 24则 BBPLL 频率 40 × 24 960 MHz。ADC 分频系数设为 250则 ADC 采样率 960 / 250 3.84 MHz。DAC 同理。这个配置下BBPLL 在 715-1430 MHz 范围内合法。ADC 采样率 3.84 MHz 也满足 AD9361 的最低要求。在 GNU Radio 里IIO source 的采样率要设成 3.84 MHz然后接一个 Rational Resampler把采样率从 3.84 MHz 降到 400 kHz。抽取倍数 3.84M / 400k 9.6不是整数所以要用 Rational Resampler插值 5抽取 485/48 × 3.84M 400k。这样就把采样率对齐了。注意AD9361 的采样率配置和 GNU Radio 里的采样率必须一致否则会出现 overflow/underflow。我一开始就是这里没对齐GNU Radio 里一直报 “O”调了好久才发现。4.3 发射流图的搭建与参数设置发射流图的结构Random Source输出比特→ Pack K BitsK2→ Constellation ModulatorQPSK差分编码开→ Root Raised Cosine Filter滚降 0.35每符号采样 4→ Rational Resampler插值 5抽取 48→ IIO sinkP201Pro 发射端口。Random Source 的输出是 byte 类型每个 byte 是 0 或 1。Pack K Bits 把 2 个比特打包成一个 byte值域 0-3。Constellation Modulator 根据这个值查星座表输出复数符号。RRC 滤波器做脉冲成型输出还是复数但采样率变成了符号率的 4 倍即 400 kHz。Rational Resampler 把 400 kHz 升到 3.84 MHz然后送给 IIO sink。IIO sink 的配置里RF 频率我设的是 2.4 GHz采样率 3.84 MHz带宽设成跟采样率匹配。增益我一开始设的是手动增益后来发现自动增益更方便就开了 AGC。但 AGC 有个问题如果接收端信号太弱AGC 会把增益拉满噪声也放大反而不好。所以后来我还是改回手动增益根据接收端的信号强度调。4.4 接收流图的搭建与同步调试接收流图IIO sourceP201Pro 接收端口→ Rational Resampler插值 48抽取 5把 3.84 MHz 降到 400 kHz→ Root Raised Cosine Filter跟发射端匹配→ Symbol SyncGardner环路带宽 0.05→ Costas Loop环路带宽 0.01→ Constellation DecoderQPSK→ Differential Decoder → Unpack K BitsK2→ 误码率计算。调试的时候我先把 Symbol Sync 和 Costas Loop 的环路带宽都设得比较宽让环路能快速锁定然后再慢慢收窄看星座图的变化。这个过程需要耐心因为环路带宽和阻尼系数是耦合的调一个会影响另一个。我的经验是先把阻尼系数固定在 1.0临界阻尼然后调环路带宽找到星座图最清晰的那个点再微调阻尼系数。误码率计算我用的是 GNU Radio 的 BER 模块把发射端的原始比特和接收端还原的比特对比。注意这里要保证两边的比特对齐因为链路有延迟直接对比会错位。我是在发射端加了一个延时让接收端的比特跟发射端的比特在时间上对齐。4.5 实测结果与性能分析调通之后实测在 2.4 GHz 频段发射功率大概 0 dBmAD9361 的输出没加外部功放距离 1 米左右误码率能到 1e-4 以下。星座图上四个点簇比较清晰眼图眼睛张得也还行。如果距离拉到 5 米误码率会上升到 1e-3 左右星座图点簇开始变大。这符合预期因为自由空间路径损耗随距离增加信噪比下降。采样率 3.84 MHz 下AD9361 的接口速率是 3.84M × 12 位 × 2I/Q 92.16 MbpsUSB 2.0 的带宽是 480 Mbps理论上够用但实际因为协议开销和 PC 的处理延迟偶尔会有 overflow。后来我把采样率降到 1.92 MHzoverflow 就基本消失了。所以如果你也遇到 overflow先降采样率试试。5. 常见问题与排查技巧实录5.1 AD9361 初始化失败速查表现象可能原因排查方法解决方法0x247 读出 0x80CP OVRG 高参考时钟频率不对确认板子晶振频率核对初始化脚本按实际参考时钟重算 BBPLL 参数RX PLL 不锁定VCO 校准未完成校准触发后加延时再读状态加 20 ms 延时无数据输出接口模式配置错误检查 LVDS/CMOS 配置寄存器按板子实际接口改配置数据断断续续采样率不匹配对比 AD9361 和 GNU Radio 采样率统一采样率增益失控AGC 配置不当检查 AGC 模式和门限改手动增益或调 AGC 门限5.2 GNU Radio 流图常见报错与处理GNU Radio 里最常见的报错就是 “O” 和 “U”分别代表 overflow 和 underflow。Overflow 是发射端数据产生太快AD9361 来不及发underflow 是接收端数据来得太慢GNU Radio 等不及。这两个问题的根源都是采样率不匹配或者 PC 处理能力不够。我的处理顺序是先确认 AD9361 和 GNU Radio 的采样率一致然后看 CPU 占用如果 CPU 跑满了就降采样率或者简化流图比如去掉一些不必要的显示模块。显示模块很吃 CPU尤其是星座图和眼图调试的时候开正式跑的时候关掉。还有一个坑是 gr-iio 的缓冲区大小。默认缓冲区可能太小导致数据流不稳定。我是在 IIO source/sink 的配置里把缓冲区调大具体多大要看你的采样率和 PC 内存我设的是 32768 个样本效果不错。5.3 星座图不收敛的排查思路星座图不收敛原因可能有很多。我的排查顺序是先看频谱确认信号在不在再看眼图确认定时同步锁没锁再看星座图确认载波同步锁没锁最后看误码率确认判决对不对。如果频谱上信号很弱那是射频前端的问题检查增益和天线。如果眼图闭合那是定时同步的问题调 Symbol Sync 的环路带宽。如果星座图旋转那是载波同步的问题调 Costas Loop 的环路带宽。如果星座图点簇很大但不旋转那是信噪比的问题检查发射功率和接收增益。提示调试的时候把发射端和接收端放在同一台 PC 上用线缆直连加衰减器先排除空口的影响把基带链路调通再上天线。这样能省很多时间。5.4 实操心得与避坑建议第一AD9361 的初始化脚本不要拿来就用一定要根据自己板子的实际情况改。参考时钟、接口模式、增益配置这些都可能不一样。我见过有人直接抄 Pluto 的脚本用在 P201Pro 上结果死活不出数据就是因为接口模式不同。第二GNU Radio 的流图要模块化。不要把所有的东西都堆在一个流图里发射和接收分开调试的时候可以单独跑。我一开始就是把收发放在一个流图里结果一出问题就不知道是发射的问题还是接收的问题后来拆开就好多了。第三采样率不要设得太高。AD9361 虽然支持很高的采样率但 USB 接口和 PC 的处理能力是瓶颈。我实测下来3.84 MHz 的采样率在 USB 2.0 下已经有点吃力1.92 MHz 比较稳。如果你用的是 USB 3.0可以适当提高。第四眼图和星座图要实时看但不要一直开着。调试的时候开正式跑的时候关能省不少 CPU。第五误码率计算要注意比特对齐。链路有延迟发射端和接收端的比特不是同时产生的直接对比会错位。我是在发射端加了一个延时模块让两边的比特在时间上对齐这样算出来的误码率才准。6. 后续可以继续折腾的方向这条链路跑通之后能扩展的地方还挺多。一个是提高频谱效率把 QPSK 换成 16QAM 或者 64QAM看看在同样的带宽下能传多少比特。另一个是加信道编码比如卷积码或者 LDPC把误码率再降一降。还有就是做双向链路现在只做了单向双向的话需要处理收发切换和冲突避免。硬件上可以加外部功放和低噪放把通信距离拉远。也可以换更好的天线比如定向天线提高接收信噪比。软件上可以试试用 GNU Radio 的 OFDM 模块做宽带传输。我个人在实际操作中的体会是SDR 链路调试最耗时的不是写代码而是排查那些“看起来应该对但实际不对”的地方。AD9361 的初始化、采样率的匹配、同步环路的参数每一个都可能让你卡半天。但一旦跑通看到星座图上那四个点簇稳稳地待在那里那种成就感还是挺足的。