简介这份资源面向通信工程、信号处理方向的学生与工程师聚焦卷积编码与Viterbi译码的性能对比仿真帮助读者理解软判决与硬判决在实际链路中的差异。包内共16个文件以10个m脚本为核心涵盖编码、调制、匹配滤波、软硬判决接收与译码等模块另有4个mat数据文件保存仿真结果并附1段mp4操作录像和1张效果图压缩包约109.92MB。仿真基于matlab2021a参数设定BitRate9600、ChipRate1228800、N184采用升余弦匹配滤波与给定Viterbi生成多项式可直接复现误码率对比曲线。已有1529人学习读者能获得完整可运行工程、分模块脚本、结果数据与录屏演示便于快速上手并对照排查路径与参数设置问题。1. 卷积编码与Viterbi译码为什么硬判决和软判决的差距值得你亲手跑一遍做通信基带仿真的人迟早会撞上这个问题同样的卷积编码同样的Viterbi译码为什么别人论文里的误码率曲线比你的低两三个数量级答案往往藏在判决方式里。硬判决把解调后的信号硬生生切成0和1再送进译码器软判决则保留解调器输出的多比特量化信息让译码器在欧氏距离层面做最大似然搜索。这个差别在信噪比恶化时会被急剧放大而MATLAB恰好是验证这件事最顺手的工具——不需要射频硬件不需要采集卡一台笔记本就能把编码、调制、信道、解调、译码全链路跑通还能把误码率曲线画出来做定量对比。这篇笔记面向正在做课程设计、论文复现或基带算法选型的工程师和学生从参数设定到代码实现到踩坑排查把卷积编码和Viterbi译码的软硬判决对比仿真讲透让你能自己复现出那条分叉的BER曲线。2. 卷积编码与Viterbi译码的MATLAB建模从网格图到误码率曲线2.1 卷积编码器的参数选择与poly2trellis配置卷积编码的核心参数有三个约束长度、生成多项式、码率。约束长度决定了编码器的记忆深度常见取值是3到9约束长度越大纠错能力越强但译码复杂度指数增长。生成多项式定义了每个输出比特如何由当前输入和移位寄存器状态组合而成。码率是输入比特数与输出比特数之比1/2码率意味着每输入1比特输出2比特冗余度最高纠错能力也最强。MATLAB里用poly2trellis函数把生成多项式转成网格结构这是后续Viterbi译码的基础。以约束长度7、生成多项式[171 133]、码率1/2为例八进制表示下171对应二进制1111001133对应1011011这是NASA深空通信标准里用过的经典配置。% 卷积编码器参数配置 K 7; % 约束长度 G [171 133]; % 生成多项式八进制表示 rate 1/2; % 码率 trellis poly2trellis(K, G); % 生成网格结构 % 查看网格结构的基本信息 numStates trellis.numStates; % 状态数 2^(K-1) 64 numInputs trellis.numInputSymbols; % 输入符号数 2 numOutputs trellis.numOutputSymbols; % 输出符号数 4 fprintf(状态数: %d\n, numStates); fprintf(输入符号数: %d\n, numInputs); fprintf(输出符号数: %d\n, numOutputs);这段代码做了三件事定义约束长度和生成多项式调用poly2trellis生成网格然后打印网格的基本属性。约束长度7意味着64个状态每个状态在输入0或1时各有两条转移路径Viterbi译码就是在这些路径里找累积度量最小的那条。生成多项式[171 133]是行业里经过验证的优选组合自由距离大误码率性能好。如果你换成[7 5]这种约束长度3的配置状态数降到4译码速度快但纠错能力明显下降适合对时延敏感的场景。参数调整时注意约束长度每增加1状态数翻倍Viterbi译码的计算量也大致翻倍。约束长度9时状态数512普通笔记本跑百万比特的仿真还能接受再往上就要考虑硬件加速了。生成多项式不能随便选要查表用那些经过验证的优选对否则自由距离不够误码率曲线会出现错误平台。2.2 硬判决Viterbi译码的完整链路实现硬判决的流程是随机比特流经过卷积编码BPSK调制后通过AWGN信道接收端解调后直接做符号判决得到0/1序列再送入Viterbi译码器。这个过程中解调器输出的软信息被丢弃了只保留了符号极性。% 硬判决Viterbi译码仿真 clear; close all; % 参数设置 K 7; G [171 133]; trellis poly2trellis(K, G); tracebackDepth 5*K; % 回溯深度通常取5倍约束长度 numBits 1e5; % 仿真比特数 EbNo_dB 0:1:8; % 信噪比范围 BER_hard zeros(size(EbNo_dB)); for idx 1:length(EbNo_dB) EbNo 10^(EbNo_dB(idx)/10); % 编码 data randi([0 1], numBits, 1); encoded convenc(data, trellis); % BPSK调制 txSig 2*encoded - 1; % AWGN信道 snr EbNo 10*log10(1/2); % 码率1/2符号信噪比转换 rxSig awgn(txSig, snr, measured); % 硬判决 hardBits rxSig 0; % Viterbi译码 decoded vitdec(hardBits, trellis, tracebackDepth, ... trunc, hard); % 误码率计算 [~, BER_hard(idx)] biterr(data, decoded); end % 绘制BER曲线 semilogy(EbNo_dB, BER_hard, b-o, LineWidth, 1.5); grid on; xlabel(Eb/N0 (dB)); ylabel(误码率 (BER)); title(硬判决Viterbi译码性能);这段代码的关键点在于vitdec函数的参数。第一个参数是硬判决后的比特序列第二个是网格结构第三个是回溯深度。回溯深度取5倍约束长度是经验值太小会导致译码性能下降太大则增加时延和存储开销。trunc表示截断模式适合连续数据流如果是分组数据可以用term模式但需要额外的尾比特把编码器状态归零。hard明确指定硬判决输入。信噪比转换那里有个容易翻车的地方awgn函数的snr参数是符号信噪比而Eb/N0是比特信噪比码率1/2时符号信噪比等于Eb/N0减去3dB。这个换算搞错了整条曲线会平移和别人对比时对不上。2.3 软判决Viterbi译码量化比特数与性能增益软判决不把解调输出切成0/1而是保留多比特量化值送入译码器。Viterbi译码器用欧氏距离代替汉明距离做路径度量信息更丰富性能自然更好。MATLAB里软判决通过vitdec的soft选项和量化参数实现。% 软判决Viterbi译码仿真 nsoft 3; % 量化比特数 BER_soft zeros(size(EbNo_dB)); for idx 1:length(EbNo_dB) EbNo 10^(EbNo_dB(idx)/10); data randi([0 1], numBits, 1); encoded convenc(data, trellis); txSig 2*encoded - 1; snr EbNo 10*log10(1/2); rxSig awgn(txSig, snr, measured); % 软判决量化把接收信号映射到0到2^nsoft-1的整数 % 量化区间根据信号幅度自适应 scale max(abs(rxSig)) / (2^nsoft - 1); softBits round((rxSig max(abs(rxSig))) / scale); softBits max(0, min(2^nsoft - 1, softBits)); % 软判决Viterbi译码 decoded vitdec(softBits, trellis, tracebackDepth, ... trunc, soft, nsoft); [~, BER_soft(idx)] biterr(data, decoded); end % 对比绘制 semilogy(EbNo_dB, BER_hard, b-o, EbNo_dB, BER_soft, r-s, ... LineWidth, 1.5); grid on; legend(硬判决, 软判决(3比特量化)); xlabel(Eb/N0 (dB)); ylabel(误码率 (BER)); title(软硬判决Viterbi译码性能对比);软判决的关键在量化。nsoft取3表示每个符号用3比特表示量化电平数8。量化比特数不是越多越好3到4比特就能拿到大部分软判决增益再增加收益递减而计算量线性增长。量化区间的选择也影响性能这里用最大幅度自适应缩放实际系统中通常根据AGC输出固定量化门限。软判决相比硬判决的增益在AWGN信道下大约是2dB左右具体数值和码率、约束长度有关。这个增益在低信噪比区域更明显高信噪比时两条曲线会逐渐靠近。如果你跑出来的曲线软判决只比硬判决好0.5dB大概率是量化方式有问题检查量化区间是否覆盖了信号的主要动态范围。3. 仿真参数调优与性能对比从BER曲线读出编码增益3.1 回溯深度与译码时延的权衡回溯深度是Viterbi译码里最容易被忽视的参数。它决定了译码器在做出比特判决前需要看多远的未来。理论上回溯深度越大译码越接近最大似然但时延和存储也跟着涨。回溯深度相对性能译码时延存储需求3K基准-0.3dB低小5K基准中中7K基准0.1dB高大10K基准0.15dB很高很大我一般取5K作为起点跑完曲线后如果发现和理论值差距超过0.5dB再往上加到7K试试。超过7K后性能提升微乎其微但时延增加明显实时系统里不划算。注意回溯深度和约束长度是两回事约束长度决定网格复杂度回溯深度决定译码窗口大小。3.2 puncturing与码率调整对BER的影响实际系统里不一定用1/2码率有时为了匹配信道带宽会做删余得到2/3、3/4等更高码率。MATLAB里用convenc和vitdec的删余向量参数实现。% 2/3码率删余卷积编码 puncPattern [1 1 0 1]; % 删余模式 trellis_punc poly2trellis(K, G); % 编码时指定删余 encoded_punc convenc(data, trellis_punc, puncPattern); % 译码时同样指定删余模式 decoded_punc vitdec(encoded_punc, trellis_punc, tracebackDepth, ... trunc, hard, puncPattern);删余模式[1 1 0 1]表示每4个编码比特里删掉第3个码率从1/2变成2/3。删余会降低纠错能力BER曲线整体右移具体右移多少取决于删余模式。选删余模式的原则是尽量均匀删除避免连续删除同一生成多项式产生的比特。2/3码率的编码增益比1/2码率大约少1dB3/4码率再少0.5dB左右。3.3 软判决量化比特数的收益递减规律软判决增益和量化比特数的关系不是线性的。1比特量化就是硬判决2比特量化能拿到大约1.2dB增益3比特量化约1.8dB4比特量化约2dB再往上增加很有限。% 不同量化比特数的软判决性能对比 nsoft_range [1 2 3 4 5]; BER_quant zeros(length(nsoft_range), length(EbNo_dB)); for q 1:length(nsoft_range) nsoft nsoft_range(q); for idx 1:length(EbNo_dB) EbNo 10^(EbNo_dB(idx)/10); data randi([0 1], numBits, 1); encoded convenc(data, trellis); txSig 2*encoded - 1; snr EbNo 10*log10(1/2); rxSig awgn(txSig, snr, measured); if nsoft 1 decoded vitdec(rxSig 0, trellis, tracebackDepth, ... trunc, hard); else scale max(abs(rxSig)) / (2^nsoft - 1); softBits round((rxSig max(abs(rxSig))) / scale); softBits max(0, min(2^nsoft - 1, softBits)); decoded vitdec(softBits, trellis, tracebackDepth, ... trunc, soft, nsoft); end [~, BER_quant(q, idx)] biterr(data, decoded); end end跑完这段代码你会看到2比特到3比特的增益最明显3比特到4比特就小很多了。工程上3比特量化是性价比最高的选择ADC位数少译码器存储和计算量都可控。如果你用4比特以上先确认ADC和前端动态范围能不能支撑否则量化精度上去了但信号本身已经被压缩或截断增益拿不到。4. 仿真避坑与排查那些让BER曲线跑偏的细节4.1 现象软判决BER曲线和硬判决几乎重合原因量化区间设置错误软信息退化成硬信息。常见情况是量化后的整数序列只有两个值或者量化范围远大于信号动态范围导致所有值挤在中间几个电平上。解决在量化后加一行unique(softBits)看看实际用了几个电平。如果只有两个检查scale的计算是否除了零或者信号被限幅了。正常3比特量化应该用到6到8个电平。4.2 现象BER曲线在高信噪比处出现平台原因回溯深度不够或者编码器尾比特没处理干净。截断模式下译码器不知道编码器的终止状态最后几个比特的译码可靠性下降。解决把回溯深度从5K加到7K或者在数据末尾补K-1个零比特做网格终止译码时用term模式。如果平台还在检查生成多项式是不是选错了自由距离不够的码在高信噪比下会出现错误平台。4.3 现象Eb/N0换算错误导致曲线整体平移原因awgn函数的snr参数是符号信噪比不是比特信噪比。码率1/2时符号信噪比等于Eb/N0减3dB码率2/3时减1.76dB码率3/4时减1.25dB。解决统一用snr EbNo 10*log10(rate)做转换rate是码率。转换后和理论曲线对比如果还差1dB以上检查awgn的measured选项是否打开了这个选项会先测量信号功率再加噪声信号功率归一化不对时结果会偏。4.4 现象仿真跑得特别慢百万比特要跑十几分钟原因vitdec在MATLAB循环里逐帧调用每帧都做一次函数开销。或者biterr在循环里反复调用。解决把vitdec的输入改成矩阵形式一次处理多帧。或者用comm.ViterbiDecoder系统对象它支持流式处理和状态复用速度比函数调用快不少。另外把biterr移到循环外累积错误比特数和总比特数最后一次性算BER。4.5 现象软判决增益只有0.5dB远低于理论值原因量化前信号被限幅或AGC范围不对软信息动态范围被压缩。也可能是vitdec的软判决输入范围不对MATLAB要求软判决输入是0到2^nsoft-1的整数如果传了负数或超范围的值译码器内部会截断。解决在量化后加assert(all(softBits 0 softBits 2^nsoft-1))做检查。检查awgn输出信号的幅度分布确保没有超过量化范围。如果信号功率波动大考虑加一个简单的AGC或者用固定量化门限而不是自适应缩放。5. 从仿真到落地用MATLAB Coder生成可移植C代码的实操技巧仿真跑通只是第一步真正要落地到DSP或FPGA上还得把MATLAB代码转成C。MATLAB Coder能把vitdec相关的译码逻辑生成可读的C代码但有几个地方需要提前处理。第一vitdec函数本身不支持代码生成需要用comm.ViterbiDecoder系统对象替代。系统对象支持代码生成配置方式和函数调用类似但参数名不同。第二软判决量化部分要写成显式的整数运算避免浮点比较和动态内存分配。第三回溯深度和网格结构要作为编译时常量传入不能运行时改变。% 可代码生成的Viterbi译码器配置 function decoded viterbi_decode_soft(rxSig, nsoft) persistent hVitDec if isempty(hVitDec) hVitDec comm.ViterbiDecoder(... TrellisStructure, poly2trellis(7, [171 133]), ... InputFormat, Soft, ... SoftInputWordLength, nsoft, ... TracebackDepth, 35, ... TerminationMethod, Truncated); end % 量化 scale 2^nsoft - 1; softBits int32(round((rxSig 1) / 2 * scale)); softBits max(0, min(scale, softBits)); decoded step(hVitDec, softBits); end这段代码用persistent变量保持译码器对象避免每帧重建。comm.ViterbiDecoder的SoftInputWordLength要和量化比特数一致TracebackDepth设成355倍约束长度。量化部分用int32显式类型避免代码生成时的类型推断问题。生成C代码的命令是codegen viterbi_decode_soft -args {zeros(1000,1), 3}生成后在codegen/lib/viterbi_decode_soft目录下能看到C源文件和头文件。生成的代码里译码核心是一个状态转移循环没有动态内存分配可以直接集成到嵌入式工程里。一个血泪经验MATLAB里仿真用的浮点量化在C里要改成定点否则代码生成会报浮点不支持。定点化时注意保留足够的 fractional bits量化误差太大会吃掉软判决增益。我一般先用fi对象在MATLAB里验证定点量化后的BER确认和浮点差距在0.2dB以内再生成C代码。最后说一个验证技巧生成C代码后用同一组输入数据分别跑MATLAB和C版本对比输出比特流是否完全一致。如果不一致大概率是量化边界处理不同检查round和floor的差异以及溢出保护逻辑。这个对比测试能帮你省掉上板调试时才发现问题的后悔药。希望帮到你。本文还有配套的精品资源点击获取
