资讯动态

基于pi/4-QPSK与Turbo码的MATLAB通信链路误码率仿真

发布时间:2026/10/20 21:05:40 来源:尧图企业网站定制
简介面向通信系统仿真学习者与课程设计者的MATLAB资源完整实现了基于pi/4-QPSK调制解调与Turbo编译码的通信链路误码率仿真。仿真流程从随机二进制信息序列出发依次完成Turbo编码、pi/4-QPSK调制、AWGN信道加噪、pi/4-QPSK解调、Turbo译码并与原始信息序列比对统计误码率整套链路可直接用于通信原理、数字信号处理及编码理论课程的仿真验证。压缩包内共10个文件以7个m脚本程序为核心分别对应编码、调制、信道、译码、误码统计等模块另有2个mat中间结果文件和1个说明文本整体仅41KB结构紧凑便于快速部署。目前已有113人学习下载。程序附有详细中文注释和操作演示视频在MATLAB 2022a环境下运行即可复现误码率仿真结果适合课程设计、毕业设计或科研预研中的链路级误码性能分析。1. 基于pi/4-QPSK调制解调Turbo编译码通信链路的MATLAB误码率仿真做这个项目之前我一直在用一个现成的pi/4-QPSK调制解调方案应付频偏测试但把Turbo编译码串进链路后仿真就暴露出不少问题。这套资源是一份基于MATLAB的完整误码率仿真工程包含带中文注释的程序源码和操作视频覆盖了pi/4-QPSK调制解调、Turbo编译码、AWGN信道建模与误码率统计的完整流程。它适合两类人一类是刚接手课程设计或毕业设计的通信方向学生另一类是需要在产品预研阶段快速验证链路预算的工程师。这个设计的核心价值不在调制本身而在于把Turbo迭代译码如何与pi/4-QPSK的软比特输出正确衔接讲清楚了——我第一次照跑出来的曲线就很能说明问题不调整好交织器和软比特缩放误码率曲线能差出好几个dB。2. 链路结构与pi/4-QPSK原理选它不只是为了对抗频偏2.1 星座映射关系8个候选相位点与差分编码pi/4-QPSK这个名字里的pi/4指的是每个符号周期内相位最多跳变正负pi/4或正负3pi/4而不是把星座点画在45度、135度这些绝对位置上。它的星座图其实是两套QPSK星座交替使用偶数时刻用一套奇数时刻用另一套两套之间旋转了45度。第1套候选点pi/4、3pi/4、5pi/4、7pi/4第2套候选点0、pi/2、pi、3pi/2发送端每个符号对应的相位增量如下表符号索引二比特相位增量所在象限00pi/4第一/第三象限013pi/4第二/第四象限11-3pi/4第二/第四象限10-pi/4第一/第三象限实际发送的相位是“前一个发送相位 当前符号相位增量”这与普通QPSK的绝对相位映射有本质区别。接收端在相干解调时不再依赖一个固定的绝对相位零点而是利用相邻符号之间的相位差来判决。我在仿真里把这个差分编码过程单独写成了一个函数没有把它合并到调制映射里原因后面会讲——Turbo译码器需要的是每个比特的对数似然比而不是直接解出来的硬符号差分编码和软信息提取必须分开处理。2.2 与普通QPSK的差异为什么链路上游要加高通量的脉冲成形普通QPSK在相邻符号之间可能出现相位差为pi的跳变也就是星座点要从正x轴直接穿到负x轴信号包络在通过带限滤波器时会被严重压扁甚至归零在非线性功放下会产生频谱再生。pi/4-QPSK把最大相位跳变限制在135度以内包络波动被压缩了这是它适合卫星链路和移动通信终端的重要原因。做仿真的时候如果你只在高斯信道下观察误码率pi/4-QPSK和普通QPSK几乎没有差别这条结论是正常的不说明代码写错了。在AWGN信道、相干解调、差分编码判决的条件下pi/4-QPSK的理论误码率性能比理想相干的QPSK差大约0.3 ~ 0.5 dB。原因在于差分编码把误码从单比特错误扩展成了前后两个符号联合判决的错误。我一般在仿真报告里把这个理论差值单独标注然后让Turbo编码增益去覆盖这部分损耗。链路中加入根升余弦滤波器和匹配滤波器之后还要额外注意滤波器群时延带来的符号同步偏移这个坑在后面的避坑章节单独说。2.3 链路整体框图与模块接口关系仿真的整体数据流是信源比特 - Turbo编码 - 交织 - pi/4-QPSK差分映射 - 根升余弦成形 - AWGN信道 - 匹配滤波 - pi/4-QPSK差分解调软信息提取 - 解交织 - Turbo迭代译码 - 误码率统计这个资源里并没有采用纯理想符号级仿真而是把成形滤波的过采样也放进了链路。我一般把每个符号的采样点设置为8根升余弦滚降系数取0.35。这个配置下的频谱利用率接近实际系统但也引入了前后符号的码间串扰匹配滤波器必须严格匹配发送端的滚降系数否则误码率曲线在Turbo译码后会出现一个明显的下限。需要特别注意的一点是Turbo编码器的交织器长度必须与一帧信息比特数量严格对应而pi/4-QPSK的差分相位状态是逐符号累积的所以如果调制器前面有交织器改变符号顺序差分映射必须发生在交织之后。这听起来是基础逻辑但我在实际排错时见过不少工程把交织放在调制之后导致整帧相位状态错乱误码率在0.5附近。3. Turbo编码器与迭代译码编码增益从两个寄存器里面讨出来3.1 两个RSC分量编码器与一帧交织器的配合Turbo码的编码端一般由两个递归系统卷积码RSC分量编码器并联构成。常见约束长度取3生成多项式用八进制表示就是反馈多项式7、前向多项式5对应二进制系数为[1 1 1]和[1 0 1]。这个码的网格状态数是2的约束长度减1次方也就是4个状态译码复杂度低但迭代译码的增益已经足够说明问题。MATLAB中建立这个网格结构用的是poly2trellis函数代码如下% 约束长度3反馈多项式7前向多项式5 trellis poly2trellis(3, [7 5], 7); % 把多项式系数打印出来看看确认是递归系统卷积码 disp(trellis); disp(trellis.nextStates);这段代码的作用是建立分量编码器的状态转移网格返回的trellis结构体会被后面的Turbo编码器和译码器共用。第三个参数7表示递归反馈连接也就是编码器的寄存器状态会反馈回输入端这正是RSC码与普通卷积码的本质区别。poly2trellis的第二个参数是前向多项式第三个参数是反馈多项式如果漏掉第三项网格就退化成了非递归码Turbo迭代增益会消失。我在最初核对网格信息时就是靠打印nextStates发现状态转移图不对称才定位到多项式顺序写错的问题。一帧信息比特经过第一个RSC编码器得到一路校验比特同时经过交织器后进入第二个RSC编码器得到另一路校验比特。交织器在MATLAB里就是一个索引置换向量常用伪随机交织例如用randperm函数生成。交织器长度必须等于信息比特帧长否则第二个分量编码器的输入长度不匹配。删除模块则把两路校验比特按一定规律删减得到码率1/2的输出。3.2 迭代译码外信息到底“外”在哪里Turbo译码器是由两个Log-MAP或Max-Log-MAP译码器通过迭代交换信息完成的。第一个分量译码器接收系统比特软信息、第一路校验比特软信息和上一次迭代得到的先验信息计算出一组后验对数似然比把后验LLR减去输入先验就得到外信息。外信息经过交织器后作为第二个分量译码器的先验输入第二个译码器输出后再经过解交织减去自己的先验信息生成新一轮外信息回传给第一个译码器如此循环。% 这里用一个简化的循环描述迭代过程 % 译码器1的后验LLR减去输入先验得到外信息 % extrinsic1 llr_after_dec1 - prior_input; % 外信息经过交织后送入译码器2 % prior_for_dec2 interleave(extrinsic1, interlvrIndices); % 译码器2的后验LLR经过解交织减去其输入先验作为译码器1的新先验 % prior_for_dec1 deinterleave(llr_after_dec2 - prior_for_dec2, interlvrIndices);上面这段是迭代逻辑的骨架实际代码里不会在顶层重复写这个循环而是用通信工具箱里的TurboDecoder系统对象封装。但理解这个循环是调整迭代次数和判断收敛的基础。我一般设置迭代次数为6因为迭代次数超过6以后误码率曲线在低信噪比区域几乎不再下降计算量却成倍增加。如果你使用的是Max-Log-MAP而不是Log-MAP每次迭代损失约0.2 dB但译码速度能提升接近一半这个取舍看具体仿真需求。3.3 迭代次数与早停判据仿真中还有一个细节值得单独说并不是每一帧都要固定跑满6次迭代。我一般会统计相邻两次迭代之间的外信息变化量如果变化量低于阈值就提前停止这样在高信噪比区域能省去大量无效迭代。% 伪代码判断是否早停 if abs(mean(extrinsic_new - extrinsic_old)) 0.05 break; end这段代码的阈值0.05是我在重复仿真中试出来的经验值。阈值设太大误码率曲线会轻微变差设太小早停机制基本不生效。0.05对应的是LLR均值的变化量不同帧长下可能需要调整。4. MATLAB仿真框架参数表、主循环与四个模块的落地实现4.1 仿真参数表与全局约定正式写程序前我习惯先建一个参数区把所有可调项集中放在文件顶部。这套资源的参数设置大致如下参数名称数值说明信息比特帧长512Turbo编码交织长度码率1/2两个RSC校验比特删除后得到约束长度3分量编码器寄存器数量生成多项式7, 5反馈多项式7前向多项式5迭代次数6Turbo译码迭代过采样倍数8每个符号的采样点数根升余弦滚降系数0.35成形与匹配滤波信噪比范围0:0.5:5 dBEB/N0扫描区间初始化代码里最需要注意的是能量归一化pi/4-QPSK星座图的平均符号能量并不等于每个符号点坐标的平方和再除4这个简单值因为差分映射会让两套星座交替出现取平均后符号能量才会归一到1。我习惯在初始化时先让调制器产生一帧标准信号计算平均功率再用这个功率去归一化噪声方差。% 参数段开始 clear; clc; close all; EbNoVec_dB 0:0.5:5; % EB/N0扫描范围 infoLen 512; % 每帧信息比特数 iterNum 6; % Turbo译码迭代次数 sps 8; % 过采样倍数 rolloff 0.35; % 根升余弦滚降系数 totalFrames 100; % 每个信噪比点仿真帧数这是一个典型的参数初始化写法变量名尽量保持与项目原注释一致。totalFrames设为100意味着每个EB/N0点统计5120个信息比特这在高信噪比区域得到的误码率点数会比较稀疏画出来的曲线在尾部抖动大。如果要验证Turbo码的错误平层一帧至少需要几千个信息比特100帧的数据量勉强可用但需要多跑几次取平均。4.2 发送端Turbo编码、交织与pi/4-QPSK差分映射发送端核心分成两步先对信息比特做Turbo编码和交织再把编码后的比特映射为pi/4-QPSK符号中的相位增量。必须先编码后映射不能颠倒顺序因为Turbo编码器输出的校验比特顺序由交织器决定任何中间插入的符号重排都会破坏接收端原有的比特级交织关系。% 构造Turbo编码器与译码器 interlvrIndices randperm(infoLen); % 随机交织索引一帧一个 turboEnc comm.TurboEncoder( ... TrellisStructure, trellis, ... InterleaverIndices, interlvrIndices); % 待编码信息比特每帧取512位 infoBits randi([0 1], 1, infoLen); % Turbo编码输出长度为码率倒数乘以信息长度 encodedBits turboEnc(infoBits);通信工具箱里的TurboEncoder默认会做1/3码率输出要实现1/2码率需要设置PuncturePattern删除部分校验比特。这里我直接用默认删余配置跑通链路后续再按需要调整。校验位删除在译码器侧是自动补零的如果你要把它更换成自己的分量编码器译码端也要同步修改删余和补零逻辑这是常见出错点。接着做pi/4-QPSK调制。差分映射的核心是维护上一个符号的相对相位function modSymbols pi4qpsk_mod(encodedBits, sps, rolloff) % encodedBits为Turbo编码后比特流长度必须为偶数 % 将比特流reshape为每列两个比特再转为符号索引 bits_mat reshape(encodedBits, 2, []).; symIdx bi2de(bits_mat, left-msb); % 0到3的符号索引 % 差分相位增量 phaseInc symIdx * pi/4 pi/4; % 逐符号累积得到实际发送相位 txPhase cumsum(phaseInc); % 复数基带符号功率归一到1 symbols exp(1j * txPhase); % 根升余弦成形滤波sps倍过采样 rrcFilter rcosdesign(rolloff, 6, sps, sqrt); modSymbols upfirdn(symbols, rrcFilter, sps); end这段代码里symIdx乘以pi/4再加pi/4得到的是相位增量这是pi/4-QPSK的标准映射关系。有人会把符号索引直接映射到绝对星座点上那样发送信号就退化为普通QPSK加一个旋转角度失去差分编码的抗相位跳变能力。upfirdn输出的长度会比输入符号数乘以sps多出滤波器阶数那么多接收端匹配滤波后需要做延时补偿。4.3 接收端解调与软信息提取接收端先做匹配滤波再按发送端的差分编码逻辑逆向计算每个比特的软信息。相干解调下的软比特不能用简单的星座点距离直接计算因为差分编码引入了一个不断变化的参考相位。我采用的方法是把当前接收符号除以前一接收符号得到差分接收值然后计算该差分接收值到四个候选相位点的欧氏距离再用LLR公式输出两个比特的软信息。function llr pi4qpsk_demod_soft(rxSymbols, rrcFilter, sps) % 匹配滤波 rxFiltered upfrdn(rxSymbols, rrcFilter, 1, sps); % 去除滤波器群时延滤波器阶数为6*sps delay 6 * sps / 2; rxSymbols rxFiltered(delay1 : delaylength(rxFiltered)-delay-1); % 归一化接收符号功率 rxSymbols rxSymbols / sqrt(mean(abs(rxSymbols).^2)); % 差分接收当前符号除以前一符号 diffRx rxSymbols(2:end) .* conj(rxSymbols(1:end-1)); % 四个候选相位增量对应的欧氏距离 candidates exp(1j * ([1 3 5 7] * pi/4)); dist abs(diffRx - candidates); % 计算LLR两个比特分别对应符号索引的bit0和bit1 llr zeros(length(diffRx) * 2, 1); end这段代码里upfrdn是上采样加滤波最后一个参数是抽取倍数把它设为sps就能让接收信号回到符号率。群时延补偿如果没做误码率曲线会在高信噪比区域掉不下去因为符号定时偏了一个采样点。LLR计算的具体公式在不同实现里略有差异但不影响整体结论——关键是软信息要从差分接收值计算而不是从绝对星座位置计算。4.4 主循环与EB/N0扫描主循环的结构是将EbNoVec_dB的每个点作为外层循环每个点内跑totalFrames帧最后把误码率汇总做平均。噪声方差换算要与符号能量匹配这是整个仿真中最容易出错的地方之一for idx 1:length(EbNoVec_dB) EbNoLin 10^(EbNoVec_dB(idx)/10); EsNoLin 2 * EbNoLin; % pi/4-QPSK每符号2比特 N0 1 / EsNoLin; % 符号能量已归一化为1 noiseVar N0 / 2; % 复噪声实部虚部各占N0/2 errBits 0; totalBits 0; for frame 1:totalFrames % 随机信息比特Turbo编码调制发送 infoBits randi([0 1], 1, infoLen); encodedBits turboEnc(infoBits); txSignal pi4qpsk_mod(encodedBits, sps, rolloff); % 加高斯白噪声 noise sqrt(noiseVar) * (randn(size(txSignal)) 1j*randn(size(txSignal))); rxSignal txSignal noise; % 接收端处理 rxLLR pi4qpsk_demod_soft(rxSignal, rrcFilter, sps); % Turbo迭代译码 decodedBits turboDec(rxLLR); % 误码统计 errBits errBits sum(decodedBits(1:infoLen) ~ infoBits.); totalBits totalBits infoLen; end ber(idx) errBits / totalBits; end这段主循环的噪声添加方式是按复基带等效噪声功率处理的。如果接收端在成形滤波后做了自动增益控制那么噪声方差计算也要随之修正否则BER曲线会整体平移。comm.TurboDecoder系统对象接收的输入既可以是软LLR也可以是硬判决比特我强烈建议输入LLR而不是硬判决因为硬判决输入会直接抹掉迭代译码的软信息增益。5. 误码率仿真避坑五个在Turbo曲线上的翻车现场5.1 现象低信噪比下误码率曲线完全不下降拦腰截断在0.5附近原因交织器长度和帧长不匹配Turbo译码器输出的校验位置映射到了错误比特位置相当于整帧信息在译码过程中被随机置换。另一种可能是接收端的解交织索引与发送端交织索引不一致但方向相反。解决在代码里加一个自检函数把交织索引interlvrIndices同时给编码器和译码器并且打印交织前后的第一个比特位置。很多MATLAB函数对向量是行还是列敏感comm.TurboEncoder输入列向量、输出也是列向量交织索引在整型数组下的索引基础是从0开始还是从1开始两套体系都常见选一套以后就不要再混用。5.2 现象高信噪比下误码率曲线不再下降出现明显错误平层原因这是Turbo码的固有错误平层效应通常由低权重码字引起。帧长越短错误平层出现的信噪比越低分量码自由距离不够也会导致平层抬高。不一定是代码逻辑错误。解决若平层出现在可接受误码率之上应改用更大交织长度或选择约束长度更长的分量码。我习惯把帧长从512提升到2048再对比一次若平层下移超过0.5 dB说明当前链路还有优化空间。5.3 现象pi/4-QPSK的误码率曲线和普通QPSK完全重合没有任何差距原因如果接收端用绝对星座点做硬判决那么差分编码就从未真正参与解调过程两个系统自然重合。pi/4-QPSK只是把后验相位转成星座点而Turbo译码对绝对相位完全不敏感这才导致曲线重合。解决看星座图。把接收符号连点画出相位轨迹如果相位轨迹每符号固定旋转45度说明调制端差分逻辑生效。再检查接收端是否计算差分接收值而不是直接把接收符号与标准星座点比距离。5.4 现象一段仿真要跑几十分钟尤其在高信噪比下异常慢原因在高信噪比区域误码率很低为了统计到足够多的错误帧程序需要跑大量帧每次帧数固定则会出现不停重复无误码帧的情况。另一个主要原因是迭代译码次数固定为6次但高信噪比下可能第一次迭代就收敛了。解决我把外层EB/N0点单独拆分低信噪比少跑帧高信噪比多跑帧并按目标错误帧数做动态停止。同时建议设置上面3.3节提到的早停判据。还可以把RxLLR转成single精度降低译码过程的数值计算开销。5.5 现象误码率曲线与理论曲线有固定偏差但形状完全一致原因成形滤波器或匹配滤波器引入了固定能量损耗或者信噪比换算时误把EB/N0当成了ES/N0。更隐蔽的原因是Turbo编码后码率不一定是干净的二分之一删余模式不同实际码率会有微小偏差换算EB/N0时用的码率分母如果写死成2就会产生恒定偏置。解决在信道输入端测量发送符号平均功率在接收端测量噪声方差两者相除得到实际ES/N0再换算成EB/N0。这个实测值能倒推出码率偏差。我在调试时会把所有信噪比的换算参数都打印出来逐一对照。6. 验证手段与收尾技巧用星座图和LLR分布确认链路没有“假收敛”6.1 用星座图确认pi/4-QPSK相位轨迹正确这步验证最简单也最直观在发送端不接噪声取一帧符号画出复数平面上的轨迹。pi/4-QPSK的星座轨迹会呈现两个交替的QPSK星座网格接收端星座点点状图看起来像是8个点旋转对称排列。如果只看到4个点说明差分映射没有生效相位状态没有随符号索引变化。figure; plot(real(rxSymbols(1:200)), imag(rxSymbols(1:200)), .); axis equal; grid on; title(接收星座图前200个符号);我在跑这个项目时把这条验证语句放成了强制检查项。星座图还能同时看出成形滤波是否引起码间串扰如果点云在星座点位置散开成环形大概率是匹配滤波器与发送滤波器不匹配或者定时同步偏差积累。6.2 用LLR绝对值均值观察Turbo迭代的收敛情况把Turbo译码器每一次迭代输出的LLR绝对值均值打印出来正常情况是迭代次数增加、绝对值均值增大并逐渐收敛。如果第二次迭代的均值比第一次还低说明系统比特软信息没有正确进入译码器或者交织方向反了。这个方法比单纯看BER曲线更能定位问题。6.3 一帧大数据量的长时间仿真技巧最后给一个验证链路最终性能的技巧把低信噪比区域用短帧快速筛选只对高信噪比区域跑长帧精确测误码率这一项可以节省十分之九的时间。我在这套资源上跑通后习惯先把迭代次数调成1只验证调制差分解调逻辑再逐步加上迭代次数这样一旦曲线出现异常马上能判断是调制端问题还是Turbo译码问题。从那以后每次拿到新的Turbo链路项目我都强制自己先看星座图、再看LLR均值、最后才上误码率曲线希望这个检查顺序也能帮你少走一点弯路。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