1. 从一根天线说起为什么我要折腾这条 SDR 收发链路手里这块 P201Pro 已经在我桌上躺了小半年最初买它其实想法很简单——就是想亲眼看看手机里、Wi-Fi 里那些看不见摸不着的电磁波到底长什么样。结果这一折腾就收不住了从最开始用现成软件随便听听调频广播到后来非要自己搭一条完整的收发链路把一串 0 和 1 变成空中的 QPSK 点簇再原封不动地解回来。这个过程踩的坑、绕的弯路比我预想的多得多但收获也远超预期。这篇文章算是对我这段时间工作的一个阶段性复盘。核心目标很明确基于 P201Pro 这块 AD9361 方案的 SDR 硬件配合 GNU Radio 这套开源信号处理框架搭一条从比特流生成、QPSK 调制、上变频发射到接收、下变频、解调、误码统计的完整链路。说白了就是自己动手实现一个最简版的数字通信收发系统把教科书上那些抽象的星座图、眼图、匹配滤波变成屏幕上真实跳动的点簇。适合谁看如果你手里正好有一块 AD9361 系列的 SDR 板子P201Pro、PlutoSDR、各类 AD9361 开发板都算装好了 GNU Radio但一直停留在能收个广播的阶段想往数字通信的深水区走一步那这篇东西应该能帮你省下不少查资料的时间。如果你是完全的新手也没关系我会把每个环节为什么这么做讲清楚你照着抄作业也能跑起来。整条链路涉及的关键词——P201Pro、GNU Radio、SDR、QPSK、AD9361——我会在后面的章节里逐个拆开讲透。先说结论这条链路我最终跑通了在合适的增益和天线条件下近距离同桌面、隔一两米能做到几乎零误码星座图上的四个点簇收得比较干净。但跑通和跑好之间隔着大量细节增益怎么设、滤波器怎么配、采样率怎么选、时钟怎么同步每一个都能让你卡上半天。下面我就按我实际的搭建顺序把整条链路从头到尾捋一遍。2. 链路整体设计与方案选型为什么是这套组合2.1 为什么选 P201Pro AD9361 这套硬件市面上的 SDR 硬件大致分几个档次。最便宜的是那种几十块的 RTL2832U 电视棒只能收不能发带宽也窄玩接收入门可以但要做收发链路就力不从心了。往上是 HackRF、PlutoSDR 这类能收能发覆盖频段也够宽。再往上就是 USRP 这种专业设备性能好但价格劝退。P201Pro 用的是 AD9361 这颗射频捷变收发器这是它的核心价值所在。AD9361 是一颗集成度非常高的 RFIC单芯片里集成了两路独立的发射和接收通道频率覆盖从 70 MHz 到 6 GHz瞬时带宽最高能到 56 MHz。它内部包含了完整的混频器、滤波器、ADC/DAC、增益控制你只需要通过数字接口给它配置参数它就能把基带信号搬到射频上发出去或者把射频信号搬回基带。对于我这种想认真研究数字通信链路的人来说AD9361 提供的灵活性和可控性刚好够用价格又在能接受的范围内。选 P201Pro 而不是原版 PlutoSDR主要是因为它把 AD9361 的接口做得更开放时钟、参考输入输出、外部触发这些引脚都引出来了方便后面做同步实验。而且它的固件和驱动生态跟 PlutoSDR 基本兼容GNU Radio 里的 IIO 模块可以直接识别省去了很多适配的麻烦。2.2 为什么用 GNU Radio 而不是自己写代码理论上你完全可以用 C 或者 Python 从零写一套信号处理程序直接调用 AD9361 的驱动接口。但那样做的工作量是巨大的——你需要自己实现滤波器、重采样、调制解调、同步算法还要处理实时数据流的调度问题。GNU Radio 把这些都封装成了现成的模块你只需要在流程图里把模块连起来配置好参数就能跑起来。更重要的是GNU Radio 的模块是可视化的你能实时看到信号在每一个环节的样子。星座图、频谱图、时域波形这些可视化工具对于理解信号处理过程太重要了。我调试的时候经常就是盯着星座图看根据点簇的形态判断是哪个环节出了问题。这种所见即所得的调试体验是自己写代码很难快速获得的。当然 GNU Radio 也有它的局限比如实时性不如纯 C 代码复杂流程图的 CPU 占用会比较高。但对于学习和原型验证来说它的开发效率优势是压倒性的。等链路跑通、算法验证完毕再考虑用 C 重写成嵌入式版本这是更合理的路径。2.3 为什么选 QPSK 作为调制方式数字调制方式有很多种从最简单的 BPSK、ASK到复杂的 16QAM、64QAM再到 OFDM 这种多载波方案。我选 QPSK 作为切入点理由有三条。第一QPSK 的星座图足够简单只有四个点分布在复平面的四个象限。这意味着它的判决逻辑很直观——接收端只需要判断接收到的符号落在哪个象限就能还原出两个比特。调试的时候星座图上的问题一目了然不会像高阶 QAM 那样点簇挤在一起看不清。第二QPSK 的频谱效率和抗噪性能比较均衡。它在相同带宽下能传两倍于 BPSK 的数据同时对信噪比的要求又不像 16QAM 那么苛刻。对于近距离、低功率的桌面实验来说这个平衡点刚刚好。第三QPSK 是很多实际通信系统的基石。卫星通信、深空探测、4G/5G 的部分信道底层都用到了 QPSK 或者它的变种。把 QPSK 吃透再往上理解更复杂的调制方式就有了扎实的基础。2.4 整条链路的信号流梳理在动手之前我先把整条链路的信号流在纸上画了一遍确保每个环节的输入输出都对得上。发射侧随机比特流 → 串并转换每两个比特一组→ 符号映射映射到四个相位之一→ 脉冲成型滤波把离散符号变成连续波形→ 上采样 → 送入 AD9361 的发射通道 → 上变频到射频 → 天线辐射。接收侧天线接收 → AD9361 接收通道下变频 → 基带采样 → 匹配滤波 → 符号同步找到最佳采样时刻→ 载波同步纠正频偏和相偏→ 判决 → 并串转换 → 还原比特流 → 与原始比特对比统计误码。这条链路里发射侧相对简单因为所有信号都是你自己生成的时序完全可控。接收侧才是难点因为你要从混着噪声、带着频偏和相偏的信号里把原始信息抠出来。后面我会重点讲接收侧的同步问题。3. 核心细节解析AD9361 配置与 GNU Radio 模块选型3.1 AD9361 的关键参数怎么设AD9361 的配置参数很多但对于我们这条链路来说最关键的几个是采样率、本振频率、增益和滤波器带宽。这几个参数设错了后面怎么调都白搭。采样率决定了基带信号的处理带宽。AD9361 的采样率是通过一个复杂的时钟树分频得到的最终反映在发射和接收的采样率上。我最初设的是 1 MSPS每秒百万采样这个速率对于 QPSK 来说够用了。为什么选这个值因为 QPSK 的符号率如果设成 100 kSymbol/s每个符号用 10 个采样点来表示1 MSPS 刚好够。采样点越多脉冲成型的效果越好但数据量也越大对 CPU 的压力越高。1 MSPS 是一个比较舒服的平衡点。本振频率就是你实际发射和接收的中心频率。我选的是 915 MHz 附近的 ISM 频段这个频段在很多地区是免许可的适合做短距离实验。选频率的时候要注意别选到有强干扰的频段否则接收端会被淹没。我一开始选了个 433 MHz结果发现周围有好多遥控器、传感器在用底噪特别高后来换到 915 MHz 就干净多了。增益是发射和接收都要设的。发射增益决定了你发出去的信号有多强接收增益决定了你对微弱信号的放大能力。这两个值不是越大越好。发射增益太大信号会失真星座图会散开接收增益太大会把噪声也放大甚至让 ADC 饱和。我的经验是发射增益先设一个中等值比如 -10 dB接收增益从低往高慢慢加边加边看星座图找到点簇最清晰的那个点。滤波器带宽要和你的信号带宽匹配。AD9361 内部有可配置的模拟滤波器和数字滤波器。如果滤波器带宽设得比信号带宽大太多会引入额外的噪声设得太小会把信号本身削掉。对于 100 kSymbol/s 的 QPSK信号带宽大约是 100 kHz 到 200 kHz取决于脉冲成型滤波器的滚降系数所以滤波器带宽设在 200 kHz 到 500 kHz 之间比较合适。3.2 GNU Radio 里用哪些模块GNU Radio 的模块库非常庞大但搭这条链路用到的核心模块其实就那么几个。发射侧我用了 Random Source 产生随机比特Unpacked to Packed 做串并转换Constellation Modulator 做 QPSK 映射和脉冲成型然后通过 IIO Device Sink 送到 AD9361。接收侧反过来IIO Device Source 从 AD9361 取数据经过 Constellation Decoder 做匹配滤波和判决再通过 Packed to Unpacked 还原比特最后用 Error Rate 模块统计误码。这里有个细节值得说一下。GNU Radio 里的 Constellation Modulator 模块内部其实包含了符号映射、脉冲成型滤波和上采样三个功能。你只需要给它一个星座图对象QPSK 的星座图就是四个点坐标分别是 (11j)、(1-1j)、(-11j)、(-1-1j)归一化之后再设好每个符号的采样点数samples per symbol和滚降系数excess bandwidth它就能输出成型好的基带波形。滚降系数这个参数很多人会忽略但它对信号质量影响很大。滚降系数越小频谱越紧凑但时域波形的拖尾越长对定时同步的要求越高。滚降系数越大频谱越宽但时域波形越干净。我一般用 0.35这是很多实际系统的默认值兼顾了频谱效率和时域特性。3.3 采样率、符号率与采样点数的关系这三个参数是绑在一起的必须一起考虑。关系很简单采样率 符号率 × 每符号采样点数。比如符号率 100 kSymbol/s每符号 10 个采样点采样率就是 1 MSPS。每符号采样点数这个值我建议至少取 4最好取 8 到 16。为什么因为接收端的符号同步算法需要在每个符号周期内找到最佳的采样时刻。如果每符号只有 2 个采样点那同步的精度就非常粗糙判决时很容易采到波形的过渡点上导致误码。采样点越多同步算法能看到的波形细节越多找到最佳采样时刻的精度就越高。但采样点也不是越多越好。采样点多了同样的符号率下采样率就高数据量大CPU 处理不过来而且 AD9361 的采样率是有上限的。所以这是一个权衡。我实测下来每符号 8 个采样点是一个比较甜的点同步效果不错CPU 占用也能接受。3.4 发射和接收的时钟关系这是很多人容易忽略的一个大坑。AD9361 的发射和接收通道如果用的是同一个参考时钟那它们的采样时钟是严格同步的。但如果发射和接收用的是两块独立的板子或者虽然在一块板子上但参考时钟不同源那就会存在采样时钟的频率偏差。这个偏差会导致什么后果发射端每秒钟发 1,000,000 个采样点接收端每秒钟只采 999,999 个那经过一段时间后接收端的采样时刻就会慢慢漂移最终导致符号定时错乱。对于短时间的实验这个漂移可能不明显但如果你要连续跑几分钟甚至几小时就必须解决这个问题。我的做法是发射和接收用同一块 P201Pro这样收发共用同一个参考时钟采样时钟天然同步省去了很多麻烦。如果你非要用两块板子那就需要把一块板子的时钟输出接到另一块板子的时钟输入上让它们共时钟。P201Pro 引出了这些时钟引脚就是为了这种场景准备的。4. 实操过程从零搭起这条链路4.1 环境准备与硬件连接先说硬件。你需要一块 P201Pro或者任何 AD9361 方案的 SDR 板两根合适频段的天线发射一根、接收一根一根 USB 数据线连接电脑还有一台性能不要太差的电脑。我用的是笔记本i7 处理器16G 内存跑 GNU Radio 流程图的时候 CPU 占用大概在 30% 到 50% 之间还算流畅。软件方面你需要装 GNU Radio。我推荐用 GNU Radio 3.10 以上的版本因为它对 IIO 模块的支持更完善。安装方式有很多种Ubuntu 下可以直接 apt 装也可以用 PyBOMBS 编译安装。我图省事直接用的 apt 版本虽然版本不是最新的但稳定性好模块齐全。装完 GNU Radio 之后还要确认 IIO 驱动能识别到你的 P201Pro。插上板子在终端里敲iio_info如果能看到设备信息说明驱动没问题。如果看不到可能是 udev 规则没配好需要手动加一下。这一步网上教程很多我就不展开了。硬件连接上有个细节要注意发射和接收天线不要靠得太近。我一开始把两根天线并排放在桌上距离不到 10 厘米结果接收端被发射信号饱和了星座图糊成一团。后来把接收天线挪到一米开外问题就解决了。如果你要做近距离实验可以在发射端加一个衰减器把信号强度降下来。4.2 发射侧流程图的搭建发射侧的流程图相对简单我按信号流顺序说。第一步用 Random Source 模块产生随机比特。这个模块输出的是 0 和 1 的流速率可以设成你想要的比特率。我设的是 200 kbps因为 QPSK 每个符号承载 2 个比特所以对应的符号率就是 100 kSymbol/s。第二步用 Unpacked to Packed 模块把比特流打包。这个模块的作用是把每 2 个比特合成一个字节方便后面的星座映射模块处理。注意这里的参数要设对bits per chunk 设成 2因为 QPSK 是每 2 个比特映射一个符号。第三步用 Constellation Modulator 模块做调制。这个模块需要你提供一个星座图对象。在 GNU Radio 里你可以用 Constellation Object 来定义 QPSK 的星座图。四个点的坐标我前面说了归一化之后每个点的幅度是 1相位分别是 45 度、135 度、225 度、315 度。模块的 samples per symbol 设成 8excess bandwidth 设成 0.35。第四步把调制好的基带信号送到 IIO Device Sink。这个模块负责跟 AD9361 通信把基带采样点通过 USB 或者网口送到板子上。你需要在这里配置采样率、本振频率、发射增益这些参数。采样率设 800 kSPS因为 100 kSymbol/s × 8 800 kSPS本振频率设 915 MHz发射增益先设 -10 dB。这里有个坑我要提醒一下IIO Device Sink 的采样率参数必须和前面 Constellation Modulator 输出的采样率一致。如果前面输出的是 800 kSPS这里设成 1 MSPS那信号就会被拉伸或者压缩接收端根本解不出来。我一开始就是这里没对齐调了半天才发现。4.3 接收侧流程图的搭建接收侧比发射侧复杂因为多了同步环节。第一步IIO Device Source 从 AD9361 取基带采样。采样率设成和发射侧一样的 800 kSPS本振频率也设 915 MHz接收增益先设 20 dB后面再调。第二步把采样数据送到 Constellation Decoder 模块。这个模块内部做了匹配滤波、符号同步、载波同步和判决。它需要你提供和发射侧相同的星座图对象以及 samples per symbol 参数。匹配滤波器的系数它会根据星座图和滚降系数自动生成。第三步Constellation Decoder 输出的是解调后的符号还需要经过 Packed to Unpacked 模块还原成比特流。注意这里的 bits per chunk 也要设成 2。第四步用 Error Rate 模块统计误码。这个模块需要两路输入一路是接收端还原出来的比特另一路是发射端的原始比特。但问题是发射端和接收端在不同的流程图里怎么把原始比特送过来我的做法是在同一个流程图里同时放发射和接收链路用一条线把发射端的随机比特直接引到 Error Rate 模块。这样虽然发射和接收在同一个进程里但信号是通过 AD9361 实际发出去再收回来的不影响验证效果。4.4 关键参数的调试过程参数调试是这条链路最耗时间的部分。我按我实际的调试顺序说。先调发射增益和接收增益。发射增益我从 -10 dB 开始接收增益从 20 dB 开始然后看星座图。如果星座图上的点簇很散说明信噪比不够可以适当提高发射增益或者接收增益。但要注意增益提高的同时噪声也会被放大而且如果接收增益太高ADC 会饱和星座图会变成一团乱麻。我最后稳定的配置是发射增益 -5 dB接收增益 30 dB。再调符号同步。Constellation Decoder 模块内部有符号同步算法但它的性能跟 samples per symbol 和滚降系数有关。如果同步不上星座图会旋转或者点簇会沿着圆周方向散开。我试过 samples per symbol 取 4、8、16最后发现 8 的效果最好。滚降系数我试过 0.2、0.35、0.50.35 的星座图最干净。最后调载波同步。载波同步主要是纠正收发之间的频率偏差和相位偏差。如果频偏太大星座图会快速旋转根本没法判决。AD9361 的收发共用时钟理论上频偏很小但实际上还是会有一些残余频偏需要载波同步算法来纠正。Constellation Decoder 内部的载波同步环路带宽可以调带宽太窄收敛慢带宽太宽噪声大。我一般用默认值效果就不错。4.5 实测结果与星座图分析调好参数之后我盯着星座图看了很久。四个点簇分布在四个象限每个点簇的半径大概占整个星座图半径的十分之一左右。这个点簇半径其实反映的就是噪声和残余误差的大小。点簇越小说明信号质量越好误码率越低。我实测下来在发射和接收天线距离一米、发射增益 -5 dB、接收增益 30 dB 的条件下误码率能低到 10 的负 5 次方以下基本上跑几分钟才偶尔出一个错。这个结果对于桌面实验来说已经相当不错了。但我也遇到过星座图很糟糕的情况。有一次点簇完全散开四个象限分不清误码率接近 50%等于完全没解出来。排查了半天发现是接收增益设得太高ADC 饱和了。把接收增益降下来星座图立刻就清晰了。还有一次星座图整体旋转点簇位置对不上后来发现是载波同步没收敛调整了环路带宽就好了。5. 常见问题与排查技巧实录5.1 星座图散开、误码率高的排查思路星座图散开是最常见的问题原因可能有很多。我整理了一个排查顺序从最可能的原因开始查。先看接收增益是不是太高。如果接收增益过高ADC 饱和信号被削顶星座图会散得很厉害。判断方法很简单把接收增益降 10 dB如果星座图明显改善那就是增益过高。如果降了之后星座图反而更差那可能是增益不够信号太弱被噪声淹没了。再看发射和接收的采样率是不是一致。如果两边采样率不一致符号定时会漂移星座图会沿着圆周方向散开。检查方法是看星座图上的点簇是不是呈弧形分布如果是基本可以确定是采样率不匹配。然后看本振频率是不是一致。如果收发本振频率有偏差星座图会旋转。偏差越大旋转越快。如果星座图旋转得让你看不清点簇那就是频偏太大了。AD9361 收发共用时钟的话频偏应该很小如果还是旋转可能是配置有问题。最后看天线和连接。天线没接好、馈线断了、接口松动都会导致信号质量急剧下降。这个用眼睛看、用手拧一拧就能排查。5.2 发射端信号发不出去的几种可能发射端的问题相对少但也不是没有。最常见的是 IIO Device Sink 报错说设备忙或者参数不合法。这通常是采样率或者本振频率设成了 AD9361 不支持的值。AD9361 的采样率有范围限制本振频率也有范围限制设之前最好查一下数据手册。还有一种情况是信号发出去了但接收端完全收不到。这时候先确认发射增益是不是设得太低低到信号被噪声淹没了。然后确认天线是不是接在了发射口上有些板子的发射和接收口是分开的接错了就发不出去。最后确认本振频率是不是真的设对了有时候界面上显示的是一个值实际生效的是另一个值需要用频谱仪或者接收端扫频确认。5.3 采样率不匹配导致的符号定时漂移这个问题我单独拿出来说因为它很隐蔽。采样率不匹配不会让星座图立刻散开而是让点簇慢慢漂移。你可能跑了几秒钟发现星座图还挺好跑了一分钟就发现点簇开始沿着圆周滑动跑了几分钟就完全散开了。根本原因是收发采样时钟有微小的频率差。比如发射端实际采样率是 800,000 Hz接收端实际采样率是 800,001 Hz差了一赫兹。这一赫兹的差异经过几百万个采样点之后累积的定时误差就足以让符号采样时刻偏移一个完整的符号周期。解决办法有两个。一是确保收发共用时钟这是最彻底的方案。二是用符号同步算法实时跟踪定时误差Constellation Decoder 内部的符号同步环路就是干这个的。但如果频差太大同步环路也跟不住所以还是共用时钟最靠谱。5.4 常见问题速查表现象可能原因排查方法解决办法星座图散开成一片接收增益过高ADC 饱和降低接收增益 10 dB 观察调低接收增益至点簇清晰星座图沿圆周散开采样率不匹配检查收发采样率设置统一收发采样率共用时钟星座图整体旋转载波频偏过大观察旋转速度检查本振频率调整载波同步带宽误码率接近 50%完全没解出来检查星座图是否有四个点簇逐环节排查从增益开始发射端报设备忙参数超出 AD9361 范围查数据手册确认参数范围调整采样率或本振频率接收端收不到信号发射增益太低或天线接错确认天线接口和增益设置提高发射增益检查天线连接跑一段时间后误码率升高采样时钟漂移观察星座图是否随时间漂移收发共用参考时钟5.5 几个我踩过的坑和独家心得第一个坑是 USB 带宽。P201Pro 通过 USB 跟电脑通信USB 的带宽是有限的。如果你的采样率设得太高比如 10 MSPS 以上USB 可能传不过来数据会丢包。丢包的表现是接收信号里突然出现一段乱码星座图上偶尔冒出一些远离点簇的散点。解决办法是降低采样率或者用 USB 3.0 接口。我用的 800 kSPS 对 USB 2.0 来说绰绰有余从来没遇到过丢包。第二个坑是电脑的电源管理。有些笔记本为了省电会降低 USB 口的供电或者让 CPU 降频。这会导致 GNU Radio 流程图运行不稳定偶尔卡顿。我的做法是把电源计划设成高性能并且把 USB 的省电模式关掉。这个设置很多人会忽略但它确实会影响 SDR 的稳定性。第三个心得是关于调试顺序的。很多人一上来就调同步算法调半天调不好。我的经验是先把增益调好让星座图上的点簇尽可能清晰然后再调同步。增益没调好的时候同步算法是在一堆噪声里找信号怎么调都调不好。增益调好了信号清晰了同步算法很容易就锁定了。第四个心得是关于记录实验条件的。我一开始不记录调着调着就忘了上次是什么参数跑通的。后来我养成了习惯每次跑通一个配置就把采样率、增益、本振频率、滚降系数这些参数记下来。这样下次再搭的时候直接照着抄就行省了很多重复调试的时间。6. 这条链路还能怎么扩展链路跑通之后我脑子里冒出了很多扩展的想法。最直接的是换调制方式把 QPSK 换成 16QAM 或者 8PSK看看星座图变复杂之后同步和判决会遇到什么新问题。16QAM 的星座图有 16 个点对信噪比的要求更高对同步精度的要求也更苛刻是一个很好的进阶练习。另一个方向是加信道编码。现在的链路里比特流是裸奔的没有任何纠错能力。如果加上卷积码或者 LDPC 码就能在同样的信噪比下获得更低的误码率或者在同样的误码率下容忍更差的信道条件。GNU Radio 里有现成的信道编码模块接上去就能用。还可以做双向通信。现在这条链路是单向的发射端只管发接收端只管收。如果做成双向的两端都能收能发就能实现真正的通信协议比如自己定义一个简单的握手、确认、重传机制。这个复杂度会高不少但很有意思。再远一点可以研究一下 AD9361 的快速跳频功能。AD9361 支持在多个频率之间快速切换切换时间可以做到微秒级。如果能实现跳频通信抗干扰能力会大大增强。这个需要深入配置 AD9361 的寄存器比现在这套基带处理要底层得多但也是 AD9361 这颗芯片真正强大的地方。我个人在实际操作中的体会是SDR 这个东西入门容易精通难。你能很快用现成软件收到信号但要想真正理解信号是怎么从比特变成电磁波、又从电磁波变回比特的必须自己动手搭一遍链路。搭的过程中遇到的每一个问题都是对通信原理的一次深刻理解。星座图散开的时候你才会真正明白为什么需要匹配滤波符号定时漂移的时候你才会真正理解采样时钟同步的重要性。这些体会是看书看视频得不到的。最后再分享一个小技巧。如果你在调试的时候卡住了不妨把信号录下来存成文件然后用 GNU Radio 离线分析。离线分析的好处是你可以反复播放同一段信号慢慢调参数不用担心实时性的问题。等离线调通了再切回实时模式往往一次就能成功。这个先离线后实时的调试策略帮我省了很多时间。