☰
GNU Radio+AD9361实现QPSK收发:从bit流到星座图点簇的完整调试指南
2026/10/8 18:08:00 网站建设 项目流程

看着QT GUI里的星座图从一片雪花里慢慢聚拢,最后变成四个边界清晰的小点团时,我才敢确认,这周的软件无线电实验算是真正跑通了。这次实验用的是手头这台P201Pro,一块基于AD9361方案的软件无线电开发板,软件侧全部跑GNU Radio;目标很朴素:让一串随机bit流经过发射链路映射成QPSK符号,发射、接收、解调,最后在对端屏幕上看到四团干净的点簇。看起来就是教科书里的东西,真正在SDR平台上走一圈,坑比想象的多得多。

这篇算是软件无线电实验第四期的阶段性总结,核心就是“从bit流到QPSK点簇”这条链路的完整搭建过程。适合正在搭简单收发系统、或者在GNU Radio里见过一团混沌星座图的朋友参考。无论你用的是P201Pro,还是其他AD9361开发板,只要也是通过SoapySDR接入GNU Radio,整个调试思路基本是通用的。

1. 项目整体架构与设计思路

1.1 为什么用 P201Pro + GNU Radio 这套组合

选这套组合,很大程度上是图省事。P201Pro这类板子把射频前端、正交调制解调器、ADC/DAC都集成在AD9361这颗芯片里,覆盖70MHz到6GHz,带宽从200kHz到56MHz可调。以前做类似实验要自己搭正交混频器、滤波器和基带板,现在一块板子全搞定。类似ADI官方AD-FMCOMMS5-EBZ也是同一颗AD9361方案,操作逻辑和P201Pro基本一致,驱动栈可以复用。

GNU Radio的价值在于,所有基带信号处理都变成流图里的一个个方块。你不需要先精通DSP才能做实验,搭模块的过程本身就是画框图的思路。对于这种验证型实验,GNU Radio的调试效率比Matlab高一大截,流图里的QT GUI仪器可以直接看波形、频谱、星座图。软件定义无线电的“软件”二字,在GNU Radio里体现得淋漓尽致。

还有一个选型理由就是成本。为了测试QPSK收发链路去弄一套专用测试台架不现实,手头这块几千块的板子加一台普通电脑就够了。这套组合非常适合学生做课程实验、工程师验证算法、或者爱好者复现通信原理。

1.2 从 bit 流到 QPSK 点簇:链路到底经历了什么

很多人看星座图觉得玄乎,其实就是把接收端解调后的IQ采样点,以I为横轴、Q为纵轴画在二维平面上。整个链路可以用一条线串起来:

随机bit源 → QPSK符号映射 → RRC脉冲成形 → AD9361的DAC和IQ混频 → 射频发射 → 电磁波传播 → 接收端射频前端和下变频 → ADC得到数字IQ → RRC匹配滤波 → 载波同步 → 定时同步 → 星座图显示。

bit流是二进制的0和1,进入映射器后每两个bit被编成一个符号,对应IQ平面上的一个复数点。QPSK一共四个点,所以叫四相相移键控。发射时这四个点被脉冲成形滤波器处理,变成适合信道传输的波形;到接收端经过匹配滤波和同步恢复,理想情况下采样点应当精确地落在四个理想位置上。真实系统里噪声、干扰、频偏、相位噪声都会让采样点偏离理想位置,于是就成了“点簇”——每一个簇的中心基本对应一个理想星座点,散开的半径反映了信道质量。

这个过程中最容易忽略的点是:星座图上看到的不是调制器里的理想点,而是经过完整收发链路之后、尚未判决的采样值。所以它既反映了发射端的符号映射和滤波质量,也反映了接收端的同步是否精确、增益是否合适、滤波是否匹配。看到四个干净的点簇,说明这条链路每一环都工作正常。

1.3 调试顺序:先回环、再空口

这次实验我坚持了一个顺序:先在GNU Radio内部做数字回环,再用射频电缆短接测试,最后才上双天线空口。数字回环就是把发射流图里的调制后的信号直接通过虚连模块送给接收流图,此时整个链路里没有射频器件、没有空中损耗、没有频偏,如果星座图还是乱的,那一定是基带算法或参数问题。射频电缆回环加入AD9361的发射和接收,可以检查板卡本身的IQ通道、增益设置、本振配置是否正常。最后做空口,加上天线和信道因素。

