4QAM与16QAM调制解调仿真:MATLAB星座图与误码率详解
2026/9/9 0:58:34 网站建设 项目流程

简介:四进制与十六进制正交幅度调制(4QAM、16QAM)的调制解调仿真电路,面向通信原理课程设计、实验报告和数字调制自学者,重点解决调制映射、星座图生成、加噪传输与解调判决的完整建模问题。资源围绕两种调制方式的系统框图、公式说明、各点波形及误码性能展开,并给出仿真结果之间的因果分析,各节点波形与星座图、误码率曲线对照呈现,便于校验实现正确性。压缩包内含16个m文件,分别完成调制、解调、AWGN信道、升余弦滤波、误码率统计和波形绘制等功能,整体仅11KB,结构紧凑,文件命名清晰,适合逐段阅读和二次修改。目前已有5230人学习下载。通过该资源可快速掌握QAM系统Matlab实现思路,理解星座点分布与判决门限对误码率的影响,还能直接修改信噪比等参数观察曲线变化,是课程设计或自学调制的实用参考。 说实话,每次看到“4QAM、16QAM调制解调仿真”这种题目,我的第一反应都不是“这有什么难的”,而是反问一句:你打算把横轴画成SNR还是Eb/N0?你星座图上的点归一化了吗?这两个问题要是没搞定,代码跑出来的曲线多半跟理论值隔着十万八千里。这篇文章就用MATLAB把4QAM、16QAM的调制解调仿真链路完整搭一遍,从比特生成到星座图输出、误码率统计,把每个环节为什么这么写都讲清楚。内容适合正在做通信原理实验、课设作业,或者刚接触QAM仿真、想搞清楚星座图和误码率曲线到底怎么来的人。

1. QAM仿真先搞清楚:4QAM和16QAM在I/Q平面上到底差在哪

1.1 从I/Q坐标理解“星座”是什么

QAM的全称是正交幅度调制,核心思想很简单:把信息同时放到两个正交载波上,一个叫I路(同相分量),一个叫Q路(正交分量)。已调信号写成公式就是:

s(t)=I(t)cos(2πf_c t)-Q(t)sin(2πf_c t)

这里I(t)和Q(t)分别取不同的幅度值,一个符号就能携带多个比特。星座图说白了就是把I幅度当横轴、Q幅度当纵轴,把所有的合法取值点画在复数平面上。每一个点代表一种符号状态,接收端收到信号之后,就看这个符号落在哪个点附近,从而判回对应的比特组合。

我以前给朋友打过一个比方:星座图就像靶子上的若干个靶心,发送端往其中一个靶心射箭,信道里的噪声就是风,会把箭吹得偏离靶心,接收端看到落点之后,去猜你原本瞄准的是哪个靶心。靶心越少、间距越大,越不容易猜错;靶心越多、排列越密,单位时间能传的信息就越多,但抗噪声能力也随之下降。QAM的整个权衡过程,本质就是在这里面找平衡。

1.2 4QAM其实就是QPSK,16QAM是它的密度升级版

很多初学者第一次看到“4QAM”会有点懵,因为通信原理教材里更多讲的是QPSK。实际上4QAM和QPSK是一回事,都是把比特两两分组,每一组映射到四个星座点之一,点的位置一般取(±1, ±1)的四个角。每个符号携带2个比特。

16QAM则是把4个比特合成一个符号,星座图上有16个点,通常按4×4的方阵排列。拿标准的矩形星座来说,I路和Q路各取{-3, -1, 1, 3}四个电平值,两两组合正好16个点。每个符号携带4个比特,比4QAM翻了一倍。

麻烦也随之而来:在星座图平均功率大致相当的前提下,16个点塞进同样大的复数平面范围,点与点之间的欧氏距离明显缩小了。以平均功率归一化后的标准星座为例,16QAM相邻星座点的距离大约只有4QAM相邻点距离的1/3左右。同样的噪声功率下,判决出错的概率自然会升高。这个直觉会贯穿整篇文章:16QAM频谱效率高,但代价是误码率性能变差。仿真做出来之后,你会亲眼看到这条曲线比4QAM的曲线往右偏。

2. 主链路搭建:从比特流到噪声方差,每个环节都不含糊

2.1 发射端:比特分组、格雷映射和功率归一化

写发射端第一件事是生成随机比特序列。我习惯设一个符号块长度,比如每次处理1万个符号,这样一个循环内计算量适中,误码统计也比较好控制。核心代码如下:

k = log2(M); % 每个符号的比特数 bitsPerBlock = 1e4; % 每个符号块的数量 dataBits = randi([0 1], 1, k*bitsPerBlock); % 比特按 k 个一组分组,转成符号索引 symIdx = bi2de(reshape(dataBits, k, []).', 'left-msb');

接下来是星座映射。MATLAB里可以直接用qammod生成标准星座点,但这里有一个非常容易踩的坑:qammod(0:M-1, M)默认输出的星座点平均功率不是1。比如16QAM默认星座点的平均功率是10,4QAM默认星座点的平均功率是2。如果你不归一化就直接拿它调制、加噪声,后面误码率曲线的横轴会整体偏移,而且偏移量还随M变化,查起来非常头疼。

我的做法是先归一化再查表:

constellation = qammod(0:M-1, M, 'gray'); constellation = constellation / sqrt(mean(abs(constellation).^2));

这样四个角点还是 (±1, ±1) 那种相对位置关系,但整体平均功率变成1。后面噪声方差直接用1/EsN0计算,物理意义非常清晰,不容易出错。

那为什么用'gray'参数?格雷编码保证相邻星座点之间只有1个比特不同。信道噪声导致误判时,最常发生的就是判到相邻点,格雷编码可以把这类错误限制在1个比特之内,从而显著降低比特误码率。如果不加这个参数,同样信噪比下的BER会难看不少,而且理论公式还对不上。

2.2 AWGN信道:从Eb/N0换算到复噪声功率

加噪声是整条链路里最需要想清楚的一步。很多人直接把awgn函数拿过来写awgn(txSym, snr),然后发现曲线位置不对,问题就出在“SNR”和“Eb/N0”根本不是同一个量。

信道模型采用加性高斯白噪声。接收信号是:

r(n)=s(n)+w(n)

其中 w(n) 是复高斯随机变量,实部和虚部相互独立,每一维方差都是 N0/2。这样复噪声的总功率就是 N0。

仿真时我们习惯用 Eb/N0 作为横轴,因为它是归一化的比特信噪比,跟调制阶数脱钩,便于对比不同调制方式的性能差异。但星座图上的符号能量是 Es,两者关系是:

Es = Eb × log2(M)

所以加噪声之前要做一步换算:

EbN0 = 10^(EbN0dB/10); EsN0 = EbN0 * k; noiseVar = 1 / EsN0; % 星座平均功率为1时,复噪声总功率 noise = sqrt(noiseVar/2) * (randn(size(txSym)) + 1j*randn(size(txSym))); rxSym = txSym + noise;

这里的sqrt(noiseVar/2)是因为实部和虚部各占一半功率。很多第一次接触复基带仿真的人会在这里卡壳:明明 EsN0 是10倍、20倍的关系,为什么噪声还要除以2?想清楚复信号功率的定义之后,这个困惑自然就解开了。

2.3 接收端:最小欧氏距离判决等价于最大似然判决

解调端最理论化的做法是最大似然判决,但在高斯白噪声信道下,最大似然判决化简之后就是最小欧氏距离判决。也就是说,对每个接收到的符号,计算它和所有星座点的欧氏距离,距离最小的那个星座点就是最可能的发送符号。

dist = abs(rxSym.' - constellation); [~, estIdx] = min(dist, [], 2); estIdx = estIdx - 1; % MATLAB索引从1开始,需要减1还原符号索引 estBits = de2bi(estIdx, k, 'left-msb').';

这段代码里rxSym.'是行向量转置成列向量,constellation是列向量,两者相减会自动扩展成矩阵,每一行对应一个接收符号到所有星座点的距离。用矩阵运算代替循环,仿真速度会快很多。曾经我见过有人用两层for循环遍历符号和星座点,1百万比特要跑好几分钟,改成矩阵之后秒级出结果。MATLAB的数组思维在这里体现得十分明显。

另外,如果题目的要求里写着“仿真电路”,有些人习惯到Simulink里去搭模块,思路其实完全一样:发射端用Random Integer Generator、Rectangular QAM Modulator Baseband,信道用AWGN Channel模块,接收端用Rectangular QAM Demodulator Baseband,星座图直接用Constellation Diagram模块观察。我个人更推荐先用脚本把原理链路跑明白,再去Simulink里搭电路图,因为脚本里每一个中间变量都能打印出来检查,而模块连线出了问题,排查起来往往更费劲。

3. 星座图观察:云团大小、旋转、畸变分别暗示什么

3.1 在不同信噪比下对比星座图

星座图是判断调制链路是否正常的最直观工具。MATLAB里一句scatterplot(rxSym)就能画出来。建议挑几个有代表性的Eb/N0看:0dB、8dB、14dB,分别对应很差、中等、很好的信道环境。

低信噪比时,接收符号围绕真实星座点形成一个一个的大云团,云团之间甚至互相粘连,肉眼很难区分这团点属于哪个星座点。随着信噪比升高,云团明显收缩,16个点的轮廓逐渐清晰。我自己跑仿真时的经验是:对16QAM,Eb/N0到10dB以上时,星座图基本能看出每个点都聚成独立的小团;到了14dB以上,云团半径已经很小,判决几乎不会出错。

为什么云团会缩?因为复高斯噪声的标准差是 σ=√(N0/2),而EsN0越高,N0越小,云团在星座图上的散布范围就越小。云团的半径大概在2~3个σ之间,你可以从图上目测估计噪声功率,反过来验证自己的噪声方差设得对不对。

3.2 星座图出现“异常形态”时怎么排查

星座图不只用来欣赏,它还是定位系统问题的第一手线索。我在调试过程中总结过几种典型异常形态和对应的原因:

  • 星座点在圆周上旋转、呈环状分布:说明接收端存在载波频率偏移。AWGN基带仿真里一般没有这个问题,但如果你引入了真实采集信号或多普勒频移,就会看到这种形态。
  • 星座图整体呈拉伸的椭圆形、不再是正方形:典型原因是I/Q两路增益不平衡,或者I/Q本振幅度不一致。
  • 星座点没有聚成团、而是沿径向发散:常见原因是相位噪声偏大,也可能跟发射端没有做脉冲成型、接收端没有匹配滤波有关。
  • 星座图主体正常,但少数点明显落在错误区域:多数是突发错误或者前端削波造成的,需要回头检查是否有幅度限幅逻辑。

看星座图还有个技巧:把I路和Q路分别画直方图,看分布有几个峰。4QAM的I路直方图应该有两个峰(对应-1和+1),16QAM的I路应该有四个峰(对应-3、-1、+1、+3)。如果峰的数量不对,映射表大概率写错了。

4. 误码率曲线:蒙特卡洛仿真和理论公式的碰撞

4.1 理论误码率边界

误码率曲线画出来后,最好把理论值叠上去做参照。4QAM(即QPSK)在AWGN信道下的比特误码率理论公式是:

BER_4QAM=0.5×erfc(√(Eb/N0))

16QAM在采用格雷映射、高信噪比条件下的近似公式是:

BER_16QAM≈(3/8)×erfc(√(0.4Eb/N0))

这两个公式在MATLAB里可以直接画:

EbN0lin = 10.^(EbN0dB/10); if M == 4 BER_theory = 0.5 * erfc(sqrt(EbN0lin)); elseif M == 16 BER_theory = 3/8 * erfc(sqrt(0.4 * EbN0lin)); end

我的建议是:别只画自己仿真的曲线,理论曲线一定要叠上去。因为理论曲线就是你调试代码的最好参照物。如果仿真曲线和理论曲线的形状对不上,或者整体平移了几个dB,那八成是归一化、噪声功率或者横轴定义出了问题,而不是“系统误差”。

4.2 蒙特卡洛仿真:样本量怎么控制才合理

误码率仿真的本质是蒙特卡洛实验,核心就是“扔足够多的比特,统计扔错的比例”。这里有个工程经验:每个信噪比点至少统计到100个错误比特,曲线才比较平滑。信噪比很高时,误码率可能低到10⁻⁶甚至更低,这时如果还限制总比特数,很可能一个错误都统计不到,曲线就会出现断崖式下降。

我习惯用双重控制条件:

maxErrs = 100; % 每个信噪比点最少统计错误比特数 maxBits = 2e6; % 最大仿真比特数,防止高信噪比下跑太久 while totalErrs < maxErrs && totalBits < maxBits % 每次生成一个符号块,调制、加噪、判决、统计 end

这样做的好处是低信噪比时不用白跑太多数据,高信噪比时也不会因为样本不足而让曲线尾部乱跳。实际跑下来的规律是:Eb/N0从0dB到14dB,计算量主要集中在尾部那几个高信噪比点,一次完整仿真大概也就几十秒到一两分钟。

4.3 两张图的完整绘制逻辑

把星座图和误码率曲线放到同一个仿真脚本里的逻辑是很自然的:星座图在低、中、高三个信噪比各取一个点,误码率曲线在全部信噪比点上跑循环。画误码率曲线时横轴用Eb/N0的dB值,纵轴用对数坐标,这样一个坐标图里能同时看清10⁻¹和10⁻⁵两个数量级的差异。

figure; semilogy(EbN0dB, BER_sim, 'o-', 'LineWidth', 1.5); hold on; semilogy(EbN0dB, BER_theory, 'x--', 'LineWidth', 1.2); grid on; xlabel('Eb/N0 (dB)'); ylabel('BER'); legend('仿真值', '理论值', 'Location', 'southwest');

仿真点和理论曲线贴合得越好,说明链路越可靠。16QAM在高信噪比段,仿真曲线有时会和近似理论公式有轻微分离,这是正常的,因为理论公式本身是近似表达式,低信噪比段本来就不保证高精度。别一看到0.2dB的差异就以为代码写错了。

5. 调试中绕不开的四个细节坑

5.1 横轴用Eb/N0还是Es/N0

这是误码率曲线最经典的坑。如果你用awgn(txSym, snr, 'measured')直接加噪声,这个snr是符号信噪比,不是Eb/N0。直接把横轴标成Eb/N0,16QAM曲线会相对理论曲线偏移10*log10(k)=6dB左右,4QAM偏移3dB。顺着这个线索,你就能理解为什么网上很多仿真代码曲线位置看起来怪怪的。

5.2 星座点功率没归一化

前面强调过,qammod默认输出的星座点平均功率不是1。在没归一化的情况下,噪声方差按1/EsN0算就错了,星座图的云团大小也会比预期的膨胀或者收缩。排查方法很直接:发射端生成完星座点之后,打印mean(abs(constellation).^2),确认它等于1再继续。

5.3 误码统计对不齐

偶尔会出现BER算出来是0.5这种离谱数值,这多半是比特序列没对齐。最常见的原因是reshape的方向写反了。记一下我的习惯:比特流用行向量,reshape的时候写成reshape(dataBits, k, []),这样得到的是k行、每列一个符号;后面de2bi出来的estBits也保持同样的排列方向,逐位对比才不会错位。

5.4 曲线尾部抖动

高信噪比下误码率本身就很小,如果每个点只跑几万个比特,运气好的时候一个错误都没碰到,曲线就会突兀地掉到底端。解决办法就是4.2节说的:用最大错误数和最大比特数双重控制。我自己写仿真脚本时,还会顺手把每个Eb/N0点实际跑过的比特数也记录下来,看到那些末尾剧烈抖动的点,先看看是不是统计比特数太少了,而不是急着调算法。

再分享一个调试顺序上的建议:先把星座图在固定几个信噪比下看顺眼,再做误码率统计。星座图如果形态都不对,误码率曲线十有八九也不对。等链路基础跑通了,再去扩展脉冲成型、信道编码、频偏补偿这些模块,会顺畅得多。QAM仿真的核心并不在于代码有多花哨,而在于每个环节的物理含义都算得清清楚楚,这一点想明白了,换成64QAM、256QAM也只是改改参数的事。

本文还有配套的精品资源,点击获取

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

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

立即咨询