十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

8ASK+Turbo码的Matlab误码率仿真:从LLR计算到迭代译码

8ASK+Turbo码的Matlab误码率仿真:从LLR计算到迭代译码 简介基于8ASK调制解调与Turbo编译码的通信链路MATLAB误码率仿真资源定位清晰面向通信工程、电子信息类本科生与研究生也适合需要快速搭建数字调制与信道编码联合仿真场景的研发人员。整套资源以RAR压缩包形式发布共12个文件大小仅19KB其中9个.m源码覆盖主程序、Turbo编码/译码、星座映射与误码率统计等模块2个.mat为仿真过程保存的数据文件1个.txt记录程序操作视频的演示地址。当前已有78人学习使用结合CSDN同名博文可对照波形与误码曲线理解不同信噪比下的系统表现。程序基于MATLAB 2024b编写并附带中文注释从随机二进制序列生成、Turbo编码、8ASK调制、AWGN信道加噪到接收端解调、译码与误比特统计流程完整随附操作视频还重点提示了MATLAB当前文件夹路径的设置要点便于新手快速复现并进一步修改参数开展扩展实验。1. 为什么选 8ASK Turbo 来做误码率仿真在通信链路仿真里QPSK 和 16QAM 几乎是默认选项8ASK 反而少见。一个直觉上的原因是 ASK 的星座点只在同相轴上分布频谱效率不如 QAM但在实际工程中8ASK 有一个不易替代的场景当发射机非线性受限、或者系统对峰均比敏感时幅度调制比正交调制更容易控制信号的包络特性。同时8ASK 解调时的判决边界是等间距的软信息计算比 QAM 更直观特别适合用来验证 Turbo 码在非 QPSK 信源下的增益表现。这套仿真的核心意义在于Turbo 码本质上是通过迭代译码获得逼近香农限的性能但它对解调器输出的软信息质量极其敏感。8ASK 每符号承载 3 个比特软信息必须落到比特级而非符号级才能与 Turbo 译码器对接。把这个链路在 Matlab 里跑通既能看清高阶调制下 LLR 计算的细节也能为后续扩展到 16QAM、64QAM 甚至 OFDM 系统打好底子。适合的读者是正在做数字通信课程设计、或者刚接触编码调制联合仿真的工程师。2. 8ASK 调制解调的 Matlab 实现与星座映射2.1 从符号映射到归一化星座点8ASK 的调制把每 3 个比特映射为一个幅度电平。常见做法是自然映射和格雷映射两种。自然映射按二进制数值直接对应电平相邻电平之间可能同时翻转多个比特格雷映射则保证相邻电平只差一个比特在高信噪比下能显著降低误比特率。工程上几乎都采用格雷映射这个选择直接决定后面的 LLR 计算复杂度。8ASK 的星座点分布在实数轴上等间隔排列。常见的电平集合是 {-7, -5, -3, -1, 1, 3, 5, 7}经过能量归一化后变为实际发送符号。归一化因子是所有电平平方和的均值再开方8 个星座点的平均功率是 (192549) * 2 / 8 21所以每个符号要除以 sqrt(21)保证平均符号能量为 1。这一步不做的话后续 Eb/N0 的换算会整体偏移误码率曲线会平移好几个 dB。%% 8ASK 格雷映射表生成 M 8; % 8ASK levels [-7 -5 -3 -1 1 3 5 7]; % 原始电平 Es mean(levels.^2); % 平均符号能量 21 sym levels / sqrt(Es); % 归一化星座点能量为 1 % 格雷码顺序000-001-011-010-110-111-101-100 gray_idx [0 1 3 2 6 7 5 4]; constellation sym(gray_idx 1); % 按格雷序重排 % 每符号对应 3 比特 bitsPerSym log2(M);归一化之后的星座点平均值接近 0方差为 1可以直接用于 AWGN 信道仿真。gray_idx 的顺序必须与实际比特组合一一对应否则解调端映射关系会错乱。建议把这张映射表存成常量调制解调两端共用一份避免手写两遍不一致。2.2 调制解调的映射与硬判决边界调制过程本质上就是查表每 3 个比特算出一个十进制索引再从 constellation 里取出对应符号。解调端在 AWGN 信道下最佳判决边界是相邻星座点的中点即 -6/sqrt(21)、-4/sqrt(21) 等 7 个边界。由于星座等间距这个判决规则等价于找到最近的星座点。硬判决解调虽然简单但它输出的已经是比特丢失了可靠性信息。Turbo 译码需要软信息所以完整链路中不会直接用硬判决但单独验证 8ASK 解调正确性时硬判决是第一步。%% 硬判决解调仅用于验证调制映射 function rxBits ask8_hard_demod(rxSym, constellation) % rxSym: 接收符号含噪声 % 遍历每个接收符号找最近的星座点索引 rxBits zeros(length(rxSym) * 3, 1); for k 1:length(rxSym) [~, idx] min(abs(rxSym(k) - constellation)); % 将索引转回 3 比特 binStr de2bi(idx - 1, 3, left-msb); rxBits((k-1)*31 : k*3) binStr; end end这段代码里min返回最近星座点的下标de2bi把索引转成比特序列。硬判决的复杂度极低但误码率表现平庸——相邻星座点一旦被噪声跨越直接就错 1 个或 2 个比特。格雷映射保证了跨越相邻点只错 1 比特这就是前面选格雷序的原因。2.3 软判决 LLR 计算的边界条件Turbo 译码器需要的是对数似然比即每个比特为 0 或 1 的对数概率比值。8ASK 的软解调需要计算符号级条件概率再按比特位置做边缘化求和。硬判决无法提供置信度而 LLR 的绝对大小恰好表达了置信度|LLR| 越大说明该比特越确定。在 AWGN 信道下条件概率遵循高斯分布。实际仿真中LLR 计算通常用对数域的 max 近似来降低复杂度也就是取最大值代替求和。%% 8ASK 比特级 LLR 计算Max-Log 近似 function LLR ask8_soft_demod(rxSym, constellation, N0) % rxSym: 接收符号序列 % N0: 噪声功率谱密度按 Es/N0 换算 M length(constellation); numBits length(rxSym) * 3; LLR zeros(numBits, 1); for k 1:length(rxSym) % 计算接收符号到每个星座点的欧氏距离平方 dist2 abs(rxSym(k) - constellation).^2; % 条件概率 p(r|x) 与 exp(-dist2/N0) 成正比 logProb -dist2 / N0; % 对每个比特位置分别处理 for b 0:2 % 找出该比特为 0 和 1 的星座点集合 bit0_idx find(bitand(0:7, 2^(2-b)) 0) 1; bit1_idx find(bitand(0:7, 2^(2-b)) ~ 0) 1; % Max-Log 近似取对数域最大值 max0 max(logProb(bit0_idx)); max1 max(logProb(bit1_idx)); LLR((k-1)*3 b 1) max0 - max1; end end end这段代码有两个关键参数。N0 由符号信噪比决定如果星座平均能量为 1则 Es/N0 10^(SNR_dB/10)N0 1 / (Es/N0)。bitand的掩码需要按比特位置左移bid 从高位到低位取掩码 4、2、1。max代替sum会损失约 0.3~0.5 dB 性能但换来了数值稳定性和更快的运行速度——在迭代译码中这个取舍通常值得。3. Turbo 编译码结构与 Matlab 迭代译码3.1 RSC 分量码与交织器的选型Turbo 码的核心是并行级联卷积码由两个递归系统卷积码通过交织器并联而成。分量码一般选择 RSC 而非普通卷积码因为 RSC 本身是递归结构能让低权重码字变少从而获得更好的距离谱。Matlab 通信工具箱里poly2trellis函数可以方便地生成 RSC 网格结构。常用的分量码约束长度是 3 或 4生成多项式 [7 5] 是约束长度 3 中性能最均衡的选择。反馈多项式 7 对应八进制 111即 1 D D^2前向多项式 5 对应 101即 1 D^2。约束长度更大的码性能更好但译码复杂度按状态数指数增长。在误码率仿真里约束长度 3 的码足够验证链路正确性跑起来也快。%% Turbo 编码器参数配置 trellis poly2trellis(3, [7 5], 7); % 约束长度3反馈多项式7前向多项式5 L_total 1536; % 信息位长度含尾部 interleaver randperm(L_total); % 随机交织器仿真时固定种子 % 两个分量编码器的输出 % 第一个直接编码原始序列 % 第二个编码交织后的序列poly2trellis的三个参数分别是约束长度向量、前向生成多项式、反馈连接多项式。返回的 trellis 结构体包含状态转移表、输出表、输入符号表后续convenc和vitdec或手写 MAP 译码都依赖它。交织器的长度必须是信息位长度randperm产生随机置换注意每次运行结果不同仿真对比时必须用rng固定种子。3.2 编码流程与打孔速率匹配Turbo 编码器的输出由三部分组成第一分量码的系统位、第一分量码的校验位、第二分量码的校验位。码率 1/3 时不打孔三个部分全部发送抗噪能力最强但效率低。如果想提升频谱效率可以采用打孔矩阵只保留部分校验位得到 1/2 码率。在 8ASK 链路里打孔之后的信息还要经过串并转换变成 3 比特一组再映射到 8ASK 符号。这个拼接过程最容易出错的是比特顺序系统位、校验位在交织前后的排列必须严格对应译码端的分路逻辑。%% Turbo 编码码率 1/3 function [sys1, par1, par2] turbo_encode(bits, trellis, interleaver) % 第一分量编码器输入为原始比特 sys1 convenc(bits, trellis, truncated); % 提取系统位和校验位RSC 输出 [系统位, 校验位] 交替 sys1 sys1(1:2:end); par1 sys1_parity(bits, trellis); % 实际由 convenc 输出分离 % 第二分量编码器输入为交织后的比特 bits_inter bits(interleaver); sys2 convenc(bits_inter, trellis, truncated); par2 sys2_parity_from_output(sys2); end3.3 Max-Log-MAP 迭代译码的信息传递Turbo 译码的精髓是两个分量译码器交换外部信息。第一分量译码器利用信道信息和先验信息输出关于信息位的外信息这个外信息经过交织后成为第二分量译码器的先验信息第二分量译码器输出外信息再解交织回来作为第一轮迭代的先验输入。如此循环若干次最后对两个译码器的输出做硬判决。Max-Log-MAP 简化了经典 MAP 算法中对数域指数运算把前向、后向递推中的加减运算改为取最大值。这个近似会损失约 0.3 dB但避免了 exp 和 log 的反复调用仿真速度提升明显。工程中还有一种线性近似如 Constant-Log-MAP在性能与复杂度之间折中不过教学仿真里 Max-Log-MAP 足够说明问题。%% Max-Log-MAP 单次分量译码核心递推示意 function LLR_out max_log_map(rx_sys, rx_par, L_a, trellis) % rx_sys: 接收系统位软信息 % rx_par: 接收校验位软信息 % L_a: 先验 LLR初始为 0迭代时来自另一个分量译码器 numStates trellis.numStates; numSym length(rx_sys); % 前向递推 alpha -inf(numStates, numSym 1); alpha(1, 1) 0; % 初始状态为 0 for k 1:numSym for s 1:numStates % 遍历输入比特 0/1计算分支度量 % 分支度量 系统位软信息 * 输入比特 校验位软信息 * 校验比特 for u 0:1 [nextState, outBits] next_state_output(trellis, s-1, u); branchMetric rx_sys(k) * (1-2*u) ... rx_par(k) * (1-2*outBits(2)) ... L_a(k) * (1-2*u) / 2; alpha(nextState1, k1) max(alpha(nextState1, k1), ... alpha(s, k) branchMetric); end end end % 后向递推与总 LLR 合并省略与前向对称 % LLR_out max(后向前向分支 对u0) - max(对u1) end这段代码展示的是前向递推的框架。next_state_output需要按 trellis 结构拆出已知输入状态下的下一状态和输出比特分支度量由三部分组成信道系统位软信息、信道校验位软信息、先验 LLR。注意先验 LLR 前面有系数 1/2因为 LLR 定义为 ln(P(u1)/P(u0))在概率计算中要做换算。这是最容易被手写译码器忽略的系数错误。迭代次数是个关键参数。迭代太少外信息没有充分交换迭代太多性能收敛后不再提升只会增加计算时间。一般 3~5 次即可8ASK 链路因为 LLR 质量不如 BPSK有时需要 6 次才完全收敛。实际仿真时可以每轮迭代后记录一次误码率看 5 次与 8 次之间是否有明显差异。4. 8ASK Turbo 完整链路与 Eb/N0 误码率仿真4.1 帧结构设计与发射端信号流完整链路需要把前面各模块串起来。发射端的数据流是随机比特生成 → Turbo 编码 → 比特加交织可选→ 串并转换 → 8ASK 映射 → 加噪声。接收端是8ASK 软解调 → 比特级 LLR → 解交织如在发射端做过→ Turbo 迭代译码 → 误码率统计。帧长的选择在误码率仿真中直接影响结果稳定性。帧太短单个帧的误码个数太少统计起伏大帧太长仿真时间成倍增加。常见的做法是取信息位 1024~2048 比特每个 Eb/N0 点跑 100~500 帧。Turbo 码是分组处理的结构译码器需要完整帧才能开始迭代所以帧长也决定了交织器长度和译码延迟。%% 完整仿真链路主循环单个 Eb/N0 点 rng(42); % 固定随机种子保证可复现 EbN0_dB 4:0.5:8; % 仿真范围按 Turbo 增益调整 numFrames 200; % 每点帧数 maxIter 5; % Turbo 迭代次数 for idx 1:length(EbN0_dB) totalBits 0; totalErr 0; for frame 1:numFrames % 1. 生成随机信息位 infoBits randi([0 1], L_total, 1); % 2. Turbo 编码得到系统位 两路校验位 % 3. 串并转换为 每符号3比特 的格式 % 4. 按 8ASK 映射表调制为符号 txSym map_bits_to_8ask(turboCodedBits, constellation); % 5. 计算噪声功率并加噪 EsN0_dB EbN0_dB(idx) 10*log10(3); % 每符号3比特 N0 10^(-EsN0_dB/10); % 符号能量归一化为1 noise sqrt(N0/2) * randn(size(txSym)); rxSym txSym noise; % 6. 软解调得到比特级 LLR LLR_in ask8_soft_demod(rxSym, constellation, N0); % 7. Turbo 迭代译码 decodedBits turbo_decode(LLR_in, trellis, interleaver, maxIter); % 8. 统计误码 errs sum(decodedBits ~ infoBits); totalErr totalErr errs; end BER(idx) totalErr / (L_total * numFrames); end这段主循环把整条链路的骨架写清楚了。值得注意的换算关系在第 5 步Eb/N0 到 Es/N0 的转换必须加上每符号比特数 3因为每个 8ASK 符号载 3 个信息比特。如果 Turbo 编码码率是 1/3那每个 8ASK 符号实际只载 1 个信息比特这个换算还要进一步调整。具体规则是Es/N0 Eb/N0 10log10(每个符号承载的信息比特数)。4.2 关键仿真参数表与预期曲线形态下面这张参数表是这套 8ASK Turbo 链路的推荐起点适用于教学和工程预研。信噪比范围要根据 Turbo 编码增益动态调整未编码 8ASK 在 BER1e-3 时需要约 11 dB加入码率 1/3 的 Turbo 码后理论上可以把这个点拉到 3~4 dB 附近前提是 LLR 计算正确且迭代充分。参数推荐值说明调制方式8ASK每符号 3 比特格雷映射分量码约束长度 3[7 5]poly2trellis(3, [7 5], 7)码率1/3 或打孔到 1/21/3 更接近香农限信息位长度1536交织器长度建议 1024交织器randperm rng 固定不能每帧重新随机迭代次数5低于 3 增益明显损失仿真帧数200~500低信噪比点多跑帧数Eb/N0 范围2~8 dB步进 0.5视实际 BER 再扩范围误码率曲线的形态可以参考 Turbo 码的典型特征在高信噪比区域会出现明显的“误码率瀑布”下降斜率远陡于未编码曲线随后进入错误地板区交织器长度和分量码自由距离决定地板高度。如果仿真曲线没有出现瀑布区大概率是 LLR 符号或先验信息的正负号反了这是 Turbo 链路排错中最常见的问题。4.3 仿真耗时控制与低信噪比点统计策略误码率仿真的耗时瓶颈集中在 Turbo 迭代译码特别是后向递推需要完整保存前向状态度量再回推。200 帧、每帧 1536 比特、5 次迭代、7 个信噪比点在普通桌面级 CPU 上大约需要 10~30 分钟取决于是否使用 MATLAB 的并行计算。这个时间在工程上是可接受的但如果帧数加到 1000单次仿真就可能跑到小时级。减少耗时的常用做法是分两层。第一层低信噪比区域BER 在 1e-1 到 1e-2每帧误码数量多100 帧的统计可信度就足够高信噪比区域BER 在 1e-4 以下可以逐步增加帧数到 500 甚至 1000确保能统计到足够的误码个数。第二层把for frame换成parfor在并行池开启的情况下可以获得接近线性的加速。%% 不同信噪比点差异化帧数配置 if EbN0_dB(idx) 5 numFrames 150; % 低信噪比区帧数少 elseif EbN0_dB(idx) 7 numFrames 300; else numFrames 600; % 高信噪比区必须多跑 end另一个容易被忽略的细节是误码率为 0 时的处理。如果 600 帧全对曲线会得到一个 0 点在 log 坐标下无法显示。常见的做法是设置最小误码数阈值比如至少统计到 50~100 个误码才认为该点可信否则继续增加帧数。这也解释了为什么高信噪比点的仿真时间最长。5. 排错技巧与链路验证方法Turbo 链路第一次跑通时最常见的现象是 BER 曲线在 0.3~0.5 处平坦下降也就是误码率只降不发。这通常是 LLR 计算里的一个符号问题系统位 LLR 或校验位 LLR 的极性定义不一致导致迭代译码时外信息在互相削弱甚至反向。验证方法很简单——把迭代次数改为 1、先验 LLR 置 0跑一次硬判决译码如果此时 BER 已经能明显下降说明前端调制解调正确问题出在迭代环节的信息极性。还有一类隐蔽问题来自交织器。randperm每次调用都产生不同的置换如果编码端和解码端各自调了一次randperm交织映射必然错位。解决方案是把交织器向量保存为工作区变量或者用rng固定种子后生成一次全局统一使用。更稳妥的做法是把交织器和解交织器做成函数的输入参数而不是在函数内部重新生成。调制符号归一化也是个常见坑。如果发送符号能量不是 1那么 N0 的换算会系统性偏移。验证方法是在加噪前计算mean(abs(txSym).^2)确认结果接近 1。如果偏了检查归一化因子是否用了峰值电平而非平均能量。这个错误会让整条 BER 曲线左右平移单看某一条曲线很难发现但和理论曲线对比时一目了然。最后给一个链路自检的具体操作顺序%% 自检步骤先验证无编码 8ASK 解调 % 1. 生成随机比特 → 8ASK 映射 → 加噪 → 硬判决解调 → 对比误码率 % 应与理论 8ASK 误码率曲线吻合相差 0.2 dB 以内 % 2. 加入 Turbo 编码但译码器迭代次数设为 1验证最小功能 % 3. 迭代次数从 2 逐步加到 5观察 BER 是否单调下降 % 4. 固定某一 Eb/N0 点分别打印每次迭代后的 BER这样做的思路是把链路拆成三段独立验证。第 1 步确认信道模型无误第 2 步确认编码译码能工作第 3 步确认迭代有效果。建议第 4 步把每轮迭代的 BER 打印出来如果第 2 轮到第 3 轮几乎没有改善说明外信息被过度放大或极性错误需要检查 Max-Log-MAP 中分支度量公式里的系数。当前 Matlab 主流的几个版本对poly2trellis和convenc的函数行为一致但 R2021b 之后部分工具箱函数有性能重构遇到结果异常时可以优先排查是否用了新版本引入的默认参数变化。本文还有配套的精品资源点击获取
返回列表