资讯详情

资讯详情

SBPSK的Viterbi译码改进:从相位记忆到软输出

简介面向卫星通信与数字调制研究者的专题讲座报告围绕SBPSK信号的Viterbi译码改进展开。报告先介绍SBPSK基本原理说明其相位连续、旁瓣下降快等优势也指出整形带来的检测效率损失随后剖析传统Viterbi译码复杂度高、代码量大的不足提出改进算法并通过仿真与匹配滤波、改进的匹配滤波对比验证了改进算法在译码性能和代码量上的改善同时揭示其与匹配滤波方案相比仍无优势的原因。资源为1个PDF文件约730KB便于阅读目前已有95人学习。对通信系统研究者及课程学习者而言这份报告完整呈现了问题来源、算法推导、仿真对比与总结分析可帮助理解SBPSK解调中不同译码方案的实际性能差异与适用场景。1. SBPSK 的 Viterbi 译码卡在哪从 180° 突变到相位记忆SBPSK串行二进制相移键控这类带限调制工程上常见于深空测控和卫星数传链路。它把传统 BPSK 的 180° 相位突变约束成受状态机控制的连续相位轨迹频谱旁瓣掉了好几 dB代价是接收端不能再按符号独立判决。这时 Viterbi 译码不只在解卷积码更多时候是在做最大似然序列检测MLSE把 SBPSK 的相位状态连同信道记忆一起估计出来。很多实现跑了几轮仿真发现性能离理论曲线上不去原因往往不在信噪比而是分支度量没把相位记忆算进去或者回溯深度取短了导致误码平台。下面顺着 SBPSK 的网格模型把 Viterbi 译码的改进路径拆开讲度量怎么改、软信息怎么出、工程上怎么降到可落地的复杂度。2. 为什么 SBPSK 必须用 Viterbi状态网格与分支度量的关系在展开改进之前需要把 SBPSK 的网格结构说清楚。SBPSK 的相位轨迹可以由相位状态和输入比特描述接收端的最佳检测就是在这个网格上做 Viterbi 译码。这里的译码对象其实是调制本身与卷积码译码共用同一套加比选结构。2.1 SBPSK 相位状态机与接收信号模型SBPSK 调制可以理解成连续相位 FSKCPFSK的一种特例相位增量受调制指数约束。以常见的 h0.5 为例发送符号 a_n ∈ {1, -1}相位在第 n 个符号间隔内从 θ_n 线性变化到 θ_{n1}增量是 π/2 * a_n。相位状态集合是 {0, π/2, π, 3π/2}状态转移满足模 4 累加θ_{n1} (θ_n π/2 * a_n) mod 2π每个状态有两条出边分别对应 a_n1 和 -1因此 4 状态网格可以完整描述信号。接收端在符号边界采样得到复基带观测r_n sqrt(E_s) * exp(jθ_{n1}) n_n其中 n_n 是复高斯白噪声。最佳序列检测要在所有可能的符号序列里找使序列似然最大的那条路径。和 BPSK 直接按实部符号判决不同r_n 的相位绑定的是符号结束时的状态 θ_{n1}而 θ_{n1} 又依赖前一个状态和当前符号所以必须用网格搜索而不是逐符号判决。这里有一个容易忽视的细节如果调制指数 h 不是 0.5比如 0.25 或 0.75相位状态数会变成 8 或更多网格规模成倍增加。h0.5 是最常见的选择因为它在频谱收紧和接收机复杂度之间取了平衡下面的改进也都是基于这个配置展开。2.2 分支度量里的相位记忆传统 BPSK 度量为什么失效如果直接把 BPSK 的 Viterbi 译码器搬过来分支度量只写成 Re(r_n * conj(s_n))其中 s_n 是当前符号的本地参考这个度量忽略了一个事实SBPSK 符号相位不是独立旋转的而是始终在四个相位点之间有约束地移动。具体来说BPSK 度量允许任意状态对之间都有分支而 SBPSK 的状态转移只有 ±90° 的相位增量分支度量的计算必须同时包含当前相位状态 θ_n 和到达状态 θ_{n1} 的关系。常见的错误是把本地参考直接置成 a_n * exp(jθ_ref)其中 θ_ref 用的是当前符号的估计相位。这样相当于把状态记忆当成了已知量Viterbi 检测器退化成一个逐符号判决器丢失了网格约束。改进的最核心一步就是在分支度量里显式写入状态转移的目标相位BM(s, s) Re(r_n * conj(exp(jθ_{s})))这里 θ_{s} 是目标状态的相位且只有满足 s→s 允许转移的边才参与比较。这样 Viterbi 译码器同时完成了 MLSE 解调和相位状态估计。另一个容易踩的点是接收端是否做差分编码。SBPSK 本身存在相位模糊0、π/2、π、3π/2 的4重模糊很多实现会在调制前加差分编码让 Viterbi 译码输出的硬值先经过差分解码再送外层。改进后的软输出方案里差分编码会破坏软信息的可加性所以更推荐在发送端用已知帧头解决相位模糊而不要采用差分编码。2.3 Viterbi 译码的基准实现流程和参数下面给一个可跑的 Python 骨架用来模拟 4 状态 SBPSK 的 Viterbi 检测。发送符号与相位状态绑定接收序列直接用相位状态上的加噪观测。import numpy as np # SBPSK h0.5 的 4 状态网格状态 0,1,2,3 - 相位 0, pi/2, pi, 3pi/2 states [0, 1, 2, 3] # prev_state - list of (input_symbol, next_state) # 相位增量 pi/2 * a transitions {} for s in states: for a in [1, -1]: next_s (s (1 if a 1 else 3)) % 4 # 模 4 相位累加 transitions.setdefault(s, []).append((a, next_s)) def viterbi_detect(r, trellis, traceback_len32): # r: 复数观测长度 N已补偿载波 N len(r) n_states len(states) # 路径度量初始化为 0hist 记录每个时刻到达每个状态的幸存前一状态 metric np.zeros(n_states) hist np.empty((N 1, n_states), dtypeint) for n in range(N): new_metric np.full(n_states, -np.inf) for next_s in states: for prev_s in states: # 检查是否允许从 prev_s 转移到 next_s for a, ns in trellis[prev_s]: if ns next_s: # 分支度量匹配目标状态相位 phase np.pi / 2 * next_s bm np.real(r[n] * np.exp(-1j * phase)) cand metric[prev_s] bm if cand new_metric[next_s]: new_metric[next_s] cand hist[n 1, next_s] prev_s # 归一化防止长序列数值溢出 metric new_metric - np.max(new_metric) # 回溯取最后度量最大的状态 best_state int(np.argmax(metric)) path [best_state] for n in range(N, 0, -1): best_state hist[n, best_state] path.append(best_state) path.reverse() return path[1:] # 状态序列相邻状态差即符号这段代码就是 SBPSK Viterbi 检测的最小实现。关键参数有三个trellis 定义了状态转移规则traceback_len在滑动窗实现里决定译码延时而metric - np.max(metric)这步归一化在定点处理器上等价于减去最大路径度量用来防止数值溢出。为什么分支度量取r[n] * np.exp(-1j * phase)的实部复观测 r_n 等于 exp(jθ)噪声与本地共轭参考做内积后取实部就是相关度量。参考相位必须取目标状态相位而不是当前状态相位因为观测采样点落在符号边界对应的是转移后的状态。下面的表格列出基准参数后续所有改进都以这组参数作为对照参数典型值说明调制指数 h0.5决定状态数h 越大状态数越多状态数4h0.5 时相位模 4回溯深度16~32 符号对 MLSE 检测建议不低于 16 个符号分支度量相关度量必须匹配目标状态相位路径度量归一化每步减最大值替代溢出保护采样点符号边界时刻与网格的状态定义严格对齐这一章的重点是把 Viterbi 译码的对象从卷积码扩展到 SBPSK 的相位网格。后续的改进全部建立在这个网格模型之上先修正度量再输出软信息最后处理工程实现问题。3. 改进一分支度量的软判决化与 SOVA 输出SBPSK 链路后面通常还跟着卷积码译码器如果内层只输出硬判决符号外层卷积码的软判决增益会被吃掉 1.5~2 dB。改进的第一步是在 Viterbi 检测器内保留软信息把度量从硬判决相关改成对数似然比形式并通过 SOVA 输出符号可靠性。3.1 软判决分支度量的计算方式硬判决 Viterbi 分支度量只取 Re(r * conj(ref))它等价于在高斯白噪声下最小欧氏距离但没有把噪声方差和先前符号的可靠性带进来。软判决分支度量可以写成BM -|r_n - exp(jθ_{s})|² / (2σ²)在实现上不需要真的除 σ²因为公共因子不参与路径比较同一帧内各分支的噪声方差一致所以可以直接用欧氏距离平方的负值。但这有个前提接收机的自动增益控制AGC必须把信号幅度稳定在一个已知水平否则欧氏距离和复观测幅度不对齐软信息的标定就失真了。逐符号判决时硬值很容易掩盖靠近边界的错误而欧氏距离形式保留了度量差。比如在信噪比 6 dB 时某条分支的欧氏距离可能只比竞争路径小 0.2硬判决照样输出了一个可靠的符号但软判决会把 0.2 的差距作为低可信度传给外层外码据此对该位置降权这一下就能挽回后面好几位错符。3.2 给外码留软信息的 SOVA 实现SOVASoft-Output Viterbi Algorithm在传统 Viterbi 回溯阶段比较幸存路径和竞争路径的度量差输出每个符号的可靠性。对 SBPSK 检测器把软信息定义成当前状态路径与次优路径的度量差再做限幅量化def sova_update(metric, new_metric, comp_metric, L8): # metric: 当前状态路径度量 # new_metric: 到达状态的度量 # comp_metric: 竞争路径度量 # L: 软输出量化位数 delta np.abs(new_metric - comp_metric) max_val 2**L - 1 delta_clipped np.clip(delta, 0, max_val) # 返回的不是路径度量而是度量差差越大表示这一位越可靠 return delta_clipped这段代码的核心思想是软信息不是输出路径度量本身而是输出度量差。度量差越大说明这条幸存路径相对竞争路径的优势越明显判决越可靠。外层卷积码 Viterbi 译码器可以直接把delta_clipped当作信道软比特使用。实际工程中更优的选择是 max-log-MAP 或 BCJR 输出完整后验 LLR但复杂度会高几倍。SOVA 在性能上比 max-log-MAP 差约 0.3 dB但计算量几乎和普通 Viterbi 一样因此在深空接收机里SBPSK 内层用 SOVA 是性能和功耗的折中点。实现 SOVA 时要注意路径存储结构。普通 Viterbi 只需要记录每个状态的幸存前一状态SOVA 需要额外保存每个符号的竞争度量差值。以帧长 4096 符号、4 状态为例额外存储是 4096×4 个量化差值按 8 bit 算约 16 KB对大多数处理器都能接收但缓存不友好会带来性能波动建议把差值打包存储。3.3 参数对比表软信息量化、路径宽度与性能下面给出一组在 AWGN 信道下内层 SBPSK 检测器的参数对比用来决定改进后的实现规格配置硬判决 ViterbiSOVA 软输出说明分支度量相关度量整数化欧氏距离平方负值软判决保留可靠性路径度量宽度 (bit)1216软信息需要更大动态范围回溯深度 (符号)1632SOVA 需要更长的竞争路径收敛软信息量化 (bit)无6~8外层译码器的输入相对性能基准1.2~1.5 dB BER1e-5高信噪比下增益更明显参数选择有一个常见误区SOVA 软信息的量化位数不是越高越好。外层 Viterbi 译码器对软信息量化的有效位宽 4~6 bit 就基本饱和再高只增加存储不会带来可观测的增益。路径度量宽度反而要留够因为滑动窗里路径度量可能长期累加12 bit 在高信噪比下会溢出导致误码率出现不该有的平台。另外外层卷积码的约束长度也会影响软信息位宽。约束长度 7 的卷积码用 6 bit 软输入和用 8 bit 软输入性能差不到 0.1 dB约束长度 9 以上的码则建议至少 8 bit。在确定量化位数之前先用浮点仿真扫一遍不同位宽找到曲线的拐点。4. 改进二复杂度与同步的工程化处理算法层面的改进到了工程落地会碰到回溯深度、定点溢出、相位偏转这三个现实问题。任何一项没处理好前面的软输出增益都会被抵消。4.1 滑动窗回溯的深度选择实时系统不可能等整帧结束再回溯必须用滑动窗sliding window。回溯深度取多少直接影响误码平台和译码延迟。对 SBPSK 的 4 状态网格经验值是 16 到 32 个符号。深度太浅幸存路径没有充分收敛尾部误码会抬高误码率深度太长多出来的只是存储开销性能基本不涨。一个快速验证方法是在仿真里把回溯深度从 8 扫到 64看误码率有没有显著下降。若从 16 到 32 改善不到 0.1 dB说明当前调制和信道条件下 16 就够了。对级联卷积码的系统内层回溯深度可以取卷积码约束长度的 2 倍再乘一个安全系数比如约束长度 7 的卷积码对应内层回溯取 28~32 符号。滑动窗实现时窗口滑动的步长通常取回溯深度的一半避免边界效应。窗口覆盖范围内的状态度量仍然每步更新但输出符号只在窗口滑到足够靠后时释放。这个输出延时等于回溯深度乘以符号周期在测控链路里如果接收机还要闭环控制天线就要把这个延时计入环路总时延预算。4.2 定点化与归一化度量防溢出给定点处理器做 Viterbi 时最隐蔽的问题是路径度量在长时间累加后溢出。浮点实现每步减去最大值但定点化后减法可能产生负数负数在状态比较里会引入歧义。正确做法是每步统计当前所有状态度量的最大值然后把每个状态度量统一减去该最大值// 定点 Viterbi 度量归一化示例 int16_t max_metric find_max(metrics, n_states); for (int i 0; i n_states; i) { metrics[i] (int16_t)(metrics[i] - max_metric); // 饱和度保护防止负数干扰后续比较 if (metrics[i] 0) metrics[i] 0; }这段归一化代码在每个符号更新后执行。find_max用排序或 SIMD 比较都可以。metrics[i]不能直接置零必须保留各状态之间的相对差值。如果全部置零下一轮的路径比较就失去了历史积累相当于把 Viterbi 退化成了逐符号判决。定点化的量化步长也要和信噪比匹配。输入 I/Q 信号按 8 bit 量化时分支度量的累加字宽建议 16 bit。若输入动态范围大可以把归一化的周期缩短到每 8 个符号一次但要注意减去最大值后仍然要保留符号位否则负度量会被截断成 0导致某些状态路径永远无法胜出。4.3 相位估计误差对 Viterbi 的影响与补偿SBPSK 对残余频偏和相位抖动比 BPSK 更敏感因为分支度量里显式依赖目标状态相位。一个固定的相位偏移会让所有目标参考相位整体旋转折算进路径度量后等效于在欧氏距离项里叠加常值偏差导致个别分支被系统性高估或低估。工程上常用决策引导环decision-directed loop补偿用 Viterbi 幸存路径的状态作为参考估计残余相位误差反馈到下一轮分支度量。补偿更新式可以写成# 决策引导相位估计 err_phase np.angle(r[n] * np.conj(np.exp(1j * theta_hat))) theta_hat Kp * err_phase增益 Kp 是环路带宽与符号速率的比值取值 0.01~0.05 比较稳。Kp 太大环路边带噪声会把相位估计抖起来Kp 太小跟踪残余频偏的速度跟不上动态场景下会出现突发误码。实现时须注意相位补偿必须放在 Viterbi 回溯之后用回溯输出的状态序列作为参考。如果在加比选过程中用未收敛的临时状态做参考环路会被错误判决拉偏导致正反馈发散。初同步可以先用一段已知帧头做粗估把相位模糊解决后再将 Viterbi 检测器切到判决导引模式。下面是本章三个工程参数的推荐表便于查对参数推荐范围失效迹象回溯深度16~32 符号误码率尾部抬高度量字宽12~16 bit高信噪比下出现突发错误环路增益 Kp0.01~0.05相位抖动收敛慢或震荡5. 验证改进效果的仿真技巧误码率曲线与回溯深度调参最后落到验证。不管改的是 SOVA 还是相位补偿最终都要用蒙特卡洛仿真出误码率曲线并且把回溯深度和软输出宽度两个参数扫一遍才能确定改进是否真实有效。5.1 用蒙特卡洛仿真画出 BER 对比曲线BER 仿真可以直接用 Python 脚本跑但要注意统计可靠性。一个典型做法是让 Viterbi 检测器作为黑盒对每个 Eb/N0 点运行足够多的帧保证至少累计 100 个误码# 伪代码BER 仿真主循环 for eb_n0_db in [2.0, 3.0, 4.0, 5.0]: ber_sum 0 bits_total 0 for _ in range(num_frames): bits np.random.randint(0, 2, frame_len) tx sbpsk_modulate(bits) # 状态机映射 rx add_awgn(tx, eb_n0_db) detected viterbi_detect(rx) # 你的改进版检测器 errs np.sum(detected ! bits) ber_sum errs bits_total len(bits) print(fEb/N0{eb_n0_db} dB, BER{ber_sum/bits_total:.2e})这个脚本的关键不是循环而是保证每个点的帧数足够。常见错误是在误码率 1e-5 时只跑 1e5 bit统计噪声非常大BER 曲线抖动得像噪声。至少跑 2e6 bit高 Eb/N0 点可以适当降低帧数但误码计数不能少于 100。另一个实用技巧是固定随机数种子。同一组随机样本下普通 Viterbi 和改进 Viterbi 使用相同的噪声序列比较的方差会大幅降低改进量哪怕只有 0.2 dB 也能从一条 BER 曲线上稳定地看出来。不固定种子的话两条曲线之间的随机波动很容易掩盖真实性能差。5.2 回溯深度和软输出宽度的经验取值在 SOVA 改进中回溯深度直接改变软信息质量。把回溯深度从 8 扫到 64通常会在 32 附近看到误码率曲线上翘平台消失。软输出宽度在 4~8 bit 之间变化时外层卷积码译码器的性能几乎一致6 bit 是功耗和性能的甜点。实际调参时建议画一张回溯深度对 BER 的曲线固定信噪比在 BER 大约 1e-3 的位置。曲线从深度 8 到 32 明显下降到 32 以后趋于平坦就说明当前网格的收敛长度已经达到。如果曲线在深度 32 之后还有明显下降需要检查分支度量是否漏了状态约束而不是单纯加大回溯深度。最后补充一个验证边界改进后的 Viterbi 检测器在高信噪比下仍然可能出现误码平台这时把接收 I/Q 信号的量化位数从 8 bit 提到 10 bit 试一下。很多 SBPSK 工程问题不是算法问题而是前端 ADC 的量化噪声和 AGC 幅度波动破坏了分支度量的线性关系。用这个思路排查比盲目改算法参数更快。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →