MATLAB/Simulink通信系统仿真:BPSK与QPSK误码率对比及避坑指南
简介:这份课程设计资源面向通信工程、电子信息类专业学生及需要完成MATLAB/Simulink仿真实训的读者,围绕AWGN信道下BPSK与QPSK两种数字调制方式展开建模与性能比较。内容涵盖两种调制解调原理推导、需求分析与设计思路、Simulink仿真模型搭建、不同信噪比下误码率曲线记录与对比分析,并附有总结、课程感言及问题解决方案,可帮助读者理解相干解调、相位模糊等关键概念,掌握从建模到结果分析的完整流程。资源包为1个docx文档,约551KB,结构按引言、正文、总结、课程感言组织,目录清晰,便于按章节查阅与引用。目前已有4922人学习下载,适合作为课程设计参考、仿真入门练手或调制方式对比的案例素材。
1. 从一次“跑了一夜还没出图”的课设说起:这套 BPSK/QPSK 仿真到底能帮你省多少事
如果你做过通信原理课设,大概率经历过这种场面:Simulink 模型搭好了,点下运行,进度条像冻住一样,一晚上过去误码率曲线还是空的。我当年第一次做 AWGN 信道下 BPSK 与 QPSK 调制比较,就栽在仿真时间设成 100000 秒上,后来改成 100 秒,曲线又抖得没法看。这套基于 MATLAB/Simulink 的通信系统建模与仿真课程设计,核心就是解决两件事:一是把 BPSK 和 QPSK 在加性高斯白噪声信道下的误码率、频带利用率、复杂度讲清楚,二是给出一套能直接跑通、能出对比曲线的模型和脚本。它适合正在做通信课设的学生、需要快速验证调制方案差异的初级工程师,以及想从理论公式落到 Simulink 模块级实现的人。你拿到的不只是一份报告,而是一个可复现的仿真框架——信源、调制、AWGN、解调、误码率计算,每个环节的参数都能改,改完立刻能看到曲线变化。
2. BPSK 与 QPSK 的调制解调原理:从相位映射到相干解调的数学底子
2.1 BPSK 的相位映射与“倒相”问题
BPSK 的本质是把二进制比特映射到载波的两个相位上,通常用 0 和 π 表示“1”和“0”。时域表达式里,(a_n) 取 +1 或 -1,分别对应两个相位。这个映射关系决定了 BPSK 每个符号只携带 1 比特信息,符号能量全部用来对抗噪声,所以它在低信噪比下比 QPSK 更稳。
但 BPSK 有一个经典坑:相干解调时,本地载波恢复可能存在 180° 相位模糊。也就是说,恢复出来的载波可能和发送载波同相,也可能反相。一旦反相,解调出来的比特会全部翻转,“1”变“0”,“0”变“1”,这就是“倒相”现象。实际系统中常用差分编码来规避,但在课设仿真里,如果 AWGN 模块的初始相位设置不当,或者解调端载波相位没对齐,误码率曲线会直接飙到 0.5,看起来像模型坏了,其实是相位模糊在作怪。
理解这一点很关键:BPSK 的误码率公式 (P_e = \frac{1}{2}\text{erfc}(\sqrt{r})) 是在理想相干解调、无相位模糊的前提下推导的。仿真时如果发现 BPSK 曲线比理论值差很多,先检查载波相位,再怀疑噪声功率。
2.2 QPSK 的正交支路分解与频谱效率
QPSK 可以看作两个正交的 BPSK 叠加。基带码元被分成 I、Q 两路:I 路取奇数位置码元,Q 路取偶数位置码元,然后分别与同相载波和正交载波相乘,最后相加。这样每个符号携带 2 比特信息,在相同带宽下传输速率翻倍,频带利用率从 BPSK 的 1 bps/Hz 提升到 2 bps/Hz。
解调端采用类似的相干解调:两个相互正交的本地载波分别与接收信号相乘,经过低通滤波器滤掉高频分量,再抽样判决,最后并/串变换恢复原始比特流。这里的关键参数是低通滤波器的截止频率和抽样判决的阈值。截止频率设得太高,高频噪声滤不干净;设得太低,有用信号边带被削掉,误码率反而上升。我一般会把截止频率设在符号速率的一半到一倍之间,具体看滚降系数。
QPSK 的误码率公式是 (P_e = \frac{1}{2}\text{erfc}(\sqrt{r/2})),注意这里信噪比是 (E_b/N_0),不是 (E_s/N_0)。很多人在仿真里把信噪比单位搞混,导致 QPSK 曲线和理论值对不上。记住:Simulink 的 AWGN 模块里,Eb/No 和 Es/No 差一个 (10\log_{10}(k)),k 是每符号比特数。BPSK 的 k=1,QPSK 的 k=2,所以同样设置 Eb/No=6 dB,QPSK 的符号信噪比其实是 9 dB。
2.3 为什么同一信道下 BPSK 误码率低于 QPSK
从公式就能看出来:在相同 (E_b/N_0) 下,BPSK 的 erfc 参数是 (\sqrt{r}),QPSK 是 (\sqrt{r/2})。erfc 是单调递减函数,参数越大值越小,所以 BPSK 的误码率确实更低。但注意,这里比较的是“相同每比特信噪比”。如果比较相同符号信噪比,QPSK 因为每符号能量更高,误码率反而可能更低。课设里常见的结论“BPSK 误码率低于 QPSK”是有前提的——相同 Eb/No。这个前提不写清楚,答辩时容易被问住。
频带利用率则是另一回事:QPSK 每个符号 2 比特,BPSK 每个符号 1 比特,所以在相同带宽下 QPSK 能传更多数据。复杂度上,BPSK 只需要一次符号映射,QPSK 需要两次,接收端判决区域也从两个变成四个,硬件资源和算法复杂度都更高。选型时的权衡逻辑很直接:信道条件好、追求速率,上 QPSK;信道条件差、追求可靠性,用 BPSK。
3. Simulink 模型搭建:从模块选型到参数配置的完整链路
3.1 核心模块清单与连接顺序
这套仿真用到的 Simulink 模块不多,但每个都有讲究。下面这张表是我整理的关键模块和参数设置,照着填就能跑通。
| 模块名称 | 库位置 | 关键参数 | 作用 |
|---|---|---|---|
| Random Integer Generator | Communications Toolbox / Comm Sources | M=2, 采样时间=1e-6 | 产生随机二进制序列 |
| BPSK Modulator Baseband | Communications Toolbox / Modulation | 相位偏移=0 | 将比特映射到 0/π 相位 |
| QPSK Modulator Baseband | Communications Toolbox / Modulation | 相位偏移=π/4 | 将两比特映射到四个相位 |
| AWGN Channel | Communications Toolbox / Channels | Eb/No 模式, 输入信号功率=1 | 添加高斯白噪声 |
| BPSK Demodulator Baseband | Communications Toolbox / Modulation | 判决类型=硬判决 | 相干解调恢复比特 |
| QPSK Demodulator Baseband | Communications Toolbox / Modulation | 判决类型=硬判决 | 相干解调恢复两比特 |
| Error Rate Calculation | Communications Toolbox / Comm Sinks | 接收延迟=0, 计算模式=整个帧 | 统计误码率 |
| To Workspace | Simulink / Sinks | 变量名=simout01, 保存格式=数组 | 导出误码率数据 |
连接顺序是:Random Integer Generator → BPSK/QPSK Modulator → AWGN Channel → BPSK/QPSK Demodulator → Error Rate Calculation。Error Rate Calculation 的 Tx 端口接原始比特,Rx 端口接解调后比特,输出误码率和错误比特数。To Workspace 模块接在 Error Rate Calculation 的输出上,把误码率数据导出到 MATLAB 工作区。
注意:To Workspace 模块的“保存格式”要选“数组”,不要选“结构体带时间”。否则脚本里用
simout01.signals.values取数据会报错。
3.2 用 MATLAB 脚本批量跑信噪比扫描
Simulink 模型搭好后,手动改一次 Eb/No 跑一次太慢。正确做法是写一个 MATLAB 脚本,循环调用sim命令,自动扫描信噪比范围,把每次的误码率存下来。下面是我常用的脚本框架,和原始附件里的思路一致,但加了注释和容错。
clc; clear all; close all; % 信噪比扫描范围,单位 dB ebno = 0:0.5:10; snr_linear = 10.^(ebno/10); % 换算成线性值 % 预分配数组,避免循环中动态扩容 ber_bpsk_sim = zeros(1, length(ebno)); ber_qpsk_sim = zeros(1, length(ebno)); % 循环调用 Simulink 模型 for i = 1:length(ebno) EbNo = ebno(i); % 将 EbNo 写入工作区,供 AWGN 模块读取 assignin('base', 'EbNo', EbNo); % 运行模型,模型名根据实际文件名修改 sim('BPSK_QPSK.slx'); % 从 To Workspace 导出的变量中取最后一个误码率值 % 注意:数组长度取决于仿真步数,取 end 更稳妥 ber_bpsk_sim(i) = simout01.signals.values(end, 1); ber_qpsk_sim(i) = simout02.signals.values(end, 1); % 打印进度,方便观察卡在哪一步 fprintf('EbNo = %.1f dB, BPSK BER = %.2e, QPSK BER = %.2e\n', ... EbNo, ber_bpsk_sim(i), ber_qpsk_sim(i)); end % 理论误码率计算 ber_bpsk_theory = 0.5 * erfc(sqrt(snr_linear)); ber_qpsk_theory = 0.5 * erfc(sqrt(snr_linear/2)); % 绘制误码率对比曲线 figure; semilogy(ebno, ber_bpsk_sim, 'ro-', 'LineWidth', 1.5); hold on; semilogy(ebno, ber_qpsk_sim, 'bs--', 'LineWidth', 1.5); semilogy(ebno, ber_bpsk_theory, 'r-', 'LineWidth', 1); semilogy(ebno, ber_qpsk_theory, 'b--', 'LineWidth', 1); grid on; legend('BPSK 仿真', 'QPSK 仿真', 'BPSK 理论', 'QPSK 理论', 'Location', 'southwest'); xlabel('Eb/No (dB)'); ylabel('误码率 (BER)'); title('AWGN 信道下 BPSK 与 QPSK 误码率对比');这段脚本的逻辑很直白:外层循环遍历 Eb/No,每次把值写进工作区,调用 Simulink 模型,从 To Workspace 变量里取误码率。assignin('base', 'EbNo', EbNo)是关键,因为 AWGN 模块的 Eb/No 参数通常填的是变量名EbNo,不是固定数字。这样改一次工作区变量,模型里的噪声功率就跟着变。
参数说明:ebno的范围 0 到 10 dB 是课设常用区间,步长 0.5 兼顾精度和速度。simout01和simout02对应两个 To Workspace 模块的变量名,如果你在模型里改了名字,脚本里也要同步改。signals.values(end, 1)取的是最后一次采样点的误码率,因为 Error Rate Calculation 模块输出的是累计误码率,仿真结束时最后一个值才是最终结果。
3.3 频带利用率曲线的绘制
误码率曲线只是性能对比的一半,频带利用率是另一半。BPSK 和 QPSK 的频带利用率计算方式不同,下面这段代码可以直接接在上面的脚本后面。
% 频带利用率计算 R = 1; % 归一化比特速率,单位 bps B = 1; % 归一化带宽,单位 Hz % BPSK 频带利用率:每符号 1 比特,带宽效率 1 bps/Hz bpsk_bw_usage = R / B * ones(size(ebno)); % QPSK 频带利用率:每符号 2 比特,带宽效率 2 bps/Hz qpsk_bw_usage = 2 * R / B * ones(size(ebno)); figure; plot(ebno, bpsk_bw_usage, 'r-', 'LineWidth', 2); hold on; plot(ebno, qpsk_bw_usage, 'b--', 'LineWidth', 2); grid on; legend('BPSK 频带利用率', 'QPSK 频带利用率', 'Location', 'east'); xlabel('Eb/No (dB)'); ylabel('频带利用率 (bps/Hz)'); title('BPSK 与 QPSK 频带利用率对比'); ylim([0, 2.5]);这里要注意:频带利用率和信噪比没有直接关系,它只取决于调制阶数。BPSK 是 1 bps/Hz,QPSK 是 2 bps/Hz,画出来是两条水平线。原始附件里的频带利用率公式带了信噪比项,那是另一种定义方式,把噪声功率折算进带宽了。课设里两种画法都见过,关键是图注要写清楚定义,不然答辩时容易被追问。
4. 避坑与排查:仿真跑不通、曲线不对时先查这五条
4.1 仿真时间设太大,跑一夜没结果
现象:点运行后进度条几乎不动,CPU 占用高但误码率数据一直不更新。 原因:Simulink 的仿真时间设成了 100000 秒,而采样时间只有 1e-6 秒,总步数达到 10^11 量级,普通电脑根本跑不完。 解决:把仿真时间改成 100 秒到 1000 秒之间,同时把 Error Rate Calculation 的“计算模式”设为“整个帧”,让它在仿真结束时输出累计误码率。如果担心精度不够,可以增加信源采样率,而不是拉长仿真时间。
4.2 To Workspace 导出数据报“无法解析名称”
现象:脚本里用simout01.signals.values取数据时,MATLAB 报错“无法解析名称 simout01”。 原因:Simulink 的“单一仿真输出”选项默认开启,所有输出被打包成一个Simulink.SimulationOutput对象,而不是独立的变量。 解决:在模型配置参数里,找到“数据导入/导出”选项卡,取消勾选“单一仿真输出”。这样 To Workspace 模块才会把数据以独立变量形式返回到工作区。改完之后,simout01和simout02就能正常访问了。
4.3 误码率曲线在低信噪比下饱和在 0.5
现象:Eb/No 从 0 到 4 dB 时,误码率一直在 0.5 附近,降不下来。 原因:要么是 AWGN 模块的 Eb/No 参数没跟着循环更新,要么是解调端载波相位和调制端没对齐,导致 BPSK 出现“倒相”,QPSK 出现 I/Q 支路交换。 解决:先检查assignin是否成功把 EbNo 写进工作区,可以在循环里加disp(EbNo)确认。再检查调制器和解调器的相位偏移参数是否一致,BPSK 都设 0,QPSK 都设 π/4。如果用的是差分编码,确认编码器和解码器匹配。
4.4 QPSK 误码率比 BPSK 还低,和理论矛盾
现象:仿真图上 QPSK 曲线跑到 BPSK 下面去了,和公式推导相反。 原因:信噪比单位搞混了。AWGN 模块如果设的是 Es/No,而理论公式用的是 Eb/No,QPSK 的每符号能量是 BPSK 的两倍,换算到 Eb/No 要减 3 dB。看起来 QPSK 更好,其实是比较基准不一致。 解决:统一用 Eb/No 模式。在 AWGN 模块里把“Eb/No”选上,并设置每符号比特数为 1(BPSK)或 2(QPSK)。这样仿真和理论就在同一基准下比较了。
4.5 频带利用率曲线画成斜线,和预期不符
现象:频带利用率随信噪比变化,低信噪比时接近 0,高信噪比时接近 2。 原因:用了带信噪比项的频带利用率公式,把噪声功率折算进有效带宽了。这种定义在特定文献里有,但和通常说的“频带利用率只取决于调制阶数”不是一回事。 解决:如果课设要求对比调制方式本身的频谱效率,直接用常数 1 bps/Hz 和 2 bps/Hz 画水平线。如果要求考虑噪声影响,就在图注里写清楚公式来源和定义,避免和理论值混淆。
5. 从能跑到好用:把仿真脚本改成可复用的性能评估工具
5.1 参数化封装与一键切换调制方式
原始脚本把 BPSK 和 QPSK 写死在两个模型里,改一次参数要动 Simulink 界面。更高效的做法是用 MATLAB 函数封装,把调制方式、信噪比范围、仿真时长作为输入参数,内部自动切换模型或模块使能。下面是一个简化版的函数框架。
function [ebno, ber_sim, ber_theory] = run_mod_sim(mod_type, ebno_range, sim_time) % mod_type: 'BPSK' 或 'QPSK' % ebno_range: 信噪比范围,如 0:0.5:10 % sim_time: 仿真时长,单位秒 ebno = ebno_range; snr_linear = 10.^(ebno/10); ber_sim = zeros(1, length(ebno)); % 根据调制方式选择模型文件 if strcmp(mod_type, 'BPSK') model_name = 'BPSK_only.slx'; k = 1; % 每符号比特数 elseif strcmp(mod_type, 'QPSK') model_name = 'QPSK_only.slx'; k = 2; else error('不支持的调制方式:%s', mod_type); end % 设置仿真时长 set_param(model_name, 'StopTime', num2str(sim_time)); for i = 1:length(ebno) assignin('base', 'EbNo', ebno(i)); assignin('base', 'BitsPerSymbol', k); sim(model_name); % 假设 To Workspace 变量名为 ber_out ber_sim(i) = ber_out.signals.values(end, 1); end % 理论误码率 ber_theory = 0.5 * erfc(sqrt(k * snr_linear)); end这个函数的价值在于:换调制方式只需要改一个字符串,信噪比范围和仿真时长都是入参,跑完直接返回仿真值和理论值。k是每符号比特数,BPSK 取 1,QPSK 取 2,理论公式里乘在信噪比上,正好对应 Eb/No 和 Es/No 的换算关系。
5.2 用理论曲线校验仿真结果的可信度
仿真跑完不能只看曲线好不好看,得和理论值对一遍。我一般会算一个“偏差指标”:在 Eb/No 大于 4 dB 的区间,仿真误码率和理论误码率的对数差如果超过 0.5 个数量级,就说明模型有问题。常见原因包括:信源比特数不够导致统计涨落、AWGN 模块的随机种子固定导致噪声样本重复、解调器判决门限偏移。
下面这段代码可以自动标注偏差过大的点。
% 计算仿真与理论的偏差(对数域) valid_idx = ebno >= 4; % 只看高信噪比区间 deviation_bpsk = abs(log10(ber_bpsk_sim(valid_idx)) - log10(ber_bpsk_theory(valid_idx))); deviation_qpsk = abs(log10(ber_qpsk_sim(valid_idx)) - log10(ber_qpsk_theory(valid_idx))); % 找出偏差超过 0.5 个数量级的点 bad_bpsk = find(deviation_bpsk > 0.5); bad_qpsk = find(deviation_qpsk > 0.5); if ~isempty(bad_bpsk) fprintf('BPSK 在 Eb/No = %s dB 处偏差过大\n', mat2str(ebno(valid_idx(bad_bpsk)))); end if ~isempty(bad_qpsk) fprintf('QPSK 在 Eb/No = %s dB 处偏差过大\n', mat2str(ebno(valid_idx(bad_qpsk)))); end这个校验步骤花不了几分钟,但能帮你提前发现模型里的系统性错误。比如有一次我跑出来 QPSK 曲线在高信噪比下翘尾,查了半天发现是 Error Rate Calculation 模块的接收延迟设成了 1,而实际解调输出没有延迟,导致比特对齐错位。把延迟改成 0 就正常了。
5.3 从课设到工程:这套模型还能怎么改
这套仿真框架的边界很清楚:它只覆盖 AWGN 信道,只比较 BPSK 和 QPSK,只做硬判决。如果你想用它做更接近实际的评估,有三个方向可以扩展。第一,把 AWGN 换成瑞利衰落信道,观察多径对两种调制的影响,这时候 QPSK 的优势可能会被削弱。第二,把硬判决改成软判决,配合卷积编码和 Viterbi 译码,看编码增益能补回多少误码率损失。第三,加入载波频偏和符号定时偏差,模拟同步不完美时的性能下降,这一步最接近真实接收机。
我自己的习惯是:每次改完模型,先跑一遍 Eb/No = 0 到 10 dB 的扫描,和理论曲线对一遍,偏差在可接受范围内再往下做。从那以后我每次搭新的 Simulink 通信模型,都强制走一遍“理论校验—偏差标注—参数回查”的流程,省下来的返工时间远比写校验脚本的时间多。希望这套东西能帮你在课设或项目里少熬一个晚上。
本文还有配套的精品资源,点击获取