资讯动态

QPSK调制解调MATLAB仿真实战:星座图、格雷映射与误码率详解

发布时间:2026/9/9 17:11:04 来源:尧图企业网站定制
老实说QPSK 这个名词在通信课本里被包装得挺唬人教材一上来就是星座图、误码率、相干解调好几页推导。但做调制解调这事儿往透了说就两句话发送端把比特折腾到载波的相位上接收端想尽办法把相位再还原成比特。QPSK 调制解调这事儿说复杂也不复杂核心就是把四个相位点搞清楚再用 MATLAB 把它老老实实写出来。今天这篇直接上干货不铺垫教材我把生成 QPSK 信号的 MATLAB 核心代码、解调链路、软判决和硬判决的区别以及仿真里最容易踩的几个坑全部揉在一起讲。适合正在学通信原理、做课程设计、或者刚接触软件无线电想快速跑通一个调制解调流程的朋友照着抄就能用。1. 不啰嗦直接上生成 QPSK 信号的 MATLAB 核心代码1.1 先搭一套能用的系统参数写仿真最忌讳上来就写代码连采样率、符号率都没定后面全乱套。QPSK 仿真第一步是定参数我这里给一套很常用的组合采样率 8 MHz符号率 1 MHz也就是每个符号采 8 个点。这个过采样倍数不算高也不算低既能看清波形细节又不至于让仿真跑半天。%% 生成QPSK信号的MATLAB核心代码 clear; close all; clc; rng(2025); % 系统参数 Fs 8e6; % 采样率 8 MHz Rs 1e6; % 符号率 1 MHz sps Fs / Rs; % 每符号采样点数 8 nSym 1e4; % 发送符号数量 fc 2e6; % 载波频率 2 MHz span 8; % 成形滤波器截断的符号跨度 rolloff 0.35; % 滚降因子这里每次跑仿真记得加一句rng(2025)固定随机数种子。很多人仿真结果每次都不一样调了半天以为代码有问题其实就是随机种子没固定。实际调试阶段固定随机源能让前后两次运行结果完全一致排查问题方便得多。1.2 格雷映射0 和 1 怎么变成星座点QPSK 一次传 2 个比特所以需要把偶数位和奇数位拆开分别映射到 I 路和 Q 路。格雷映射的原则是相邻星座点只差一个比特这样在 AWGN 信道下最常见的错误是误判到相邻星座点但只错 1 个比特误比特率能低不少。% 1) 产生二进制比特流 bits randi([0 1], nSym * 2, 1); % 2) 格雷映射到 QPSK 符号 % 00 - 11j, 01 - -11j, 11 - -1-1j, 10 - 1-1j % 偶数位走 I 路奇数位走 Q 路 symbols (1 - 2 * bits(1:2:end)) / sqrt(2) ... 1j * (1 - 2 * bits(2:2:end)) / sqrt(2);这段代码里1 - 2*bits的作用很直观比特 0 变成 1比特 1 变成 -1。I 路和 Q 路各分到一路组合起来就是一个复数符号。除以sqrt(2)是为了把星座点归一化到单位圆上保证每个符号的能量为 1。这个归一化很关键后面算信噪比、算误码率全都建立在能量归一的基础上一旦这一步漏了整个误码率曲线都会偏移。1.3 脉冲成形和载波调制基带符号序列是离散的、窄窄的冲激串直接发出去会占很宽的频谱也不符合真实信道的带限特性。所以要在每个符号中间插值、滤波。工程上最常用的是根升余弦滤波器收发两端各用一个根升余弦级联起来等效成升余弦既能限带又能做到符号间无串扰。% 3) 根升余弦脉冲成形 rrc rcosdesign(rolloff, span, sps, sqrt); % 4) 上采样并滤波 xBase filter(rrc, 1, upsample(symbols, sps)); % 5) 载波调制 t (0:length(xBase) - 1). / Fs; xTx real(xBase .* exp(1j * 2 * pi * fc * t));upsample在每个符号后面补 7 个 0变成 8 倍采样率的序列再过根升余弦滤波器就得到平滑的基带波形。filter用的是直接卷积滤波注意它会在输出里引入固定延迟这个延迟在接收端必须补偿不然采样点全偏了。这点后面讲解调的时候细说。载波调制这步我把复数基带信号乘上exp(1j*2*pi*fc*t)再取实部得到的就是真实的带通信号。因为 QPSK 信号本质上是两路正交载波cos 和 -sin各自被一路 BPSK 调制用复指数乘以基带信号再取实部正好实现这个正交调制过程。2. 四个相位点的物理含义QPSK 就是两路 BPSK 的正交叠加2.1 星座图为什么会是四个点很多初学者盯着星座图看半天想不通为什么明明有很多符号图上却只有四个点。这是因为每 2 个比特组合出 4 种状态每种状态的 I、Q 幅度固定所以不管传多少符号落在复平面上的点永远只有四个位置45°、135°、225°、315°四个方向。举个例子发送比特00I 路是1/sqrt(2)Q 路是1/sqrt(2)合成矢量的模是 1相位是 45°。发送01时I 路变成-1/sqrt(2)相位跳到 135°。相位每 90° 一跳这就是 QPSK 名字里“四相”的来历。要理解 QPSK最舒服的姿势是把它看成两路 BPSK 的叠加。BPSK 只用 I 路或者只用 Q 路一次传 1 个比特相位只有 0° 和 180° 两个。QPSK 同时用 I 和 Q 两路等于把两个 BPSK 信号放在同一个频带里并行传输一次传 2 个比特。这让带宽效率直接翻倍同样带宽能多传一倍的比特代价是相位间隔从 180° 缩小到 90°抗噪声能力稍微下降了一点。2.2 格雷映射的价值在哪格雷映射看起来只是排列方式不同实际对误码性能影响很大。AWGN 信道下接收端最常见的错误是把某个星座点判成它旁边的点也就是相位偏移 90° 的误判。如果是自然二进制映射相邻星座点可能差 2 个比特一次误判就错 2 bit格雷映射把相邻点设计成只差 1 个比特一次误判只错 1 bit。所以尽量养成习惯所有 PSK/QAM 仿真都用格雷映射这是通信系统设计里最基础也最划算的优化之一。2.3 带宽效率到底高多少从频谱角度算一笔账BPSK 的频谱效率理论极限是 1 bit/s/HzQPSK 是 2 bit/s/Hz。在 1 MHz 带宽的信道里BPSK 每秒最多传 1 MbitQPSK 每秒能传 2 Mbit。这也是为什么 QPSK 及其变体在卫星通信、数字电视、WCDMA 这些系统里到处出现它是在复杂度和性能之间很均衡的选择。别忘了成形滤波器。加了根升余弦滤波之后实际占用带宽变成Rs*(1rolloff)滚降因子 0.35 意味着带宽大约是符号率的 1.35 倍。仿真的时候如果想观察频谱可以用pwelch看一下基带信号功率谱能清楚看到带外衰减和滚降特性。3. 解调端怎么把这些相位读回 0 和 1同步、延迟与最佳采样3.1 接收机的标准处理链路解调端要做的事情和发射端正好反过来先下变频搬回基带再过匹配滤波器然后在最佳采样时刻抓点最后按象限判决。我直接给一段能跑通的相干解调代码前提是假设载波同步和定时同步都理想。实际系统里同步要靠锁相环或者前导序列完成但理解理想情况下的流程是第一步。%% 接收端相干解调理想载波同步与定时同步 % 6) 下变频到基带 rBase xTx .* exp(-1j * 2 * pi * fc * t); % 7) 匹配滤波 rMatch filter(rrc, 1, rBase); % 8) 补偿滤波器延迟并抽样 delay span * sps / 2; rSym rMatch(delay 1 : sps : end); rSym rSym(1 : nSym); % 截掉滤波器边缘效应导致的残余符号 % 9) 硬判决 bitsHat zeros(size(bits)); bitsHat(1:2:end) real(rSym) 0; % I 路判决 bitsHat(2:2:end) imag(rSym) 0; % Q 路判决 % 10) 统计误码率 errRate sum(bits ~ bitsHat) / length(bits); disp([BER , num2str(errRate)]);3.2 最容易翻车的点滤波器延迟补偿rcosdesign生成的根升余弦滤波器是偶对称的长度为span*sps 1。用filter做卷积后输出信号整体会延迟半个滤波器长度也就是span*sps/2个采样点。我给的例子里span8、sps8算出来延迟是 32 个采样点。如果不做这个补偿直接从第 1 个点开始每 8 个点抽一个采到的根本不是符号正中心星座图上会出现 8 个点而不是 4 个误码率会惨不忍睹。我自己刚学的时候就在这栽过星座图上明明能看到四个亮团但每个亮团周围还有一圈模糊的拖尾查了半天才发现是采样时刻偏了 4 个采样点。所以只要用了filter做成形接收端一定要补上span*sps/2这个延迟这是仿真链路里最容易忽略、又最致命的细节。3.3 实际系统里还有三个同步问题上面代码是理想同步的简化情况。真实系统里接收端拿到的信号存在三类同步问题载波频率同步收发两端晶振有偏差或者多普勒频移导致下变频后的基带信号还在缓慢旋转。最常用的手段是 Costas 环或者 FFT 频偏估计。载波相位同步即使频率同步了初相也可能对不上星座图整体旋转一个固定角度需要导频或者锁相环把相位拽回来。定时同步接收端不知道符号从哪一刻开始需要用早迟门、Gardner 算法或者 Mueller-Muller 算法恢复出最佳采样时钟。这些问题在 MATLAB 仿真里可以先用理想假设绕过但做系统级仿真时一个都躲不掉。我的建议是分步来先把理想同步下的调制解调链路调通再逐个加入非理想因素否则一上来全耦合在一起出了问题根本定位不到哪一环。4. 硬判决 vs 软判决为什么解调输出不只是一个 0/1 判断4.1 硬判决就是查象限前面接收端代码里的判决很简单实部大于 0 判 I 路比特为 0实部小于 0 判为 1Q 路同理。这就是硬判决输出结果就是一个确定的 0 或 1相当于查星座图落在哪个象限。好处是简单直观硬件实现成本低。坏处是把接收信号包含的置信度信息全扔了。接收点如果落在(0.9, 0.1)附近说明信噪比很高这个点可信度极强如果落在(0.05, 0.03)附近虽然相位还是第一象限但它离判决边界很近很可能翻盘。硬判决对这俩一视同仁都输出同样的 0 和 1后续解码器拿不到“这比特到底有多可信”的信息。4.2 软判决的本质是输出置信度软判决不直接给硬判断而是给一个软信息常用的是对数似然比也就是 LLR。LLR 的正负表示判 0 还是判 1绝对值大小表示确信程度。接在后面的 Turbo 码、LDPC 码、卷积码的维特比软译码都需要这种软信息才能发挥纠错能力。如果前面只有硬判决后端解码器相当于失明很多纠错增益白白浪费掉。打个比方硬判决像一个学生考试只写答案“选 A”软判决是“我选 A但有 90% 把握”或者“我蒙的 A其实只有 55% 把握”。对老师来说后一种信息显然更适合用来判断怎么加权。信道译码器就是这个老师。4.3 QPSK 的 LLR 计算可以很省事好在 QPSK 的软判决计算不复杂。因为 QPSK 的两路是正交的I 路和 Q 路的判决互不影响所以每一路的 LLR 可以单独算本质上就是两路 BPSK 的软输出。在符号能量归一为 1 的情况下发送端 I 路的幅度是A 1/sqrt(2)接收样本的实部为y噪声每维方差为N0/2N0 是噪声功率谱密度则 I 路比特的 LLR 近似为L_I ≈ 2 * sqrt(2) * real(rSym) / N0Q 路同理把real(rSym)换成imag(rSym)就行。实际代码里可以直接这么写% 软判决 LLR 计算 N0 0.5 * 10^(-EbN0dB / 10); % QPSK 符号能量归一为 1Eb0.5 LLR_I 2 * sqrt(2) * real(rSym) / N0; LLR_Q 2 * sqrt(2) * imag(rSym) / N0;这里我补充一句如果后面不接信道编码硬判决和软判决的裸 BER 一模一样软判决的优势只有在级联了 FEC 解码器之后才体现出来。但既然 LLR 计算成本这么低我建议在做系统仿真时一开始就保留软信息输出别等到设计信道编码时再回头改解调模块。5. 信噪比换算和波形仿真里的三个老坑5.1 坑一Eb/N0 和 SNR 傻傻分不清这是 QPSK 仿真里最经典的问题。很多朋友直接拿awgn(x, snr, measured)往信号上加噪然后发现仿真 BER 曲线和理论曲线差了 3 dB怎么都对不上。原因很简单awgn函数里的 SNR 是采样点级别的信噪比而理论误码率曲线用的是比特能量与噪声功率谱密度的比值 Eb/N0俩不是一回事。换算关系其实就三步符号级 SNR 采样级 SNR 10*log10(sps) Es/N0 符号级 SNR Eb/N0 Es/N0 - 10*log10(2) QPSK 每符号 2 比特反过来如果你想用理论上的 Eb/N0 去控制加噪强度公式是N0 0.5 / 10^(EbN0dB/10)。这个 0.5 就是 QPSK 的比特能量因为符号能量归一为 1每符号 2 比特所以 Eb 1/2。我见过太多同学卡在这个 3 dB 的偏移上疯狂调代码实际上就是换算错了。下面这张表可以帮你快速对照仿真参数含义典型关系式采样级 SNR每个采样点信号功率 / 噪声功率直接用于awgn符号级 SNR每个符号信号功率 / 噪声功率 采样级 SNR 10log10(sps)Es/N0符号能量 / 噪声功率谱密度 符号级 SNREb/N0比特能量 / 噪声功率谱密度 Es/N0 - 3.01 dB5.2 坑二星座图看起来旋转或者有杂散散点星座图如果不正常先别急着怀疑算法。常见两类症状一是星座图整体在旋转说明载波频率没对准。仿真里如果故意让接收端本振偏了比如 10 kHz星座图就会变成一圈圆环而不是四个点。用scatterplot看最明显。二是四个点周围有一圈小尾巴或者额外的亮弧多半是采样时刻偏了就是前面说的滤波器延迟没补偿。还有一种可能是滤波器没有配对发射用了根升余弦接收用了普通升余弦或者用了矩形窗匹配滤波都会造成符号间串扰。遇到星座图异常我的排查顺序是先检查延迟补偿再用scatterplot看点的数量最后才怀疑噪声和同步问题。延迟问题是最便宜、最容易修、也最容易踩的。5.3 坑三误码率统计不收敛仿真点数太少会导致 BER 曲线在高信噪比下剧烈抖动。信噪比高、误码率低的时候比如理论 BER 是 10^-4想可靠测到这个数量级至少得发 10^5~10^6 个符号否则统计出来的误码数只有几个相对误差极大。我做仿真时的经验是低 Eb/N0 区间用 1e4 个符号够了但 Eb/N0 到 8 dB 以上时至少跑 1e5 个符号BER 才能勉强平滑。如果电脑跑得动干脆全链路统一用 1e5 个符号省得不同区间精度不均。另外记得把误码数打印出来看一眼如果某个点误码数小于 20这个点的可信度就要打个问号。6. 完整误码率仿真链路跑到与理论曲线基本重合前面把零散的模块都过了一遍最后我放一个完整可直接运行的蒙特卡洛仿真脚本。这个脚本把发送端、信道加噪、接收端、硬判决 BER 统计全部串起来并且把仿真曲线和理论曲线画在同一张图上方便验证整个链路是否正确。%% QPSK 完整误码率仿真AWGN 信道 clear; close all; clc; rng(2025); % 系统参数 Fs 8e6; Rs 1e6; sps Fs / Rs; fc 2e6; span 8; rolloff 0.35; nSym 1e5; % 发射端滤波器 rrc rcosdesign(rolloff, span, sps, sqrt); delay span * sps / 2; % 仿真 Eb/N0 范围 EbN0dB -2:2:10; berSim zeros(size(EbN0dB)); berTheory qfunc(sqrt(2 * 10.^(EbN0dB / 10))); for k 1:length(EbN0dB) % 随机比特 格雷映射 bits randi([0 1], nSym * 2, 1); symbols (1 - 2 * bits(1:2:end)) / sqrt(2) ... 1j * (1 - 2 * bits(2:2:end)) / sqrt(2); % 发射端上采样 成形滤波 xBase filter(rrc, 1, upsample(symbols, sps)); % AWGN 信道基带复数噪声功率按 N0 设定 N0 0.5 * 10^(-EbN0dB(k) / 10); noise sqrt(N0 / 2) * (randn(size(xBase)) 1j * randn(size(xBase))); rBase xBase noise; % 接收端匹配滤波 rMatch filter(rrc, 1, rBase); % 延迟补偿 最佳采样 rSym rMatch(delay 1 : sps : end); rSym rSym(1 : nSym); % 硬判决 bitsHat zeros(size(bits)); bitsHat(1:2:end) real(rSym) 0; bitsHat(2:2:end) imag(rSym) 0; % 误码率统计 berSim(k) sum(bits ~ bitsHat) / length(bits); end % 画图 figure; semilogy(EbN0dB, berSim, o-, LineWidth, 1.5); hold on; semilogy(EbN0dB, berTheory, r-, LineWidth, 1.2); grid on; xlabel(Eb/N0 (dB)); ylabel(误比特率 BER); legend(仿真曲线, 理论曲线, Location, southwest); title(QPSK AWGN 信道误码率仿真);跑完这个脚本你会看到仿真点和理论曲线在低信噪比段贴合得很好高信噪比段如果符号数不够可能会有点毛刺把nSym加到 2e5 再跑一次就平滑了。这也说明整条链路从映射、成形、加噪、匹配滤波到判决都是对的后续再往里面加频偏、相位噪声、多径信道都是在验证“非理想因素带来的额外恶化”。最后分享一个我自己的调试心得每次改完代码先别急着看高档信噪比的曲线先跑一个非常低的 Eb/N0比如 -2 dB这时候误码率应该在 0.1 附近。如果这个点都不对说明链路里有根本性错误比如映射反了、判决极性反了、延迟偏了如果低信噪比点对高信噪比点不对才需要去怀疑噪声换算和统计精度问题。按这个顺序排查QPSK 调制解调的 MATLAB 仿真基本半天就能从头调到尾。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价