
简介面向通信类专业学生及 Matlab 初学者这份代码以 BPSK 与 QPSK 调制方案对比为主线通过参数化编程和详细注释展示两种常见数字调制方式在相位表示、频谱利用率和接收复杂度上的差异可直接服务于课程设计、期末大作业或毕业设计中的仿真环节。压缩包共 2 个文件包含 1 个 m 格式的 Matlab 主程序和 1 个 txt 格式的说明文件整体仅 844B结构精简适合快速研读和修改参数。已有 68 人浏览/下载学习。代码支持在 Matlab 2014/2019a/2024a 等版本运行用户可方便地调整信噪比、调制参数等观察性能变化同时附有可直接运行的脚本省去数据准备与调试时间便于学习者对照理解 BPSK 的低复杂度特性和 QPSK 的高频谱效率优势。对于希望从理论走向实践的读者这是一份轻量而完整的参考。1. BPSK和QPSK的比较代码课设曲线和工程选型都需要的Matlab方案当课设要求在“BPSK和QPSK调制方案的比较”里画出误码率曲线时大部分人的第一反应是去拼一个Matlab脚本结果搜到的m文件要么只有发射端星座图、要么把AWGN信道写成固定信噪比最后画出来的两条曲线根本对不上教科书。这个标题里的rar本质上是一套可复现的比较链路同一份随机比特流分别走BPSK和QPSK的调制、AWGN信道、接收判决最终用误码率曲线把两种方案的差异说清楚。它回答的是两个层面的问题——对做课设和复试的人来说是怎么快速得到一条能交代的曲线对做选型的人来说是BPSK和QPSK到底在带宽、功率、误码率上差多少QPSK省下的带宽是用什么换来的。整个过程只用Matlab基础语法不依赖通信工具箱适合从零搭仿真的人照着改。2. 先把理论摆正BPSK与QPSK在带宽、功率、误码率上的取舍在动手跑代码之前需要先把两件事理清星座图差异和比较基准。否则就会出现后面仿真里最常见的翻车——误码率曲线整体比理论高3dB却分不清是代码问题还是理论写错了。2.1 从星座图看本质区别BPSK是两个点QPSK是四象限BPSK把0和1映射成两个幅度相等的实信号归一化后就是-1和1星座图上是横轴上两个点每符号携带1比特。QPSK把两位比特拼成一个符号用实部和虚部同时传输信息归一化后落在复平面的四个象限上每符号携带2比特。从I/Q分解的角度看QPSK本质上就是两路正交的BPSKI路和Q路各有一个±1的实信号分别乘上cos和-sin载波后相加。这个拆分决定了QPSK的带宽效率翻倍信息速率一致时QPSK的符号速率只有BPSK的一半主瓣带宽也就接近减半。代价呢归一化符号能量后QPSK四个星座点到原点的距离相同但相邻星座点的欧式距离从BPSK的2变成了√2因为每符号要摊两份比特的能量。欧式距离越小同样噪声下判错的概率越高这就是后面误符号率比BPSK高的来源。这个区别也会直接反映在仿真脚本的结构设计上BPSK的调制和判决都只要处理一个实数向量QPSK则必须引入复数符号和I/Q两路分开判决。为了让两种方案在同一个脚本里公平对比发射端不能让双方各用各的符号速率而是应该固定总比特数让BPSK产生N个符号、QPSK产生N/2个符号再去比较谁的误码率更低。2.2 Eb/N0与Es/N0不同符号速率公平比较的基准比较两种调制方案横轴不能用信噪比也不能只用Es/N0必须用Eb/N0。因为QPSK每个符号携带2比特能量BPSK只有1比特如果横轴是符号信噪比QPSK等于占了双倍符号能量的便宜曲线自然更好看。把横轴换算成每比特能量Eb/N0后两种方案才站在同一起跑线上比效率。换算关系是Es/N0 log2(M)·Eb/N0。M4时QPSK的Es/N0比BPSK高3dB。这3dB只是符号能量换比特数带来的差不代表QPSK本身更抗噪。事实上在无编码AWGN信道、相干解调、格雷映射这三个前提同时满足时QPSK的误比特率理论公式与BPSK完全相同BPSK误比特率Pb 0.5·erfc(sqrt(Eb/N0))QPSK格雷映射误比特率Pb 0.5·erfc(sqrt(Eb/N0))QPSK误符号率Ps 2Q(sqrt(2Eb/N0)) - Q²(sqrt(2Eb/N0))误符号率的表达式在Matlab里直接写成erfc形式更方便Ps erfc(sqrt(Eb/N0)) - 0.25·erfc²(sqrt(Eb/N0))。后面第3章的代码里理论曲线就来自这几个公式仿真曲线要和它们对照才算验证通过。误比特率重合不代表代码写错这是教科书结论。对比项BPSKQPSK格雷映射每符号比特数12星座点数24相同比特率下符号速率RbRb/2通带主瓣带宽约2RbRb相同Eb/N0下误比特率0.5·erfc(√(Eb/N0))与之相同接收判决一维实数阈值I/Q两路分别判决这张表里的数据是后面所有代码断言的基础。比如带宽那一行决定了为什么QPSK在带宽受限的移动通信系统里更吃香误比特率那一行又决定了为什么不能简单说“QPSK比BPSK误码率低”。仿真脚本最后画出来的曲线本质上就是给这张表的两个关键行为打上实验证据误比特率重合、误符号率偏高。2.3 复杂度代价相位模糊和接收判决差异误码率之外还有两个工程上很现实的差异。第一是相位模糊QPSK的四象限星座接收端如果锁相失步90°星座整体旋转没有差分编码或导频辅助时判决结果会成段错位。BPSK只有两个点对180°相位模糊可以通过差分BPSK处理实现上更直接。第二是接收判决的维度BPSK只比较一个实数的正负QPSK要同时判断实部和虚部而且恢复比特时要注意映射顺序这一部分恰好是写Matlab代码时最容易埋雷的地方。在工程选型上这个差异直接决定接收机复杂度低信噪比、突发传输、接收机成本敏感的场景BPSK往往更稳带宽受限、需要提升频谱效率的场景QPSK是绕不开的选择。这也是为什么仿真脚本里把两个解调器设计得如此不对称——BPSK的解调只有一行判定QPSK却要先拆I/Q再重排比特。理解了这层取舍后面读代码时就不会觉得QPSK部分是多余的了。3. 用Matlab把两种调制跑通发射、AWGN信道、判决的抄作业代码下面是可直接运行的Matlab脚本核心段落。为了避免不同电脑上通信工具箱版本不一致导致跑不起来全部用基础语法实现。整体结构是先构造随机比特源再分BPSK、QPSK两条链路分别完成调制、加噪、判决最后统计误码率和误符号率。3.1 发射端用同一份随机比特喂给两种调制器比较的前提是输入信息一致所以先统一生成N个随机比特BPSK直接映射QPSK按两位一组映射。% 参数区样本数固定为偶数便于QPSK按2比特分组 N 1e6; % 蒙特卡洛试验总比特数越大曲线越平滑 rng(2026); % 固定随机种子保证两次运行结果可比 bits randi([0 1], N, 1); % (N,1) 等概率随机比特 % ---- BPSK调制0 - -11 - 1平均符号功率为1 ---- sym_bpsk 2 * bits - 1; % 实数基带符号长度N % ---- QPSK调制每2比特为一组格雷映射 ---- bit_pairs reshape(bits, 2, N/2); % 每列一个QPSK符号 b1 bit_pairs(1, :); % I路比特 b2 bit_pairs(2, :); % Q路比特 map_I 2 * b1 - 1; % I路幅度 -1 map_Q 2 * b2 - 1; % Q路幅度 -1 sym_qpsk (map_I 1j * map_Q) / sqrt(2); % 每个符号平均功率归一化为1逻辑说明bits源只用一次BPSK链路和QPSK链路消费的比特内容完全一致只是QPSK按两行重排后传输。格雷映射表对应关系是00→第一象限、01→第四象限、11→第三象限、10→第二象限相邻象限只差一个比特。除以sqrt(2)这一步不是可有可无的细节它让每个QPSK符号的模长平方恒为1与BPSK的符号功率对齐后面计算噪声功率才能用同一套Eb/N0公式。如果省略这一步符号功率会变成2Es/N0整体偏大3dB画出来的曲线会合到理论曲线上但对应关系完全是错的。3.2 AWGN信道按Eb/N0手工加噪不走awgn函数常见的翻车方式是用awgn函数直接加“信噪比”但awgn的SNR参数指的是信号功率和噪声功率的比值与误码率曲线横轴的Eb/N0是两码事。这里手工计算噪声方差保证横轴定义严格对应教科书公式。EbN0_dB 0:2:12; % 横轴范围单位dB ber_bpsk zeros(size(EbN0_dB)); % 存放BPSK误码率 ber_qpsk zeros(size(EbN0_dB)); % 存放QPSK误码率 ser_qpsk zeros(size(EbN0_dB)); % 存放QPSK误符号率 for k 1:length(EbN0_dB) EbN0 10^(EbN0_dB(k) / 10); % 转线性值 % ---- BPSK加噪实AWGN噪声方差 N0/2其中 N0 1/EbN0 ---- noise_var_bpsk 1 / (2 * EbN0); % 因为BPSK符号功率EsEb1 rx_bpsk sym_bpsk sqrt(noise_var_bpsk) * randn(N, 1); % ---- QPSK加噪复AWGN每维噪声方差 N0/2N0 1/(2*EbN0) ---- N0_qpsk 1 / (2 * EbN0); % Es1且 Es/N0 2*Eb/N0 noise_var_per_dim N0_qpsk / 2; % 实部、虚部分摊噪声功率 rx_qpsk sym_qpsk sqrt(noise_var_per_dim) * (randn(1, N/2) 1j*randn(1, N/2));参数说明EbN0_dB取0到12dB、步进2dB这个范围对BPSK足够展示从10⁻²到10⁻⁵的下降趋势如果只看10⁻³以下的区间可以追加到14dB但样本数N要同步加大。randn生成的是标准正态分布随机数乘上sqrt(噪声方差)后方差就变成对应数值。QPSK的复噪声拆成实部、虚部两个独立的randn每一维方差是N0/2这里最容易写错成sqrt(N0)那样噪声总功率会偏大3dB曲线整体偏移。如果想快速验证链路是否跑通把N改成1e5、EbN0_dB改成0:2:8一分钟内就能出结果。正式画图时再回到大样本量。3.3 接收判决与误码统计QPSK的比特对齐接收端BPSK只需要对实数符号做阈值判决QPSK分别判决I/Q实虚部的正负映射回比特流时按发射时的顺序拼接。注意QPSK接收判决出来的比特流长度是N与发射bits完全对应不需要额外截断。% ---- BPSK判决0判为10判为0与发射映射一致 ---- bits_hat_bpsk (rx_bpsk 0); % ---- QPSK判决I路对应每组第一位Q路对应第二位 ---- I_hat real(rx_qpsk) 0; % 0/1逻辑向量 Q_hat imag(rx_qpsk) 0; % 0/1逻辑向量 bits_hat_qpsk reshape([I_hat; Q_hat], N, 1); % 恢复为列向量 % ---- 误码率统计 ---- ber_bpsk(k) mean(bits_hat_bpsk ~ bits); ber_qpsk(k) mean(bits_hat_qpsk ~ bits); ser_qpsk(k) mean((I_hat ~ b1) | (Q_hat ~ b2)); % 一个符号任一位错就算符号错 end逻辑说明mean(bits_hat ~ bits)计算的是错误比特占比等价于sum(...)/N。QPSK的误符号率单独统计判断条件是I路或Q路任一比特出错就算该符号错误对应前面理论公式里的Ps。QPSK链路从头到尾没有补位操作因为N在参数区就设计成了偶数任何一次reshape都要确认维度是2×(N/2)最后恢复成(N,1)列向量再比较。发射比特对齐这一点跑仿真的人十有八九在这翻过车。循环结束后画图figure; semilogy(EbN0_dB, 0.5*erfc(sqrt(10.^(EbN0_dB/10))), k--, LineWidth, 1.2); hold on; semilogy(EbN0_dB, ber_bpsk, bo-, LineWidth, 1); semilogy(EbN0_dB, ber_qpsk, rs-, LineWidth, 1); semilogy(EbN0_dB, ser_qpsk, m^--, LineWidth, 1); grid on; xlabel(Eb/N0 (dB)); ylabel(误码率 / 误符号率); legend(理论BER(BPSK/QPSK), 仿真BER BPSK, 仿真BER QPSK, 仿真SER QPSK, Location, southwest);画完以后预期看见的景象是BPSK和QPSK两条误比特率曲线几乎重合且贴住理论曲线QPSK的误符号率曲线明显高于误比特率曲线大约在误码率10⁻³附近高出1.5到2倍左右。如果看见的是QPSK误比特率大幅高于BPSK优先检查格雷映射的顺序和reshape的对齐方式这两处是绝大多数bug的来源。4. 画准误码率曲线主脚本结构与三个必调参数第3章的代码是链路核心但解压后的工程通常还会有一个主脚本把这些段落组织起来并把参数集中到文件头部。这里讲脚本的组织方式和调参经验。4.1 主脚本骨架参数、发射、信道、画图分开常见做法是把脚本分成4段参数区、发射端、AWGN循环、画图区。第3章的代码按这个方式归位后主脚本的骨架是这样% bpsk_qpsk_compare.m 主脚本骨架 % 1) 参数区 N 1e6; % 样本比特数 rng(2026); % 随机种子 EbN0_dB 0:2:12; % 横轴范围 % 2) 发射端构造同一份bits生成sym_bpsk、sym_qpsk bits randi([0 1], N, 1); sym_bpsk 2 * bits - 1; bit_pairs reshape(bits, 2, N/2); b1 bit_pairs(1, :); b2 bit_pairs(2, :); sym_qpsk ((2*b1-1) 1j*(2*b2-1)) / sqrt(2); % 3) AWGN循环按Eb/N0手工加噪统计BER/SER % 代码见3.2、3.3节循环体内只做加噪、判决、统计三件事 % 4) 画图区误码率曲线、星座图 % 代码见4.3节这样组织不是为了好看而是为了排错时能定位。曲线对不上理论时先怀疑第1段参数有没有改错再看第3段循环里的噪声功率公式最后才怀疑第2段映射。按这个顺序排查五分钟内就能把问题缩小到某个具体段落。另外Matlab脚本和函数的选择也有讲究。课设规模用脚本就够了直接F5运行如果后面要复用调制部分再把发射端和判决抽成function输入参数换成比特流和调制阶数。脚本里用变量名覆盖函数名的隐患也要注意比如变量名取了awgn或ber会遮蔽同名内建函数运行时不报错但行为诡异。4.2 三个必调参数N、EbN0范围、随机种子第一个参数是样本数N。N直接决定误码率曲线的下限和抖动程度。想看到10⁻⁴的误码率N至少取10⁵想看到10⁻⁵N至少10⁶。因为误码率统计本质上是伯努利实验统计误差和√(Pb(1-Pb)/N)成正比。曲线在低误码率段出现锯齿时第一反应是增大N而不是怀疑判决写错。第二个参数是EbN0_dB的范围与步进。课设常用0:2:12想延伸低误码区可以改成0:1:14步进1dB曲线更平滑但循环次数翻倍。注意EbN0最大值要和N联动N1e6时14dB的BPSK误码率大约在10⁻⁷量级一次运行里可能一个错误比特都没有统计结果掉到0semilogy画图会断线。所以要么把N加到10⁷以上要么把最大值收到12dB。第三个参数是随机种子rng。固定种子意味着每次运行结果可复现改代码后能准确判断曲线变化是代码改动引起的还是随机抖动引起的。答辩时同一个图如果两次运行完全不同解释起来非常被动。我一般习惯把所有随机源放在同一个rng种子之后生成包括后面要扩展的16QAM仿真保证整套代码从头到尾可复现。这三个参数在代码里通常只改头部几行但很多人忽略它们之间的联动关系。样本量N、最高Eb/N0、随机种子三者要一起评估单独调大其中一个会带来运行时间暴涨或曲线残缺的副作用。4.3 星座图和曲线双重验证8dB下该看见什么误码率曲线之外还要画接收端星座图。散点图能直观暴露映射错误和功率归一化问题比只盯数值效率高得多。% 选8dB附近的接收信号画星座图需要把对应ebn0点的rx单独存下来 idx find(EbN0_dB 8, 1); rx_bpsk_plot rx_bpsk; % 最后一次循环的结果 rx_qpsk_plot rx_qpsk; figure; subplot(1,2,1); plot(real(rx_bpsk_plot), zeros(size(rx_bpsk_plot)), b., MarkerSize, 3); hold on; plot([-3 3], [0 0], k-); xlabel(I路); ylabel(Q路); title(BPSK接收星座 (8dB)); axis([-3 3 -1 1]); grid on; subplot(1,2,2); plot(real(rx_qpsk_plot), imag(rx_qpsk_plot), r., MarkerSize, 3); hold on; plot([-1.5 1.5], [0 0], k-); plot([0 0], [-1.5 1.5], k-); xlabel(I路); ylabel(Q路); title(QPSK接收星座 (8dB)); axis equal; grid on;注意这段代码假设rx_bpsk和rx_qpsk是最后一次循环12dB的结果如果想指定8dB在循环里把对应信噪比的接收信号另存一份即可。8dB下BPSK星座图会看到两团散点在±1附近云团的半径反映了噪声功率QPSK的四团散点中心在±0.7附近相邻象限之间还有明显间隙。如果QPSK四个中心出现在±1附近而不是±0.7附近就是功率归一化的sqrt(2)被漏掉了。看到这个现象时不用怀疑是信道问题直接回发射端检查映射代码。运行时间上N1e6、7个Eb/N0点的脚本循环在普通笔记本上大约几十秒内跑完N改成1e7会到分钟级。画星座图时如果散点太多导致图上糊成一团可以对rx信号做随机抽样比如rx_qpsk_plot(1:100:end)每100个点取1个显示图像会清爽很多。5. 仿真避坑指南Eb/N0、噪声功率与样本数的5个排查点BPSK和QPSK仿真的坑几乎都集中在横轴定义、噪声功率和样本量这三件事上。代码长度总共不到100行翻车点却非常固定。下面按现象、原因、解决的顺序整理5个高频问题。5.1 曲线整体偏移3dB噪声功率和符号功率没对上现象仿真误码率曲线比理论曲线向右偏移约3dB同等误码率下仿真需要的Eb/N0大了3dB。原因最常见的来源有两个。一是QPSK符号功率没有归一化符号能量变成2而不是1导致Es/N0偏小3dB二是复噪声加噪时用了sqrt(N0)而不是sqrt(N0/2)噪声功率偏大3dB。这两个错误正好都表现为“整体偏移3dB”很难凭肉眼区分。解决先查发射端平均功率再查噪声公式。在代码里插入这两行mean(abs(sym_bpsk).^2) % 期望输出 1 mean(abs(sym_qpsk).^2) % 期望输出 1如果QPSK功率输出是2补上除以sqrt(2)。然后核对加噪段BPSK实噪声方差是1/(2·EbN0)QPSK每维复噪声方差是1/(4·EbN0)。这两处对上了3dB偏移基本就消失了。5.2 BPSK和QPSK误码率曲线重合代码没错是结论正确现象在Eb/N0横轴下BPSK和QPSK的误比特率仿真曲线几乎重合有人以为是代码写错了QPSK应该更差才对。原因这个“重合”恰恰是教科书结论。格雷映射的QPSK在相同Eb/N0下误比特率理论值与BPSK一致它和BPSK的真正差异体现在误符号率和带宽上不在误比特率。如果强行把QPSK曲线调得比BPSK差反而违背理论。解决不用改代码把QPSK的误符号率SER曲线一起画出来。SER高于BER也高于BPSK的BER这才是QPSK在噪声性能上付出的代价。报告里说明“误比特率相同、误符号率更高、带宽减半、星座点距离更近”这套完整结论比单独秀两条重合的BER曲线有说服力得多。5.3 用awgn函数加噪后曲线对不上SNR不是Eb/N0现象用snr EbN0_dB调用awgn函数加噪再以EbN0_dB为横轴画图结果与理论曲线对不上且偏差不是固定值。原因awgn函数的SNR参数指的是信号功率与噪声功率之比单位dB和误码率曲线横轴的Eb/N0是两套定义。调制映射后符号功率被归一化成1此时SNR与Es/N0对应而Es/N0与Eb/N0还差一个log2(M)的换算因子QPSK正好是3dB。这个偏差在不同Eb/N0点上都存在但画成log坐标后看起来像整体偏移。解决最稳妥的做法是放弃awgn函数用randn手工加噪公式按3.2节来。如果因为某些原因必须用awgn换算关系是对BPSKsnr_dB EbN0_dB对QPSKsnr_dB EbN0_dB 10*log10(2)。但要记得awgn默认把信号功率按1计算如果发射端做了功率归一化之外的其他缩放这个换算又要重算。实践下来手工加噪的代码不过多两行换来的是横轴定义完全可控血泪经验是仿真里能用randn的地方就别用awgn。5.4 低误码率抖动甚至掉零样本量不足怎么救现象Eb/N0在8dB以上仿真曲线上下跳动某个点误码率统计出来恰好是0semilogy图里线段断开。原因样本量不足。误码率统计的波动随N增大按√(1/N)下降Eb/N0越高错误越少少量错误比特的随机波动就主导了曲线。当预期误码率是10⁻⁶、N只有10⁵时期望错误数只有0.1个跑出0个错误非常正常。解决按目标误码率反推N原则是每个点至少要有几十个错误比特。比如目标BER是10⁻⁵N取10⁶时错误数约10个曲线能看但抖动N取10⁷更稳。如果只想快速看趋势可以接受10⁻⁴以上的点N1e5够用。对于掉零的点画图前把0值替换成NaNsemilogy会自动跳过该点或者干脆把EbN0范围收窄到N能支撑的区间。5.5 QPSK补位导致比特错位reshape和长度对齐现象QPSK误码率统计出来比理论高好几倍高信噪比点也不收敛曲线整体像平移了一个符号周期。原因发射端为了把N个比特按2分组给奇数长度的bits补了一位0接收端恢复出来的比特流长度比原始bits多一个直接用mean(hat ~ bits)比较时维度对不上Matlab会隐式扩展或者错位比较导致成片判决错误。解决从源头避免补位。参数区写死N为偶数比如N1e6而不是N1e61QPSK发射时用reshape(bits, 2, N/2)接收时用reshape([I_hat; Q_hat], N, 1)两端维度严格对应。如果需要处理任意长度比特流通用做法是把有效长度记录成变量N_valid统计时只对比前N_valid个判决比特补位部分不计入。但这份比较代码场景里固定N为偶数是最省心的写法不要为通用性牺牲正确性。6. 进阶玩法把比较代码改造成通用调制性能验证工具这套代码的价值不只是交一份BPSK/QPSK课设稍微改一下就能变成验证其他调制方案的通用工具。核心改造点是把发射端映射和接收端判决各抽成一个函数入口参数加一个MM2就是BPSKM4就是QPSKM16就是16QAM。以16QAM为例映射时把比特流按log2(M)位分组星座点用标准格雷映射铺到±1、±3格点上再做功率归一化接收判决先按象限定位大区再按阈值判细分比特。信道和噪声功率部分不用重写只有映射函数和判决函数在变。这样要对比8PSK、32QAM时改动面非常小而且能直接复用已经调通的AWGN信道和画图代码。验证方法上有一个值得养成的习惯在仿真报告里把每个Eb/N0点的样本数N和目标误码率一起记录。因为误码率的统计误差大致是√(Pb(1-Pb)/N)N1e6时BER10⁻³处的绝对误差约3×10⁻⁵相对误差3%左右到BER10⁻⁵处绝对误差约10⁻⁵相对误差接近100%几乎不可信。所以低误码率区域的每个点都要检查错误比特数量是否足够。我后来跑仿真养成一个固定习惯每个Eb/N0点的样本数按该点目标误码率倒数的100倍去设比如预期BER10⁻⁵就取N10⁷而不是所有点统一用同一个N。这样曲线既平滑又知道每个点的可信度边界。回到标题里这个rar它给的比较脚本解决的是一个完整的判断问题BPSK和QPSK在误比特率上等价差在带宽和符号级错误率上。这个结论靠背公式容易忘但自己跑一遍曲线、再对着星座图看8dB和12dB的散点分布会比任何文档都直观。把映射、加噪、判决这三段代码按模板改一改这套方法论还能继续用在16QAM、8PSK甚至更高阶调制的课设和项目预研里。希望帮到你。本文还有配套的精品资源点击获取