简介:这是一份面向本硕博通信类教研学习的OFDM+16QAM与OFDM+64QAM对比仿真资料,使用MATLAB完成,重点对比两种高阶正交幅度调制在OFDM系统中的误码率表现,并绘制星座图、分析基带波形差异,适合课程设计、毕业设计以及数字通信方向自学进阶。压缩包内共7个文件,包含5个可运行的MATLAB脚本、1份文本说明和1个操作录像,整体仅1.45MB,脚本由主程序统一调用调制、信道、解调等子函数,结构清晰,便于学习者定位各处理模块并独立修改参数。通过对比仿真结果,可以直观看出16QAM与64QAM在频谱效率和抗噪声能力之间的权衡,为后续系统设计提供参考。附带的avi操作视频演示了从环境检查到运行出图的完整流程,可帮助避开当前文件夹设置等常见运行问题。目前已有733人学习下载,除完整仿真代码外,还可借助说明文档理解不同调制阶数对系统性能的影响,节省搭建与调试验证时间。
1. 先看这个仿真在算什么:OFDM+16QAM 和 OFDM+64QAM 的差距不是「多两个点」
OFDM 加 QAM 调制一直是通信物理层仿真里最常被问起的一组对比:同样一套 OFDM 链路,子载波数不变,只把 16QAM 换成 64QAM,误码率曲线会差多少?星座图会变成什么样子?基带波形跟原来有什么不同?这个标题要解决的,就是把这套「能跑、能出图、能解释」的 MATLAB 仿真一次讲清楚。很多同学第一步就栽在对比口径上:要么星座图没归一化,两条曲线画出来信噪比都对不上;要么把 EbN0 和 SNR 混着当横轴,得出「64QAM 误码率更低」这种错误结论。这篇文章按一条完整基带链路走:先搭 OFDM 帧结构和调制映射,再写误码率仿真主循环,然后画星座图和基带波形,最后给出一份避坑清单。适合正在做课程设计、保研面试准备或者刚接触物理层仿真、想把星座图和 BER 曲线真正看懂的人。
2. 搭 OFDM 基带链路:从比特到星座点的调制映射与帧结构
2.1 帧结构和参数:为什么选 64 个子载波、CP 设为 16
做 OFDM 仿真第一个要定的就是帧结构。常见做法是参考一个「可解释、可跑动」的参数集:子载波数取 64,循环前缀 CP 取 16,每帧放 1000 个 OFDM 符号,数据比特用 randi 随机生成。这套参数不是随便拍的——子载波数 64 是 2 的幂,FFT 直接算,速度快,而且 64 个频域符号在画星座图时足够密,能看出调制阶数带来的差异;CP 取 16 是符号长度的四分之一,在演示场景里能明显看到 CP 对多径的抵抗效果,实际系统里 CP 占比通常会小于这个值,但仿真里把差异放大反而更容易观察。
先看参数初始化和发射端的第一段代码:
% 仿真参数设置 N = 64; % 子载波数(FFT 点数) CP = 16; % 循环前缀长度 nSymbols = 1000; % 每帧内的 OFDM 符号数 modOrder = 64; % 调制阶数,16QAM 改成 16 即可 nBitsPerSym = log2(modOrder); % 每帧总比特数 = OFDM 符号数 * 每个符号承载的比特数 nBits = nSymbols * N * nBitsPerSym; % 随机生成比特流 dataBits = randi([0 1], nBits, 1); % 比特 -> 调制符号(UnitAveragePower 保证平均功率为 1) txSym = qammod(dataBits, modOrder, 'InputType', 'bit', 'UnitAveragePower', true);这段代码里最值得注意的不是 randi 也不是 qammod 本身,而是UnitAveragePower这个参数。16QAM 和 64QAM 的星座点坐标范围不同:16QAM 的坐标是 ±1、±3,64QAM 是 ±1、±3、±5、±7,如果不做归一化,64QAM 星座图的整体幅度天然比 16QAM 大一圈,画在一起根本没法对比。UnitAveragePower=true会把星座图的平均功率归一化到 1,这样两条误码率曲线的横轴 SNR 才有可比性。旧版本 MATLAB 里这个参数叫AveragePower,语义是反的,老代码跑出来星座图大小不一的情况多半就是这里引起的。
然后是 OFDM 调制部分:串并转换、IFFT、加 CP。
% 串并转换:把一长串符号重新排成 [N x nSymbols] 矩阵 txSymMat = reshape(txSym, N, []); % IFFT 把频域符号变成时域采样点 txTimeMat = ifft(txSymMat, N, 1); % 加循环前缀:把每个 OFDM 符号的最后 CP 个采样点复制到开头 txTimeCP = [txTimeMat(end-CP+1:end, :); txTimeMat]; % 并串转换,得到最终发送波形 txSignal = txTimeCP(:);这些操作背后是 OFDM 的核心逻辑:IFFT 把频域数据映射到时域,子载波之间的正交性靠 FFT 的周期性维持;CP 的作用是吸收多径带来的符号间干扰,只要多径时延不超过 CP 长度,前一个符号的尾部就不会污染后一个符号的有效数据段。这里有个概念要在脑子里立住:CP 不是用来「加强信号」的,它只是一个抗干扰缓冲区,在 AWGN 信道下不会带来任何误码率增益,后面避坑章节还会提到因此产生的误解。
值得一提的是,reshape(txSym, N, [])默认按列填充,也就是第一个子载波对应第一行,第二个对应第二行,这个顺序本身不影响误码率统计,但如果你要单独看某几个子载波上的星座点,就得知道符号是按列排的,不然提取出来的子载波索引是反的。
2.2 调制映射:16QAM 和 64QAM 的归一化、比特映射与功率控制
调制映射决定了「比特」如何变成「星座点」,也决定了系统在高噪声下的表现。16QAM 每个符号承载 4 比特,64QAM 承载 6 比特,这是频谱效率上的差异——同样带宽、同样符号速率下,64QAM 的比特率是 16QAM 的 1.5 倍。代价是星座点密度更高:16QAM 有 16 个星座点,分布在 4×4 的网格上;64QAM 有 64 个点,分布在 8×8 的网格上。单位平均功率归一化之后,64QAM 相邻星座点之间的欧氏距离只有 16QAM 的约一半。具体算一下:归一化后 16QAM 最小距离约为 0.632,64QAM 约为 0.309,对应大约 6.2dB 的差距。换句话说,同一套 OFDM 链路里,64QAM 要比 16QAM 多付出约 6dB 信噪比才能达到同样的误码率水平。这个 6dB 是个非常好的心理标尺,后面看误码率曲线时你会发现两条曲线之间的水平间距基本就落在这附近。
bit 到符号的映射,MATLAB 里直接把InputType设为'bit'就行,不需要自己写格雷映射。但注意:qammod 的默认星座顺序在不同版本里保持一致,都是 Gray 编码,这点对误比特率统计很重要,因为格雷映射下相邻星座点只有 1 比特不同,误码率近似等于误比特率除以 log2(M),解调出来的比特错误数会明显小于符号错误数。如果你自己手写了映射表,一定要确认是格雷码,否则星座图看起来一样,误码率会高出一截,而且这个「高出一截」在曲线上是整体平移,很容易误判成信道问题。
功率控制这块,UnitAveragePower=true只是让平均功率为 1,但 16QAM 和 64QAM 的峰值功率差别很大——64QAM 的角落点幅度是平均幅度的约 1.53 倍,16QAM 约 1.34 倍。这个差异会直接反映在峰均比 PAPR 上,后面第 4 章画基带波形时你会看到 64QAM 的时域信号偶尔会出现更高的尖峰。在实际系统里,高峰均比意味着发射机功放需要更大的回退,效率变差,这也是高阶调制「频谱效率高但工程代价大」的另一个维度。仿真里可以不做功放模型,但解读结果时心里要有这根弦。
3. 误码率仿真主循环:AWGN 下两套系统的 BER 曲线
3.1 仿真主代码:加噪声、解调、统计误码
误码率仿真的核心是一个双层循环:外层遍历信噪比,内层跑多帧统计错误比特。高频信噪比下的误码率很低,比如 SNR 20dB 时 16QAM 的误码率可能在 1e-6 量级,一帧 1000 个符号根本统计不到一个错误,所以内层必须循环多帧累加错误数。我一般会把内层循环写成「跑够帧数并且错误数超过 100 才停」,这样既能保证统计精度,又不会在低误码率场景里死等。
来看主循环代码:
SNRdB = 0:2:20; % 信噪比扫描范围 nFrames = 200; % 每个 SNR 点最多跑的帧数 ber = zeros(length(SNRdB), 1); % 存放误码率结果 for snrIdx = 1:length(SNRdB) totalErrs = 0; totalBits = 0; for frameIdx = 1:nFrames % 重新生成随机比特,保证每一帧独立 dataBits = randi([0 1], nBits, 1); txSym = qammod(dataBits, modOrder, 'InputType', 'bit', 'UnitAveragePower', true); txSymMat = reshape(txSym, N, []); txTimeMat = ifft(txSymMat, N, 1); txTimeCP = [txTimeMat(end-CP+1:end, :); txTimeMat]; txSignal = txTimeCP(:); % AWGN 信道:'measured' 表示按输入信号实际功率加噪声 rxSignal = awgn(txSignal, SNRdB(snrIdx), 'measured'); % 接收端:去 CP、FFT、解调 rxTimeMat = reshape(rxSignal, N+CP, []); rxTimeNoCP = rxTimeMat(CP+1:end, :); rxSymMat = fft(rxTimeNoCP, N, 1); rxSym = rxSymMat(:); % 星座点解调回比特 rxBits = qamdemod(rxSym, modOrder, 'OutputType', 'bit', 'UnitAveragePower', true); % 统计这一帧的错误比特数和总比特数 errs = sum(rxBits ~= dataBits); totalErrs = totalErrs + errs; totalBits = totalBits + numel(dataBits); % 累计到 100 个错误就提前结束,避免高信噪比下浪费时间 if totalErrs >= 100 break; end end ber(snrIdx) = totalErrs / totalBits; end这里有几个关键点要解释。awgn(txSignal, SNRdB, 'measured')的第三个参数'measured'表示先测输入信号的功率再决定噪声方差,这是最稳妥的写法,因为发射端经过 IFFT 加 CP 后信号功率会因为 CP 的存在略有变化,不测就会在低信噪比点出现偏差。接收端reshape(rxSignal, N+CP, [])是按列拆帧,rxTimeMat(CP+1:end, :)是去掉每个符号前 CP 个采样点——这里要特别注意索引,如果 CP 从行号 17 开始取,意味着你丢弃的是符号开头的 16 个采样点,保留的是有效数据段,顺序不能反。fft(rxTimeNoCP, N, 1)按列做 FFT,得到的是频域符号矩阵,行号对应子载波索引。
解调时用qamdemod且OutputType='bit',它会自动按发射端反向映射回比特,不需要手动做星座点最近邻判断。在 AWGN 信道下接收端没有做任何均衡,因为信道响应是 1,FFT 后频域符号直接就是发射符号加噪声,没有幅度和相位畸变,直接用没问题。但要注意:这里的rxSym是复数矩阵,qamdemod内部会做归一化逆运算,前提是发射端和接收端都用UnitAveragePower=true,两边不一致星座图就会要么大要么小。
仿真跑完后把 16QAM 和 64QAM 的ber曲线画在一起,横轴用同一个SNRdB,纵轴用semilogy,这是通信论文里最常见的 BER 图。看曲线时有两个要点:一是 64QAM 曲线整体在 16QAM 右上方,也就是同样信噪比下误码率更高;二是两条曲线形状类似,都是先缓慢下降然后在某个信噪比点开始陡降,这个陡降区域俗称「瀑布区」,是 QAM 误码率曲线最具辨识度的特征。瀑布区出现的位置,大致就是本章开头提到的 6dB 差距所在。
3.2 结果解读:64QAM 比 16QAM 差多少、曲线平移的规律
代码跑通之后,要能解释结果,否则仿真只是黑匣子。以 0 到 20dB 扫描范围为例,典型仿真结果大概是:16QAM 在 SNR 12dB 附近误码率降到 1e-4 量级,64QAM 要 18dB 左右才能到同样水平。也就是说,64QAM 比 16QAM 大约差了 6dB,这个数字与星座点最小距离的比值吻合。这个「6dB 规律」不是巧合,它是由星座图的几何结构决定的,和 OFDM 本身关系不大——OFDM 在这里的角色是传输载体,误码率差异主要来自调制阶数。
一个容易误读的点是:为什么 16QAM 和 64QAM 的曲线在低信噪比区域差距没有 6dB 那么大?比如 SNR 0dB 时两者误码率可能都在 0.3 左右,看起来很接近。原因是低信噪比下星座点已经糊成一片,误码率逼近 0.5 的随机猜测上限,任何调制阶数都趋于这个极限。误码率曲线只有在瀑布区才最能反映调制阶数的真实差距,所以对比时不要只扫到 0dB,要保证扫描范围覆盖到目标误码率的瀑布区。
另外,如果你把横轴换成 EbN0(每比特能量噪声比),两条曲线的间距会发生变化。SNR 是符号信噪比,换算到每比特时要减去10*log10(log2(M)):16QAM 减 6.02dB,64QAM 减 7.78dB,差距会在原来 6dB 基础上再缩小 1.76dB。所以同样一组数据,横轴用 SNR 还是 EbN0,曲线间距是不同的,这也是一个很容易在报告里被质疑的地方。我的建议是:对比误码率曲线时统一用 SNR 或者统一用 EbN0,并在图注里写清楚。
% 把 SNR 换算成 EbN0 的公式(考虑 CP 开销) EbN0dB = SNRdB - 10*log10(log2(modOrder)) - 10*log10(N/(N+CP));这个公式里10*log10(N/(N+CP))是 CP 带来的能量开销:CP 部分承载的是冗余采样点,不携带新信息,但发射功率包含它,所以每比特有效能量要打折。实际 OFDM 系统里还会扣除导频、保护带等开销,仿真里没做这些就不必多算,但要意识到公式里少扣了什么。
4. 星座图与基带波形:怎么画、看什么、对比什么
4.1 星座图绘制:接收端均衡、归一化与 scatterplot 出图
星座图是验证调制链路正确性最直观的手段。画法上有一个基本原则:要画「接收端经过幅度归一化后的星座图」,而不是直接画接收符号。AWGN 信道里接收符号 = 发射符号 + 噪声,噪声会让每个星座点散开成一个圆簇;如果只看某个高信噪比点,16QAM 的 16 个簇清晰可辨,64QAM 的 64 个簇密集排列但依然能分辨。如果加了多径信道,接收符号会有整体相位旋转和幅度缩放,这时必须先做均衡再画图,否则星座图是一团旋转的云,看不出调制阶数。
先给出 AWGN 场景下最直接的绘图代码:
% 选择一个高信噪比点重新跑一遍发射和接收 SNR_plot = 20; % 画星座图用 20dB,簇足够清晰 rxSignalPlot = awgn(txSignal, SNR_plot, 'measured'); rxTimeMatPlot = reshape(rxSignalPlot, N+CP, []); rxSymMatPlot = fft(rxTimeMatPlot(CP+1:end, :), N, 1); rxSymPlot = rxSymMatPlot(:); % 方法一:通信工具箱 scatterplot,能直接画出高斯噪声簇效果 scatterplot(rxSymPlot, 1, 0, 'bx'); grid on; axis equal; % 方法二:自定义 scatter,方便叠加理论星座点 figure; scatter(real(rxSymPlot), imag(rxSymPlot), 10, 'b', '.'); hold on; % 理论星座点叠加:生成一个理想星座坐标 idealSym = qammod((0:modOrder-1).', modOrder, 'UnitAveragePower', true); scatter(real(idealSym), imag(idealSym), 30, 'r', '+'); legend('接收符号', '理想星座点'); xlabel('同相分量 I'); ylabel('正交分量 Q'); title(sprintf('%dQAM 接收星座图 (SNR=%ddB)', modOrder, SNR_plot));这段代码里scatterplot是通信工具箱自带函数,适合快速看一眼。axis equal很关键,因为 QAM 星座图是正方形网格,如果坐标轴不设成等比例,16QAM 看起来会被拉成矩形,误判为 I/Q 失衡。第二种画法用scatter把理想星座点叠加成+号,方便观察每个簇的中心是否和理论位置重合。在 AWGN 下它们应该完全重合,如果有偏移,说明 FFT 或去 CP 环节有 bug。
看星座图时的一个实用技巧:把每个簇点的实部和虚部分别做统计,看它们的分布方差是否一致。理想情况下 I 路和 Q 路的噪声方差相等,星座图是圆簇;如果画出来是椭圆簇,说明 I/Q 路噪声功率不等,这在纯 AWGN 下几乎只有一种解释——发射端或接收端的实虚部处理不对称,比如只给实部加了噪声。
星座图还能直观解释 64QAM 为什么误码率更高:16QAM 的簇间距大,同一个噪声方差下误判概率低;64QAM 簇间距小,落在相邻簇里的概率大。用 MATLAB 随机选几个低信噪比下的错误符号,打印它们的理想位置和实际判决位置,你会发现误判大多发生在相邻星座点之间,几乎不会跳到斜对角的点——这就是格雷映射在起作用,错误比特数远小于符号错误数。
4.2 基带 I/Q 波形对比:幅度层级、PAPR 与频谱效率
基带波形对比看的是时域特征,也是很多人忽略的一块。OFDM 时域信号是多个子载波叠加的结果,本身看起来像噪声,但调制阶数会改变幅度分布:16QAM 的时域波形幅度层级少,64QAM 层级多,体现在 PDF 上就是 64QAM 的幅度分布更接近高斯,出现高峰值的概率更大。画一段时域波形对比图:
% 分别生成 16QAM 和 64QAM 的发射波形,取前 200 个样点 txSignal16 = gen_ofdm_signal(16, N, CP, nSymbols); txSignal64 = gen_ofdm_signal(64, N, CP, nSymbols); figure; subplot(2,2,1); plot(real(txSignal16(1:200))); title('16QAM I 路时域波形'); xlabel('采样点'); subplot(2,2,2); plot(imag(txSignal16(1:200))); title('16QAM Q 路时域波形'); xlabel('采样点'); subplot(2,2,3); plot(real(txSignal64(1:200))); title('64QAM I 路时域波形'); xlabel('采样点'); subplot(2,2,4); plot(imag(txSignal64(1:200))); title('64QAM Q 路时域波形'); xlabel('采样点');gen_ofdm_signal是需要自己封装一个函数,把前面发射端的代码整合进去,参数modOrder作为输入。封装它有个额外好处:后面做多径信道仿真或者换调制阶数时,不用每次复制大段代码。
64QAM 时域波形最直观的特征是:同样的平均功率下,波形时不时会冲出比 16QAM 更高的尖峰。这个尖峰来自 64QAM 星座图角落点与 OFDM 子载波同相叠加的共振效应。定量指标是峰均比 PAPR,计算方式:
% 计算时域信号的峰均比(dB) papr16 = 10*log10(max(abs(txSignal16).^2) / mean(abs(txSignal16).^2)); papr64 = 10*log10(max(abs(txSignal64).^2) / mean(abs(txSignal64).^2)); fprintf('16QAM PAPR = %.2f dB, 64QAM PAPR = %.2f dB\n', papr16, papr64);OFDM 本身 PAPR 就高,多载波叠加导致信号幅度动态范围大,64QAM 的高阶星座会进一步把 PAPR 抬高大约 1 到 2dB。这个差异在纯基带仿真里不产生任何误码率影响,但如果你继续做射频前端仿真,比如加入非线性功放模型,64QAM 会因为高峰值被削波得更厉害,误码率恶化比 16QAM 更明显。这也是高阶调制在真实系统里「看起来频谱效率高,用起来代价大」的第二个证据。做基带对比时跑不出这个效果,但可以在报告里作为延伸讨论写进去,能体现你对链路整体性的理解。
频谱效率的对比更适合用表格表达,因为基数差异一目了然:
| 调制方式 | 每符号比特数 | 同带宽下相对比特率 | 归一化最小距离 | 相对 SNR 代价 |
|---|---|---|---|---|
| 16QAM | 4 | 1.0 | 0.632 | 0 dB(基准) |
| 64QAM | 6 | 1.5 | 0.309 | 约 6.2 dB |
这张表的信息量足够支撑报告里的「为什么做这个对比」部分:64QAM 用多 50% 的比特率,换来了约 6dB 的信噪比代价。所谓「基带对比仿真」,本质上就是把这张表从理论变成可视化证据——星座图展示几何差异,BER 曲线展示误码代价,时域波形展示工程代价。
5. OFDM 仿真避坑清单:误码率不对时先查这五处
5.1 星座图大小不一:调制归一化没统一
现象:16QAM 和 64QAM 的星座图画在同一张图里,一个半径约 1,一个半径约 3,看起来像是两个系统的功率完全不在一个量级。原因:qammod 在不同 MATLAB 版本里的默认归一化行为不同,旧版本默认按峰值功率归一化,新版本需要显式指定UnitAveragePower。解决:发射端和接收端都加上UnitAveragePower=true,解调端的qamdemod也要对应设置。检查方法很简单:分别生成 16QAM 和 64QAM 的理想星座点,计算mean(abs(sym).^2),两者都应该等于 1,不满足就说明归一化没生效。
5.2 误码率曲线锯齿严重:帧数和统计口径的问题
现象:BER 曲线在低信噪比段平滑,到高信噪比段开始上下乱跳,甚至出现 SNR 更高误码率反而更大的倒挂。原因:高信噪比下误码率低,一帧数据里可能一个错误都没有,这一帧对总误码率的贡献是 0,下一帧偶发了一个错误,误码率又跳上去,统计波动被帧数太少放大。解决:把提前终止条件从「固定帧数」改成「累计错误数达到 100 或 200 再停」,并设置最大帧数兜底。如果最大帧数跑完错误数还是不到 100,说明误码率已经低于 1e-6 量级,曲线没必要往这个精度画,扩大扫描范围更有意义。
5.3 高信噪比下误码率出现「地板」:CP 和同步误差背锅
现象:误码率曲线在 18dB 之后不再下降,维持在 1e-5 左右,像被一块地板托住了。原因:如果加噪声环节没问题,最可能是去 CP 时起始索引错了一位。rxTimeMat(CP+1:end, :)取的是第 17 行到末尾,对应 CP 之后的 FFT 窗口,如果写成rxTimeMat(CP:end, :),FFT 窗口整体往前移了一个采样点,子载波正交性被破坏,高频子载波上会出现固定的串扰,这个串扰不随 SNR 增加而消失,误码率就卡在地板上。解决:确认reshape后矩阵的行数等于N+CP,去 CP 后行数等于N,再检查 FFT 后的星座图在高信噪比下是否还有一圈「雾状」扩散。这类 bug 可以这样定位:把发射端信号不加噪声直接走一遍接收端,如果误码率不为 0,说明问题在收发链路本身,跟噪声无关。
5.4 EbN0 和 SNR 换算错误:16QAM 和 64QAM 对比的大坑
现象:把 16QAM 和 64QAM 的误码率画在同一张图里,横轴用 EbN0,结果发现两条曲线几乎重合,于是得出「调制阶数对误码率没有影响」的错误结论。原因:横轴换算时只减了10*log10(log2(M)),却忘了两个调制阶数本身对比时,EbN0 = SNR - 10*log10(log2(M)),16QAM 减 6.02dB,64QAM 减 7.78dB,这两个偏移量不一样,如果都按同一个值减,64QAM 的曲线被错误地拉向左边,看起来就和 16QAM 重合了。解决:分别计算各自的换算公式,不要在循环里用同一个modOrder之外的手写偏移量;另一种更稳妥的做法是横轴直接用 SNR,不做 EbN0 换算,只在图注里说明是符号信噪比。
5.5 CP 存在下误码率没变好反变差:冗余开销不是增益
现象:有的同学为了展示 CP 的「作用」,对比加 CP 和不加 CP 两条误码率曲线,发现不加 CP 的误码率竟然更低,于是怀疑 OFDM 加 CP 是错误的。原因:AWGN 信道下没有多径,CP 不提供任何抗干扰保护;加 CP 相当于发送了更多采样点但信息量不变,每比特有效能量被稀释,所以误码率会略微变差。这不是 bug,是物理规律。解决:要展示 CP 的价值必须引入多径信道,比如用comm.RayleighChannel设置两径延迟,CP 长度覆盖多径时延后误码率大幅下降,超过 CP 后再次恶化,这个对比才是 CP 存在的真正理由。AWGN 下正常的结果是「加 CP 和不加 CP 的误码率几乎一样,加 CP 的略差一点点」,如果两条曲线完全重合,说明功率归一化过程把 CP 的能量开销也归一化掉了,同样是不严谨的。
6. 进阶对比:多径信道下两者的距离会拉开,以及如何验证仿真正确
6.1 多径信道仿真:把 AWGN 换成频选衰落,差距会变大
AWGN 是基带仿真的第一步,但只看 AWGN 会低估高阶调制在真实信道里的代价。把信道换成两径频选衰落,64QAM 的误码率曲线会比 16QAM 恶化得更快,原因是频选衰落会在某些子载波上形成深度衰落,那些子载波上的星座点被严重衰减,64QAM 因为星座点间距小,对这些弱子载波更敏感。一个简单的多径信道可以这样建模:
% 两径信道:主径增益 0dB,第二径增益 -5dB,延迟 5 个采样点 chan = comm.RayleighChannel( ... 'SampleRate', 1, ... % 基带仿真采样率设为 1 'PathDelays', [0 5], ... % 单位是采样周期 'AveragePathGains', [0 -5], ... % 单位是 dB 'MaximumDopplerShift', 0); % 静态信道,便于复现 rxSignalMP = chan(txSignal);接收端必须补一个单抽头均衡器:对每个子载波估计信道响应,把接收符号除以信道估计值,再送进qamdemod。最简单的估计方式是利用导频符号——在频域里把某些子载波固定为已知序列,接收端用rxSym ./ pilotSym得到信道响应估计,再插值到所有子载波。这个环节是 OFDM 从 AWGN 走向真实系统最重要的一步,也是 64QAM 和 16QAM 差距进一步拉大的根本原因:16QAM 在信道估计误差较大时依然能正确判决,64QAM 的星座点容纳误差的余量小很多。仿真的结论会变成:多径信道 + 简单均衡下,64QAM 需要比 16QAM 多大约 8 到 10dB 信噪比,而不是 AWGN 下的 6dB。
6.2 用理论曲线验证仿真链路:berawgn 对数坐标画一起
仿真做出来之后,怎么证明自己写的链路是对的?最硬核的方法是用理论误码率曲线做基准。MATLAB 通信工具箱提供berawgn函数,可以直接画出 AWGN 信道下 QAM 的理论 BER:
figure; semilogy(SNRdB, ber, 'o-', 'LineWidth', 1.5); hold on; theoreticalBER = berawgn(SNRdB, 'qam', modOrder); semilogy(SNRdB, theoreticalBER, 'x--', 'LineWidth', 1); grid on; legend('仿真 BER', '理论 BER'); xlabel('SNR (dB)'); ylabel('误比特率');仿真曲线和理论曲线在低信噪比段几乎重合、在高信噪比段略有偏差,是正常现象——偏差来自有限帧数的统计波动。如果两条曲线在某个信噪比之后明显分离且仿真曲线更高,优先怀疑同步或者归一化 bug,而不是怀疑理论公式。berawgn里的'qam'参数适用于矩形 QAM,16QAM 和 64QAM 都在范围内,理论上这两个星座的误比特率计算都有闭式近似公式,拿来当锚点非常可靠。我自己的习惯是:每换一个调制阶数、每改一次帧结构,先跑一遍理论曲线对比,不要直接跳到多径信道——链路正确性验证比结果本身重要得多。
6.3 视频辅助的用法:对着操作视频敲代码的三个习惯
标题里提到「MATLAB 操作视频」,很多人会把视频当作「照着敲一遍」的唯一依据,这个用法效率很低。我的建议是:第一遍用 1.25 倍速完整看一遍,只建立整体流程概念——参数在哪里改、图在哪里出、脚本和命令行的分界在哪里;第二遍再逐段暂停,把关键参数和函数名抄下来,但不要逐字符模仿,因为你自己的 MATLAB 版本可能和视频里的不同,函数参数名会有细微差异。自己录操作视频给别人看时,最好的做法是把编辑器当前行高亮打开、命令行逐条执行而不是整段粘贴、图窗单独放到屏幕一侧,这样回看的人才知道每一步对应哪个输出。
最后一个我自己的习惯:仿真文件夹里永远保留三个文件——主脚本、封装函数、参数配置文件。主脚本只做「跑循环、出图」两件事,封装函数处理 OFDM 调制解调,参数配置单独写清楚每个数值的含义。这样 16QAM 换 64QAM 就是改一个数字的事,不会因为某一个脚本里残留旧参数而翻车。误码率仿真的价值不在跑出的曲线多漂亮,而在你能随时回答「为什么这里设 64 个子载波」「为什么 CP 是 16」「为什么 64QAM 差 6dB」这三连问。把这几件事想透了,这套仿真才算真正吃透。希望帮到你。
本文还有配套的精品资源,点击获取