ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

16QAM调制解调Matlab仿真:从星座映射到误码率分析

16QAM调制解调Matlab仿真:从星座映射到误码率分析 很多人刚接触通信系统仿真时接到的第一个练习往往就是16QAM调制解调。这个东西看起来简单无非是比特流映射成符号、加噪声、再判回来但真要把误码率曲线做得和理论吻合、把代码写得能让人一眼看懂里面其实藏着不少细节。这篇博文就围绕“16QAM调制解调仿真”展开用Matlab完整实现一套发送端到接收端的基带仿真链路中间会把方案设计思路、星座映射逻辑、信噪比换算、理论误码率公式推导、完整代码和逐段注释全部讲清楚。适合正在学通信原理、数字通信课程的学生也适合需要快速验证调制解调性能的工程师。1. 先想清楚再写代码项目目标与整体方案设计很多同学拿到仿真任务就直接打开Matlab敲代码结果往往是跑出来一条不知道对不对的曲线也不知道该信仿真结果还是信书上的公式。我自己踩过这个坑之后养成了一个习惯先花半小时把方案想清楚再动手写代码。1.1 为什么选16QAM而不选QPSK或64QAM16QAM属于正交幅度调制家族里“性价比”比较高的一个成员。QPSK每个符号只携带2比特频谱效率偏低64QAM每个符号携带6比特频谱效率高但对信噪比要求非常苛刻。16QAM每个符号携带4比特是两者之间的折中。从仿真角度看16QAM还有一个很大优势它的星座图是规则的4×4方形网格I路和Q路各自是4电平脉冲幅度调制4ASK便于手推理论误码率曲线。64QAM的推导思路完全一样只是把4电平换成8电平计算量稍微大一点QPSK则因为每路只有2电平反而体现不出“二维联合判决”的特点。我在给本科生讲这个实验时通常会建议大家先把16QAM彻底吃透因为从这个点出发往低可以看QPSK往高可以推64QAM往上还可以衔接OFDM中子载波映射。选了16QAM等于用一个例子同时掌握了方形QAM这个大类。1.2 整条仿真链路怎么搭这次仿真采用复基带等效模型不做载波调制也不做脉冲成形把问题聚焦在“调制映射—加噪声—解调判决”这条最核心的链路上。这样做的好处是信噪比和误码率之间的关系非常直接不会因为滤波器时延、同步误差等因素干扰对调制方式本身的理解。完整链路如下生成随机二进制比特流长度固定每个Eb/N0点都使用同一段比特保证对比公平每4个比特为一组转换成0~15的十进制符号序号通过星座映射把符号序号映射到复数平面上的一个点得到调制符号给调制符号叠加上高斯白噪声模拟AWGN信道在接收端对含噪符号做最近邻判决还原符号序号把符号序号逆向解映射回比特流与原始比特比较统计误比特率和误符号率扫描不同的Eb/N0值得到误码率曲线与理论曲线对照这个方案里没有加信道编码也没有加均衡器因为目标就是验证16QAM调制解调本身在AWGN下的极限性能。加了信道编码之后曲线会往下走但那是另一个话题不在本次讨论范围内。2. 调制端设计从比特流到星座图调制端是整个仿真里最直观的部分但也是大家最容易不复盘的部分。很多代码跑通之后就扔一边了实际上“为什么这么映射”才是最有价值的知识点。2.1 星座映射与4bit分组的逻辑16QAM要把4个比特映射到一个复数符号上。为什么是4个比特因为2的4次方等于164比特恰好有16种组合每种组合对应星座图上唯一的点。星座图的排布方式是I路和Q路各取4个电平值。归一化之后这4个电平通常是-3、-1、1、3注意Matlab的qammod函数默认输出平均功率为1所以实际数值按比例缩放。I路和Q路各4种取值两两组合就是16个星座点形成一个4×4的网格。这种方形网格的好处是I路和Q路的判决可以分开做先判I路落在哪个区间再判Q路落在哪个区间相当于两个独立的4ASK判决。这个性质在后面推导理论误码率时非常有用。实际代码里我习惯先把比特流用reshape命令重排成符号组dataBits randi([0 1], 1, numBits); dataSymbolIdx bi2de(reshape(dataBits, k, [])., left-msb);注意Matlab默认按列读取矩阵所以reshape时把k放在第一个参数位置生成的是每列含k个比特的矩阵转置之后每行就对应一个4比特组合。这里如果搞混了字节顺序后面统计误码率会得到完全对不上的结果属于经典坑位。2.2 格雷映射的好处和Matlab里的实现星座点之间的距离有限加噪声之后判决出的符号很可能落在相邻星座点上。如果采用自然二进制映射相邻星座点可能在多个比特位上不同一个符号错误会导致好几个比特错。采用格雷映射后相邻星座点之间只差1个比特符号错误时大概率只带来1个比特错误。用通信领域的行话说格雷映射在高信噪比下能把误符号率近似转换成误比特率除以log2(M)。这一点对16QAM尤其重要因为星座点数目多自然映射和格雷映射的比特误码率差距在低误码区域非常明显。Matlab里做这件事非常省事qammod函数自带Gray选项modSym qammod(dataSymbolIdx, M, gray);解调端用对应的qamdemod也要加gray参数收发两端的映射表必须完全一致。如果把发送端的gray去掉接收端却保留或者反过来误码率会直接飙升到无法理解的程度。我在帮别人排查代码时至少碰到过三次这种低级但致命的错误。2.3 噪声模型与信噪比换算AWGN信道的基带模型是接收信号等于发送信号加上复高斯噪声。Matlab里生成复噪声的方式是noise sigma * (randn(size(modSym)) 1j * randn(size(modSym)));关键是sigma怎么取。qammod默认输出平均功率为1所以每个符号的能量Es等于1。而Es和Eb之间的关系是Es k × Eb其中k是每个符号携带的比特数16QAM的k等于4。因此如果我们想以Eb/N0为横轴画误码率曲线那么Es/N0就等于4倍Eb/N0。仿真时给定了EbN0dB之后先转成线性值再乘以k得到EsN0Lin。噪声方差N0等于1除以EsN0Lin而复噪声实部虚部每个维度分到的方差是N0/2。为什么每个维度只分N0/2因为复噪声的总功率是实部方差加虚部方差两个维度加起来正好等于N0。这个细节决定了信噪比换算的准确性如果这里写错整条曲线会左右平移好几个分贝。很多网上的代码会直接写成sigma 1 / sqrt(2 * 10^(EbN0dB/10))其实就是假定了符号平均功率为1然后拿去生成复噪声。这个写法如果配合k4使用其实暗含了每个符号携带4比特这个条件。换成其它调制阶数时必须重新推导不能照抄。3. 解调端设计与理论误码率的对照解调端乍一看就是把qamdemod调一下没什么可讲的。但如果你要真的理解16QAM的性能边界理论误码率的推导是不能跳过的。3.1 最近邻判决的相干解调16QAM在AWGN信道下的最优解调方式是最近邻判决把收到的含噪复数符号和星座图上所有16个参考点比较距离选择距离最近的那个点作为判决输出。这就是qamdemod内部做的事情。这个判决规则非常好理解可以类比成“地上画了16个圆圈扔一个石子进去判断石子离哪个圆心最近”。因为AWGN的能量在二维平面上是各向均匀的最近邻判决在等概率发送且噪声独立同分布的假设下就是最大似然判决。由于星座图是方形网格最近邻判决在实现上还可以分解成I路和Q路各自的4电平判决。这种分解是计算理论误码率的关键因为4ASK的错误概率比16QAM整体判决的错误概率容易算得多。3.2 16QAM理论误码率公式推导推导16QAM理论误码率最简洁的思路是16QAM可以看作两路独立的4ASK并联。先算一路4ASK的符号错误概率再换算成16QAM的符号错误概率。一路4ASK中4个电平等间隔排列对于中间的2个电平噪声超出判决边界就判错对于两端的2个电平只需要防一侧。综合平均下来4ASK的符号错误概率是P_4ASK ≈ (3/4) × erfc(√(Es/(10N0)))16QAM的符号错误发生在I路判错或者Q路判错但由于两个方向同时判错的概率远小于单方向判错工程近似处理为P_s ≈ 3 × Q(√(Es/(5N0)))其中Q(x)是高斯Q函数在Matlab里直接用qfunc函数。如果用erfc表示Q(x) 0.5 × erfc(x/√2)。格雷映射下每个符号错误平均导致1个比特错误因此P_b ≈ P_s / 4把Es/N0换成4Eb/N0之后就能画出一条以Eb/N0为横轴的理论误比特率曲线。这个公式是近似式在低信噪比时误差稍大但在高信噪比区域和蒙特卡洛仿真结果几乎重合用来验证代码正确性足够了。3.3 仿真统计和理论曲线怎么放一起仿真得到的误码率是离散的观测点理论曲线是一条连续曲线。要把两者放在一起对比横轴必须统一用Eb/N0单位是dB。我习惯用semilogy画纵轴对数坐标因为误码率从0.1到10的负6次方跨越了几个数量级线性坐标根本看不出细节。理论曲线上不需要扫描直接用一组密集的EbN0Theo 0:0.5:14计算即可。仿真曲线只在设定的几个信噪比点上有值点之间用带标记的连线连接。对比时重点关注曲线形状是否一致以及在低误码率区域是否贴合。如果两条线整体平行但水平偏移了几个dB十有八九是信噪比换算出了问题。4. 完整Matlab代码及逐段说明不啰嗦直接上完整代码。我把所有功能写在一个脚本里方便copy到Matlab后直接运行。环境要求是安装了Communications Toolbox因为用到了qammod和qamdemod。4.1 参数设置与随机种子%% 16QAM调制解调仿真AWGN信道误码性能 clear; close all; clc; rng(1); % 固定随机种子保证结果可复现 M 16; % 调制阶数 k log2(M); % 每个符号携带比特数 k4 numBits 1e6; % 每个信噪比点发送的比特数 EbN0dB 0:2:14; % 扫描的Eb/N0范围 numSnr length(EbN0dB); ber zeros(1, numSnr); % 仿真误比特率 ser zeros(1, numSnr); % 仿真误符号率 dataBits randi([0 1], 1, numBits); % 一次性生成全部比特随机种子固定为1这样别人跑你的代码可以得到一模一样的结果。如果你希望每次运行结果不同把rng(1)去掉即可。numBits取1e6是兼顾速度和精度的选择跑一组14个信噪比点大概需要几十秒不同机器会有差异。4.2 调制、加噪、解调主循环for idx 1:numSnr % --- 比特流分组成符号 --- dataSymbolIdx bi2de(reshape(dataBits, k, [])., left-msb); % --- 16QAM调制格雷映射 --- modSym qammod(dataSymbolIdx, M, gray); % --- 加性高斯白噪声信道 --- EbN0Lin 10^(EbN0dB(idx)/10); EsN0Lin k * EbN0Lin; N0 1 / EsN0Lin; % 星座平均功率为1Es1 noiseSigma sqrt(N0 / 2); noise noiseSigma * (randn(size(modSym)) 1j * randn(size(modSym))); rxSym modSym noise; % --- 最近邻判决解调 --- demodSymbolIdx qamdemod(rxSym, M, gray); % --- 符号错误统计 --- symErr sum(demodSymbolIdx ~ dataSymbolIdx); ser(idx) symErr / length(dataSymbolIdx); % --- 比特流恢复与误比特统计 --- demodBits de2bi(demodSymbolIdx, k, left-msb).; demodBits demodBits(:).; bitErr sum(demodBits ~ dataBits); ber(idx) bitErr / numBits; end这一段是整个仿真的骨架。需要特别解释一下de2bi恢复比特流的过程demodSymbolIdx是长度为numBits/k的行向量de2bi把每个符号序号展开成k比特的行向量得到的是numBits/k行k列的矩阵。转置后按列展开正好恢复出原始的比特序列。整个过程在低信噪比时会有少量比特错误这是正常现象。加噪部分的写法有前提条件qammod默认输出平均功率为1因此Es1N01/EsN0Lin成立。如果你自己实现了星座映射且没有做功率归一化这里的Es不再是1就需要额外计算实际平均功率否则噪声功率配置就会出错。4.3 误码率理论曲线与画图%% 理论误码率 EbN0Theo 0:0.5:14; EsN0Theo k * 10.^(EbN0Theo / 10); serTheo 3 * qfunc(sqrt(EsN0Theo / 5)); berTheo serTheo / 4; %% 画图误比特率曲线 figure; semilogy(EbN0dB, ber, bo-, LineWidth, 1.2); hold on; semilogy(EbN0Theo, berTheo, r--, LineWidth, 1.2); grid on; xlabel(Eb/N0 (dB)); ylabel(误比特率 (BER)); legend(仿真值, 理论值); title(16QAM在AWGN信道下的误码性能);理论误码率公式用到了qfunc它是通信工具箱里的标准函数。serTheo公式里的EsN0Theo/5来源于M16时的近似公式P_s≈3Q(√(3Es/((M-1)N0)))。这个公式在M为偶数且方形星座时成立代入M16就是3Q(√(Es/5N0))。4.4 星座图绘制与观察%% 画图接收星座图选一个低信噪比和一个高信噪比点 for plotIdx [1, numSnr] EbN0Lin 10^(EbN0dB(plotIdx)/10); EsN0Lin k * EbN0Lin; N0 1 / EsN0Lin; noiseSigma sqrt(N0 / 2); noise noiseSigma * (randn(size(modSym)) 1j * randn(size(modSym))); tmpSym qammod(bi2de(reshape(dataBits, k, [])., left-msb), M, gray); rxPlot tmpSym noise(1:length(tmpSym)); figure; plot(real(rxPlot), imag(rxPlot), b., MarkerSize, 3); hold on; plot(real(qammod(0:M-1, M, gray)), imag(qammod(0:M-1, M, gray)), ro, ... MarkerSize, 8, LineWidth, 1.5); grid on; axis equal; xlabel(I分量); ylabel(Q分量); title(sprintf(16QAM接收星座图Eb/N0%.0f dB, EbN0dB(plotIdx))); end画星座图时注意axis equal否则横纵坐标比例不一致会把方形星座变成矩形。红色圆圈是参考星座点蓝色点是接收端带噪声的符号。低信噪比时蓝点一大片互相粘连高信噪比时蓝点聚拢在红点周围这个观感和理论上的“噪声云”概念完全对应。5. 仿真结果解读与结论代码跑完之后结果文件里有两条误码率曲线和若干星座图。这些东西不是看一眼就完了值得逐项解读。5.1 星座图随信噪比的变化在Eb/N0等于0dB时接收符号的散点图几乎糊成一片只有靠外侧的点隐约能看出4个角。这是因为噪声功率远大于信号点之间的距离判决器已经接近瞎猜。当Eb/N0升到14dB时16个点各自聚成一小团云团半径明显缩小相邻云团之间有清晰的间隙。这个间隙就是判决裕量误码率低的地方间隙必然足够大。星座图还有一个容易被忽略的细节角落上的4个点周围的噪声云形状更靠近圆形而中间的点被周围邻点包夹实际判决区域是有限多边形。这解释了为什么内点比角点更容易出错也解释了为什么理论误码率推导不能简单按“整个星座是一个大圆”来套。5.2 误码率曲线说明什么仿真得到的误比特率曲线和理论曲线在高Eb/N0区域几乎是重合的。从曲线斜率看Eb/N0每增加2dB误码率大约下降一个数量级这个趋势在所有方形QAM中都是一致的。与QPSK对比要达到同样的误比特率16QAM需要的Eb/N0更高。代价是每个符号多传了2比特。这是通信系统设计中永恒的交换带宽效率换信噪比余量。16QAM之所以广泛应用于有线宽带和部分无线系统正是因为它的工作点可以选在实际信道能够提供足够信噪比的地方。从工程角度看这条曲线的意义在于给出了16QAM在AWGN信道下的性能上界。现实中任何衰落信道、失真、同步误差都会让实际曲线相对这个上界右移。做系统设计时参考这个上界再预留2~3dB余量是很常见的做法。5.3 从这套仿真里最值得记住的三个点第一格雷映射带来的增益是免费的。它不消耗额外功率不需要额外带宽只是改了一张查表就能在高信噪比下把误比特率降低大约log2(M)倍。这也是几乎所有实际QAM系统都采用格雷映射的原因。第二信噪比换算必须自己推一遍。Es/N0和Eb/N0之间差一个log2(M)因子漏掉这个因子的人会得到整整6dB的偏移。我见过很多实验报告里的曲线看起来形状对、位置不对原因就是这里。第三蒙特卡洛仿真在低误码率区域需要海量比特才能获得稳定估计。想看到10的负6次方的误码率至少需要10的8次方量级的比特这在Matlab里跑起来可能很慢。遇到这种情况要么适当降低目标精度要么改用解析近似。6. 常见问题与排查技巧实录这部分是我在亲自写代码以及帮别人调试时积累出来的经验比任何教科书都有现场感。6.1 误码率曲线下不去的几种原因曲线在某个误码率水平上“卡住”不降最常见的原因是统计精度不够。如果每个信噪比点只发送10000个比特最低可测误码率就是10的负4次方量级再低的误码次数根本观测不到曲线自然在某个平台附近乱抖。其次是数据恢复流程写错了。比如解调出的比特和发送比特没有对齐那不管信噪比多高误码率都会停留在0.5附近。遇到这种情况先把numBits改成一个小值比如1000在Matlab里打印前几个符号的收发对比用眼睛确认一下映射和解映射链路是否通顺。还有一个容易忽略的因素是随机种子。不固定随机数种子时每次跑出来的低误码率点会有波动尤其在10的负5次方以下的区间波动幅度可能达到一个数量级。这不是程序错误是蒙特卡洛统计的本质。6.2 噪声功率算错的经典症状噪声功率算错最典型的症状是仿真误码率曲线和理论曲线形状完全一致但整体向右平移了若干分贝。如果你把Es/N0直接当成Eb/N0使用也就是少乘k那么实际信噪比会低于标称值仿真曲线整体右移6dB。反过来如果你把Eb/N0当成Es/N0仿真曲线会整体左移。另一个常见错误是复噪声写成只加实部。这样虚部完全无噪声等效于只用半个信道的能量传输信息误码率曲线也会偏移而且星座图看起来会是拉长的椭圆而不是圆形的噪声云。看到椭圆形云团第一反应就检查噪声是不是两个维度都加了。6.3 从仿真到真实系统的三个扩展方向做完这个仿真之后扩展方向其实很多。第一是加入升余弦脉冲成形和匹配滤波看看带限信道下星座点之间的码间干扰如何处理。第二是加入频率偏移和相位噪声观察星座点在解调前旋转、散开的现象这是所有真实接收机都要面对的问题。第三是做OFDM版本把16QAM映射到多个子载波上很多热门的无线通信课程设计就是在这个基础上延伸出来的。如果只是想继续验证调制解调本身我建议先做第二个方向。加频偏和相噪的代码改动量很小但对理解实际接收机中“载波同步为什么重要”会非常有帮助。做完之后你会对同步环路、导频估计这些概念有完全不同的体会。
返回列表