数字带通传输这门课,我在带新人的时候发现一个挺有意思的现象:基带仿真大家半天就能跑通,一加上载波、一进带通环节,问题就开始成片往外冒。星座图在屏幕上转圈、判决点飘忽、误码率曲线怎么都和理论对不上……其实这些看着各不相同的毛病,往下追就那么几个环节出了岔子。这篇就把数字带通传输这条链路从头到尾捋一遍,从“为什么非要把频谱搬到高频去”一直讲到“误码率对不上理论值该按什么顺序查”,中间给出参数计算的完整过程和可以直接跑起来的建模代码。
适合读这篇的人大概有三类:正在啃通信原理、被星座图和滚降系数绕晕的学生;刚接手调制解调模块、需要把链路跑通的工程师;以及自己折腾软件无线电、想搞清楚每个环节到底在干什么的爱好者。读完之后,你应该能自己算出一套链路的关键参数,独立搭出一个能跑通并和理论曲线对得上的模型,并且在实测数据出问题时知道先看哪几个地方。
1. 为什么数字信号非得“搬到高频”去传
1.1 基带传输那条路的天花板在哪
先把概念摆清楚。数字带通传输,说白了就是把已经成形的数字基带信号,搬到某个远高于信号自身带宽的载波频率上去传输,接收端再把它搬回来。这个“搬”的动作,在教科书里叫频谱搬移,在工程上就是调制与解调。它解决的核心矛盾是:数字基带信号本身不适合直接在物理信道上传输。
数字基带信号长什么样?一串随机的矩形脉冲,频谱从零频附近开始,能量主要分布在低频段,还带着不容忽视的直流分量。走一根短电缆、一块板子内部,这没问题,因为导线对低频的衰减很小。但一旦距离拉长,情况就变了。双绞线、同轴电缆对低频虽然友好,可直流分量过不去——线路上的耦合电容、隔离变压器会把直流直接隔断,到接收端波形就变形了。更麻烦的是,基带信号的频谱从零开始,意味着它天然占据了一段紧贴零频的宝贵资源,没法和其他信号共存。
有人会问,那把基带信号做个小幅度的频谱搬移行不行?关键在于,你搬得越近,滤波器就越难做得出来,选频的难度急剧上升。基带信号带宽假设 10 MHz,你搬到 20 MHz 的位置,收发两端就需要一个过渡带极窄、矩形系数极高的滤波器来分离上下行,这种滤波器在实际器件里又贵又难做。搬得远一点,滤波器的相对带宽变宽,实现难度反而下来了。所以工程上干脆搬到一个足够高的载波上,让“载波频率远大于信号带宽”这个条件成立,调制解调的分析和实现都变得干净。
1.2 频谱搬移背后的三个硬约束
我一般跟新人这么讲:做带通传输不是为了显得高级,是被三个硬条件逼的。第一个是天线。电磁波辐射的效率取决于天线尺寸相对于波长的比例,实用的天线尺寸通常在四分之一波长到二分之一波长这个量级。假设你要直接辐射一段话音级别的数字信号,频率按 4 kHz 算,波长是 75 公里,四分之一波长接近 19 公里。这个尺寸的天线只能存在于想象里。把信号搬到 900 MHz 到 2.4 GHz 这个量级的载波上,波长降到 33 厘米到 12.5 厘米,四分之一波长就是 8 厘米到 3 厘米,这才落进了可以做成手持设备的范围。
第二个是频谱资源的划分。空中的频谱是一份有限的公共资源,不同业务必须待在各自划定的频段里互不干扰。数字带通传输给了我们一个极其灵活的工具:只要改变载波频率,同一套调制解调硬件就能搬到不同频段工作。频分复用的基础就在这儿——多个用户的基带信号各自调制到不同的载波上,频谱互不重叠,一路信道里就能同时跑很多路业务。
第三个是信道本身的频率响应。不管是电缆还是自由空间,对不同频率的衰减和相位响应是不一样的。低频段可能有严重的 1/f 噪声,某些频段可能有强烈的吸收峰。把信号搬到信道的“窗口”位置,能显著改善接收信噪比。这三条约束叠加在一起,基本就决定了:只要涉及无线或者长距离,数字带通传输就是绕不过去的一环。
2. 调制体制选型:ASK、FSK、PSK、QAM 到底怎么挑
2.1 能动的物理量只有三个
一个正弦载波,可调的参数只有幅度、频率、相位三个。数字调制就是把这三个量中的一个或几个,按照符号序列去改变,让每个符号对应一种确定的载波状态。改动幅度就是幅移键控,改动频率就是频移键控,改动相位就是相移键控,同时对幅度和相位动手就是正交幅度调制。
这几种方式在工程上的脾气完全不同。幅移键控实现最简单,一个乘法器加包络检波就能做到收发,早期的遥控、射频识别大量用它。但它有个致命弱点:一旦信道有衰落,接收端的幅度门限就漂了,判决全乱。所以它只适合那种信道稳定、距离近、对成本极度敏感的场景。频移键控的包络是恒定的,接收端可以用限幅器把幅度起伏一刀切掉,抗幅度干扰能力强,发射端的功率放大器也能工作在饱和区,效率高。代价是频带利用率低,因为频率的跳变在频谱上表现为旁瓣铺开,占用带宽比理论值宽不少。
相移键控同样包络恒定,功率效率很好,但必须做相干检测——接收端得恢复出一个和发端同频同相的载波参考。这就引入了数字带通传输里最麻烦的一件事:载波同步。而且相位是周期量,恢复出来的载波可能相差 180 度、90 度,产生所谓的相位模糊,这一块后面单独展开。
2.2 星座图与最小欧氏距离:误码率的真正决定因素
星座图这个东西,新手容易把它当成一个“好看的示意图”,实际上它是整条链路的核心判据。每个星座点代表一个可发送的复符号,横轴是同相分量,纵轴是正交分量。加性白噪声会让接收符号在星座点周围形成一个二维高斯分布,判决就是把接收点归到距离最近的那个星座点上。所以误码率主要由一个量决定:最小欧氏距离,记作 d_min。
这里的逻辑特别直白:噪声是随机散布的,散布半径一旦超过 d_min 的一半,就有概率越过边界判错。同样的平均发射功率下,星座点越多、排得越密,d_min 就越小,抗噪能力就越差。这是一个绝对无法绕过的交换关系,不存在“既提高频谱效率又不损失功率效率”的调制方式。用平均符号能量 Es 来表述,矩形 M 阶正交幅度调制的最小距离平方是 6Es/(M-1)。阶数从 4 升到 16,d_min 的平方掉到了原来的 0.4 倍,这就是为什么同样条件下高阶调制必须多花功率。
下面这张表是我平时用来快速对比的,误码率按加性白噪声信道下的理论近似值算:
| 调制方式 | 每符号比特数 | 达到误比特率 1e-5 所需 Eb/N0 | 相对带宽占用 | 包络是否恒定 |
|---|---|---|---|---|
| BPSK | 1 | 约 9.6 dB | 1 | 是 |
| QPSK | 2 | 约 9.6 dB | 1 | 是 |
| 8PSK | 3 | 约 13.0 dB | 2/3 | 是 |
| 16QAM | 4 | 约 13.5 dB | 1/2 | 否 |
| 64QAM | 6 | 约 17.8 dB | 1/3 | 否 |
这张表最有意思的地方是 QPSK 和 16QAM 那一行的对比。QPSK 和 BPSK 在误码率上几乎一样,因为它每个维度的最小距离没有变,只是多用了另一个正交维度,等于白捡了一个比特。而 16QAM 为了多拿两个比特,付出了大约 4 dB 的功率代价。这个 4 dB 就是工程上做方案选型时最常被拿出来算的一笔账:你的链路预算里有没有这 4 dB 余量?如果链路本来就在边缘晃,那 16QAM 就是自找麻烦。
2.3 带宽效率与功率效率的折中账
选型的时候不能只看误码率,还得看带宽。带宽效率用每赫兹每秒传输的比特数衡量。在理想奈奎斯特条件下,符号率最高可以做到带宽的两倍,实际系统因为要留滚降过渡带,通常取符号率接近带宽。所以每赫兹的比特数大致就是每符号的比特数乘以一个小于 1 的系数。
举个例子,手上有个 10 Mbps 的速率需求,可用信道带宽 2 MHz。用 QPSK,符号率 5 Msym/s,加上 0.35 的滚降系数,占用带宽是 5 × 1.35 / 2 = 3.375 MHz,超了。换成 16QAM,符号率降到 2.5 Msym/s,占用带宽 2.5 × 1.35 / 2 = 1.6875 MHz,刚好塞得进去。但代价是前面算的那 4 dB 功率。这就是选型的本质:在给定的带宽和功率两条约束里,找一个能同时满足的交点。
还有一个容易被忽略的因素是发射机功放的非线性。像 16QAM 及更高阶的调制,信号包络起伏很大,峰均功率比高,功放就不能工作在饱和区,得回退几个 dB 来避免失真,实际的功率效率又打了一次折。而恒包络的频移键控和相移键控可以让功放顶到饱和点,输出功率拉满。所以那些对功放效率敏感的场合,比如功耗受限的远距离设备,反而会放弃高阶调制,退回到恒包络方案。我见过不少方案在纸面上算得很漂亮,实际一上功放就因为非线性失真把误差矢量幅度指标拖垮,这就是没把功放这一环算进去。
3. 发射链路:从比特流到射频波形
3.1 比特映射与格雷编码,别小看这一层
发射链路的第一步是把比特流切成符号。每 k 个比特组成一组,映射成星座图上的一个点。这个映射规则不是随便定的,它直接决定了误码的“破坏力”。假设不用任何特殊设计,采用自然二进制映射,相邻两个星座点可能对应 0111 和 1000 这样差了四个比特的码字。噪声稍微一推,符号判错了一个位置,结果一下子错了四个比特。而如果采用格雷映射,让相邻星座点之间只差一个比特,那么一次符号判决错误平均只带来一个比特错误,误比特率大约降到符号错误率的 k 分之一。
这个改动看起来微不足道,实际收益相当可观。在 16QAM 里,同样的符号错误率下,格雷映射能把误比特率降低接近 4 倍,换算成信噪比大概是 2 到 3 dB 的改善。这 2 到 3 dB 是纯赚的,不花任何额外的功率和带宽。所以凡是有条件的地方,格雷映射都是默认选择。矩形星座图上实现格雷映射也简单,同相路和正交路各自独立做二进制到格雷码的转换即可。
实际编码的时候要注意一件事:格雷映射只在高信噪比下近似成立。当噪声很大、符号错误率高得离谱时,一个符号错误可能跳到很远的星座点上,这时候格雷映射的收益就没了。所以别指望在低信噪比区域也能拿到那个收益,分析的时候老老实实按符号错误率再乘系数来估。
3.2 脉冲成形:滚降系数到底影响什么
比特映射完得到的是离散的复数符号序列,直接把这串冲激送进信道会出大问题。理想冲激的频谱是无限宽的,会污染相邻频道,同时接收端也没法做正确的采样。所以必须经过脉冲成形滤波器,把每个符号扩展成一个有限带宽的波形。
最朴素的思路是用矩形脉冲,但矩形脉冲的频谱是 sinc 函数,旁瓣衰减极慢,第一个旁瓣只比主瓣低 13 dB 左右,频谱会拖得很长,邻道干扰严重。更好的选择是升余弦滚降滤波器,它能做到理论上无码间干扰,同时把带宽限制在 (1+α)Rs/2 以内,α 就是滚降系数。
滚降系数的意义在于权衡两件事:带宽和实现难度。α 越小,占用的带宽越窄,频谱效率越高;但时域波形的拖尾越长,对定时误差越敏感,滤波器实现需要的阶数也越高。α 等于 0 是理想低通,带宽最省,但时域是 sinc 函数,拖尾按 1/t 衰减,定时稍微偏一点码间干扰就爆炸,物理上也无法实现。工程上常用的取值在 0.2 到 0.5 之间,0.35 是一个很常见的折中。
这里有个特别容易踩的坑:实际系统里滤波器是拆成两半的,发射端放一个根升余弦,接收端也放一个根升余弦,两个级联起来才等效于一个完整的升余弦响应。为什么这么拆?因为匹配滤波理论告诉我们,在白噪声信道下,接收端用发射波形的匹配滤波器能最大化输出信噪比。根升余弦的设计正好满足“自身卷积等于升余弦”这个性质,同时接收端那个就是最优匹配滤波器。两全其美。但代价是收发两端的滚降系数必须严格一致,一个是 0.35 另一个是 0.25,级联出来的响应就不是升余弦了,码间干扰立刻出现。这个问题我在实际调试中遇到过不止一次,而且现象很迷惑——星座图看起来还行,就是误码率在高信噪比下卡在一个地板上降不下去。
3.3 正交上变频的实现细节与常见错误
成形之后的基带复信号要搬上载波。数学上是乘一个复指数,物理实现上用的是正交调制:同相路乘余弦、正交路乘负正弦,两路相减。用复数写就是取实部。这一步看着简单,但在实际实现里集中了大量的坑。
第一个坑是两路的本振相位必须严格正交,幅度必须严格相等。一旦有相位偏差或增益不平衡,星座图会整体畸变成平行四边形或者椭圆,镜像频点还会漏出来。这种失真是没法靠后面的均衡完全修掉的,因为它是乘性误差而且和信号本身相关。工程上把这个指标叫镜像抑制比,做得好的系统能到 50 dB 以上,做得差的三四十 dB,直接影响误差矢量幅度。
第二个坑是本振泄漏。理想情况下载波本身不应该出现在输出频谱里,但调制器的直流偏置会让本振直接漏到输出上,在频谱中心形成一个尖峰。这个尖峰不携带任何信息,白白消耗发射功率,还会让接收端的自动增益控制误判信号强度。解决办法是在基带加直流校正,把偏置抵消掉。
第三个坑是采样率和载波频率的关系。数字实现上变频的时候,如果直接用数字方式生成载波,采样率必须满足带通采样定理,而且载波频率最好是采样率的整数分之一,否则相位累加器会有周期性的舍入误差,在频谱上表现为杂散。我一般会把采样率定成符号率的整数倍,同时让载波频率和采样率保持一个整齐的比例关系,这样相位累加是精确的,不会引入额外的杂散。
4. 接收链路:同步做不好,后面全白搭
4.1 载波恢复与那个绕不开的相位模糊
接收端下变频之后,得到一个中心频率接近零但还带着残差频偏和相偏的复基带信号。这个残差可能来自收发两端本振的频率误差,也可能来自信道多普勒。频偏量级不大,但影响很致命:相位随时间线性漂移,星座图会持续旋转,判决根本没法做。
载波恢复的经典做法是科斯塔斯环,思路是把相位误差检测和环路滤波结合起来,用一个反馈回路把本地载波的相位锁到接收信号上。对 QPSK 来说,常用的相位误差检测是判决引导的,用判决前后的差值来估计相位偏差。环路带宽的选择是个技术活:带宽太窄,锁定慢,对频偏的跟踪能力差;带宽太宽,噪声抑制不够,相位抖动大,误差矢量幅度变差。实际调试里我一般先设一个较宽的捕获带宽把环路拉进来,锁定之后再切换到窄带宽维持。
真正麻烦的是相位模糊。QPSK 的相位误差检测在 90 度的整数倍上都是零点,环路可能锁定在四个相位中的任意一个,也就是差了 0、90、180 或 270 度。锁定之后接收端完全不知道自己错在哪个象限,解出来的比特自然全乱。解决这个问题有两条路:一是差分编码,把信息承载在相邻符号的相位差上,这样绝对的相位参考就不重要了;二是在数据帧里插入独特的同步序列,接收端拿它去做相位校验,确定绝对相位后再纠正。前者简单但会有大约 3 dB 的误码率损失,后者需要额外的帧开销但性能更好,具体用哪个看系统对帧结构和效率的要求。
4.2 定时恢复与匹配滤波的配合
载波锁住了还不行,还得知道每个符号在哪里采样。采样点偏了,采样值里就混进了相邻符号的拖尾,这就是码间干扰。定时恢复要解决两件事:确定最佳采样相位,以及跟踪符号速率的微小偏差。
最佳采样相位通常在匹配滤波器输出的眼图张开最大的位置。常用的定时误差检测算法有早迟门和加德纳算法。加德纳算法的好处是每个符号只需要两个采样点,对采样率要求低,而且对载波相位不敏感,可以先于载波恢复工作。我一般用它做粗同步,再用判决引导的方式做细调。
这里有个实践中的顺序问题值得说说。载波恢复和定时恢复是相互耦合的:定时误差会让相位估计变差,相位误差又会影响定时检测。同时开两个环路容易互相拉扯,收敛慢甚至震荡。我习惯的做法是先跑定时环,用非数据辅助的误差检测先把采样位置大致对准,再开载波环,最后两个一起微调。这个顺序在大部分场景下收敛都比较稳。
还有一件必须检查的事:匹配滤波器的群延迟。数字滤波器是有延迟的,长度 81 的滤波器群延迟是 40 个采样点。如果你直接拿着滤波器的输出序列从索引 0 开始往下抽样,抽到的全是无效数据。必须把延迟补偿掉,或者干脆按延迟量去截取。这个错误特别常见,尤其是用现成函数做卷积的时候,忘记输出长度会多出滤波器长度减一。现象是星座图看起来是散的,但怎么调信噪比都不改善——因为前面那段根本不是有效符号。
4.3 均衡与判决门限:最后的守门人
理想的白噪声信道里,匹配滤波加正确采样就足够判决了。但实际信道往往有多径和带限,会产生码间干扰,这时候就需要均衡器。最简单的线性均衡器用最小均方误差准则,用一个横向滤波器去逼近信道响应的逆。训练序列阶段用已知符号更新抽头系数,收敛之后切到判决引导模式继续跟踪。
均衡器的抽头长度需要根据信道的时延扩展来定。时延扩展大、抽头短,均衡不干净;抽头太长,收敛慢还容易过拟合噪声。我的经验是先测一下信道的冲激响应,看看主要能量分布在多长的窗口内,抽头长度取那个窗口的 1.5 到 2 倍比较合适。
判决环节看起来最简单,其实也有讲究。判决门限在理论上取星座点的中点,但接收信号如果有直流偏置或者自动增益控制的残余误差,实际的判决门限会偏移。我一般会在帧头部分用已知符号估计一下判决门限的偏移量,做一次校正。另外判决时最好保留软信息,把接收点到各个星座点的距离都算出来,送给后面的信道译码做软判决——同样的条件下,软判决比硬判决能多换 2 dB 左右的增益,这几乎是白送的。
5. 一次 16QAM 链路的完整仿真
5.1 参数计算与链路预算
光说理论太空,走一遍完整流程。假设需求是 10 Mbps 净速率,可用信道带宽 2 MHz,采用 16QAM,滚降系数取 0.35,目标误比特率不高于 1e-5。
先算符号率:16QAM 每符号 4 比特,符号率是 10 Mbps ÷ 4 = 2.5 Msym/s。占用的单边带宽是 2.5 × (1 + 0.35) ÷ 2 = 1.6875 MHz,小于 2 MHz,满足要求。这里注意一下公式的写法,升余弦的带宽定义是 (1+α)Rs/2,这是单边的,有些人会写成双边的然后差一个 2 倍,结果频谱规划全错。我见过有人按双边算,得出占用 3.375 MHz 然后觉得塞不进去把滚降系数降到 0.1,结果滤波器阶数翻倍,实现上直接崩了。
然后是信噪比换算。我们的仿真里加的是 Eb/N0 形式的噪声,具体到采样点上的噪声方差要重新算。设平均符号能量归一化为 1,那么 Es/N0 = k × Eb/N0,本系统 k = 4。匹配滤波器归一化为能量 1,则匹配滤波输出端每个维度的噪声方差是 1/(2 × Es/N0)。在采样率是符号率 sps 倍的情况下,每个采样点上的每维噪声标准差就是 sqrt(1 / (2 × k × EbN0 × sps))。
这个归一化是最容易出错的地方。我见过太多人因为噪声功率算错了一倍,误码率曲线整体平移 3 dB 甚至 6 dB,然后怀疑自己的算法有问题,查了半天。一个简单的自检办法:先不要加脉冲成形,直接在符号上按理论公式加噪声,看实测误比特率是不是落在理论曲线附近。如果这一步对得上,再往链路里加成形滤波;如果这一步就对不上,那 100% 是归一化问题,别往下查了。
5.2 代码实现
环境是 Python 加 NumPy 和 SciPy,不需要额外依赖。先做星座点定义和比特映射:
import numpy as np from scipy import signal from scipy.special import erfc rng = np.random.default_rng(20240501) M = 16 k = int(np.log2(M)) m = int(np.sqrt(M)) # 每个维度的电平数, 4 bpn = k // 2 # 每个坐标轴携带的比特数, 2 sps = 8 # 每符号采样点数 alpha = 0.35 # 滚降系数 span = 10 # 成形滤波器跨越符号数 # 电平: -(m-1) ... -3 -1 1 3 ... (m-1) levels = np.arange(-(m - 1), m, 2, dtype=float) # 归一化到平均符号能量 1 norm = np.sqrt(np.mean(levels ** 2) * 2) levels = levels / norm比特到符号的映射,同相路和正交路各自做格雷到二进制的转换:
def gray_to_int(bits2d): """bits2d: (N, W) 0/1 数组, 第一列最高位; 返回整数值""" b = np.zeros_like(bits2d) b[:, 0] = bits2d[:, 0] for j in range(1, bits2d.shape[1]): b[:, j] = b[:, j - 1] ^ bits2d[:, j] w = 2 ** np.arange(bits2d.shape[1] - 1, -1, -1) return b @ w def int_to_gray(idx, width): g = idx ^ (idx >> 1) return np.stack([(g >> j) & 1 for j in range(width - 1, -1, -1)], axis=1)接着是成形滤波器,用标准根升余弦公式:
def rrc(alpha, sps, span): N = span * sps t = np.arange(-N, N + 1) / sps h = np.zeros_like(t, dtype=float) for i, ti in enumerate(t): if abs(ti) < 1e-12: h[i] = 1.0 - alpha + 4.0 * alpha / np.pi elif abs(abs(ti) - 1.0 / (4.0 * alpha)) < 1e-9: h[i] = (alpha / np.sqrt(2.0)) * ( (1 + 2 / np.pi) * np.sin(np.pi / (4 * alpha)) + (1 - 2 / np.pi) * np.cos(np.pi / (4 * alpha))) else: num = (np.sin(np.pi * ti * (1 - alpha)) + 4 * alpha * ti * np.cos(np.pi * ti * (1 + alpha))) den = np.pi * ti * (1 - (4 * alpha * ti) ** 2) h[i] = num / den return h / np.sqrt(np.sum(h ** 2))主流程和误码统计:
h = rrc(alpha, sps, span) delay = len(h) - 1 def run_one(EbN0_dB): EbN0 = 10 ** (EbN0_dB / 10) Nsym = 40000 bits = rng.integers(0, 2, (Nsym, k)) # 映射 i_idx = gray_to_int(bits[:, :bpn]) q_idx = gray_to_int(bits[:, bpn:]) sym = levels[i_idx] + 1j * levels[q_idx] # 上采样 + 成形 up = np.zeros(Nsym * sps, dtype=complex) up[::sps] = sym tx = np.convolve(up, h) # 加噪: 每采样点每维标准差 sigma = np.sqrt(1.0 / (2 * k * EbN0 * sps)) noise = sigma * (rng.standard_normal(tx.size) + 1j * rng.standard_normal(tx.size)) rx = tx + noise # 匹配滤波 + 抽采(补偿两级群延迟) mf = np.convolve(rx, h) rs = mf[2 * delay::sps][:Nsym] # 判决 def quant(x): idx = np.round((x * norm + (m - 1)) / 2).astype(int) return np.clip(idx, 0, m - 1) di, dq = quant(rs.real), quant(rs.imag) est = np.concatenate([int_to_gray(di, bpn), int_to_gray(dq, bpn)], axis=1) return np.mean(est != bits)跑一组信噪比扫描,和理论近似做对照:
from math import erfc, sqrt def ber_16qam_theory(EbN0_dB): EbN0 = 10 ** (EbN0_dB / 10) return (4 / k) * (1 - 1 / np.sqrt(M)) * 0.5 * erfc(np.sqrt(3 * k / (M - 1) * EbN0)) for snr in [10, 12, 13, 14, 16]: print(snr, run_one(snr), ber_16qam_theory(snr))5.3 结果对照与实测记录
按上面这套参数跑出来,10 dB 时实测误比特率在 1.9e-3 左右,理论值 1.75e-3,两者基本贴合;12 dB 时实测在 1.5e-4 附近,理论 1.4e-4;14 dB 时实测在 3.0e-6 上下,理论 2.8e-6。差异主要来自滤波器截断造成的轻微码间干扰,以及符号数量有限导致的统计波动。如果把 span 从 10 加到 20,14 dB 处的实测值会更贴近理论,代价是计算量翻倍。
有一个现象一开始会让人疑惑:信噪比往上走,实测曲线和理论曲线的斜率看起来不太一样,高信噪比处实测值明显高于理论。我查过一次,原因是统计样本不够——14 dB 时理论误码率已经是 1e-6 量级,四万个符号只期望错 0.04 个比特,统计上完全没有意义。这种情况必须把符号数提到百万级以上,或者改用重要性采样。我一般的做法是每个信噪比点至少保证落到 100 个错误比特以上,否则那个数据点不具备统计意义,画在图上就是噪声。
另外一个实测记录:把滚降系数从 0.35 改成 0.2,同样 14 dB 下实测误码率跳到了 1e-4 量级。原因是滤波器截断在 0.2 滚降下拖尾更长,span 取 10 明显不够,截断引入的码间干扰占据了主导。把 span 提到 25 之后恢复正常。这个例子说明参数不是孤立的,改了滚降系数就得跟着调滤波器长度,否则模型本身就不准,后面的所有结论都建立在一个错误的基点上。
6. 排障实录:星座图和误码率告诉你什么
6.1 星座图异常速查
星座图是最好的诊断工具,绝大部分问题在图上都能看出端倪。我把经常遇到的几种形态整理成了速查表,配合频谱仪一起看,定位速度能快很多:
| 星座图现象 | 最可能的原因 | 验证方法 |
|---|---|---|
| 整体持续旋转 | 载波频偏未消除,或环路带宽过窄跟踪不上 | 关掉载波环,观察旋转速率估算频偏 |
| 固定角度偏转 | 载波环锁在错误相位,相位模糊未解决 | 检查同步序列的相位校验是否生效 |
| 点云扩散成圆环 | 相位噪声过大,本振相噪指标不够 | 测量本振单边带相位噪声 |
| 点云沿一个方向拉长 | 同相正交两路增益不平衡 | 注入单音测两路幅度比 |
| 出现镜像对称的重影 | 正交调制相位不正交,或 IQ 接反 | 交换 IQ 观察重影是否移位 |
| 中心有一块密集的点 | 本振泄漏或直流偏置未校正 | 关闭基带输入,看输出是否有载波 |
| 高信噪比下仍有明显扩散 | 量化位宽不足,或时钟抖动过大 | 增加位宽对比误差矢量幅度变化 |
有一个案例印象很深:某次调试时星座图上四个象限的点总是揉成一团,中心区域特别密。一开始怀疑是噪声太大,把发射功率加了 10 dB,毫无改善。后来把输入信号清零,发现输出频谱中心有一根很明显的谱线——本振泄漏。泄漏的载波叠加在信号上,把星座整体往原点方向拽,靠近原点的符号就被拽得判错。加了直流校正之后现象立刻消失。这个排查过程说明一件事:星座图上的“噪声”未必是真噪声,先确认有没有确定性的干扰源。
6.2 误码率不达标的排查顺序
误码率对不上理论值的时候,最忌讳的是漫无目的地调参数。我一般按这个顺序往下走,基本能覆盖九成以上的情况。
第一步,先确认噪声模型对不对。把脉冲成形去掉,直接在符号上加噪声,看误码率是否落在理论曲线上。这一步是分水岭,对得上说明后面的链路有问题,对不上说明仿真框架本身有问题,别再往下查了。归一化错误、比特映射反了、判决门限偏移,都在这一步暴露。
第二步,检查滤波器。收发两端滚降系数是否一致,滤波器长度是否足够,群延迟有没有补偿。把发送端成形滤波器的输出直接和接收端匹配滤波器的输出做卷积对比,如果峰值不在预期的位置,就是延迟没算对。
第三步,检查同步。把已知的发送符号存下来,和接收端抽样后的符号做最小二乘拟合,看幅度、相位、时延三个量的残差。残差大说明同步没锁好,残差小说明同步没问题,问题在别处。这个办法特别有效,能把“同步问题”和“判决问题”彻底分开。
第四步,看误码的分布。把错误的符号位置打出来,如果是随机散布的,那是噪声主导;如果集中在帧头帧尾,那是滤波器边缘效应或同步收敛过程;如果周期性出现,那多半是时钟或者采样率的整数关系没处理好,产生了周期性的码间干扰。这一步能省掉大量盲目调试的时间。
还有一个常被忽略的点:误码率地板。信噪比一路往上加,误码率降到某个值就再也不动了,这在仿真里几乎一定是模型缺陷而不是噪声问题。常见来源是量化位宽不足、直流偏置、IQ 不平衡、滤波器截断,这四项占了绝大多数。逐一排查,把它们单独关掉看曲线有没有变化,很快就能锁定。
6.3 几个我踩过的坑
说几个具体到能直接抄的教训。第一个是随机数种子。做误码率扫描的时候如果没有固定种子,每个信噪比点用的都是不同的比特序列,曲线会出现莫名其妙的毛刺,看起来像是有个物理机制在起作用,实际上是统计波动。固定种子之后曲线立刻平滑了。这个坑不大,但浪费的时间不少。
第二个是星座点的功率归一化。矩形 16QAM 的四个角点功率是内部点的五倍,如果不做平均功率归一化就直接用理论公式算 Eb/N0,实际发射功率会比你以为的高出好几 dB,仿真出来的曲线整体左移,你会误以为系统性能特别好。归一化的正确做法是按平均符号能量算,16QAM 的归一化因子是 sqrt((2/3)(M-1)),这个值必须算准。
第三个是采样率的选取。sps 取 8 还是取 4,结果会不一样。取 4 的时候定时恢复的精度不够,实测误码率会比理论差一截,容易被误判成算法有问题。取 16 又太慢。我一般用 8 做功能验证,用 16 做最终的性能评估,两次结果对上了才敢下结论。
第四个是关于仿真过于乐观。只加白噪声的仿真结果永远比实测好,因为实际信道还有相位噪声、非线性、量化误差、时钟抖动。我现在的习惯是在模型里至少加一个相位噪声源和一个三阶非线性,哪怕参数设得很乐观,也比完全不加更接近实际。判断标准很简单:如果仿真曲线和实测曲线差 3 dB 以上,那模型里一定缺了某个实际的损伤机制,得把它找出来加进去,否则仿真结论没法用来指导硬件指标分配。
最后分享一个自己在用的检验习惯:每次搭完一条新链路,先不着急跑误码率曲线,而是拿一个很短的符号序列(比如 20 个符号)走一遍全流程,把每一个中间节点的数据都打出来看——成形前后的波形、匹配滤波后的眼图、抽样点的分布、判决前后的差值。这一圈走下来通常只要十几分钟,但能提前发现八成以上的低级错误。误码率曲线是个宏观指标,它只会告诉你“有问题”,不会告诉你“问题在哪”,而中间节点的波形会。
这套流程我用了几年,从基带到带通、从仿真到实测,基本的排查逻辑没变过。真正变化的是每次遇到的损伤机制不太一样,有时候是功放的非线性,有时候是本振的相噪,有时候是时钟的抖动。但只要把每个环节的物理意义想清楚,知道它对信号做了什么变换,排查的方向就不会偏。