ARTICLE DETAIL

资讯详情

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

Turbo均衡原理与Matlab仿真:从MAP算法到迭代接收机实现

Turbo均衡原理与Matlab仿真:从MAP算法到迭代接收机实现 简介面向通信与信号处理学习者的一套Turbo均衡与MAP算法仿真资料包重点解决多径衰落信道下接收信号失真、误码率偏高等问题。资源共227个文件压缩包约2.79MB以112个mat数据文件、48个m脚本、16个c源文件和14个fig图像为主涵盖QPSK、8PSK、16QAM等调制的MAP均衡仿真、卷积编码解码与迭代处理流程便于直接运行或二次开发。已有667人学习适合正在研究信道均衡、Turbo码或相关课程设计的学生与工程师使用。资料包含仿真主程序、前后向均衡器实现、信道构建与误码率统计脚本并通过结果图直观展示均衡前后性能对比目录按调制方式与功能模块组织整体覆盖从信道建模、均衡器设计到误码率分析的完整链路可帮助读者深入理解MAP算法如何利用后验概率逐步逼近最优序列以及Turbo迭代机制在卷积码与高阶调制系统中的实际效果。1. Turbo均衡不是“Turbo码均衡”而是把译码软信息送回均衡器做过信道均衡的人都知道最小均方误差均衡器MMSE-LE和判决反馈均衡器DFE在深衰落多径信道下误码率平台压不下去。真正能把性能推到接近最大似然序列估计MLSE的方案是Turbo均衡。它并不是Turbo码和均衡器的简单串联而是把信道编码器的软译码结果——外信息——送回均衡器把均衡从“单次线性滤波”变成“迭代联合检测与译码”。这套思想最早源自Turbo码的迭代译码但套在信道均衡上同样有效。本文面向想自己搭仿真链路、看懂MAP均衡内核、以及被“迭代后BER不降反升”折磨的工程师。我会从MAP算法讲起给出可直接运行的Matlab仿真骨架再把收敛性、参数边界和踩坑点一次说清。2. 从MAP均衡到Turbo均衡先搞懂这两步迭代在做什么2.1 信道均衡在解决什么问题ISI与最大后验准则移动通信里信号经过多径信道当前符号会与前后符号叠加形成码间干扰ISI。均衡器的任务就是从接收序列 ( y_k ) 中恢复发送符号 ( x_k )。传统线性均衡用有限长抽头滤波器逼近信道逆噪声会被放大。MAP均衡走的是另一条路在已知信道冲激响应CIR和噪声方差的前提下计算每个发送符号的后验概率 ( P(x_k | \mathbf{y}) )然后取使后验概率最大的符号作为输出。这个准则在加性高斯白噪声AWGN下是最优的因为它最小化符号错误概率。但直接计算后验概率需要遍历所有可能的符号序列。对长度 ( L ) 的序列、( M ) 进制调制状态数是 ( M^{L} )完全不可行。实际的MAP均衡器利用信道的有限记忆特性把问题建模成隐马尔可夫模型HMM用BCJR算法在网格图上做前向-后向递归复杂度从指数级降到 ( O(L \cdot M^{S}) )其中 ( S ) 是信道记忆长度抽头数减一。这就是MAP均衡的理论根基。2.2 MAP均衡器的栅栏图与对数域实现BCJR算法需要信道网格图。假设信道冲激响应有 ( S1 ) 个抽头 ( h_0, h_1, \dots, h_S )那么时刻 ( k ) 的接收信号是[ y_k \sum_{i0}^{S} h_i x_{k-i} n_k ]网格状态由最近 ( S ) 个发送符号组成( \sigma_k (x_{k-1}, x_{k-2}, \dots, x_{k-S}) )。从状态 ( \sigma_k ) 转移到 ( \sigma_{k1} ) 时输入符号 ( x_k ) 决定分支度量[ \gamma_k(\sigma_k, \sigma_{k1}) P(x_k) \cdot \exp\left( -\frac{|y_k - \sum_i h_i x_{k-i}|^2}{N_0} \right) ]其中 ( P(x_k) ) 是先验概率第一次迭代时假设等概。前向度量 ( \alpha_k(\sigma_k) ) 和后向度量 ( \beta_k(\sigma_k) ) 分别从序列首尾递归计算[ \alpha_{k1}(\sigma_{k1}) \sum_{\sigma_k} \alpha_k(\sigma_k) \cdot \gamma_k(\sigma_k, \sigma_{k1}) ][ \beta_k(\sigma_k) \sum_{\sigma_{k1}} \beta_{k1}(\sigma_{k1}) \cdot \gamma_k(\sigma_k, \sigma_{k1}) ]最后符号 ( x_k ) 的后验概率由三个度量的乘积求和得到。实际工程中所有计算都在对数域进行把乘法变成加法避免数值下溢。对数域的BCJR就是Log-MAP算法Max-Log-MAP则用最大值近似求和性能损失约0.20.3 dB但实现简单得多。MAP均衡器的输出是对数似然比LLR不是硬判决符号这一点是Turbo均衡能迭代的前提。2.3 外信息是怎么被“挤”出来的Turbo均衡的关键在于信息论上的“外信息”概念。均衡器输出LLR包含三部分来自信道的观测信息、来自符号先验的信息、以及本次迭代新产生的信息。如果直接把输出LLR当作先验送回去信息会被重复使用迭代会自嗨式收敛到错误结果。正确做法是从输出LLR中减去输入先验LLR差值就是外信息。这个外信息送给译码器译码器也按同样的规则产生自己的外信息再送回均衡器。往复迭代均衡器和译码器互相交换“对方不知道的新信息”直到两者都收敛。我在仿真里看到很多人第一次写Turbo均衡忘了做“减先验”这一步。结果是BER曲线在第一次迭代后漂亮地下降第二次迭代直接崩掉。原理上就是信息泄漏。后面我会在避坑章节专门展开这部分。3. 搭建一个可复现的Turbo均衡仿真链路以BPSKProakis C信道为例3.1 系统模型与参数表这一节给出一个最小可复现的仿真骨架。选BPSK调制是因为符号只有 ±1MAP均衡器的网格状态更直观信道选Proakis C它的冲激响应是 ( h [0.227, 0.460, 0.688, 0.460, 0.227] )频谱凹口严重是检验均衡器性能的经典信道。Turbo均衡的外码用简单卷积码译码器用Log-MAP也可以换成BCJR。系统参数如下表照着这个配置跑能把问题限定在“均衡与迭代”本身而不是被高阶调制分散注意力。参数取值说明调制方式BPSK符号映射0→11→-1信道Proakis C5抽头能量归一化编码器卷积码 (7,5) 八进制码率1/2约束长度3交织器随机交织块长1024按帧处理迭代次数4后续可调接收端算法Log-MAP均衡 Log-MAP译码均为软输入软输出信噪比定义( E_b/N_0 )每信息比特3.2 发射机与信道从比特到接收序列仿真链路从随机信息比特开始经过卷积编码、交织、BPSK映射再通过多径信道叠加高斯噪声。这里给出发射机部分代码重点看交织器的作用它把突发错误打散成随机错误让Turbo均衡的外信息相关性降低。% 参数 frameLen 1024; % 交织块长度 h [0.227, 0.460, 0.688, 0.460, 0.227].; % Proakis C h h / sqrt(sum(h.^2)); % 能量归一化保证 SNR 定义一致 trellis poly2trellis(3, [7 5]); % 卷积码 (7,5) interleaver randperm(frameLen); % 随机交织器 % 发射机 infoBits randi([0 1], frameLen/2, 1); % 信息比特码率 1/2 codedBits convenc(infoBits, trellis); % 卷积编码长度 frameLen interleavedBits codedBits(interleaver); % 交织 symbols 2 * interleavedBits - 1; % BPSK: 0 - -1, 1 - 1 % 经过多径信道保持长度不变用全卷积 txSignal filter(h, 1, symbols); % 加噪声EbN0 需扣除编码率与调制阶数 EbN0_dB 6; % 仿真点 EbN0 10^(EbN0_dB/10); N0 1 / (2 * EbN0); % BPSK 且码率 1/2 时的单边噪声功率谱密度 noise sqrt(N0/2) * randn(size(txSignal)); rxSignal txSignal noise;参数说明filter(h, 1, symbols)做的是有限冲激响应滤波输出长度和输入一致但会引入符号间拖尾N0 1/(2*EbN0)是从 ( E_b/N_0 ) 反推出来的前提是码率1/2、BPSK所以每个编码符号对应 ( E_s E_b \cdot R E_b/2 )功率还要注意信号方差为1。如果调制阶数变了这个换算必须同步改否则仿真曲线整体偏移。3.3 单次MAP均衡器内核BCJR前向后向递归这是整个Turbo均衡的核心。输入接收序列rxSignal、信道冲激响应h、以及符号先验LLRL_a输出符号后验LLRL_p。实现采用对数域Log-MAP对BPSK网格做了状态枚举。function L_p map_equalizer_bpsk(rxSignal, h, L_a) % rxSignal: 接收序列 (N x 1) % h: 信道冲激响应 (S1 x 1) % L_a: 符号先验LLR正数代表符号1更可能 (N x 1) % L_p: 符号后验LLR (N x 1) N length(rxSignal); S length(h) - 1; % 信道记忆长度 numStates 2^S; % BPSK 状态数 states zeros(numStates, S); % 状态对应的历史符号 [x_{k-1},...,x_{k-S}] for s 1:numStates bits de2bi(s-1, S, left-msb); % 注意 bit 顺序约定 states(s,:) 2*bits - 1; end % 建立状态转移表prevState, inputSymbol, nextState, branchIndex trans []; for s 1:numStates for x [-1 1] prevHist states(s,:); nextHist [x, prevHist(1:end-1)]; nextState find(all(states nextHist, 2)); trans [trans; s, x, nextState]; end end % 前向递归 alpha zeros(numStates, N1); alpha(:,1) log(1/numStates) * ones(numStates,1); % 初始等概 for k 1:N gamma_k zeros(numStates, 2); % 第1列输入-1第2列输入1 for s 1:numStates for i 1:2 x (i2)*2 - 1; % -1 或 1 % 分支度量信道无噪声部分 % 需要找到转移到哪个下一个状态但 gamma 按当前状态和输入存即可 ideal sum(h .* [x, states(s,:)]); % 注意 [x, x_{k-1},...] gamma_k(s,i) L_a(k) * x / 2 ... % 先验信息映射到LLR - abs(rxSignal(k) - ideal)^2 / N0; end end % 从 alpha(k) 推 alpha(k1) for sNext 1:numStates alpha(sNext, k1) -inf; for t 1:size(trans,1) if trans(t,3) sNext sPrev trans(t,1); x trans(t,2); inputIdx (x1) 1; alpha(sNext, k1) logsumexp(alpha(sNext, k1), ... alpha(sPrev, k) gamma_k(sPrev, inputIdx)); end end end end % 后向递归 beta zeros(numStates, N1); beta(:,N1) log(1/numStates) * ones(numStates,1); for k N:-1:1 for sPrev 1:numStates beta(sPrev, k) -inf; for t 1:size(trans,1) if trans(t,1) sPrev sNext trans(t,3); x trans(t,2); inputIdx (x1) 1; % 这里 gamma 要用第 k 时刻的似然项 ideal sum(h .* [x, states(sPrev,:)]); gammaVal L_a(k) * x / 2 - abs(rxSignal(k) - ideal)^2 / N0; beta(sPrev, k) logsumexp(beta(sPrev, k), ... gammaVal beta(sNext, k1)); end end end end % 输出后验LLR L_p zeros(N, 1); for k 1:N logSumPlus -inf; logSumMinus -inf; for t 1:size(trans,1) sPrev trans(t,1); x trans(t,2); inputIdx (x1) 1; ideal sum(h .* [x, states(sPrev,:)]); gammaVal L_a(k) * x / 2 - abs(rxSignal(k) - ideal)^2 / N0; logProb alpha(sPrev,k) gammaVal beta(trans(t,3), k1); if x 1 logSumPlus logsumexp(logSumPlus, logProb); else logSumMinus logsumexp(logSumMinus, logProb); end end L_p(k) logSumPlus - logSumMinus; end end function z logsumexp(a, b) if a -inf, z b; return; end if b -inf, z a; return; end m max(a, b); z m log(exp(a-m) exp(b-m)); end逻辑说明alpha和beta每个时刻都存所有状态的对数概率。gamma_k(s,i)在公式里需要一个全局的 ( N_0 )这段代码里没有传进来实际跑的时候可以把N0定义为全局变量或作为参数传入。分支度量里L_a(k) * x / 2是把先验LLR ( L_a \log(P(x1)/P(x-1)) ) 转换成 ( P(x) ) 的对数概率推导关系是 ( \log P(x) L_a x/2 - \log(2\cosh(L_a/2)) )常数项对LLR差值无影响所以省略。trans表的构建花了一点时间但只需要建一次可以提到循环外面避免重复计算。参数说明de2bi(s-1, S, left-msb)的比特顺序决定了状态编码。这里把[x_{k-1}]放在高位[x_{k-S}]放在低位与filter的抽头系数顺序无关只要保证[x, states(s,:)]和h的排列一致即可。如果你的信道抽头顺序是从最新到最老状态顺序就要反过来。这是最容易算错的地方。3.4 迭代Turbo均衡主循环交换LLR与先验信息有了单次MAP均衡器Turbo均衡主循环就是一个循环。每次迭代先做MAP均衡输出后验LLR减去输入先验得到外信息解交织后送译码器译码器输出后验LLR减去输入先验交织后作为下一次均衡的先验。% 初始化 L_a_equalizer zeros(frameLen, 1); % 均衡器输入先验初始无信息 L_a_decoder zeros(frameLen, 1); % 译码器输入先验 for iter 1:4 % 1. MAP均衡 L_p_equalizer map_equalizer_bpsk(rxSignal, h, L_a_equalizer); % 2. 外信息 后验 - 先验 L_e_equalizer L_p_equalizer - L_a_equalizer; % 3. 解交织作为译码器输入先验 L_a_decoder L_e_equalizer(interleaver); % 注意 interleaver 是发射端交织映射 % 4. 卷积码Log-MAP译码这里简化为调用详见函数 % 译码器输入包括系统位和校验位的LLR % 按码率1/2需要拆成两路 L_p_decoder logmap_decode(L_a_decoder, trellis); % 5. 译码器外信息 L_e_decoder L_p_decoder - L_a_decoder; % 6. 交织后送回均衡器 L_a_equalizer L_e_decoder(interleaver); % 7. 记录本次迭代的BER hardBits double(L_p_decoder(1:2:end) 0); % 取信息位硬判决 ber sum(hardBits ~ infoBits) / length(infoBits); fprintf(iter%d, BER%.6f\n, iter, ber); end逻辑说明第3步的解交织映射要格外小心。interleaver定义的是发射端从codedBits的第interleaver(k)位取数放到第k位所以接收端解交织应该让L_a_decoder(interleaver(k)) L_e_equalizer(k)这段代码里写的是L_e_equalizer(interleaver)如果你习惯用interleaver 1:frameLen的随机排列两条写法含义不同。我在调试时经常在这里栽跟头建议写个单元测试在发射端用固定交织表手动检查第 k 个编码比特跑到哪个位置。第4步的logmap_decode没有给出完整实现因为它是标准BCJR译码网上有现成代码。核心点在于译码器的先验输入需要拆分对码率1/2卷积码交织块内前1024位是信息位后1024位是校验位取决于convenc输出排列L_a_decoder里前一半是编码信息位的LLR后一半是校验位的LLR。如果交织破坏了这种排列需要按交织规则重新映射。这一处直接决定迭代是否有效。4. 参数调优与避坑为什么你的Turbo均衡不收敛4.1 现象迭代后BER反而升高我最初调Turbo均衡时第一次迭代BER下降了第二次迭代直接抬升第三次发散到接近0.5。后来排查发现问题出在“先验信息泄漏”上。原因是译码器输出的后验LLR包含了均衡器送来的信息如果直接把后验LLR当先验送回去就相当于让均衡器看到自己上一轮的输出迭代快速收敛到局部最优——而这个局部最优往往是全零或全一序列。解决严格使用外信息L_e L_p - L_a。这是Turbo原理的铁律。但更隐蔽的是即使在代码里写了减法某些MAP实现内部会重复使用先验。比如在分支度量计算时把先验加进去输出LLR里自然包含先验输出端再做一次减法实际上只减了一次系数导致残余泄漏。注意如果使用Max-Log-MAP外信息还要乘一个迭代缩放因子通常取0.71.0。因为Max-Log-MAP的LLR幅度偏大直接迭代容易振荡。4.2 原因与解决先验信息重复使用、缩放因子、交织器设计我见过三类必踩的坑第一类是信息重复使用。表象是迭代BER不单调下降或者在某个Eb/N0点突然发散。检查方法把每次迭代的外信息打印出来看方差是否持续增大或出现正反馈振荡。解决方案就是严格用外信息并且在外信息送入对方前做动态范围限制硬限幅。第二类是交织器设计不当。Turbo均衡对交织器没有Turbo码那么敏感但如果交织器把相邻编码比特映射到信道冲激响应相近的位置突发错误就不能被有效打散。我常用的是伪随机交织器但会检查“最小交织距离”——任意两个原始距离小于5的比特交织后距离至少大于信道记忆长度。你不必追求理论交织增益但至少要避免交织后大量相邻比特仍然相邻。第三类是迭代初期的先验不可靠。第一次迭代均衡器没有先验输出外信息噪声大如果直接以全强度参与第二次迭代可能会把错误信息放大。解决方式是加一个迭代控制第一次迭代的译码外信息乘一个较小的权重例如0.5再送回均衡器后面逐次加大。这类做法的工程术语叫“迭代阻尼”或者“更新松弛”在迭代接收机里很常见。4.3 常见问题清单现象原因解决第2次迭代BER比第1次高先验信息泄漏外信息计算有误检查 L_e L_p - L_a并确认交织/解交织索引方向BER一直平躺不随迭代下降交织器设计有问题错误没有被打散改用随机交织检查最小交织距离高信噪比时BER震荡Max-Log-MAP近似导致LLR超估使用Log-MAP或对LLR乘以0.7~0.8缩放低信噪比时迭代比单次还差噪声环境下先验不可靠被放大对先验LLR做限幅如限制到 ±4仿真曲线有错误平层信道抽头顺序与网格状态定义不对应验证信道卷积顺序比较分量输出与实际接收译码输出全零交织后系统位与校验位混淆检查译码器输入LLR拆分位置按这个清单逐项对照能解决90%以上的Turbo均衡仿真“玄学问题”。我自己的经验是先关掉迭代只跑MAP均衡确认单次BER符合预期再开迭代每次迭代后打印外信息的均值与方差。外信息均值应该随迭代逐渐增大但方向保持一致如果出现均值正负交替多半是交织或解交织索引写反了。5. 从BPSK到高阶调制与多径信道MAP均衡的边界5.1 高阶QAM下的MAP计算量爆炸BPSK的网格状态数是 ( 2^S )16QAM就是 ( 16^S )。Proakis C信道 ( S4 )BPSK状态数1616QAM状态数65536每个状态还要遍历16种输入符号计算量增长三个数量级。直接做Map均衡在实时系统里基本不可行。解决高阶调制的常见方案是“降低状态数”。例如先做线性均衡把信道缩短到12个抽头再对缩短后的信道做MAP或者用判决反馈把信道尾部反馈掉只对前几个抽头做MAP。这就是减少状态BCJRRSSD的思路。工程上很多人直接用基于MMSE的Turbo均衡用一次线性滤波近似MAP均衡器复杂度只随调制阶数线性增长性能损失在0.5dB以内。5.2 近似MAPMax-Log-MAP与线性均衡软反馈Max-Log-MAP是Log-MAP的近似把logsumexp换成max。在BPSK下性能损失约0.2dB在16QAM下损失可能到0.5dB以上。原因是高阶调制的分支度量差异更大最大值近似的偏差被放大。我一般建议仿真阶段先用Log-MAP确认理论上限再切换到Max-Log-MAP评估实现代价。如果两者差距在可接受范围就直接用Max-Log-MAP因为它能同时配合查表法或CORDIC实现适合FPGA。线性均衡软反馈是另一种主流近似。它的做法是用MMSE滤波器处理接收序列得到符号的软估计 ( \hat{x}_k )然后用“软符号” ( E[x_k | L_a] ) 重构干扰从接收信号中减去。这个过程迭代进行和判决反馈均衡器结构相似但反馈的是软符号而非硬判决。软符号的计算是[ E[x_k] \sum_{x \in \mathcal{X}} x \cdot P(x | L_a) ]在BPSK下软符号就是 ( \tanh(L_a/2) )。这个方案复杂度低而且很容易扩展到16QAM、64QAM。它的缺点是信道严重频率选择性时MMSE滤波器的逆运算会放大噪声收敛速度不如MAP。我通常在信道抽头数不超过5时用MAP超过5或者实时性要求高时用软反馈MMSE。5.3 时变信道与信道估计误差对Turbo均衡的影响以上讨论都假设接收端知道精确信道 ( h )。实际系统中信道是估计出来的误差会直接影响MAP分支度量。Turbo均衡对信道估计误差很敏感——因为迭代过程中均衡器会把信道模型当作真实信道反复“拟合”模型误差会被迭代放大。我第一次做车载信道仿真时就踩过静态信道下Turbo均衡增益1.5dB在时变信道下增益变成负数。解决方向有两条一是降低迭代敏感度。把信道估计值加一个最小方差约束例如用MMSE信道估计而非LS估计或者对信道抽头幅度设置一个下限避免深衰落抽头估计接近0时造成网格里该抽头信息完全依赖估计值。二是用软信息辅助信道估计。做法是每次迭代后利用译码软符号重构发送序列与接收序列做相关估计信道。这就是“Turbo信道估计”。工程上我不推荐自己从头实现优先用已有工具箱里的迭代接收机框架但你要理解它的核心估计、均衡、译码三者在迭代中循环任何一环的误差都会影响整体。如果只是为了验证算法固定信道加适度估计误差比如SNR20dB下估计出的信道就足够暴露问题。6. 验证与进阶用EXIT图判断收敛性少跑一半仿真Turbo均衡调试中最耗时的是扫BER曲线。每次迭代都要跑几十万比特才能把误码率压到 ( 10^{-4} ) 以下。我习惯先用EXIT图外部信息转移图做快速判断不需要跑完整链路就能看出迭代能否收敛以及需要几次迭代。EXIT图的做法是分别独立测量均衡器和译码器的“输入输出互信息”然后画在同一个坐标系里。横轴是输入先验互信息 ( I_A )纵轴是输出外信息互信息 ( I_E )。如果两条曲线只相交一次且交点靠近 ( I_E1 )说明迭代能够收敛到接近无误码如果曲线交叉位置低于0.5说明会卡在错误地板。计算互信息的简化方法是直接用蒙特卡洛给输入LLR叠加高斯噪声来模拟不同质量的先验统计输出LLR的互信息。互信息的公式在AWGN信道下有闭式近似[ I(\lambda) 1 - E\left[ \log_2(1 e^{-\lambda}) \right] ]其中 ( \lambda ) 是带符号的LLR符号由真实发送比特决定。一段Matlab脚本如下function I mutual_information_llr(lambda) % lambda: 带符号的LLR正数对应发送比特为1 lambda lambda(:); I 1 - mean(log2(1 exp(-lambda))); end注意这个近似在LLR幅度较大时数值不稳定建议把lambda限幅到 ±10。对均衡器生成不同质量的先验可以用L_a mu * realBits sigma * randn其中mu sigma^2 / 2遍历sigma得到一组 ( I_A )对应得到 ( I_E )。对译码器同理。两条曲线画在一张图里均衡器曲线通常随输入互信息上升译码器曲线则相反译码器输入互信息增大时输出也增大。用EXIT图选迭代次数看两条曲线之间的距离。距离大说明收敛快一般3次迭代就够距离小说明接近瓶颈迭代10次也未必能提升。我现在的调试习惯是先用EXIT图粗选交织器和迭代次数再跑BER曲线确认。EXIT图单次计算几万比特就够BER仿真要几百万比特时间省一个数量级。最后提醒一点EXIT图要求两模块是对数域软输入软输出如果均衡器用的是线性MMSE它的输出不满足高斯近似EXIT图会偏乐观。这时可以用实际外信息直方图估互信息但那是另一个话题了。我的习惯是新信道先跑EXIT图看到两条曲线有清晰的“隧道”再上BER这个习惯帮我避开了大量无效仿真。希望帮到你。本文还有配套的精品资源点击获取
返回列表