很多人第一次做实验就迫不及待拿两根天线对打,结果星座图转成一圈或者糊成毛球,根本不知道问题出在哪个环节。按这个三级顺序,出问题时至少能把故障范围缩小到数字域、射频前端还是空间信道。

2. QPSK 映射:bit 是怎么变成星座点的

2.1 一个符号如何携带两个 bit

QPSK的本质是用载波的四种不同相位来携带信息。在一个符号周期内,载波相位可以取45度、135度、225度、315度四个值,每一个相位对应一个2bit组合,所以一个符号就是2bit。在复基带信号里,这四个相位对应的是四个复数点:

相位复数表达(归一化后)对应bit(按常见Gray映射)
45°1+j00
135°-1+j01
225°-1-j11
315°1-j10

这里I路对应复数的实部,Q路对应虚部。发射端把连续的bit流每两位切一组,映射成上面四个复数点之一;接收端拿到采样值后,比较它离哪个理想点最近,再反推回bit。整个过程看起来像查一张表,这张表就是星座图的定义。

GNU Radio里做映射并不需要你自己写复数计算。用一个Random Source输出0到3之间的字节流,然后接Chunks to Symbols模块,在Symbol Table参数里填上四个复数常量,比如(1+j)/√2、(-1+j)/√2、(-1-j)/√2、(1-j)/√2。输入字节0、1、2、3就会依次映射成对应复数符号。这个模块输出的数据流是每个符号一个采样点,也就是符号率采样率。

2.2 Gray映射与功率归一化

上面表格之所以按00、01、11、10来排序,而不是顺时针00、01、10、11,是因为这是Gray码排列。相邻相位对应的bit组合只差1位。一旦某一点因为噪声被推到了相邻象限,判决错误之后只会错1个bit,而不是2个bit都错。在相同误码率指标下,Gray映射能给链路留出更多噪声余量。星座图里四个点正好落在单位圆上,功率被归一化了。为什么要除以√2?因为(1+j)的模是√2,除以√2之后模长变成1。让四个理想点的能量都等于1,方便后面接收端做幅度判决和EVM计算。如果不做归一化,不同调制下的星座点能量不同,比较性能和设置门限时会出现很多混乱。

实际调试时容易踩一个坑:GNU Radio里Constellation Object的符号表顺序和输入字节的映射关系,不同版本可能不一致。以我的GNU Radio 3.10为例,Chunks to Symbols按输入值0、1、2、3对应Symbol Table第0、1、2、3个元素。这个顺序一旦搞错,QPSK也能工作,但星座图上的四个点位会旋转,映射逻辑和接收端解调对不上。所以我建议先用固定序列[0,1,2,3]循环测试,看星座图是否按预期落到四个象限,再换随机bit流。

2.3 差分编码:什么时候要,什么时候可省

做空口实验时接收端通常要用Costas环做载波同步,而Costas环存在一个经典问题:相位模糊。QPSK的Costas环锁相后,恢复的相位可能是正确相位的0度、90度、180度或270度,这会导致解调出来的符号整体旋转,Quadrant整团歪掉。

有经验的工程师会加差分编码:发射端把当前bit和前一bit做异或后再映射,接收端再做逆差分,用相对相位来解调,这样即使相位旋转了整圈,也能恢复出正确数据。但差分编码会带来误码扩散,一个符号解错可能导致后面连续几个符号跟着错。

这次实验我没有加差分编码,原因是调试目标只是观察QPSK点簇,不是做严格误码率测试。只要Costas环收敛正确、星座图点簇位置正常,就达到了目的。如果后续要做真实数据传输,建议加上差分编码,或者用帧同步头来纠正相位模糊。

3. 发射链路设计:GNU Radio流图的搭建与参数计算

3.1 发射流图长什么样

发射端的流图很直接。我为了让每一步都可观测,没有用“一步到位”的Constellation Modulator模块,而是把符号映射、脉冲成形、速率调整分开。

核心模块链是:

Random Source(输出0~3字节) → Chunks to Symbols(QPSK映射) → Interpolating FIR Filter(RRC成形,插值倍数4) → SoapySDR Sink(P201Pro射频输出)。

Random Source要设置成Byte类型,Minimum为0、Maximum为3,这样可以保证它只产生4种符号索引。Chunks to Symbols的Symbol Table填上面那四个归一化复数点。RRC滤波器的抽头系数用firdes.root_raised_cosine函数生成,关键参数为:增益1,采样率4MHz,符号率1MHz,滚降系数0.35,过采样倍数4。插值滤波器设置Interpolation为4,意思是对每个符号插值复制出4个样点,并完成频谱整形。

这里有一个看起来矛盾、但很重要的事项:**当流图最终接的是SoapySDR Sink这种硬件设备时,不要在这个流图里加Throttle模块。**Throttle只用于无硬件时的纯仿真,它的作用是限制流图处理速率。接了硬件之后,板卡的采样时钟才是主时钟,Throttle的节奏会和硬件时钟打架,轻则速率不匹配,重则终端不停刷U/O提示。

3.2 符号率、滚降系数和硬件采样率怎么配对

这三个参数是发射链路里最需要动脑子的地方。QPSK的射频占用带宽大约是(1+α)×Rs,其中Rs是符号率,α是滚降系数。符号率定得越高,带宽越宽;滚降系数定得越大,频谱滚降越平缓,带外衰减越快,但占用带宽也越大。

我这次选择符号率1Msps、α=0.35,算出来的占用带宽是1.35MHz。硬件采样率取4Msps,过采样倍数就是sps=4。为什么不用更高的sps?因为sps越大,数据吞吐量越大,软件对实时性的要求也越高。sps=4到8之间是工程上比较常见的折中。4Msps对于P201Pro来说是很宽松的配置,AD9361的ADC/DAC最高采样率远高于此。

符号率、过采样倍速和硬件采样率之间必须满足:硬件采样率=符号率×sps。最好不要在中间加一个非整数倍的重采样器,否则既增加计算量,又可能引入额外失真。先定符号率,再考虑运放带宽,再算硬件采样率,这个顺序比较合理。如果后面要测试不同符号率,直接改这三个参数并同步更新滤波器抽头就行。

3.3 为什么脉冲成形不能省

直接拿Chunks to Symbols输出的矩形符号脉冲去调制,虽然星座图在理想情况下也能看,但射频信号频谱的旁瓣衰减非常慢,能量会泄漏到相邻频段。对带限信道来说,这就是符号间干扰的直接来源。传输链路的频率选择性会把这些矩形脉冲“弄脏”,接收端采样判决时,前一个符号的能量会残留到后一个符号上,四个点在星座图上会拉出明显拖尾。

脉冲成形的思路是给每个符号一个基带成形脉冲,限制其带宽,同时让相邻符号在采样点上不互相干扰。收发两端都使用RRC滤波器时,级联响应正好是升余弦滤波器,满足Nyquist第一准则。通俗理解:发射端RRC把符号“削”成合适形状,接收端再配一个RRC做匹配滤波,两个“削波”叠加之后,在正确的采样时刻正好只留下当前符号的值,前后的符号贡献是零。

滚降系数α的取值很关键。α越小,频谱越窄越省,但对定时误差越敏感;α越大,频谱越宽,但抗定时误差能力越强。0.35是一个常见工程值,兼顾频谱效率和实现难度。如果你在频谱仪上看到的信号带宽明显窄于按公式算出来的带宽,先检查发射端sps或α是不是设错了。

RRC滤波器抽头怎么生成,很多教程都写得含糊。GNU Radio里用firdes.root_raised_cosine。gain参数设为1,sampling_freq填4MHz,symbol_rate填1MHz,alpha填0.35,sps也就是滤波器中的每个符号抽头数填4。生成出来会是一组浮点抽头,直接给Interpolating FIR Filter用就可以了。注意发射端和接收端的RRC抽头系数必须完全一致,否则级联不再是升余弦,会出现额外ISI。

3.4 射频端增益与中心频率设置

中心频率我选了2.45GHz,这个频段对于P201Pro这类AD9361方案非常成熟,天线也好找。不过2.4GHz附近有WiFi信号,频谱环境不算干净,所以发射增益不要一开始就拉满。我的经验是先用功率增益20dB发射,接收端手动增益调到30dB左右,先观察有没有信号;等链路调通后,再逐渐调节增益,看看星座图变化。

AD9361的发射输出功率并不是越高越好。发射增益过高会导致信号经过放大器后产生非线性失真,星座图外围点会被“压扁”或“顶到极限”,点簇形状不圆润。正确做法是:先用QT GUI Frequency Sink接收端看频谱,确认信号出现了、带宽形状正确,再关注星座图。如果频谱上信号已经很明显但星座图还是很差,通常不是缺发射功率,而是同步或匹配问题。

4. 接收链路与点簇恢复:从 IQ 采样到星座图

4.1 接收端标准流图

接收端和发射端相比要复杂一些,多出的部分全部用于“对抗信道和硬件损伤”。我用的接收链路是:

SoapySDR Source → AGC → RRC匹配滤波 → Costas Loop → Symbol Synchronizer → QT GUI Constellation Sink。

SoapySDR Source设置中心频率2.45GHz、采样率4MHz,与发射端保持一致。AGC模块把接收到的信号幅度归一化到1附近,否则发射增益或接收增益变化时,星座图的缩放比例会变,看起来像是点簇时大时小。RRC匹配滤波器的抽头参数和发射端完全一样。Costas Loop负责载波同步,Symbol Synchronizer负责定时同步,最后星座图窗口把这些恢复好的符号采样点画出来。

这个流图里,最难理解的是Costas Loop和Symbol Synchronizer的顺序。我把Costas放在前面,Symbol Sync放在后面,因为Gardner定时同步在残余频偏较大时表现会变差。先用Costas把大部分频偏纠正掉,定时环路处理起来就稳得多。

4.2 匹配滤波为什么必须和发射端成对

很多人以为接收端滤波就是随便加个低通,这是误区。发射端用了RRC成形,接收端就必须也用RRC做匹配滤波,两个RRC级联后才等效于升余弦。如果只用一个RRC,频响是另一个RRC的前半段,不会形成无ISI的Nyquist形状,星座图上的点会明显散开。

匹配滤波还有一层含义是“最大信噪比接收”。在加性白高斯噪声信道下,接收滤波器的频率响应匹配发射信号的频谱形状时,输出信噪比最大。RRC根升余弦滤波器的平方等于升余弦,恰好同时满足匹配滤波和零ISI两个要求。

我最初调试时就吃了一次亏:发射端α设了0.35,接收端随手设了0.25,星座图点簇虽然没有转圈,但每个点像被上了一层短毛,怎么调增益都不解决。后来把接收端α改回0.35,点簇立刻清晰。遇到点簇模糊,先检查两边RRC参数是否一致,成本几乎为零。

4.3 载波同步与定时同步:把旋转的星座图按停

空口实验中,收发两端的本振不可能完全同频,接收信号会存在载波频偏。频偏的直观表现就是星座图绕着原点旋转。频偏越大,旋转越快。我这次实验里两个板卡的本振频偏大概在几十到一两百赫兹,符号率1Msps时看起来是缓慢旋转,肉眼可见;如果频偏达到几千赫兹,星座图就会变成几圈同心圆环,完全看不出四个点。

Costas Loop在这里的核心作用是用锁相环跟踪残余频偏和相位。模块参数Order设4,对应QPSK。Loop Bandwidth控制环路的噪声带宽:设太大,环路跟踪快但噪声大,点簇会有明显的抖动;设太小,锁相慢,甚至锁定失败。我的经验是先用2π×0.01,如果发现锁定太慢,再逐步放大到2π×0.02左右。

定时同步解决的是采样时刻偏移问题。发射端和接收端的采样时钟来自不同晶振,即使标称都是4MHz,实际频率也会有微小偏差,长时间运行后采样点会渐渐偏离符号最佳判决时刻。Symbol Synchronizer模块里的Gardner定时错误检测算法,不需要事先知道载波相位,很适合这种场景。Samples per Symbol填4,Loop Bandwidth同样从2π×0.01开始调。点簇如果从清晰的四团变成“四个朦胧椭圆”,大概率是定时环路没有锁定或者参数太钝。

4.4 点簇质量的量化:EVM

星座图肉眼看着“还行”是不够的,最好用EVM(误差矢量幅度)来量化。EVM衡量实际接收到的符号点与理想星座点之间的误差矢量,归一化到理想点幅度后取百分比:

EVM(%) = sqrt(mean(|r - s|²) / P_ref) × 100

其中r是实际接收点,s是最近理想星座点,P_ref是理想星座点平均功率。EVM在5%到10%之间,通常说明链路质量不错;超过15%,星座图四个点之间开始有粘连风险,误码率会明显上升。

