基于P201Pro与GNU Radio的QPSK收发链路实战:从硬件配置到射频联调
1. 项目缘起与整体链路设计1.1 为什么选择 P201Pro 搭配 GNU Radio 这套组合最早接触 SDR 收发链路是从一块 RTL-SDR 接收棒开始的只能收不能发做做频谱扫描、听听广播还行一旦想验证自己设计的调制波形就彻底卡住了。后来陆续试过几款带发射能力的板子要么是驱动封闭、只能跑厂商给的 GUI要么是采样率和射频带宽对不上做 QPSK 这种需要精确相位关系的调制时星座图糊成一团。折腾了一圈之后我把主力平台定在了 P201Pro 这块基于 AD9361 射频捷变收发器的板子上上位机软件用 GNU Radio 来搭。这个选择不是拍脑袋定的。AD9361 这颗芯片在 SDR 圈子里算是久经考验的射频前端2x2 收发通道频率覆盖从 70 MHz 到 6 GHz瞬时带宽可以到 56 MHz12 位 ADC/DAC最关键的是它内部集成了完整的收发链路——混频器、滤波器、增益控制、频率合成器都在片内你只需要通过 SPI 配置好寄存器剩下的交给它自己跑。P201Pro 把 AD9361 和一片 FPGA 封装在一起对外提供标准的接口GNU Radio 通过相应的驱动模块就能直接读写采样数据。这套组合的好处是射频部分足够专业软件部分足够开放中间不用被厂商的私有协议绑架。GNU Radio 这边我选它的理由更直接。它是一个数据流驱动的信号处理框架每个处理模块就是一个 block你用流程图把 source、filter、modulator、sink 连起来它自动帮你处理缓冲区、线程调度、采样率匹配这些琐事。对于 QPSK 这种需要“比特流→符号映射→脉冲成型→上变频→发射→接收→下变频→匹配滤波→符号同步→判决”的完整链路用 GNU Radio 搭起来逻辑非常清晰每一级都能单独抓波形看调试起来心里有底。提示如果你之前只用过 SDR# 这类纯接收软件转到 GNU Radio 会有一个思维方式的转变——从“调参数看结果”变成“搭流程控数据”。建议先花时间把 GNU Radio Companion 里几个基础 block 的输入输出数据类型搞清楚不然后面接错线会浪费很多时间。1.2 整条收发链路的阶段划分这个项目从开始到跑通我大致分成了四个阶段每个阶段有明确的验证目标不建议跳步。第一阶段是环境搭建与硬件连通性验证。目标很简单让 GNU Radio 能识别到 P201Pro能收到一个单音信号能发出去一个单音信号并在频谱仪或者另一台接收机上看到。这个阶段不涉及任何调制解调纯粹是打通数据通路。第二阶段是基带 QPSK 调制解调闭环验证。不经过射频直接在 GNU Radio 内部把比特流调制成 QPSK 符号再解调回比特流确认调制解调算法本身没问题。这一步用到的核心 block 包括 Constellation Modulator、Constellation Decoder、Costas Loop、Clock Recovery MM 等。第三阶段是射频收发联调。把基带信号通过 P201Pro 发射出去再用同一块板子的接收通道收回来或者用另一块板子收观察星座图、误码率、频谱形状。这个阶段会暴露很多实际问题IQ 不平衡、直流偏置、相位噪声、采样时钟偏差等等。第四阶段是性能优化与稳定性测试。调整滤波器参数、增益分配、同步算法参数让链路在一定的信噪比条件下稳定工作记录不同配置下的误码率曲线。整个链路的核心数据流是这样的上位机生成随机比特流经过差分编码、星座映射、脉冲成型滤波变成基带 IQ 采样通过驱动送到 AD9361 的发射通道经过 DAC、混频、放大后从天线发射。接收端反过来天线收到的信号经过低噪放、混频、ADC 变成数字 IQ 采样送回上位机做匹配滤波、定时同步、载波同步、判决恢复出比特流。2. 核心细节解析与实操要点2.1 AD9361 的 profile 配置到底在配什么很多人拿到 P201Pro 之后第一步就卡在 AD9361 的初始化上。厂商通常会提供一个 profile 文件里面是一大堆寄存器的值你直接加载进去就能工作。但如果你想改采样率、改射频带宽、改滤波器系数就必须理解这些寄存器在干什么。AD9361 的配置核心是几个关键参数采样率、射频带宽、本振频率、增益模式。采样率决定了数字基带的速率射频带宽决定了模拟前端滤波器的截止频率本振频率决定了你工作在哪个频段增益模式决定了是自动增益控制还是手动固定增益。以我自己的配置为例我用的采样率是 2 MSPS射频带宽设成 2 MHz本振频率设在 915 MHz 附近这是一个通用的 ISM 频段适合做短距离实验。为什么采样率选 2 MSPS因为 QPSK 调制符号率我定在 500 kHz每个符号 4 个采样点2 MSPS 刚好够用再高的话数据量太大上位机处理起来吃力再低的话脉冲成型滤波器的过渡带不够频谱会扩散到相邻信道。AD9361 内部有一个可编程的 FIR 滤波器你可以自己设计系数加载进去。我一开始偷懒用了默认配置结果发现发射频谱的带外抑制只有 30 dB 左右邻道泄漏比较明显。后来用 MATLAB 设计了一个 64 阶的根升余弦滤波器滚降系数 0.35把系数导出成 AD9361 能识别的格式加载进去带外抑制做到了 50 dB 以上。注意AD9361 的 FIR 滤波器系数是定点数加载之前一定要确认好位宽和格式不然滤波器响应会完全不对。我第一次加载的时候没注意把浮点系数直接扔进去结果发射出来的信号频谱乱七八糟排查了半天才发现是系数格式的问题。2.2 GNU Radio 里 QPSK 调制链路的 block 选型GNU Radio 自带的 QPSK 调制解调示例不少但直接拿来用往往会遇到各种问题。我把自己用的 block 组合和选型理由整理一下。发射端这边比特源我用的是 Random Source输出 0/1 字节流。然后是Differential Encoder做差分编码。为什么要差分编码因为 QPSK 存在相位模糊问题接收端恢复出来的载波可能有 90 度的整数倍相位旋转如果不做差分编码解调出来的比特会整体翻转或者错位。差分编码把信息承载在相位变化上而不是绝对相位上这样即使有固定的相位旋转解调结果也不受影响。差分编码之后是Constellation Modulator它内部集成了星座映射和脉冲成型。星座点我选的是标准的 QPSK 星座四个点分别在 45 度、135 度、225 度、315 度。脉冲成型滤波器选根升余弦滚降系数 0.35每符号采样数 4。这个 block 的输出就是基带 IQ 采样可以直接送给 SDR sink。接收端这边从 SDR source 出来的 IQ 采样先经过Root Raised Cosine Filter做匹配滤波然后进Clock Recovery MM做定时同步再进Costas Loop做载波同步最后进Constellation Decoder做符号判决再经过差分解码恢复出比特流。这里有一个容易踩的坑Clock Recovery MM 的参数设置。这个 block 的 Omega 和 Gain Omega 参数直接影响定时同步的收敛速度和稳定性。Omega 是每符号采样数的初始估计值我设成 4.0跟发射端一致。Gain Omega 是环路增益设太大收敛快但抖动大设太小收敛慢但稳定。我试过 0.01 到 0.1 的范围最后定在 0.05 左右兼顾收敛速度和稳态抖动。2.3 射频参数的计算与选择过程射频参数的选择不是随便填的背后有一些基本的计算。我以自己用的配置为例把计算过程写出来。发射功率AD9361 的输出功率范围大概是 -10 dBm 到 5 dBm 左右具体取决于频段和增益设置。我一开始设成最大增益结果接收端饱和了星座图完全糊掉。后来把发射增益降到 -5 dBm接收增益设成手动 30 dB链路就正常了。这里的原则是在保证接收端信噪比的前提下发射功率越低越好减少对相邻设备的干扰也避免接收端饱和。采样率与符号率的关系前面提到采样率 2 MSPS符号率 500 kHz每符号 4 个采样点。这个关系不是随便定的。根据奈奎斯特准则采样率至少要大于信号带宽的两倍。QPSK 信号的带宽大约是符号率乘以 (1滚降系数)也就是 500 kHz × 1.35 675 kHz。2 MSPS 的采样率对应的奈奎斯特带宽是 1 MHz刚好覆盖信号带宽留了一定的余量。频率偏移的容忍度AD9361 的本振精度一般在 ±2 ppm 左右在 915 MHz 频段±2 ppm 对应 ±1830 Hz 的频率偏差。Costas Loop 的环路带宽必须大于这个频率偏差才能锁定。我一开始把 Costas Loop 的环路带宽设成 100 Hz结果根本锁不住后来加到 5000 Hz 才稳定。当然环路带宽也不能太大太大会引入更多的噪声影响误码率。3. 实操过程与核心环节实现3.1 环境搭建从零到第一个单音信号先说一下我的软硬件环境。硬件是一块 P201Pro配了两根 915 MHz 的橡胶天线一根发射一根接收。上位机是一台装了 Ubuntu 22.04 的笔记本USB 3.0 接口直连。软件方面GNU Radio 用的是 3.10 版本配套的 gr-iio 模块用来跟 AD9361 通信。安装过程这里不展开网上教程很多。重点说一下验证步骤。装完之后先跑一个最简单的流程图一个IIO Sourceblock中心频率设成 915 MHz采样率设成 2 MSPS增益设成手动 40 dB输出接到一个QT GUI Frequency Sink。运行之后你应该能看到底噪在 -80 dBm 左右波动如果附近有信号能看到明显的峰值。如果什么都看不到先检查天线接好没有再检查增益是不是设得太低。发射通道的验证稍微麻烦一点因为你需要另一台设备来接收。我的做法是用另一块 SDR 接收板或者同一块板子的接收通道来监听。流程图里放一个IIO Sinkblock输入接一个Signal Source产生一个 100 kHz 的单音幅度设成 0.5。运行之后在接收端的频谱图上应该能看到 915.1 MHz 处有一个明显的峰值。如果看不到检查发射增益是不是太低或者本振频率是不是设错了。提示第一次跑通收发链路的时候建议先用单音信号验证不要一上来就搞 QPSK。单音信号的问题排查起来简单得多确认硬件通路没问题之后再上复杂的调制。3.2 基带 QPSK 闭环不经过射频的纯软件验证这一步的目的是确认调制解调算法本身没问题。流程图里不需要任何 SDR 硬件直接用 GNU Radio 的仿真 block 搭一个闭环。发射端Random Source 产生随机比特Differential Encoder 做差分编码Constellation Modulator 做 QPSK 映射和脉冲成型。接收端Root Raised Cosine Filter 做匹配滤波Clock Recovery MM 做定时同步Costas Loop 做载波同步Constellation Decoder 做判决Differential Decoder 做差分解码。最后用一个Error Rateblock 对比发射和接收的比特流算误码率。这个闭环跑通之后误码率应该是 0星座图应该是四个清晰的点。如果星座图是糊的或者误码率很高问题大概率出在同步环节。我一开始跑的时候Clock Recovery MM 的 Omega 参数设错了设成了 2.0结果定时完全对不上星座图是一团乱麻。改成 4.0 之后立刻就正常了。还有一个细节Costas Loop 的环路带宽。在纯软件闭环里因为没有频率偏差环路带宽可以设得很小比如 100 Hz这样噪声抑制效果好。但到了射频环节因为有本振频率偏差环路带宽必须加大。所以我在纯软件验证阶段用的参数到了射频阶段是要调整的。3.3 射频收发联调从单音到 QPSK 星座图这是整个项目最折腾的阶段。基带闭环跑通之后我把发射端接到 IIO Sink接收端接到 IIO Source满心以为能直接看到星座图结果现实给了我一巴掌。第一个问题是直流偏置。AD9361 的零中频架构存在直流偏置接收到的 IQ 采样不是以零点为中心的而是有一个固定的偏移。这个偏移会导致星座图整体平移判决的时候容易出错。解决办法是在接收端加一个DC Blockerblock或者用 AD9361 内部的直流校准功能。我两个都用了效果比较明显。第二个问题是IQ 不平衡。理想情况下 I 路和 Q 路的增益应该完全一致相位差应该正好 90 度。但实际上 AD9361 的 I 路和 Q 路会有微小的增益差异和相位误差导致星座图从正方形变成椭圆形。这个问题可以用IQ Balanceblock 来校正或者用 AD9361 内部的 IQ 校准寄存器。我一开始没注意这个问题星座图一直是椭圆的误码率下不去。后来加了 IQ 校正星座图明显变方了。第三个问题是相位噪声。AD9361 的本振相位噪声在 915 MHz 频段大概是 -80 dBc/Hz 左右偏离载波 10 kHz 处。这个相位噪声会导致星座点围绕理想位置抖动信噪比恶化。这个问题没法完全消除只能通过增大 Costas Loop 的环路带宽来跟踪相位变化但环路带宽大了又会引入更多噪声需要折中。经过反复调整最终的星座图是四个比较清晰的点簇每个点簇的半径大概是理想星座点间距的 15% 左右。误码率在发射增益 -5 dBm、接收增益 30 dB、距离 1 米的条件下可以做到 10^-4 量级。3.4 关键参数配置速查表为了方便复现我把最终稳定工作的参数整理成一张表。参数项取值说明中心频率915 MHzISM 频段适合短距离实验采样率2 MSPS兼顾处理速度和信号带宽射频带宽2 MHz覆盖 QPSK 信号带宽符号率500 kHz每符号 4 个采样点滚降系数0.35根升余弦滤波器发射增益-5 dBm避免接收端饱和接收增益30 dB手动模式保证信噪比Costas 环路带宽5000 Hz跟踪本振频率偏差Clock Recovery Omega4.0与发射端采样率匹配Clock Recovery Gain Omega0.05兼顾收敛速度和稳定性这张表里的参数不是绝对的不同的硬件、不同的环境可能需要微调。但作为一个起点应该能帮你省下不少试错时间。4. 常见问题与排查技巧实录4.1 星座图糊成一团怎么办这是最常见的问题可能的原因有好几个需要逐一排查。先看接收增益是不是太高。如果接收增益设得太大AD9361 的 ADC 会饱和采样值被限幅星座图会变成一团。解决办法是把接收增益降下来或者改用自动增益控制模式。我一开始把接收增益设成 60 dB结果星座图完全没法看降到 30 dB 之后就正常了。再看同步环路是不是锁定了。如果 Costas Loop 的环路带宽太小或者 Clock Recovery 的参数不对同步环路根本锁不住星座图会一直旋转或者抖动。排查方法是把 Costas Loop 的输出接到一个QT GUI Constellation Sink上观察星座图是不是在旋转。如果一直在转说明载波没有同步需要加大环路带宽。如果星座点在四个位置之间跳来跳去说明定时没有同步需要检查 Clock Recovery 的参数。还有一个容易被忽略的原因是发射和接收的本振频率不一致。如果发射端和接收端用的是两块不同的板子两块板子的本振频率可能有偏差。这个偏差如果超过了 Costas Loop 的捕获范围环路就锁不住。解决办法是先用单音信号测一下频率偏差然后在接收端做频率校正。4.2 误码率居高不下的排查思路误码率高但星座图看起来还行这种情况通常是因为信噪比不够或者有某种系统性的损伤。先测信噪比。在 GNU Radio 里可以用Probe SNR或者自己写一个简单的信噪比估计 block。如果信噪比低于 10 dB误码率很难做低。提高信噪比的办法无非是增大发射功率、降低接收增益减少噪声、缩短距离、换更好的天线。如果信噪比没问题但误码率还是高检查差分编码和解码是不是配对。我有一次调试的时候发射端用了差分编码接收端忘了加差分解码结果误码率一直是 0.5等于随机猜。这种低级错误听起来可笑但在流程图复杂的时候真的容易犯。还有一种可能是滤波器不匹配。发射端用的是根升余弦接收端也必须用根升余弦而且滚降系数要一致。如果接收端用了普通的低通滤波器或者滚降系数设错了会引入码间干扰误码率会明显上升。4.3 AD9361 快速跳频时的注意事项AD9361 支持快速跳频可以在微秒级别切换本振频率。我在项目后期尝试过跳频收发踩了一些坑。第一个坑是跳频时的相位连续性。AD9361 在切换频率的时候本振相位是不连续的这会导致接收端的 Costas Loop 失锁。解决办法是在跳频之前先冻结环路跳频之后再重新捕获。GNU Radio 里可以通过消息机制来控制 Costas Loop 的使能端。第二个坑是跳频时的滤波器切换。如果跳频的范围比较大比如从 900 MHz 跳到 2.4 GHz射频前端的滤波器可能需要切换。AD9361 内部有多个滤波器通道可以通过寄存器配置来切换。但如果切换不及时跳频瞬间的信号会被滤掉导致丢包。第三个坑是跳频图案的同步。发射端和接收端必须使用相同的跳频图案而且时间要对齐。我用的是基于 GPS 秒脉冲的同步方案每秒对齐一次跳频间隔设成 1 毫秒。如果时间对齐有偏差接收端会在错误的频率上等待什么都收不到。注意快速跳频对 AD9361 的 SPI 配置速度要求很高。如果 SPI 时钟太慢跳频切换时间会拉长影响跳频速率。建议把 SPI 时钟设到 20 MHz 以上并且提前把频率配置字算好跳频的时候直接写寄存器不要临时计算。4.4 常见问题速查表现象可能原因排查方法解决办法星座图糊成一团接收增益过高导致饱和降低接收增益观察星座图变化接收增益降到 30 dB 左右星座图持续旋转载波未同步观察星座图是否在转加大 Costas Loop 环路带宽星座图四个点跳变定时未同步检查 Clock Recovery 参数调整 Omega 和 Gain Omega误码率 0.5差分编解码不配对检查收发端 block 配置确保收发端都用了差分编解码误码率偏高但星座图正常信噪比不足测量信噪比增大发射功率或缩短距离跳频时丢包跳频图案不同步检查时间对齐用秒脉冲对齐跳频时间发射频谱带外泄漏大FIR 滤波器系数不对检查滤波器系数格式重新设计并加载正确的系数5. 工具链与调试手段的补充说明5.1 GNU Radio 里几个好用的调试 block除了前面提到的 QT GUI Frequency Sink 和 Constellation Sink还有几个 block 在调试的时候特别有用。QT GUI Time Sink可以看时域波形。调脉冲成型滤波器的时候我会把发射端的基带 IQ 接到 Time Sink 上观察滤波后的符号波形是不是平滑的有没有过冲。如果波形有明显的振铃说明滤波器的滚降系数太小需要加大。QT GUI Waterfall Sink可以看频谱随时间的变化。跳频的时候用这个特别直观能看到信号在几个频率之间跳来跳去。如果跳频图案不对瀑布图上会显示得很清楚。Message Debug可以打印消息。GNU Radio 里很多控制是通过消息传递的比如频率切换、增益调整。用 Message Debug 可以看到消息有没有正确发送和接收。File Sink可以把 IQ 采样存成文件方便离线分析。我习惯在接收端加一个 File Sink把原始采样存下来然后用 Python 脚本做更细致的分析比如计算 EVM、画眼图等等。5.2 用 Python 做离线分析的几个技巧GNU Radio 自带的分析工具够用但有时候需要更灵活的分析我就会用 Python 写脚本。读 File Sink 存下来的文件用的是numpy.fromfile注意数据类型要跟 GNU Radio 里的一致通常是complex64。读进来之后可以用matplotlib画星座图、频谱图、时域波形。计算 EVM 的时候先把接收到的符号归一化然后跟理想星座点比较算误差矢量的均方根值。EVM 跟信噪比有直接关系EVM 越小信噪比越高。我一般把 EVM 控制在 10% 以内对应的误码率大概在 10^-4 量级。画眼图的时候需要先做定时同步把符号对齐然后把多个符号周期的波形叠在一起。眼图的张开度反映了码间干扰的程度张开度越大码间干扰越小。5.3 硬件层面的几个小改进软件调得差不多了之后我在硬件上也做了一些小改进效果比较明显。天线原装的橡胶天线增益只有 2 dBi 左右后来换了一根 5 dBi 的棒状天线接收信号强度明显提升误码率降了一个数量级。屏蔽P201Pro 板子上的 AD9361 对干扰比较敏感我用铜箔把射频部分包了一下底噪降了大概 3 dB。供电USB 供电的纹波比较大后来换了一个线性电源给板子供电相位噪声有改善星座点更集中了。散热AD9361 在满功率发射的时候发热比较明显温度升高之后增益会漂移。我加了一个小散热片长时间跑的时候稳定性好了很多。这些硬件上的改进看起来不起眼但在追求低误码率的时候每一点信噪比的提升都很宝贵。6. 后续可以继续折腾的方向链路跑通之后我还在继续折腾几个方向。一个是高阶调制比如 16QAM、64QAM在同样的带宽下提高数据速率但对信噪比的要求更高需要更精细的校准。另一个是OFDM用多个子载波来对抗多径衰落GNU Radio 里有现成的 OFDM 示例可以参考。还有一个是自适应调制编码根据信道质量动态调整调制方式和编码速率在可靠性和吞吐量之间找平衡。另外AD9361 的自动增益控制和自动频率校正功能我还没有深入用目前都是手动配置。后面打算把这部分用起来让链路在动态环境下也能稳定工作。这个项目从开始到跑通前前后后花了大概两个月的时间大部分时间都花在排查各种奇怪的问题上。但每次看到星座图上四个清晰的点簇或者误码率曲线往下掉的时候那种成就感还是很实在的。如果你也在做类似的 SDR 收发链路希望这些经验能帮你少走一些弯路。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →