QPSK调制解调仿真避坑指南:星座图散、误码率不收敛的根源与MATLAB实现

发布时间:2026/10/20 12:38:05
QPSK调制解调仿真避坑指南:星座图散、误码率不收敛的根源与MATLAB实现
简介这份MATLAB仿真资源面向通信原理学习者与数字调制入门者围绕QPSK调制解调全流程展开帮助读者打通从理论公式到代码实现之间的理解断层。包内共5个文件以4个m脚本和1个txt说明为主压缩包约6KB其中主函数负责调制解调整体流程其余脚本分别承担低通滤波、升余弦滚降成型滤波以及双边与单边频谱绘制结构清晰、便于分模块阅读。代码覆盖二进制随机序列生成、双极性变换、串并转换、上采样、成型滤波、载波调制、高斯白噪声、相干解调、匹配滤波、抽样判决与并串转换等环节并输出时域波形、功率谱、频谱图、星座图、眼图及理论与仿真误码率曲线每步均附详细注释。已有3800余人学习作者在2018与2023版MATLAB均验证可运行适合作为课程设计、实验报告与自学调试的参考脚本。1. QPSK调制解调仿真为什么你跑出来的星座图总是一团糊刚接触数字通信仿真的工程师十有八九是从 QPSK 入手的。原因很直接它足够简单只有四个相位状态映射关系一眼能看懂又足够典型I/Q 两路正交调制、相干解调、判决门限这些概念它全占了。但真正动手写 MATLAB 代码的时候很多人会发现一个尴尬的现象——代码跑通了波形也画出来了可星座图上的点不是四个清晰的簇而是一团糊在一起的散点误码率曲线更是和理论值差了十万八千里。这个问题不是 MATLAB 的锅也不是 QPSK 本身有多难而是仿真链路里几个关键环节没对齐根升余弦滤波器的滚降系数设了多少、收发两端滤波器是否匹配、采样率与符号率的比例关系、判决前有没有做正确的相位补偿。这些参数之间是耦合的改一个不动另一个星座图立刻就散。这篇笔记就按实际做仿真的顺序从信号生成、脉冲成型、信道加到接收端解调判决把每个环节的代码和参数逻辑拆开讲清楚让新手能照着复现熟手能对照检查自己的参数边界。2. QPSK 基带信号生成符号映射与差分编码怎么选2.1 格雷映射与自然映射对误码率的影响QPSK 每个符号携带 2 bit 信息四个相位状态对应四种符号组合。映射方式直接决定了相邻符号之间误一位还是误两位。常见做法是格雷映射让相邻相位的比特组合只差一位这样在信噪比不高、判决容易落到相邻区域时每个符号错误只对应一个比特错误。MATLAB 里生成 QPSK 符号最直接的方式是用pskmod函数但做仿真时我更倾向于手写映射表因为这样能完全控制 I/Q 两路的数值后面加脉冲成型和载波调制时不容易乱。下面是一段基础映射代码% 参数设置 M 4; % QPSK 调制阶数 k log2(M); % 每符号比特数 numSymbols 10000; % 仿真符号数 bits randi([0 1], numSymbols*k, 1); % 生成随机比特流 % 格雷映射表00-(11j), 01-(-11j), 11-(-1-1j), 10-(1-1j) % 映射前先归一化功率除以 sqrt(2) mappingTable [11j, -11j, -1-1j, 1-1j] / sqrt(2); % 将比特流每2位一组转成十进制索引 bitPairs reshape(bits, k, []).; symbolIndex bi2de(bitPairs, left-msb) 1; % 查表得到复符号 txSymbols mappingTable(symbolIndex).;这段代码里mappingTable的顺序决定了映射规则。bi2de把每两个比特转成 0 到 3 的整数加 1 之后作为 MATLAB 的数组索引。注意left-msb这个参数它保证第一个比特是高位和映射表的顺序对应上。如果这里搞反了后面解调出来的比特顺序会整体错位误码率直接接近 0.5。归一化除以sqrt(2)这一步容易被忽略。QPSK 四个符号的平均功率是 2实部虚部各为 ±1归一化之后平均功率变成 1这样后面加噪声时信噪比的定义才和理论公式对得上。不归一化也能跑但算信噪比的时候要额外补偿这个因子容易出错。2.2 差分编码在相位模糊场景下的必要性相干解调需要一个本地载波如果本地载波和发送载波之间存在固定相位差星座图会整体旋转。对于 QPSK相位模糊通常是 90 度的整数倍。如果信道中存在多径或者接收端载波恢复环路有延迟这个相位模糊很难完全消除。差分编码的思路是不直接传绝对相位而是传相邻符号之间的相位变化。接收端即使有固定相位旋转相邻符号的相位差不变解调时用前一符号的相位做参考就能恢复。代价是误码率会略有恶化因为一个符号的判决错误会影响相邻符号。% 差分编码当前符号相位 前一符号相位 当前映射相位 % 用复数乘法实现相位相加 diffSymbols zeros(size(txSymbols)); prev 1 0j; % 初始参考相位 for n 1:length(txSymbols) diffSymbols(n) txSymbols(n) * prev; prev diffSymbols(n); end这里用复数乘法代替角度相加避免了angle和exp来回转换带来的数值误差。初始参考相位设为10j接收端做差分解码时也用同样的初始值这样第一个符号的判决才正确。提示如果仿真里没有载波恢复环节或者信道是纯 AWGN 没有相位旋转差分编码不是必须的。加了反而让误码率曲线比理论值高一点写报告的时候要能解释这个差异。3. 脉冲成型与匹配滤波滚降系数和滤波器延迟怎么定3.1 根升余弦滤波器的参数计算基带符号是冲激串直接送进信道会占用无限带宽。实际系统里要用脉冲成型滤波器把每个符号的能量限制在有限带宽内。QPSK 仿真里最常用的是根升余弦滤波器收发各放一个合起来就是升余弦响应满足无码间串扰条件。MATLAB 里用rcosdesign生成滤波器系数关键参数有三个滚降系数beta、符号跨度span、每符号采样数sps。滚降系数决定频谱的陡峭程度取值在 0 到 1 之间。太小了频谱边沿陡实现困难太大了占用带宽宽频谱效率低。工程上常用 0.35 或者 0.5仿真里用 0.35 比较多。beta 0.35; % 滚降系数 span 6; % 滤波器跨度单位是符号数 sps 8; % 每符号采样数 % 生成根升余弦滤波器系数 rrcFilter rcosdesign(beta, span, sps, sqrt); % 对发送符号做上采样和滤波 txUpsampled upsample(txSymbols, sps); txFiltered conv(txUpsampled, rrcFilter, same);span决定了滤波器的截断长度跨度越大逼近理想升余弦的效果越好但计算量也越大。6 个符号跨度在仿真里够用了再大对误码率的改善微乎其微。sps是每符号采样数至少要 4 才能让波形看起来平滑8 或者 16 更常见。注意conv的same参数让输出长度和输入一致但这样会引入滤波器延迟后面接收端要补偿。滤波器延迟是span*sps/2个采样点。如果收发两端都用same卷积总延迟是span*sps个采样点。做误码率统计的时候必须把接收端采样时刻对准这个延迟否则采到的值全是错的。3.2 匹配滤波与采样时刻对齐接收端先通过匹配滤波器再降采样。匹配滤波器的系数和发送端一样也是根升余弦。这一步的目的是最大化信噪比同时把发送端的脉冲成型响应补成完整的升余弦消除码间串扰。% 接收端匹配滤波 rxFiltered conv(rxSignal, rrcFilter, same); % 计算总延迟并补偿 totalDelay span * sps; % 收发各贡献一半 rxAligned rxFiltered(totalDelay/2 1 : end - totalDelay/2); % 降采样取最佳采样点 rxSymbols rxAligned(1:sps:end);采样时刻的选取是仿真里最容易翻车的地方。理论上最佳采样点是眼图张开最大的时刻对应匹配滤波器输出的峰值。如果采样点偏了半个符号周期星座图立刻散开。上面代码里用1:sps:end取点前提是延迟补偿已经做对了。实际调试的时候可以先画眼图确认最佳采样位置再调整取点偏移。注意conv的same模式在 MATLAB 里对奇数长度和偶数长度滤波器的处理略有不同。如果发现星座图整体旋转或者偏移先检查滤波器长度和延迟补偿量是否匹配。4. AWGN 信道与误码率统计信噪比定义和理论曲线对比4.1 加噪声时的信噪比换算AWGN 信道就是在发送信号上叠加高斯白噪声。MATLAB 里用awgn函数最方便但它的信噪比参数默认是按采样点算的不是按符号算的。如果信号已经上采样了sps倍直接传符号信噪比进去实际噪声功率会偏小误码率曲线会好看得不真实。% 假设 Eb/N0 设为 10 dB EbN0_dB 10; EbN0 10^(EbN0_dB/10); % 符号信噪比 Eb/N0 * 每符号比特数 * 每符号采样数 % 因为噪声带宽是按采样率算的 SNR_dB EbN0_dB 10*log10(k) 10*log10(sps); % 加噪声 rxSignal awgn(txFiltered, SNR_dB, measured);measured参数让awgn先测量信号功率再按指定信噪比加噪声避免手动计算信号功率的麻烦。如果不加这个参数函数会假设信号功率为 0 dBW对于归一化后的 QPSK 信号来说这个假设不成立噪声功率会加错。另一种做法是手动生成噪声signalPower mean(abs(txFiltered).^2); noisePower signalPower / (10^(SNR_dB/10)); noise sqrt(noisePower/2) * (randn(size(txFiltered)) 1j*randn(size(txFiltered))); rxSignal txFiltered noise;手动加噪声的好处是每一步都透明调试的时候能单独检查信号功率和噪声功率是否合理。复数噪声的实部和虚部各占一半功率所以除以 2 再开根号。4.2 误码率统计与理论值对比解调判决之后把接收比特和发送比特逐位比较统计错误比特数除以总比特数就是误码率。为了和理论曲线对比通常要扫多个信噪比点每个点跑足够多的符号数。EbN0_range 0:2:12; ber_sim zeros(size(EbN0_range)); ber_theory berawgn(EbN0_range, psk, 4, nondiff); for idx 1:length(EbN0_range) % 每个信噪比点重新生成数据避免相关性 bits randi([0 1], numSymbols*k, 1); % ... 完整链路 ... % 统计误码 bitErrors sum(rxBits ~ bits); ber_sim(idx) bitErrors / (numSymbols*k); end % 画对比曲线 semilogy(EbN0_range, ber_sim, o-, EbN0_range, ber_theory, r--); grid on; xlabel(Eb/N0 (dB)); ylabel(BER); legend(仿真, 理论);berawgn是 MATLAB 通信工具箱里的理论误码率计算函数对于 QPSK 它给出的就是相干解调的理论值。仿真曲线在低信噪比时通常比理论值略高因为脉冲成型和匹配滤波会引入少量码间串扰差分编码也会恶化性能。高信噪比时仿真曲线下降速度可能变慢这时候要检查是不是符号数不够错误事件太少导致统计不收敛。提示每个信噪比点至少跑 10 万个符号误码率才能稳定到 10^-4 以下。如果只跑几千个符号高信噪比点的误码率可能是 0画在半对数坐标上会断线。5. QPSK 仿真避坑星座图散、误码率不收敛、滤波器延迟错位5.1 星座图四个簇糊在一起现象接收端降采样后的符号画在复平面上四个簇分不开或者簇的中心偏离了理论位置。原因最常见的是采样时刻没对准。脉冲成型和匹配滤波加起来有span*sps个采样点的延迟如果降采样时没有补偿这个延迟取到的就是过渡带上的值。另一个原因是收发滤波器的滚降系数不一致发送端用了 0.35接收端用了 0.5匹配滤波不成立码间串扰严重。解决先画匹配滤波器输出的眼图找到眼图张开最大的采样时刻再调整降采样偏移。确认收发两端rcosdesign的参数完全一致。如果用了conv的same模式检查延迟补偿量是否等于span*sps。5.2 误码率曲线在高信噪比处不下降现象低信噪比时仿真曲线和理论值贴合但 Eb/N0 超过 8 dB 之后仿真误码率下降变慢甚至出现平台。原因可能是相位模糊没有处理。如果信道引入了固定相位旋转而接收端没有做相位补偿判决区域整体偏转高信噪比时错误主要来自相位偏移而不是噪声。另一个可能是符号数不够高信噪比下错误事件太少统计涨落大。解决检查是否有载波相位偏移必要时加差分编码或者简单的相位估计算法。增加每个信噪比点的符号数至少 10 万个。如果用了差分编码确认理论曲线也换成了差分 QPSK 的理论值。5.3 滤波器延迟补偿后符号数对不上现象延迟补偿之后接收符号数比发送符号数少几个误码率统计时数组维度不匹配。原因conv的same模式输出长度和输入一致但延迟补偿时如果直接截掉首尾截取量计算错误就会导致符号数变化。收发各一次卷积总延迟是span*sps但same模式已经居中截取了一次实际需要补偿的量要仔细推导。解决不要用same直接用full卷积然后手动截取。发送端滤波后截掉前span*sps/2个点接收端匹配滤波后截掉前span*sps个点这样延迟关系清晰符号数也容易对齐。% 发送端 full 卷积后截取 txFiltered conv(txUpsampled, rrcFilter, full); txFiltered txFiltered(span*sps/2 1 : end - span*sps/2); % 接收端匹配滤波后截取 rxFiltered conv(rxSignal, rrcFilter, full); rxFiltered rxFiltered(span*sps 1 : end);5.4 信噪比扫描时误码率曲线抖动现象相邻信噪比点的误码率不是单调下降偶尔出现低信噪比点的误码率反而更小。原因每个信噪比点用了同一组随机比特噪声也用了相同的随机种子导致不同信噪比点之间的噪声实现相关。或者符号数太少统计涨落超过了信噪比间隔带来的差异。解决每个信噪比点重新生成比特和噪声不要复用。符号数增加到 10 万以上。如果曲线仍然抖动检查噪声生成方式确保实部和虚部独立。5.5 理论曲线和仿真曲线整体平移现象仿真曲线形状和理论曲线一致但整体高或者低几个 dB。原因信噪比换算时漏掉了上采样因子或者比特数因子。awgn函数默认按采样点算信噪比如果信号上采样了 8 倍符号信噪比和采样点信噪比差了10*log10(8)约 9 dB。漏掉这个补偿仿真曲线会整体偏移。解决用measured参数让awgn自动测量信号功率或者手动换算时把10*log10(sps)和10*log10(k)都加上。画图之前先确认横轴是 Eb/N0 还是 SNR两者差一个10*log10(k)。6. 用 MATLAB 通信工具箱加速验证从脚本到 App 的过渡技巧手写完整链路能帮你理解每个环节但调参数的时候反复跑脚本效率不高。MATLAB 通信工具箱里有一些现成的对象和 App可以用来快速验证参数是否合理再回到脚本里细化。一个实用的做法是用comm.RRCFilter系统对象代替rcosdesign加conv的组合。系统对象内部维护滤波器状态支持流式处理不用手动补偿延迟。对于 QPSK 这种简单调制还可以直接用comm.QPSKModulator和comm.QPSKDemodulator把映射和判决逻辑封装起来减少手写出错的机会。% 用系统对象搭建链路 modulator comm.QPSKModulator(BitInput, true, PhaseOffset, pi/4); demodulator comm.QPSKDemodulator(BitOutput, true, PhaseOffset, pi/4); % 发送端 txBits randi([0 1], 20000, 1); txSym modulator(txBits); % 接收端 rxBits demodulator(rxSym);PhaseOffset参数设成pi/4让星座点落在对角线上这样判决门限就是坐标轴实现简单。默认的0偏移让星座点在坐标轴上判决时需要比较实部和虚部的绝对值两种方式等价看个人习惯。验证完基本链路之后可以用bertool工具画理论曲线把自己仿真的误码率点叠上去。bertool支持多种调制方式和信道类型QPSK 在 AWGN 下的理论曲线可以直接生成不用手动调berawgn的参数。最后一个习惯每次改完参数先跑短数据看星座图确认四个簇清晰分开再跑长数据统计误码率。星座图是黑匣子链路里任何一环出问题它都会告诉你比盯着误码率数字猜原因快得多。我早期做仿真的时候总想一步到位跑完整曲线结果一个延迟补偿错误查了一下午后来养成先看星座图的习惯调试时间至少省了一半。希望帮到你。本文还有配套的精品资源点击获取