简介:面向高移动性场景下的正交时频空间调制(OTFS)系统,这份低复杂度均衡器代码包解决编码传输中的信号检测与二维干扰抑制难题。均衡器采用线性最小均方误差并行干扰消除结构,并结合一阶诺伊曼级数对矩阵求逆做近似,避免了高维求逆的爆炸式复杂度和实现难度,运算量随传输符号总数准线性增长,兼顾误码性能与工程可行性。代码可直接复现论文中的外在信息传输曲线与编码误码率对比结果,也支持与正交频分复用系统及近期的并行干扰消除方法对照评估,适合通信与信号处理方向的研究生、工程师作为算法验证和二次开发的基础。压缩包采用zip封装,大小约15.84MB,内含核心算法工程文件。目前已有797人浏览学习,对于关注低复杂度接收机设计的研究者具有不错的参考价值。
1. OTFS均衡为什么难:从MMSE的复杂度到迭代LMMSE-PIC这条路
当 N=64、M=64 时,OTFS 时延-多普勒网格里有 4096 个符号,信道矩阵就是 4096×4096。朴素 LMMSE 均衡要做一次满矩阵求逆再加矩阵乘,浮点运算量基本在 10^10 量级,链路级 Monte-Carlo 仿真跑几千个块,光这个均衡器就能吃掉大半时间。OTFS 的时延-多普勒域信道并不是稠密矩阵,它由若干条路径的循环移位叠加而成,天然带“带状”特征。满矩阵逆既浪费算力,也没有利用这个物理结构。
迭代 LMMSE-PIC 的思路是:先做一轮粗略的线性滤波,再用软符号反馈逐符号做并行干扰消除,每一轮的矩阵运算都收缩到带状相关的局部区域,复杂度从 O((NM)^3) 降到约 O(NM·B^2) 甚至更低,其中 B 由最大时延扩展和多普勒扩展共同决定。相比最大似然的指数爆炸,它工程上可落地;相比单纯一次 LMMSE,它在中高阶调制下有明显性能余量。适合正在搭 OTFS 物理层仿真、SDR 原型验证,或者准备往 FPGA 上做参考设计的人。下面这套实现和踩坑记录,就是奔着“能跑通、能调参数、能定位翻车原因”去的。
2. DD域模型与LMMSE-PIC迭代骨架:先立住模型再写代码
2.1 DD域信道矩阵为什么呈带状:索引约定和每一条路径的贡献
OTFS 发射端把 DD 域符号 X∈C^{N×M} 经 SFFT 变成时频域符号,再通过海森堡变换发出去;接收端做维格纳变换拿回时频域,再经 SFFT 回到 DD 域。如果忽略脉冲整形带来的泄漏,每条路径在 DD 域的作用就是一个旋转:发射符号在 (n,m) 位置的分量,经过时延索引 τ_i、多普勒索引 ν_i 后,出现在接收符号的 ((n-ν_i) mod N, (m-τ_i) mod M) 位置。于是整条信道矩阵 H 等于若干循环移位矩阵按路径增益加权求和。
在代码里把二维索引展平成一维时,约定很关键。常见做法是采用行优先:符号在 DD 网格中的位置 (n,m) 映射到一维索引 k = n·M + m,n 是多普勒方向(0 到 N-1),m 是时延方向(0 到 M-1)。这样每条路径的循环移位,反映到矩阵里就是把一整列搬到另一列,偏移量是 ν_i·M + τ_i。最大时延扩展 τ_max 和最大多普勒扩展 ν_max 决定了每个接收符号实际会受到哪些发送符号的影响:在展平后的矩阵里,影响的列索引分布在当前列附近约 ±(ν_max·M + τ_max) 的范围内,这就是“带状”的来源。
分数时延和分数多普勒会让能量从单个点扩散到相邻的几个网格点上,带状宽度会稍微变宽,但仍然是局部化的。写代码时建议显式构造一个“带状掩码”来限定参与计算的列范围,而不是直接对 K×K 满矩阵做运算。下面这个函数给出的是最朴素的满矩阵构造方式,用于验证信道模型是否正确;后面第 3 章会在这个基础上把带状结构利用起来。
import numpy as np def build_dd_channel(N, M, taps): """构造 DD 域信道矩阵 H (K x K)。 taps: 每条路径 (gain, delay_idx, doppler_idx) delay_idx 取值 0..M-1, doppler_idx 取值 0..N-1 """ K = N * M H = np.zeros((K, K), dtype=complex) row_idx = np.arange(K) n_row = row_idx // M # 接收符号的多普勒下标 n m_row = row_idx % M # 接收符号的时延下标 m for gain, tau, nu in taps: # 发送符号在 DD 网格里的位置 src_n = (n_row - nu) % N src_m = (m_row - tau) % M src_idx = src_n * M + src_m H[row_idx, src_idx] += gain return H函数里 row_idx 是接收位置,src_idx 是发送位置。每条路径只做一次针对 K 个位置的散点累加,不需要两层循环。注意这里默认了理想矩形脉冲整形且时延多普勒都落在整数网格上;如果后面信道的分数分量明显,需要把每个路径拆成相邻几个整数网格上的多抽头,或者对 H 做额外的卷积扩展。验证时先看 H 每一行的非零个数是否等于路径数 P,以及列偏移是否和 taps 里的 τ_i、ν_i 一致。
有些资料里会采用列优先展平,即 k = m·N + n。两种索引最终结果等价,但带状宽度和后续矩阵分解的带宽会不一样。做低复杂度实现时,建议先用行优先把多普勒方向作为外维,这样在时延扩展占主导的信道里,带状矩阵的带宽相对小,Cholesky 分解的前向/回代次数也少。
2.2 迭代LMMSE-PIC的四步更新:软符号、协方差与后验合并
LMMSE-PIC 每一轮迭代可以拆成四个步骤。第一步是拿上一轮(或译码器返回的)软符号先验,计算每个符号的期望 m_q 和方差 v_q。如果还没有任何先验信息,初始化为 m_q=0、v_q=1,前提是星座已经归一化为单位平均功率。第二步用当前先验重构接收向量:interf = H·m,这是所有符号共同造成的干扰估计。第三步对每个符号 q 并行做干扰消除和滤波,得到 y_q = y − H·m + m_q·h_q,其中 h_q 是 H 的第 q 列。减掉的是“其它所有符号”的干扰,加回来的则是符号 q 自己的贡献。第四步用滤波器 w_q 计算等效标量信道 ρ_q = h_q^H·w_q,再用高斯后验合并公式更新 m_q、v_q。
这里最容易犯的错是把符号 q 自己的先验也减掉了。如果 y_q 里没有保留 h_q·m_q,滤波器输出会低估自己的信号功率,尤其当 QPSK 或 16QAM 的先验置信度很高时,会出现自消现象,误码率反而随迭代升高。正确做法就是上面说的 y_q = y − H·m + m_q·h_q。
滤波器 w_q 在完整 LMMSE 下的表达式是 (H·diag(v)·H^H + σ^2·I)^{-1}·h_q。看起来每个符号都要解一次 K 维线性方程组,这是复杂度大头。迭代 LMMSE-PIC 与朴素 LMMSE 的关键区别在于:上一轮输出的 m、v 会作为下一轮先验,所以随着迭代进行,v_q 越来越小,干扰项越来越弱,滤波器越来越接近匹配滤波器。因此很多实现会在第 2、3 轮之后直接退化为“软符号干扰消除 + 最大比合并”,性能损失非常有限,但复杂度又降一截。
第 4 步的高斯后验合并,常用公式是:
m_q_new = m_q + (v_q * rho_q) / (1 + v_q * rho_q) * (z_q - rho_q * m_q) v_q_new = v_q / (1 + v_q * rho_q)其中 z_q = w_q^H·y_q 是滤波后的符号观测,rho_q 是该符号的有效信号增益。这里的 rho_q 在理想同步且 H 估计无偏时是实数,但仿真里因为信道矩阵是复数,rho_q 可能有很小虚部;稳妥写法是取实部。v_q_new 会随着 rho_q 增大而下降,代表这个符号的不确定性在减小。
有一种实现变体是直接输出“外部信息”,也就是把后验均值里属于先验的那部分去掉,再交给译码器。这个做法的好处是避免译码器重复使用同一份信息,Turbo 迭代不容易过冲。我一般会在均衡器内置迭代阶段用“后验直接反馈”,进入与 LDPC 译码器交互的外部迭代阶段再切换成外部信息,这样收敛速度和实现复杂度都比较平衡。
2.3 均衡器内置迭代与Turbo式迭代:两种迭代日程怎么选
OTFS 接收机里常说的“迭代”有两种。第一种是均衡器自己迭代,几轮之间没有信道译码参与,只有软符号反馈,一般 2 到 4 轮就能稳定。第二种是均衡器输出软信息给 LDPC 或 Turbo 译码器,译码器跑若干轮后返回更新的符号先验,均衡器再重新滤波。第二种配合低密度校验码时增益明显,但每轮外迭代都要重新解方程组,复杂度成倍上升。
实际工程里我不建议一上来就做全 Turbo 迭代。先把均衡器内置迭代调到误码率曲线基本收敛,再看译码器外迭代还剩多少增益。很多场景下,内置迭代 2 轮加外迭代 2 轮,就已经逼近理想迭代检测性能的 0.2 dB 以内。再往后增加轮数,收益被译码器自身的错误平层吃掉,曲线反而难看。写代码时把这两个参数拆成独立的 max_inner_iter 和 max_outer_iter,方便分别扫描。
另外要留意译码器反馈回来的先验方差不是越小越好。如果译码器对自己的输出过于自信,v_q 会被压得接近 0,干扰消除固然干净,但一旦那个符号判错,误差会在下一次迭代里扩散。因此不少实现会给 v_q 设一个下限,比如 v_min = 0.05 到 0.1 的归一化值,防止过度自信。后面第 5 章会给出具体参数建议。
3. 低复杂度LMMSE-PIC实现:Python骨架与三处关键优化
3.1 把满矩阵换成带状掩码与稀疏存储:第一处关键优化
朴素实现里 H 是 K×K 的 numpy 复数矩阵,N=M=64 时 K=4096,一个矩阵就是 256 MB 内存。这还只是存储,后续 H·diag(v)·H^H 计算直接做 K×K 矩阵乘法,一次约 10^11 次复数乘加,显然不可持续。低复杂度实现的第一个动作就是不让满矩阵出现。
利用第 2.1 节的索引约定,把 H 改造成“每行只保留带内列”的结构。假设每条路径的最大时延索引为 τ_max,最大多普勒索引为 ν_max,则展平后列的偏移范围大约是 ±(ν_max·M + τ_max)。为了留出分数时延和多普勒的泄漏余量,实际的带状半宽 band_half 会取这个值的 1.5 到 2 倍。构造时就只生成 K 行、每行最多 2·band_half+1 个非零列的稀疏矩阵,用 scipy.sparse 的 csr_matrix 存。
from scipy.sparse import csr_matrix def build_dd_channel_sparse(N, M, taps, band_half): K = N * M rows = [] cols = [] vals = [] for gain, tau, nu in taps: for k in range(K): n = k // M m = k % M src_n = (n - nu) % N src_m = (m - tau) % M # 展平后的列偏移,用循环距离判断是否落在带内 src_idx = src_n * M + src_m dist_n = abs(n - src_n) dist_n = min(dist_n, N - dist_n) dist_m = abs(m - src_m) dist_m = min(dist_m, M - dist_m) if dist_n * M + dist_m <= band_half: rows.append(k) cols.append(src_idx) vals.append(gain) H = csr_matrix((vals, (rows, cols)), shape=(K, K)) return H这里的 band_half 不是简单取 max 偏移,而是按“循环距离”判断。OTFS 的时延和多普勒都是环上的量,索引 0 和索引 N-1 之间只差一步,不能按线性距离硬算,否则会把本该在带内的路径判成带外,均衡器性能立刻变差。构造稀疏矩阵时建议顺手统计每行最大非零数,如果某个信道抽头数太多导致带内非零数量超过预期,就先检查带半宽设置。
这个稀疏 H 后续可以高效地做矩阵-向量乘,也能喂给 scipy 的稀疏求解器。矩阵占用的内存从 O(K^2) 降到 O(K·L_band),L_band 是平均每行非零数,通常只有几十到一两百。
3.2 一次迭代的代码骨架:并行干扰消除与逐符号滤波
下面这段是一次 LMMSE-PIC 迭代的参考实现。它没有把 H·diag(v)·H^H 显式构造出来,而是用稀疏矩阵乘法得到 R,再对 R 做一次稀疏 Cholesky 分解。每个符号的 w_q 求解共用同一个分解,这是第二处关键优化:K 个符号的滤波器只需要一次分解,而不是 K 次独立求逆。
import numpy as np from scipy.sparse import csc_matrix, eye from scipy.sparse.linalg import splu def lmmse_pic_iter(y, H_csc, sigma2, m, v): K = len(y) # 全干扰重构:interf = H @ m interf = H_csc @ m # 干扰协方差矩阵 R = H diag(v) H^H + sigma2 I # 利用稀疏矩阵乘法,避免生成稠密阵 V = eye(K, format='csc') V = V.multiply(v) # 对角阵,v 是长度为 K 的向量 R = (H_csc @ V) @ H_csc.conj().T R = R + sigma2 * eye(K, format='csc') # 稀疏 LU 分解,供所有符号共用 lu = splu(R.tocsc()) m_new = m.copy() v_new = v.copy() # 逐符号并行干扰消除与滤波 for q in range(K): # 取出第 q 列 h_q h_q = H_csc[:, q].toarray().ravel() # 解方程 R @ w = h_q w_q = lu.solve(h_q) # 等效标量信道 rho_q = np.vdot(h_q, w_q).real if rho_q <= 0: rho_q = 1e-6 # 干扰消除:减掉其它符号,加回自身贡献 y_q = y - interf + m[q] * h_q z_q = np.vdot(w_q, y_q).real # 高斯后验合并 gamma = (v[q] * rho_q) / (1.0 + v[q] * rho_q) m_new[q] = m[q] + gamma * (z_q - rho_q * m[q]) v_new[q] = v[q] / (1.0 + v[q] * rho_q) return m_new, v_new这段代码在 K=256 到 K=1024 的规模下可以跑得动,适合先验证算法行为。逐符号循环在 Python 里偏慢,但结构清楚,方便对拍。complex 类型下 vdot 会自动取共轭,所以 z_q 取 real 的时候要注意输入信号确实匹配;如果后面改成实数模型,vdot 的共轭行为会不同,这是移植时最容易踩的一个小坑。
splu 对 R 做的是稀疏 LU 分解,和 Cholesky 相比会多算一些。如果 R 是正定的,更合适的做法是 scipy 的 spsolve 配合对称正定标志,或者直接用 CHOLMOD 之类的库做稀疏 Cholesky。LU 的好处是通用,遇到数值病态时不容易直接报错,但会有微小额外开销。这个开销在 K=4096 时依然可以接受。
3.3 用软符号反馈替代硬判决并复用分解:第二、三处关键优化
很多早期 OTFS 均衡实现直接对上一轮硬判决符号做干扰消除。硬判决的问题是在低信噪比区域错误符号占比高,一旦某几个符号判错,干扰重构就带偏,下一轮滤波后错误传播明显。软符号反馈则用先验期望 m_q 做干扰重构,错误符号的贡献会被方差 v_q 自然地“压低”。这是第二处关键优化,也是迭代 LMMSE-PIC 名称里“迭代”两个字的主要价值来源。
第三处关键优化是每轮迭代复用同一次矩阵分解。注意观察 3.2 节代码:R 只依赖 v 和 σ²,v 在每次外层迭代结束后才更新。如果在一个外层迭代内部还要做多次“子迭代”,而且 v 不变,那就可以只分解一次 R,所有子迭代共用。把这个逻辑抽出来,加一个 reuse_lu 参数,能省掉大量重复分解时间。实际工程里,均衡器内置迭代每轮 v 都在变,没法直接复用;但如果做的是“译码器外迭代”,译码器返回的 v 在均衡器多次内部更新时先保持不变,复用收益就很明显。
进一步压缩复杂度,还可以在第 2 轮迭代之后把 w_q 近似成匹配滤波器 w_q ≈ h_q / (ρ_q·v_q + σ²)。此时不再需要解线性方程组,每个符号只做一个向量内积,复杂度从 O(K·L_band^2) 掉到 O(K·L_band)。这个近似在 v_q 足够小、干扰已经被消干净时几乎无损。很多论文里用“匹配滤波近似”替代后期 LMMSE,就是基于这个观察。把它做成一个开关,让代码在完整 LMMSE 和匹配滤波近似之间切换,方便量化不同阶段的性能差异。
4. 迭代LMMSE-PIC调试避坑:五个翻了车的实测场景
4.1 现象:迭代到第 2、3 轮误码率不降反升
有一次在某图像处理 Demo 的 OTFS 链路上调 QPSK,第一轮均衡后 BER 是 1e-2,第二轮变成 3e-2,第三轮直接到 8e-2,看起来像个不收敛的算法。查到最后原因是我在干扰消除时把 q 自己的先验也给减掉了。y_q 表达式写成了 y − H·m,没有加回 m_q·h_q,导致滤波输出丢了自己符号的信号分量。这个问题在 v_q 初始为 1 时还好,一旦迭代几轮后 v_q 变小,符号 q 的先验置信度变高,被减掉的能量就越多,性能反而随迭代恶化。
解决方式就是严格按 y_q = y − H·m + m_q·h_q 来写。检查代码时可以单独把 q 的 h_q 抽出来,计算一下 y_q 里是否还保有 h_q 的能量,或者在单符号无干扰场景下跑一次,看 z_q 是否近似等于发送符号乘 rho_q。
4.2 现象:加性高斯白噪声很低,但带内截断后 BER 始终压不下去
信道多普勒不大,时延扩展也就 3 个抽头,band_half 取了 8,按理截断不会丢路径。结果高信噪比段曲线出现平层,怎么迭代都下不去。排查后发现问题出在分数时延:路径的实际时延不是整数网格,能量泄漏到相邻几个时延索引上,而这些泄漏分量在构造 band_mask 时把距离判断写成了线性距离,跨过环边界的分量被误删。
解决方式是改用循环距离判断,也就是 3.1 节代码里 dist_n = min(abs(n-src_n), N-abs(n-src_n)) 那种写法。另外一个更省事的办法是构造信道矩阵后,统计每个接收符号实际占用的发送列范围,观察有没有落在带 mask 之外的非零项;如果有,说明带半宽或距离定义有问题。
4.3 现象:软符号反馈用成硬判决符号,星座图看着还行但 BER 有隐藏损失
迭代后期把译码器输出的 LLR 直接做符号硬判,再拿去重构干扰,误码率曲线在 16QAM 下比软反馈差 0.5 到 1 dB。原因是 16QAM 符号的置信度分布不均,有些符号后验方差仍然比较大,硬判会把这部分不确定性抹掉。软反馈只需要用 m_q = E[X_q] 做干扰重构,方差 v_q 留给滤波器加权,实现代价很低。
正确的反馈路径是:译码器输出比特 LLR,先映射成符号概率,再算符号期望和二阶矩。不要直接把星座点硬判值塞进 m。符号方差的计算公式不复杂,但要注意星座归一化,16QAM 的星座点能量均值是 10,如果前面把星座归一化到单位能量,算方差时要保持一致,否则滤波器里的 v 整体偏大,LMMSE 退化成匹配滤波器,损失信噪比。
4.4 现象:rho_q 出现负值或复数虚部大,迭代更新震荡
滤波器 w_q 解出来是复数向量,rho_q = h_q^H·w_q 理论上是实数且非负。实际仿真里因为稀疏分解误差、信道估计噪声、以及 v 接近 0 时 R 条件数变大,rho_q 可能变成很小的负数或虚部不可忽略。如果不处理,后验合并公式里的 (z_q - rho_q·m_q) 会朝错误方向更新。
处理办法是在更新前对 rho_q 做保护:取实部,并夹到 [1e-6, 1] 范围内,避免除零和负增益。更严谨的做法是检查 R 的特征值分布,如果最小特征值接近 0,说明 v 里有符号方差被压得过低,应加一个小的正则项,比如把 R 的对角线上加 1e-3 的稳定性余量,而不是直接修改 rho_q。正则项加多少要试,一般取噪声方差的 1% 到 5%。
4.5 现象:两组实现结果不一致,差距来自矩阵分解库的舍入差异
同一套信道参数,在一台机器的 scipy 稀疏 LU 和另一台机器的稠密 numpy 求逆之间,BER 曲线相差 0.1 dB 左右。这个问题不好定位,因为整体曲线趋势看起来一样。根源在于稀疏 LU 的填充顺序和稠密高斯消元不同,舍入误差分布不一样,叠加在迭代反馈里被放大。
解决方式是先固定一种求解器跑完整链路,再谈优化。对比算法时用同一个求解器,只在均衡器内部开关之间切换。硬件实现时更要小心:定点化的舍入误差和浮点参差异同理会随迭代次数放大,建议仿真阶段就设置一个迭代轮数上限,避免数值噪声成为决定 BER 平层的因素。
5. 迭代次数、软信息与复杂度权衡:一组能直接用的参数
5.1 均衡器内置迭代调到第几轮就该停:看 rho 的增量而不是 BER
判断迭代是否足够,不要只看 BER 曲线。更稳定的指标是每一轮里所有符号的 rho_q 均值或中位数。当第 t+1 轮的平均 rho 相比第 t 轮提升小于 1% 时,再加迭代轮数意义不大。如果用 16QAM 加码率 1/2 的 LDPC,一般内置迭代 2 到 3 轮就到这个状态;BPSK/QPSK 可能 1 到 2 轮就够。原因是低阶调制符号方差收敛快,干扰消除很快进入瓶颈。
外迭代次数则要看译码器返回的 LLR 波动。如果某一次外迭代前后,均衡器输出的符号均值最大变化小于 1e-3,说明译码器和均衡器已经达成一致,再迭代只是在重复信息。常见配置是 max_inner_iter=2、max_outer_iter=2 起步,然后按性能增益决定是否增加到 3。
5.2 v_min 和 rho 保护参数:一组经过验证的推荐值
归一化星座下,符号方差 v 的合理范围是 [0, 1]。完全无信息时 v=1,完全确定时 v=0。我常用 v_min=0.05 到 0.1,低于这个值的方差会被直接夹到 v_min。原因是极低方差会让 R 变得病态,而误判符号一旦进入下一次干扰重构,代价远大于保留一点点不确定性。
rho 保护参数设 rho_min=1e-6,主要防止数值除零;如果出现大量符号被夹到 rho_min,说明信道矩阵构造或求解器有问题,不要靠调这个参数掩盖真实错误。正则项加到 R 对角线上时,量级取噪声方差 sigma2 的 0.01 倍,再小就起不到稳定作用,再大则会把真实信号增益也压下去。
5.3 匹配滤波近似从第几轮开启:复杂度与性能的交叉点
低复杂度实现里最常见的问题是把匹配滤波近似开得太早。第一轮迭代时 v 全部接近 1,干扰很强,匹配滤波损失明显,直接使用会导致 BER 曲线整体偏移。我一般把开关设计成:初始化时关闭,从第 2 轮起可以开启;实际仿真后,如果误码率损失小于 0.2 dB,就保留开启,否则推迟到第 3 轮。
下表给出 K=1024、QPSK、带外 32 个稀疏 LU 的一轮迭代耗时参考,以及匹配滤波近似后的相对倍数。不同机器和浮点库会有差异,但相对关系稳定。
| 均衡模式 | 单轮复杂度 | 相对耗时 | 适用阶段 |
|---|---|---|---|
| 完整 LMMSE-PIC | O(K·L_band^2) | 1x | 第 1 到第 2 轮 |
| 匹配滤波近似 | O(K·L_band) | 0.15x 到 0.3x | 第 3 轮及以后 |
| 纯干扰消除 | O(K·L_band) | 0.1x 左右 | 高信噪比最后一轮 |
这张表的核心意思是:低复杂度迭代 LMMSE-PIC 的性能优势集中在第 1 到第 2 轮,前期预留充足算力,后期可以大胆降复杂度。做 FPGA 资源规划时,按前两轮的最坏情况分配计算单元,后两轮用同一套乘法器复用,能明显降低面积。
6. 验证正确性与继续压低复杂度:从仿真到硬件的一条踏实路径
验证迭代 LMMSE-PIC 实现正确性,建议按三层来做。第一层是模型对拍:用 3.1 节的稀疏 H 和 2.1 节的满 H 在同一组信道抽头下计算接收向量,差异应小于 1e-8 量级,否则索引约定有错。第二层是无噪声单符号诊断:让 H 只有一个非零路径,且只发送一个已知符号,跑一轮均衡后看 z_q 是否等于 h_q 能量乘发送符号,这一步能快速暴露干扰消除里是否误减自身。第三层才是 BER 曲线比较:在平坦衰落和两条径两种信道上对比迭代 1 次与迭代 5 次的性能,若迭代 5 次没有明显劣化,说明反馈链路和参数保护都正常。
继续压低复杂度,我会看三个方向。第一个是时延方向,好结构的稀疏块:OTFS 的时延维本质上还是普通线性卷积,可以用重叠保留法把矩阵-向量乘改成分块 FFT。第二个是符号方差的近似:v 在译码器反馈后往往呈现“整块相同”的特点,很多软符号方差相等,可以合并成块对角近似,减少 R 分解的填充量。第三个是 R 的重复利用:外迭代若连续两层译码器输出变化很小,第二层可以直接复用上一层的 w_q 或 LU 分解,只更新 m、v 的标量部分。
做 FPGA 落地时,另一个实际经验是:不要一开始就把整个迭代链路定点化。先把浮点模型跑通并固定所有参数保护阈值,再对 rho 和 m_q 分别量化,观察 BER 损失。rho 是标量,量化到 12 位定点就有足够裕量;而 w_q 是向量,量化位宽不足时高频分量先失真。我习惯了每次调整一个量化位宽就重新跑一遍固定信噪比点的 BER,记录成表,而不是只盯最后一整段曲线。
迭代 LMMSE-PIC 的坑集中在前几轮数值稳定性和索引约定上,把这两处做扎实,后面的复杂度优化基本是水到渠成。希望这篇笔记能帮你少走一段弯路。
本文还有配套的精品资源,点击获取