GNU Radio中没有现成的EVM显示模块,但你可以用Python块写一个简单的计算器,把Constellation Sink的输出去算一下。我的做法是在Symbol Synchronizer后面接一个Python Block,接收复数符号流,对每个符号做判决,计算误差矢量平方,再用滑动窗口取平均值。写这个块比调整整个链路快,因为你可以把EVM数值直接打印到终端。

5. 调试实录:那些让点簇变团的坑

5.1 星座图在旋转:频偏

第一次上射频空口时,四个点的位置明明能看到,但每个时刻都在绕原点转圈,转得不快,像是有人用鼠标拖着星座图在转。这是典型的残余频偏。解决方案是检查Costas Loop的Order、Loop Bandwidth,以及是否放对了位置。我在GNU Radio流图里加了一个Frequency Lock Loop(FLL)做粗频率估计,再接Costas细调,效果更稳定。但是,如果你只是观测点簇,不要求极低EVM,Costas Loop也能单独完成。旋转速度很慢时,说明频偏不大,可以先把Costas的Loop Bandwidth稍微调大一点点,让环路跟上旋转。

5.2 点簇“发棉”:噪声、增益和失配

星座图不旋转、位置也基本正确,但每个点周围糊了一大片,像棉花糖。优先级比较高的排查项是接收增益。接收增益不足时,信号幅度低、信噪比差,EVM变大;增益太高时,AD9361的ADC会削波,点簇的外部轮廓被“削平”,甚至会看到方形的簇边界。看频谱确认信号带宽正常后,从低增益往上微调,找到一个让EVM最小的点。还要检查收发端RRC滚降系数是否一致、Symbol Synchronizer是否锁定。这个排查顺序百试百灵,不要一开始就去改滤波器,而是先排除最底层的信噪比和增益问题。

5.3 只有2个点簇而不是4个

这个现象让不少人怀疑人生。实际上问题通常出在映射环节:Random Source输出的范围没设置好,或者Chunks to Symbols的Symbol Table和输入值没对上。如果数据源只输出0和1两类值,星座图自然只有两个点。先用固定序列循环0、1、2、3去测试,确认四个点都在之后,再检查随机源的范围。另一个可能原因是你用了Constellation Modulator模块,但其内部调制的符号表不是你设的那一组。模块封装越深,调试时越不透明,这也是我坚持把Chunks to Symbols和Interpolating FIR Filter分开的原因。

5.4 硬件欠载/过载:流图节奏乱了

终端刷“U”表示数据溢出,也就是接收端数据处理不过来;“O”表示欠载,发射端送数据不够快。这两个问题通常与流图调度有关。首先检查是不是在硬件流图里加了Throttle,加了就果断删掉。其次看QT GUI的刷新频率,尤其QT GUI Constellation Sink和Frequency Sink同时开启时,绘图消耗很大,可以把FFT大小或刷新率调低。电脑性能不够时,降低符号率到500ksps再试,链路逻辑完全一样,QPSK四个点照样能看到。

5.5 我自己绕过的几个小坑

第一,IQ不平衡会让星座图在某个方向上被拉伸,比如本应是圆的四个点,变成四个椭圆。AD9361有数字校准功能,最好是先用板卡自带的校准脚本跑一遍,再开始实验。第二,接收端如果使用了AGC,不要太贪心把参考电平设得太高,否则信号稍微增强就削波。第三,最后一次调试时,我把发射天线和接收天线靠得很近,信号直接灌进接收前端,星座图反而变得一团糟——接收前端饱和了。拉开几米距离后,点簇恢复正常。空口实验不是功率越大越好,天线近场效应和接收饱和都是真实存在的。

做完这个实验之后再回头看,软件无线电最大的特点就是把原来要动烙铁和示波器的射频工作,变成了GNU Radio里拖拽方块和调参数。调试时我心里一直默念的一个顺序是:先用频域确认信号在不在、带宽对不对,再用时域看波形有没有包络分明,最后才看星座图和EVM。把问题分层剥开,每一条链路都按这个顺序查,大多数坑都能定位到具体的环节。这次实验的心得就是:不要迷信任何“一步到位”的模块,不透明的东西越少,出问题时越容易找到根源。如果你也卡在点簇转圈或者糊成一片的问题里,按上面这个顺序走一遍,大概率比自己瞎猜参数更快见效。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询