资讯动态

从零手写QPSK与AWGN的MATLAB仿真:BER曲线与Eb/N0换算

发布时间:2026/9/18 21:12:37 来源:尧图企业网站定制
简介这份文档资料围绕QPSK在AWGN信道下的仿真展开面向通信原理、数字通信课程的学习者及需要完成MATLAB课程设计的学生帮助理解四相相移键控的调制解调流程与抗噪声性能评估方法。文档从QPSK基本原理切入讲解四种载波相位对应两位二进制比特的映射关系、信号波形的复数表示与星座图构成再说明加性白高斯噪声信道的建模思路并给出信号生成、调制、噪声注入、匹配滤波与相干解调、误码率统计、性能曲线绘制的完整仿真环节涉及randi、qpsk、awgn、biterr等函数的调用方式。资源包内共1个doc文件约404KB为可编辑的图文报告型文档便于直接阅读或在此基础上整理实验报告。目前已有661人学习适合作为仿真入门参考与原理复习材料。1. QPSK与AWGN为什么这套仿真值得从零手写一遍很多人用 MATLAB 通信工具箱里现成的调制器和awgn两行出图曲线看着也挺像样但一问 Eb/N0 换算到噪声方差该乘哪个系数、符号能量为什么是 1就卡住了。这个标题要落地的是把 QPSK 在加性高斯白噪声AWGN信道下的整条链路自己搭一遍比特生成、I/Q 星座映射、噪声注入、相干解调判决、误码统计最后把实测 BER 曲线和理论曲线叠在一张semilogy图上去对。它解决的正是图能画、参数说不清这个典型问题——噪声功率口径错一位曲线就能整体平移一个量级做通信仿真时这种翻车太常见了。适合通信、雷达、FPGA 方向的在校生也适合刚转基带算法的工程师把它当蒙特卡洛方法和误码率验证的练手题。2. QPSK 调制解调的数学骨架与 MATLAB 映射实现2.1 从比特到星座点QPSK 的 I/Q 映射规则QPSK 每个符号承载 2 个比特映射到复基带平面上就是 4 个点同相支路 I 和正交支路 Q 各自取 ±1。为了每个符号能量归一化成 Es1把坐标除以 √2于是四个星座点落在单位圆上幅度都是 1。这一步很关键后面所有噪声方差、Es/N0、Eb/N0 的换算都建立在符号平均能量为 1这个约定上如果忘了归一化实测曲线会和理论曲线整体错开 3 dB 左右还会让人误以为是函数用错了。原始比特串要先两两分组每组决定一个符号。分组本身不影响结果但分组后 I、Q 的取值顺序也就是映射规则会直接影响误码率在低信噪比下的表现——原因是错误落在星座上时映射规则决定了相邻点对应几个比特错误。2.2 格雷码映射与判决门限的对应关系常见的映射表用格雷码让相邻星座点之间只差一个比特双比特 b1b0I 支路Q 支路复符号 (Es1)0011(1j)/√2011-1(1−j)/√211-1-1(−1−j)/√210-11(−1j)/√2按这个表b1决定实部符号、b0决定虚部符号判决就退化成看实部正负、看虚部正负两条直线。格雷映射的好处是当某个符号因为噪声落到相邻点上时通常只错 1 个比特而不是 2 个所以总 BER 曲线会比自然映射更贴近理论公式。这也是为什么标准里几乎都用格雷映射。2.3 用 MATLAB 写出可复用的调制解调函数把映射做成独立函数主脚本只管比特流和循环方便后面反复调用改参数。调制侧把两组比特映射成实部、虚部再合成复符号function s qpsk_mod(bits) % QPSK调制输入比特流(长度必须为偶数)输出复基带符号 % 格雷映射: 00-(11j)/sqrt(2), 01-(1-1j)/sqrt(2) % 11-(-1-1j)/sqrt(2), 10-(-11j)/sqrt(2) bits bits(:).; % 强制成行向量避免维度踩坑 if mod(length(bits),2) ~ 0 error(比特数必须为偶数); end b reshape(bits,2,[]).; % 每行是一个双比特 [b1 b0] I 1 - 2*b(:,1); % b10 - 1, b11 - -1 Q 1 - 2*b(:,2); % b00 - 1, b01 - -1 s (I 1j*Q)/sqrt(2); % 归一化保证 Es 1 end解调侧与它对称按实部、虚部的符号做硬判决再还原比特顺序function bits qpsk_demod(r) % 相干解调实部虚部各自过零判决再按 [b1 b0] 顺序展开 b1 real(r) 0; % I 支路为负判决为 1 b0 imag(r) 0; % Q 支路为负判决为 1 bits reshape([b1; b0], 1, []); % 列优先展开正好还原原始顺序 end两点逻辑说明。其一reshape(bits,2,[])按列优先把比特两两分组配合末尾reshape([b1;b0],1,[])的列优先展开收发两侧顺序严格一致不需要额外转置。其二门限就是 0因为星座点是关于原点对称的判决边界是 I0 和 Q0 两条坐标轴。参数上唯一要注意的是归一化的/sqrt(2)它决定了符号能量如果做的是未编码 BER 对照丢了这一步曲线就整体偏高。3. AWGN 信道的建模Eb/N0、SNR 与 awgn 函数的参数换算3.1 SNR、Es/N0、Eb/N0 三种口径的换算关系这三种口径是 QPSK AWGN 仿真里最容易搞混的部分。它们的关系如下口径含义与 Eb/N0 的关系QPSKEb/N0每比特能量 / 单边噪声功率谱密度基准量横坐标常用它Es/N0每符号能量 / 单边噪声功率谱密度Es/N0 Eb/N0 10log10(k)k2即 3.01 dBSNR信号平均功率 / 噪声平均功率当采样率等于符号率时SNR Es/N0关键结论QPSK 每符号 2 比特Es/N0 比 Eb/N0 高 3.01 dB。很多人把awgn(x, EbN0, measured)里的EbN0直接当 Es/N0 用结果曲线整体左移 3 dB还以为是程序算错了。噪声侧N0是单边功率谱密度符号能量为 1 时Es/N0 1/N0于是N0 1/EsN0。复高斯噪声分成实部、虚部两个独立实噪声每维方差是N0/2所以每维标准差是sqrt(N0/2)。这是把噪声手写注入时的核心公式。3.2 用 awgn 函数注入噪声的 mode 参数MATLAB 自带的awgn用起来快但要对准口径EbN0_dB 4; s qpsk_mod(bits); % awgn 的 snr 参数在 measured 模式下指的是符号信噪比 Es/N0 % 而这里想按 Eb/N0 加噪所以要把 Eb/N0 换算成 Es/N03.01dB r awgn(s, EbN0_dB 10*log10(2), measured);measured会先实测输入信号的功率再加噪声所以输入信号功率是否归一化并不影响结果这也是它比手动注入更省心的原因。但它有个细节信号长度较短时实测功率会偏离理论值加噪后 SNR 会有抖动得靠足够长的信号来平均掉。若要做逐点可复现的实验还是推荐下一节的写法。3.3 手写复高斯噪声与功率校验要把噪声功率牢牢攥在手里直接生成复高斯噪声更直观EsN0_dB EbN0_dB 10*log10(2); % QPSK: k2, 加 3.01dB EsN0 10^(EsN0_dB/10); N0 1/EsN0; % Es 1, 故 Es/N0 1/N0 sigma sqrt(N0/2); % 每维标准差 n sigma*(randn(size(s)) 1j*randn(size(s))); r s n; % 功率校验实测噪声功率应逼近理论 N0 fprintf(实测噪声功率 %.4f, 理论 %.4f\n, mean(abs(n).^2), N0);逻辑是这样randn每个维度给出方差为 1 的标准正态分布乘上sigma后实部、虚部方差各为N0/2合成复噪声的总功率就是N0正好对应Es/N0 1/N0。参数上EbN0_dB是你要扫描的自变量10*log10(2)这一项就是 QPSK 的双比特补偿。加上mean(abs(n).^2)的实测打印一旦发现实测值明显大于或小于理论 N0就要怀疑是sigma少乘了sqrt(2)或者符号能量没归一化——这正是仿真发散最典型的两个来源。4. 误码率曲线跑通蒙特卡洛循环、理论 BER 与结果验证4.1 蒙特卡洛帧数与最小错误数设定BER 是概率必须靠大样本统计。比特数太少低信噪比区看着还行高信噪比区一条错误都没有曲线就会断在半空。常见做法是设一个固定比特数 N并在每帧统计错误数N 取 1e5 到 1e7 之间按信噪比高低调整参数建议值说明总比特数 N1e5 ~ 1e7越低信噪比需要越大扫描 Eb/N00 ~ 10 dB覆盖 BER 从 0.1 到 1e-5 区间最小错误数100 以上保证 BER 相对误差可用随机种子固定值曲线可复现便于对比改动实践里可以在循环中判断累计错误数是否达到 100没到就继续加帧这样高信噪比区不会白跑一整帧低信噪比区也不会因为错误太多而浪费计算。4.2 实测 BER 与 erfc 理论曲线的对齐方法QPSK 在 AWGN 下的理论误码率公式是BER Q(sqrt(2*Eb/N0)) 0.5 * erfc(sqrt(Eb/N0))把它和仿真结果画在一起一眼就能看出程序对不对clear; clc; rng(2024); % 固定种子保证曲线可复现 N 1e5; % 比特数 bits randi([0 1], 1, N); s qpsk_mod(bits); EbN0_dB 0:1:10; ber zeros(size(EbN0_dB)); ber_theo 0.5*erfc(sqrt(10.^(EbN0_dB/10))); % QPSK 理论 BER for ii 1:length(EbN0_dB) EsN0_dB EbN0_dB(ii) 10*log10(2); EsN0 10^(EsN0_dB/10); sigma sqrt(1/(2*EsN0)); % Es1, 每维方差 N0/2 n sigma*(randn(size(s)) 1j*randn(size(s))); r s n; bits_hat qpsk_demod(r); ber(ii) mean(bits ~ bits_hat); end semilogy(EbN0_dB, ber, o-, EbN0_dB, ber_theo, k--); grid on; xlabel(Eb/N0 (dB)); ylabel(BER); legend(仿真,理论);这里的逻辑说明有三点。其一理论曲线用erfc而不是erf或1-erf0.5*erfc(sqrt(Eb/N0))是 QPSK 的标准结论别写成 Q 函数的近似式。其二仿真横坐标是 Eb/N0但噪声是按 Es/N0 算的中间那10*log10(2)就是 k2 的桥。其三两条曲线应该在 0.5 dB 以内重合如果仿真曲线整体偏高或偏低优先查sigma有没有少乘 sqrt(2) 和符号有没有归一化。这两处一错曲线要么偏高约 3 dB 要么偏低约 3 dB非常典型。4.3 星座图与接收波形可视化排查数字对不上时画图比读日志快。scatterplot可以直接看接收符号的聚类形态加两行就行scatterplot(r, 1, 0, b.); % 接收符号星座 hold on; scatterplot(s, 1, 0, r); % 发送符号叠加对比 title(sprintf(Eb/N0 %d dB 下的接收星座, EbN0_dB(end)));如果噪声点像一团松散的云说明噪声量级正常如果四个簇已经糊成一团说明这一段信噪比已经落到了判决门限以下BER 高是应该的。若四簇的位置本身偏了就不是噪声问题而是映射或判决方向写反了——常见的是实部虚部顺序颠倒或者格雷表里 01 和 10 写反这种情况 BER 会在 0.5 附近居高不下跟信噪比几乎无关。5. 提升仿真效率与精度的几个具体技巧5.1 向量化替代逐符号循环比特数上到 1e6 以后逐符号for循环是主要瓶颈。把判决整个向量化用逻辑数组直接算误码速度能快一个量级% 逐符号写法慢 % err 0; % for k 1:length(bits_hat) % if bits_hat(k) ~ bits(k); err err 1; end % end % 向量化写法快 err sum(bits_hat ~ bits); % 逻辑数组求和一次算完 ber err / length(bits);bits_hat ~ bits生成的逻辑数组等于 1 表示错、0 表示对sum出来的就是错误比特数。噪声注入本身也是向量化的不需要按符号逐个加。要注意向量化后内存占用会上去一批 1e7 的复数符号大约占 160 MB机器吃紧时把总比特数分批处理再累加错误数效果一样。5.2 固定随机种子与结果复现曲线每次都抖动、改一行代码要对半天多半是没固定随机源。在脚本开头调用rng(2024)randi和randn的序列就固定了。要做更严格的可复现可以把噪声种子和比特种子分开设置用RandStream管理避免改动比特数时把噪声序列也带偏。这一条在需要给别人交付仿真报告时几乎必做否则同一份代码跑两次的 BER 能差出半个数量级。5.3 用半解析法在低误码率区外推高信噪比下 BER 掉到 1e-8 以下蒙特卡洛要跑上亿比特才能捕获几十个错误成本极高。这时可以用半解析法保留无噪声的发送星座只在判决变量上加高斯噪声然后直接算理论条件误码概率再对符号求平均% 半解析对每个符号理论误码概率用 Q 函数直接算 d abs(s).^2; % 每符号能量这里是 1 esn0_lin 10.^(EsN0_dB/10); pe 0.5*erfc(sqrt(esn0_lin)); % 单符号成对错误概率的近似 ber_halfana mean(pe);半解析法省掉了显式生成噪声和判决的步骤能在 1e-10 量级对照理论曲线。它的代价是假设噪声对每个符号独立、判决门限只由最近邻决定对非理想信道或带记忆的信道不适用。用它来验证高信噪比段的收敛趋势足够了用来分析实际链路还是要回到全蒙特卡洛。本文还有配套的精品资源点击获取

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

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

免费获取报价