简介:在无线通信与信号处理领域,当信道状态和传输信号均未知时,联合GAMP(广义近似消息传递)算法与Turbo均衡的迭代框架可显著提升接收机性能。这份MATLAB代码资源完整实现了该方案,覆盖信道建模、GAMP消息传递更新、均衡与译码模块的循环交互等关键环节,适合通信工程及信号处理方向的研究生、工程师以及相关课题的学生借鉴。压缩包内共包含4082个文件,主体为1720个m脚本,并搭配72个cpp、62个c等原生源码,以及针对不同平台编译好的mexmaci64、mexw32等MEX可执行文件,另有71个mat数据样例、PDF说明文档和较多SVN版本信息,资料结构完整,整体大小36.22MB。已有891人学习浏览,便于读者在MATLAB中直接调用MEX加速仿真,或修改m源码研究算法细节。借助这套代码,能够快速搭建GAMP-Turbo均衡实验链路,深入理解迭代接收机原理,并为后续性能优化与工程部署提供坚实起点。
1. 双盲信道下的Turbo均衡:GAMP凭什么比MMSE更值得在MATLAB里实现
接收端星座图糊成一团,你同时面对“信道未知、信号未知”的双盲条件,传统做法是先估计信道、再做均衡。问题在于信道估计误差会原封不动地进入后续符号检测,后头每一步都在放大它。我在某实验室复现GAMP算法与Turbo均衡结合的MATLAB工程时发现,只把线性均衡换成GAMP,误码率差别并不大;真正起作用的是把信道估计和符号检测放进同一个迭代框架里交替更新,让译码器反馈反过来修正信道系数。这就是alternating_GAMP的核心思路。这套MATLAB实现适合做信道未知场景的接收机算法研究,也适合给需要从稀疏先验中恢复符号的工程立项做参照,尤其当信道呈现多径稀疏特征时,它的收益比传统MMSE均衡高出一截。
2. alternating GAMP的迭代机制:从消息传递到双盲联合估计
2.1 消息传递的三个基本步骤:初始化、消息更新、阻尼
GAMP全称是Generalized Approximate Message Passing,出身于消息传递算法家族。它处理的模型是标准线性观测:
y = A x + w
其中y是接收向量,A是等效信道矩阵,x是发送符号向量,w是加性噪声。在信道未知的场景里,A本身还要被估计,这是后话。先假设A已知,看GAMP一次迭代内部到底在做什么。
GAMP把Belief Propagation里的高维积分用中心极限定理做了高斯近似,这样每次迭代只传递均值和方差两套参数,复杂度从O(N³)降到O(N²)。它的迭代可以拆成三步:线性更新、非线性更新、先验收缩。
function [x_hat_new, tau_x_new] = GAMP_iter(y, A, x_hat, tau_x, noise_var, prior, damping) % 线性更新: 计算观测预测值 z_hat = A * x_hat; tau_z = sum(abs(A).^2, 2) * tau_x + noise_var; % 非线性更新: 对应观测似然的残差 s_hat = (y - z_hat) ./ (tau_z + eps); tau_s = 1 ./ (tau_z + eps); % 反投影: 回到符号估计域 r_hat = x_hat + tau_x .* (A' * s_hat); tau_r = tau_x ./ (1 + tau_x .* sum(abs(A).^2, 1)' .* tau_s); % 先验收缩: 结合符号先验做MMSE估计 [x_hat_new, tau_x_new] = prior_shrink(r_hat, tau_r, prior); % 阻尼: 防止迭代震荡 x_hat_new = damp_coef * x_hat_new + (1 - damp_coef) * x_hat; tau_x_new = damp_coef * tau_x_new + (1 - damp_coef) * tau_x; end这段代码里,tau_z是观测域的预测方差,它由符号方差通过矩阵A的能量投影累加得到。s_hat是残差归一化后的“校正方向”。r_hat是把残差反投影回符号域后的中间量,这一步相当于在做一次匹配滤波。damp_coef是阻尼系数,取值通常在0到1之间;它越大,新信息进入越少,收敛越慢但越稳,越小收敛越快但容易震荡。
GAMP之所以在稀疏信号恢复场景下比MMSE好,是因为它在最后一步用prior_shrink做了贝叶斯收缩。如果你的信号是稀疏的,也就是大部分符号能量接近零、只有少数位置有非零值,那么prior_shrink会把后验均值向零压缩。这种操作在信道稀疏多径场景里效果尤其明显——抽头数远小于信道长度,GAMP相当于在估计的同时做了稀疏性约束。
2.2 为什么不能分开估计信道再均衡:误差传播与局部最优
有些刚接触这个方向的开发者会问:既然信道未知,我能不能先用一段导频估计信道,再固定信道去做均衡?答案是可以,但性能会差很多。导频数量有限时,估计出来的信道带有噪声,这个噪声会直接进入均衡器系数。更麻烦的是,Turbo均衡需要一个外部信息迭代闭环,如果信道估计固定不动,译码器反馈的软信息只能改善符号检测,却无法回头修正信道误差。信道误差成了整个闭环里的永久偏差,误码率高的时候再怎么迭代都压不下去。
alternating_GAMP的思路是把信道系数和符号向量都当作未知变量,在每次迭代中交替更新。先固定当前信道估计,用GAMP更新符号;再用更新后的符号软信息,重新估计信道;如此反复。这样做的好处是,译码器反馈的高可靠度符号能反过来当“虚拟导频”,等效训练序列长度随迭代轮数增长,信道估计精度也随迭代逐步提升。本质上它走的是EM算法的框架,把信道当作隐藏变量,符号当作可观测变量,但EM在高维非线性问题里容易卡在局部最优。
GAMP相比标准EM的优势在于,它在更新符号时保留了后验方差,不只是点估计。方差信息在交替过程中非常重要——信道重估时,符号方差越小,信道估计的加权越偏向于该符号位置,而不是一刀切把所有符号等权处理。我在实际跑仿真时观察过,采用点估计替代后验均值做反馈,信道估计的均方误差大约会上升30%到50%,直接表现为BER曲线在信噪比15dB以上出现平台期。
2.3 影响收敛的关键参数:阻尼系数、迭代次数、先验分布
双盲条件下GAMP的收敛不像纯压缩感知那么“有保障”。信道矩阵A本身在变,符号先验也在变,两者耦合后很容易出现迭代发散或收敛到错误点。关键参数有三个,把它们调对了,问题就解决一大半。
| 参数 | 常用范围 | 对收敛的影响 | 调整经验 |
|---|---|---|---|
| 阻尼系数 | 0.3~0.9 | 越小更新步长越大,易震荡 | 先取0.5跑一次,发散就降到0.3 |
| 迭代轮数 | 5~20 | 过少未收敛,过多损耗算力 | 观察残差,连续2轮无变化就停 |
| 先验稀疏率 | 0.05~0.5 | 过强会把非零符号也压到零 | 与信道非零抽头占比挂钩 |
阻尼系数的调试在MATLAB里最直接——跑一次完整仿真,把每次迭代的残差norm画出来,如果曲线在震荡不下降,就把damp_coef往小调。迭代轮数不是越多越好,GAMP在双盲场景下有个有趣现象:迭代到第8轮左右信道估计精度已经接近极限,继续迭代反而可能把前期积累的错误信息放大。这可能跟软反馈的置信度饱和有关,在高信噪比下尤其明显。先验的稀疏率设置,建议直接由信道模型的多径抽头数决定,比如6径信道抽头占比大概在6除以信道长度。别拍脑袋设一个0.01,那样绝大多数符号会被收缩为零,直接翻车。
3. MATLAB模块化拆解:信道模型、GAMP估计、译码器闭环
3.1 信道模型搭建:多径与频率选择性衰落的仿真设置
要验证alternating_GAMP,信道模型必须同时具备频率选择性和稀疏性,否则GAMP相对于MMSE的优势体现不出来。我一般用MATLAB的Communications Toolbox里的comm.RayleighChannel对象,或者直接手动构造一个带抽头系数和时延的稀疏信道矩阵。
% 多径稀疏信道参数设置 rng(42); fs = 10e6; % 采样率10MHz path_delays = [0 0.8 2.1] * 1e-6; % 三条多径,时延分别为0/0.8/2.1微秒 path_gains = [0 -4 -8]; % 对应路径增益,单位dB chan = comm.RayleighChannel( ... 'SampleRate', fs, ... 'PathDelays', path_delays, ... 'AveragePathGains', path_gains, ... 'NormalizePathGains', true, ... 'ResetBeforeFiltering', true);注意这里的PathDelays不是整数倍采样周期,所以等效离散信道矩阵A会出现能量泄漏,多个抽头都拿到非零值。这个细节很关键——如果你的信道模型时延刚好是采样周期的整数倍,信道矩阵会过于稀疏,GAMP分离信号太容易,仿真结果会虚高,到了实际系统里完全复现不出来。所以我建议故意设置非整数倍时延,让信道矩阵的稀疏性更贴近真实物理场景。
信道矩阵A的生成方式是把每帧符号通过这个信道滤波器,然后构造成卷积矩阵。我常用的是Toeplitz结构,它能把线性卷积表达成矩阵乘法。这个矩阵的尺寸是(块长+信道长度-1)行乘块长列,在块长512、信道长度16时,A大约是527乘512,规模不大,MATLAB直接存没问题。
% 将信道响应构造成循环卷积矩阵(块传输场景) h = [0.5 -0.3i, 0.2 + 0.1i, zeros(1, 100)]; % 等效信道冲激响应 A = toeplitz([h(1), zeros(1, N-1)], [h, zeros(1, N-length(h))]);这里h里的非零元素个数决定了信道稀疏度。在交织OFDM或者单载波块传输系统里,把A做成循环矩阵还可以用FFT加速矩阵乘法,但我建议先在密集矩阵版本上调通算法,再优化复杂度。
3.2 GAMP估计模块:稀疏伯努利-高斯先验下的符号更新
GAMP模块的函数实现重点是prior_shrink。在通信系统里,发送符号的取值来自星座映射(BPSK、QPSK等),不是真正的稀疏信号,而信道响应才是稀疏的。所以在alternating_GAMP框架里,prior_shrink要同时服务两个目标:符号更新时用星座先验,信道更新时用稀疏先验。
function [x_hat, tau_x] = prior_shrink(r, tau_r, prior) % prior: 结构体, 包含type字段( 'constellation' 或 'sparse' ) switch prior.type case 'constellation' % 星座点集合 constellations = prior.alphabet; % 例如 [1+1i, 1-1i, -1+1i, -1-1i]/sqrt(2) % 计算每个星座点的后验概率 dist2 = abs(r.' - constellations.').^2; w = exp(-dist2 ./ (2 * tau_r)); w = w ./ sum(w, 1); % 归一化成后验概率 x_hat = w * constellations.'; tau_x = sum(w .* abs(constellations.' - x_hat).^2, 1).'; case 'sparse' % 伯努利-高斯稀疏先验, sparsity_rate 表示非零概率 q = prior.sparsity_rate; theta = prior.theta; % 非零高斯分量的方差 % 后验概率: 为零的概率 vs 非零的概率 p0 = (1 - q) * normpdf(r, 0, sqrt(tau_r)); p1 = q * normpdf(r, 0, sqrt(tau_r + theta)); p1 = p1 ./ (p0 + p1 + eps); x_hat = p1 .* r .* (tau_r ./ (tau_r + theta)); tau_x = p1 .* (theta * tau_r / (tau_r + theta) + x_hat.^2) - x_hat.^2; end end这段代码里,'constellation'分支计算的是符号关于星座点的后验均值。dist2是接收中间值到各个星座点的欧氏距离平方,w经过指数变换和一维归一化后成为后验权重。这种软符号输出比硬判决好,因为它保留了每个符号的不确定性信息,在后续信道重估时可以直接作为加权系数。
'sparse'分支服务于信道估计。它假设信道每个抽头独立服从伯努利-高斯分布:以概率(1-q)为零,以概率q服从均值为零方差为theta的高斯分布。p0和p1分别是两种假设的后验概率密度。这里用了normpdf,数值稳定性尚可。x_hat是收缩后的后验均值,tau_x是后验方差。在信噪比低时tau_x会偏大,代表对信道估计的不确定性高,这会在后续的Turbo均衡中降低该符号反馈的权重。
一个常见小错误是忘记在p1分母加eps,当你设置的稀疏率q极小时,p0和p1可能同时下溢到零,得到NaN。我习惯在关键除法处都补eps,代价是每次迭代多几次浮点运算,换来的稳定性完全值当。
3.3 译码器连接:LLR计算与软符号再生
GAMP模块输出的符号估计要转成比特对数似然比(LLR),送进Turbo译码器。如果调制方式选的是QPSK,每个符号对应两个比特。标准的LLR计算要处理高斯近似下的星座点概率,在MATLAB里用max-log近似可以省不少运算。
function LLR = softSym2LLR(x_hat, tau_x, constellation_map) % x_hat: GAMP输出的符号后验均值 % tau_x: 符号后验方差 % constellation_map: 星座点与比特的映射表, 行对应符号点, 列对应比特 numBits = size(constellation_map, 2); LLR = zeros(numel(x_hat) * numBits, 1); idx = 0; for k = 1:numel(x_hat) dist2 = abs(x_hat(k) - constellation_map(:,1)).^2; for b = 1:numBits % max-log近似: 取比特b为0和1两组星座点的最小距离差 dist0 = min(dist2(constellation_map(:, b+1)==0)); dist1 = min(dist2(constellation_map(:, b+1)==1)); idx = idx + 1; LLR(idx) = (dist0 - dist1) / (2 * tau_x(k)); end end end注意这里的dist2只需要计算一次,然后按比特位分别取最小距离。max-log近似比完整log-sum-exp慢不了多少,但误差在高阶调制时会明显变大。如果调制阶数超过16QAM,我建议还是用完整LLR计算,或者用查表法做折中。LLR送入Turbo译码器后,拿到的输出是外信息LLR,需要把它重新映射成软符号反馈给GAMP。
function feedbackSymbol = LLR2SoftSym(LLR_ext, bit2sym_table) % LLR_ext: Turbo译码器输出的外LLR, 长度 = 符号数 * 每符号比特数 % 简化BPSK示例: 外信息近似为高斯分布时, 软符号 = tanh(LLR/2) feedbackSymbol = tanh(LLR_ext(1:2:end) / 2) + 1i * tanh(LLR_ext(2:2:end) / 2); feedbackSymbol = feedbackSymbol / sqrt(2); end这段BPSK/QPSK的软符号映射用了tanh近似,它在低信噪比下是好的近似,高信噪比时软符号幅度趋近±1。需要留意的是,反馈符号在进入下一轮信道估计前,要确保相位和GAMP里信道矩阵的相位定义一致,否则闭环会在某个相位偏转上锁死。这个问题我在4QAM仿真里踩过,表现是误码率卡在0.1降不下去,查了两天才发现星座经过信道后有一个固定旋转变换,反馈符号缺了共轭相乘的校正。
3.4 主循环控制:收敛判据与迭代停止条件
主循环把以上模块串起来,控制交替GAMP与Turbo译码的迭代。收敛判据我一般不只看一次迭代的残差,而是同时看符号后验方差的总和变化。
for iter = 1:max_outer_iter % Stage 1: 固定信道, GAMP更新符号 [sym_hat, sym_var] = gamp_symbol_update(rx_signal, A_hat, sym_hat_prev, sym_var_prev); % Stage 2: 符号转LLR, 译码器半轮迭代 llr_in = softSym2LLR(sym_hat, sym_var, mapping_table); [llr_ext, llr_app] = turbo_decode(llr_in, max_inner_iter); % Stage 3: 软符号反馈, 更新信道估计 soft_sym = LLR2SoftSym(llr_ext, mapping_table); A_hat = channel_reestimate(rx_signal, soft_sym, sym_var, A_hat_prev); % 收敛判据: 符号后验方差的相对变化量 delta = sum(sym_var_prev - sym_var) ./ max(eps, sum(sym_var_prev)); if delta < tol break; end end逻辑链很清晰,但三个stage的顺序有讲究。Stage 1里GAMP更新用的是上一轮的信道估计,Stage 3更新信道用的又是本轮符号软信息,这保证了每一轮迭代都到“新”信息。Turbo译码器内部的max_inner_iter一般设为2到4,外层迭代设为5到8。内层迭代过多会让LLR过度自信,外层迭代反而失去修正信道的机会。我把内层固定为2,外层动态停止,效果最稳定。
4. 参数调优与验证:阻尼、迭代轮数与BER曲线的正确读法
4.1 阻尼怎么调:固定阻尼与自适应阻尼
先给结论:在alternating_GAMP里,固定阻尼0.4到0.6通常能稳定工作,但性能不是最优。自适应阻尼在收敛速度和最终误码率上都有优势,代价是多算一次预测。
自适应阻尼的思路基于一个事实:迭代向量r_hat在收敛时幅度逐渐减小且方向渐稳定。可以用两个相邻迭代轮次的r_hat差值的符号来确定阻尼大小。更轻量的做法是每三层迭代把阻尼从0.3逐步升到0.7,相当于“前慢后快”的策略。我用过最简单的自适应方案是:
delta_r = abs(r_hat - r_hat_prev); if mean(delta_r) > 0.1 * mean(abs(r_hat_prev)) damp_coef = damp_coef * 0.9; % 差值太大, 减小步长 else damp_coef = min(0.85, damp_coef * 1.02); % 稳定后逐步放大 end这段策略的本质是跟踪迭代残差。残差大说明当前点离不动点远,减步长防止跳过;残差小说明离收敛近,加大步长减少轮数。注意damp_coef初始值在0.3附近比较稳妥,小于0.2则收敛太慢,外层迭代十几轮也压不下来误码率。
4.2 迭代轮数与性能的权衡
迭代轮数这个参数,很多人在初版代码里喜欢设一个很大的值,比如外层30次、内层10次,以为“越多迭代越好”。实测结果往往相反:在信噪比低于10dB的场景下,第6轮之前误码率确实在下降,但第8轮之后偶尔会回升。原因是信道估计在低信噪比下不准确,译码器反馈的软符号置信度虚高,第8轮时反馈错误信息开始占据主导,把本来已经接近收敛的信道估计重新“带偏”。
我建议把外层迭代设成一个自适应值:连续两次迭代符号后验均值变化小于阈值就停。阈值设0.01倍符号能量比较合理。这样高信噪比时迭代2到3轮就停,低信噪比时可能要跑到12轮上下,计算量分配正好跟着场景走。
4.3 BER曲线的正确绘制方法:别让仿真时间骗了你
画BER曲线是验证算法的最后一步,最容易出错的反而是帧数和统计方式。Turbo均衡的BER曲线在低信噪比下收敛慢,如果每个信噪比点只跑50帧,曲线会剧烈抖动得让人怀疑算法写错了。
snr_list = 0:2:16; ber_results = zeros(size(snr_list)); min_error_count = 100; % 至少统计100个错误比特 min_frame_count = 200; % 至少跑200帧 for idx = 1:numel(snr_list) total_errors = 0; total_bits = 0; frame_count = 0; while (total_errors < min_error_count && frame_count < min_frame_count) % 运行一帧完整收发流程 [ber_frame, bits_sent] = run_single_frame(snr_list(idx)); total_errors = total_errors + ber_frame; total_bits = total_bits + bits_sent; frame_count = frame_count + 1; end ber_results(idx) = total_errors / total_bits; end“至少统计100个错误比特”这个条件很重要。在误码率低于1e-3时,如果只跑固定帧数,可能只有几个错误比特,BER的置信区间大到没有意义。我在16dB信噪比点曾经只统计到5个错误比特,画出来的BER曲线在16dB处有一个假“误码平台”,浪费了两天调参。后来统一用错误计数阈值做停止条件,曲线立刻变得平滑且可复现。
4.4 复杂度对比:GAMP与MMSE、MAP的边界
在设计接收机立项时,不免被问到“GAMP复杂度到底多高”。直接做一次矩阵乘法统计就能算清楚:MMSE均衡需要计算(A^H A + σ²I)^{-1},复杂度是O(N³),N是块长度。GAMP单次迭代只有矩阵向量乘,O(N²),外层迭代按8轮算,总复杂度是8N²,仍然远小于N³。当N超过256时,GAMP的复杂度优势开始显现;N等于1024时,MMSE的矩阵求逆在MATLAB里需要特别小心存储和数值稳定性,GAMP却可以轻量跑完。
MAP检测在MIMO里复杂度是O(M^K),K是数据流数量,做Turbo均衡时完全不可行。GAMP在这个图景里的定位,就是用近似换取可行性和性能的平衡。如果你需要做成实时仿真平台,有一个加速技巧:把等效信道矩阵A按循环结构分块,结合FFT做矩阵向量乘,单轮复杂度降到O(N log N),整个Turbo均衡闭环才能真正跑起来。
5. 复现避坑指南:我在alternating GAMP调试中踩过的五个坑
5.1 稀疏先验过强,符号全部被收缩到零
现象:迭代几轮之后,x_hat的幅度普遍小于0.01,星座图缩成一个点,信噪比再高也没用。
原因:sparse分支里的稀疏率设成了0.05以下,而实际符号本来是全星座密集的,只是信道稀疏。符号更新错误地使用了稀疏先验,把所有符号都压向零。这个坑在于把“信道稀疏”错误地迁移到了“符号稀疏”。
解决:在符号更新分支必须用constellation先验,sparse先验只留给信道重估模块用。检查prior.type是否在正确的函数分支里被赋值。如果使用的是同一个prior_shrink函数,务必在每次调用前显式指定type字段。
5.2 噪声方差初值不准,GAMP直接发散
现象:外层迭代第2轮,r_hat和tau_r同时爆掉,出现量级在1e10以上的数值。残差曲线像喝了假酒,完全无规律。
原因:GAMP对噪声方差noise_var非常敏感。初值设成了真实值的十分之一,导致tau_z偏小,s_hat被放大,等效步长过大。双盲场景里噪声方差本身未知,这是最容易被低估的问题。
解决:先做一次粗信道估计,把残差功率作为噪声方差初值,再进入GAMP主循环。更稳的做法是在每轮迭代后用接收向量与重建观测值的差重新估计噪声方差:noise_var = mean(abs(y - A_hat * x_hat).^2)。这相当于给GAMP装了一个自校正环节。
5.3 交织器长度与译码器LLR顺序不匹配,软反馈全乱
现象:Turbo译码器的外LLR反馈回来,性能反而比无反馈更差,且BER随迭代增大。
原因:GAMP输出的符号顺序是按块传输的时间顺序排列的,Turbo码译码输出的是交织后的比特顺序。如果没有逆交织直接做反馈符号映射,反馈的“外信息”实际上是错位信息,等于给信道估计注入随机噪声。
解决:维护一对交织表interleaver_map和deinterleaver_map,反馈路径必须是LLR_ext(deinterleaver_map)再做符号映射。我一般把交织表定义成全局变量,在循环外一次性生成,避免在每次外层迭代里重复构造。
5.4 log-domain计算溢出,LLR出现Inf或NaN
现象:误码率在某个节点后突变为0或0.5,查LLR发现大量值为Inf。
原因:在LLR计算或者先验概率计算中,exp(-dist2/(2*tau_r))在tau_r趋近于零时分子分母同时下溢,或者dist2取到极小值导致指数项溢出。这本质是数值动态范围问题,在QPSK高信噪比场景特别容易出。
解决:LLR计算改用max-log近似,不直接求指数;概率归一化时先减去最大值再做exp。在MATLAB里更省事的是把normpdf调用改为手动计算对数域,然后在对数域做完减法再取指数。几个技巧叠加后,Inf和NaN再也没出现过。
5.5 迭代“收敛”了但BER不降:误差平台期不是参数问题
现象:残差稳定下降,收敛判据满足,信道估计误差也小,但BER卡在1e-2左右,信噪比提升看不到改善。
原因:这是Turbo均衡里典型的“反馈死锁”状态,不是某个参数能解决的。根本原因可能是编码器与交织器配置导致的BER下限,或者是低信噪比下外层迭代前期把错误LLR硬化了,后面无法纠正。
解决:先检查信道编码,确认码率和约束长度有没有超出当前信噪比的理论限。如果理论限没问题,把外层迭代轮数减小到4轮以内,同时提高内层Turbo迭代的置信度更新质量。实践里有一次我改了小帧大小并用多帧联合处理,把误差平台从1e-2压到了1e-4,这个方向值得优先试。
6. 软反馈系数与联合收敛:让GAMP和译码器真正“咬合”
在双盲Turbo均衡里,反馈的软符号进入信道估计前,通常需要乘一个软反馈系数beta。beta等于1时反馈完全相信译码器,信道估计权重大,但如果译码器错误LLR置信度高,误差就会被放大;beta等于0时退化为无反馈方案。实践中beta取0.3到0.8之间能得到最优折中。我在一版仿真里对比过beta=0.5和beta=0.8:高信噪比下0.8更好,低信噪比下0.5明显稳。建议把beta做成一个随外层迭代轮数递增的变量,比如第1轮0.3,第6轮0.7,让信道估计在前期保持独立、后期逐渐信任译码反馈。
收敛判断还有一个被忽略的技巧:符号后验方差总和的变化比残差更敏感。残差在错误收敛点也可能很小,但后验方差的下降曲线会表现出“先降后平再回升”的形态,回升点往往是信道估计开始偏离的时刻。我在主循环里保存了每一轮的sym_var_sum曲线,出现回升就把该轮的信道估计回退到上一轮版本——这就是我的“后悔药”。从那以后,我每次跑新的调制方式或帧长配置,都强制先画出sym_var_sum曲线,确认形态正常再继续调参数。
希望这份alternating_GAMP的MATLAB拆解和避坑记录能帮到你。通信系统的双盲估计是个越调越有意思的领域,祝你在自己的仿真平台上早日跑出平滑的BER曲线。
本文还有配套的精品资源,点击获取