# vitdec
使用维特比算法对二进制数据进行卷积解码
函数库: TyCommunication
# 语法
decodedout = vitdec(codedin, trellis, tbdepth, opmode, dectype)
decodedout = vitdec(codedin, trellis, tbdepth, opmode, "soft", nsdec)
decodedout = vitdec(codedin, trellis, tbdepth, opmode, dectype, puncpat)
decodedout = vitdec(codedin, trellis, tbdepth, opmode, dectype, puncpat, eraspat)
decodedout, = vitdec(codedin, trellis, tbdepth, "cont", dectype, ___, imetric, istate, iinput)
decodedout, fmetric, fstate, finput = vitdec(codedin, trellis, tbdepth, "cont", ___)
提示
使用 Val 替换输入中的字符串性能更佳 decodedout = vitdec(codedin, trellis, tbdepth, Val(:cont), Val(:soft), nsdec)
# 说明
decodedout = vitdec(codedin, trellis, tbdepth, opmode, dectype) 使用维特比算法对输入码的每个符号进行解码。 所有其他输入分别指定卷积编码网格、回溯深度、操作模式和决策类型,并在运行时共同配置维特比 (Viterbi) 算法。 示例
decodedout = vitdec(codedin, trellis, tbdepth, opmode, "soft", nsdec) 配置了维特比算法,用于解码类型(dectype)的软判决解码,并且具有 nsdec 位的量化。
decodedout = vitdec(codedin, trellis, tbdepth, opmode, dectype, puncpat) 解码被刺穿编码输入的每个符号,其中 puncpat 是穿孔模式。
decodedout = vitdec(codedin, trellis, tbdepth, opmode, dectype, puncpat, eraspat) 指定擦除模式 eraspat。若要不使用穿孔,请将 punchpat 指定为 [ ]。
decodedout, = vitdec(codedin, trellis, tbdepth, "cont", dectype, ___, imetric, istate, iinput) 指定了连续操作模式(continuous operation mode)作为 opmode,适用于任何前述语法。解码器将以 imetric、istate 和 iinput 指定的初始状态度量、回溯状态和回溯输入开始。
连续操作模式使您能够保存解码器的内部状态信息,以便在后续调用该函数时使用。如果您的数据被分成一系列向量,并且您在循环中处理它们,则重复调用此函数可能会很有用。
decodedout, fmetric, fstate, finput = vitdec(codedin, trellis, tbdepth, "cont", ___) 还会在解码过程结束时返回最终状态度量、回溯状态和回溯输入,适用于任何前述语法的连续操作模式。在后续调用此函数时,请将 fmetric、fstate 和 finput 作为 imetric、istate 和 iinput 的初始设置。
# 示例
使用维特比解码器对卷积码进行解码
使用 convenc 函数对一个二进制随机向量进行卷积编码,然后使用 vitdec 函数进行解码。
使用 poly2trellis 函数定义一个 trellis 结构体,利用该结构体配置 convenc 函数。
using TyCommunication
using TyMath
rng = MT19937ar(1234)
tr = poly2trellis([4 3], [4 5 17; 7 4 2])
x = convert.(Int64, rand(0:1, 100))
code, = convenc(x, tr)
解码时,利用先前的 trellis 结构体配置维特比解码器,回溯深度设置为 2,使用穿孔模式、硬精度。
tb = 2
decoded = vitdec(code, tr, tb, "trunc", "hard")
验证解码后的向量与信源相同。
x == decoded
ans = true
利用维特比算法解码穿孔信号
应用维特比解码一个穿孔信号。刺穿操作使得码率从 1/2 变为 3/4。
初始化编码和解码操作的参数。
using TyCommunication
trellis1 = poly2trellis(7, [171 133])
puncpat = [1; 1; 0; 1; 1; 0]
tbdepth = 96
opmode = "trunc"
dectype = "hard"
计算未穿孔码率和穿孔码率。
K = log2(trellis1.numInputSymbols)
N = log2(trellis1.numOutputSymbols)
unpunc_coderate = K / N
punc_coderate = (K / N) * (length(puncpat) / sum(puncpat))
卷积编码一个全 1 位的消息,并对编码输出进行刺穿操作。
msg = ones(100 * length(puncpat), 1)
msg = trunc.(Int64, msg)
puncturedcode = convenc(msg, trellis1, puncpat)
显示信息的长度、被刺穿的编码和穿孔模式。
length(msg)
length(puncturedcode[1])
length(puncpat)
length(msg) = 600
length(puncturedcode[1]) = 800
length(puncpat) = 6
对被刺穿的编码消息应用维特比解码。将解码后的输出与原始消息进行比较。即使对编码的消息进行了刺穿操作,维特比解码也能零错误地恢复消息。
codedin = puncturedcode[1]
decodedout = vitdec(codedin, trellis1, tbdepth, opmode, dectype, puncpat)
isequal(msg, decodedout)
ans = true
2/3 速率卷积码误码率估计
对 2/3 速率卷积码,应用 16-QAM 调制,并通过 AWGN 信道传输数据的链路进行误码率 (BER) 仿真估计。这个图显示了一个 2/3 速率的编码器,有两个输入流,三个输出流和七个移位寄存器。
定义图中表示的卷积编码网格。
using TyCommunication
using TyBase
using TyMath
rng = MT19937ar(1234)
trellis1 = poly2trellis([5 4], [23 35 0; 0 5 13])
K = log2(trellis1.numInputSymbols)
N = log2(trellis1.numOutputSymbols)
coderate = K / N
fprintf("K is %d and N is %d. The code rate is %3.2f\n", K, N, coderate)
K is 2 and N is 3. The code rate is 0.67
设置调制阶数,并计算每个调制符号的比特数。生成随机二进制数据。输入比特流必须是用于编码操作的输入比特流数 (K) 的倍数,并且必须是用于调制操作的每个调制符号比特数 (bps) 的倍数。
M = 16
bps = log2(M)
numSymPerFrame = 5000
dataIn = randi(rng, [0 1], trunc(Int64, K * bps * numSymPerFrame), 1)
对输入数据进行卷积编码。 对编码符号应用 16-QAM 调制。
codedout, = convenc(dataIn, trellis1)
txSig = qammod(codedout, M, InputType = "bit")
使用每个符号的比特数 (bps) 和编码率 (coderate),将每比特的能量与噪声功率谱密度 (EbNo) 的比率转换为 awgn 函数使用的信噪比 (snr1) 值。将 10 分贝的 Eb/No 转换为等效信噪比。通过 AWGN 信道传递信号。
EbNo = 9
snr1 = EbNo + 10 * log10(bps * coderate)
rxSig = awgn(rng, txSig, snr1, "measured")
解调接收到的信号。 指定维特比解码器的回溯深度。 在连续终止模式上使用维特比解码器解码二进制解调信号。
demodSig = qamdemod(rxSig, M, OutputType = "bit")
tbdepth = 16
dataOut, = vitdec(demodSig, trellis1, tbdepth, "cont", "hard")
通过解码器计算延迟,并在计算误码率时考虑解码延迟。将编码的误码率与理论的未编码的误码率进行比较,可以看到对编码数据改进的误码率。
decDelay = K * tbdepth
decDelay = trunc(Int64, decDelay)
berCoded = biterr(dataIn[1:end-decDelay], dataOut[decDelay+1:end]) ./ length(dataOut[decDelay+1:end])
length(dataOut[decDelay+1:end])
berUncoded = berawgn(EbNo, "qam", M)
fprintf("The coded BER is %6.5f\nThe uncoded BER is %6.5f\n", [berCoded[1] berUncoded[1]])
The coded BER is 0.00045
The uncoded BER is 0.00439
# 输入参数
codedin - 卷积编码的消息二进制向量
卷积编码的信息,指定为二进制向量。codedin 中的每个符号都由 log2(trellis.numoutsymbols)位组成。
数据类型: Int64
trellis - trellis 描述结构体
trellis 描述,指定为具有这些字段的结构,用于速率 K/N 代码。K 表示输入比特流的数量,N 表示输出比特流的数量。您可以使用 poly2trellis 函数创建 trellis 结构,也可以手动创建 trellis 结构。有关此结构的更多信息,请参见卷积码和 istrellis 函数的 trellis 描述。
| 属性名 | 描述 | 说明 | 数据格式 |
|---|---|---|---|
| numInputSymbols | 输入符号数 | 输入符号数,指定为值为 | 整数 |
| numOutputSymbols | 输出符号数 | 输出符号数,指定为值为 | 整数 |
| numStates | 状态数 | 编码器中的状态数,指定为整数。numStates 必须是 2 的幂。 | 整数 |
| nextStates | 下一个状态 | 当前状态和当前输入的所有组合的下一个状态,指定为整数值的 numStates * | 整数值矩阵 |
| outputs | 输出 | 当前状态和当前输入的所有组合的输出,指定为整数值的 numStates * | 整数值矩阵 |
tbdepth - 回溯深度正整数
回溯深度,指定为正整数。
数据类型: Int64
opmode - 运行模式"cont" | "term" | "trunc"
操作模式,指定为"cont" 、 "term" 或 "trunc"。该输入指示解码器的操作模式以及对相应编码器的操作所做的这些假设。
- "cont" —— 指定连续操作模式。在连续工作模式下,假定编码器已从全零状态启动。解码器从具有最佳指标的状态进行跟踪。在第一个解码符号出现在输出中之前,经过等于输入tbdepth符号的延迟。当您重复调用此函数并希望保持连续调用之间的连续性时,此模式是合适的。有关需要重复调用 Viterbi 解码算法的工作流,请参阅提示;
- "term" —— 指定终止的操作模式。在终止操作模式下,假定编码器已在全零状态下启动和结束,这对于 convenc 函数的默认语法是正确的。解码器从全零状态回溯。此模式产生零延迟;
当 convenc 函数的消息输入端有足够的零以填充编码器的所有存储寄存器时,此模式是合适的。零值尾位将所有消息数据位从编码器中清除。使用编码器的多项式描述,对于具有 K 个输入位和约束长度向量 ConstraintLength 的编码器,刷新编码器所需的零数为 K × max(ConstraintLength – 1)。约束长度向量是 poly2trellis 函数的第一个输入参数。
- "trunc" —— 指定截断的操作模式。在截断操作模式下,假定编码器已从全零状态启动。解码器从具有最佳指标的状态进行跟踪。此模式产生零延迟。当无法假定编码器以全零状态结束,并且不希望在连续调用此函数之间保持连续性时,此模式是合适的。
对于 "term" 和 "trunc" 模式,回溯深度 tbdepth 必须是正整数,小于或等于输入 codedin 中的输入符号数。
数据类型: String
dectype - 解码类型"unquant" | "hard" | "soft"
解码类型,指定为 "unquant"、"hard"、"soft"。此参数表示解码器做出的解码决策类型,并影响解码器期望作为 codedin 输入的数据类型。
"unquant" —— 解码器需要有符号的数字输入值,其中正值映射到逻辑 0,负值映射到逻辑 1;
"hard" —— 解码器期望二进制输入值为 0 或 1;
"soft" —— 解码器期望输入值为整数,范围在 [0, (2^nsdec – 1)] 之间。维特比算法的决策标准将 0 视为最可靠的 0,将 2^nsdec – 1 视为最可靠的 1。
数据类型: String
nsdec - 软判决量化位数整数,范围为 [1, 13]
软判决量化位数,指定为范围 [1, 13] 内的整数。作为参考,使用 3 位量化的软判决解码相比于硬判决解码,可以改善约 2 dB 的错误解码恢复性能。
依赖关系
要启用此输入参数,请将 dectype 输入参数设置为 "soft"。
puncpat - 穿孔模式二进制向量
指定为二进制值的向量。指示带 0 的刺穿位和 1 的未穿孔位。puncpat 向量的长度必须是输入消息向量长度 length(msg) 的整数除数。
数据类型: Int64
eraspat - 擦除模式二进制向量
擦除模式,指定为二进制值的向量。用 1 表示擦除位,用 0 表示未擦除位。擦除模式的长度必须与输入代码的长度相同。
imetric - 解码器状态度量整数 | 整数值向量
解码器状态度量,指定为整数或整数值向量。imetric 中的每个值表示相应解码器状态的起始状态度量。当将 imetric 设置为向量时,长度必须为 trellis.numStates。要使用默认的解码器状态度量,请将 imetric 指定为 []。
依赖关系
要启用此输入参数,请将 opmode 输入参数设置为 "cont"。
istate - 解码器初始回溯状态整数值矩阵
解码器初始回溯状态,指定为 trellis.numStates × tbdepth 的整数值矩阵,范围为 [0, (trellis.numStates – 1)]。要使用默认的解码器初始回溯状态,请将 istate 指定为 []。
输入参数 istate 和 iinput 共同指定解码器的初始回溯记忆。如果编码器的原理图有多个输入流,则接收第一个输入流的移位寄存器提供 istate 中的最低有效位,接收最后一个输入流的移位寄存器提供 istate 中的最高有效位。
依赖关系
要启用此输入参数,请将 opmode 输入参数设置为 "cont"。
iinput - 解码器初始回溯输入整数值矩阵
解码器初始回溯输入,指定为 trellis.numStates × tbdepth 的整数值矩阵,范围为 [0, (trellis.numStates – 1)]。要使用默认的解码器初始回溯输入,请将 iinput 指定为 []。
输入参数 istate 和 iinput 共同指定解码器的初始回溯记忆。
依赖关系
要启用此输入参数,请将 opmode 输入参数设置为 "cont"。
# 输出参数
decodedout - 解码的消息二进制向量
解码的消息,作为二进制向量返回。向量解码器中的每个符号都由 log2(trellis.numInputSymbols) 位组成。
fmetric - 解码器最终状态度量整数值向量
解码器最终状态度量,以具有 trellis.numStates 元素的整数值向量形式返回。fmetric 中的每个值表示相应解码器状态的最终状态度量。
在连续模式下调用 vitdec 时,fmetric 通常用于为后续调用 vitdec 函数设置 imetric。
依赖关系
当 opmode 参数设置为 "cont" 时,此输出适用。
fstate - 解码器最终回溯状态整数值矩阵
解码器最终回溯状态,以 trellis.numStates × tbdepth 的整数值矩阵返回,范围为 [0, (trellis.numStates – 1)]。
输出 fstate 和 finput 共同描述解码器的最终回溯记忆。如果编码器的原理图有多个输入流,则接收第一个输入流的移位寄存器提供 fstate 中的最低有效位,而接收最后一个输入流的移位寄存器提供 fstate 中的最高有效位。
在连续模式下调用 vitdec 时,fstate 通常用于为后续调用 vitdec 函数设置 istate。
依赖关系
当 opmode 参数设置为 "cont" 时,此输出适用。
finput - 解码器最终回溯输入整数值矩阵
解码器最终回溯输入,以 trellis.numStates × tbdepth 的整数值矩阵返回,范围为 [0, (trellis.numStates – 1)]。
输出 fstate 和 finput 共同指定解码器的最终回溯记忆。
在连续模式下调用 vitdec 时,finput 通常用于为后续调用 vitdec 函数设置 iinput。
依赖关系
当 opmode 参数设置为 "cont" 时,此输出适用。
# 更多相关
回溯和解码延迟
回溯深度会影响解码延迟。解码延迟是输出中第一个解码符号之前的零符号数。
- 对于连续工作模式,解码延迟等于回溯深度符号的数量;
- 对于截断或终止的工作模式,解码延迟为零。在这种情况下,回溯深度必须小于或等于每个输入中的符号数。
回溯深度估计
作为一般估计,典型的回溯深度值大约是两到三倍
K 是输入符号的数量,N 是输出符号的数量,PuncturePattern 是穿孔图案向量。
例如,应用此一般估计值,将生成这些近似的回溯深度。
- 速率为 1/2 的代码的回溯深度为 5
- 速率为 2/3 的代码的回溯深度为 7.5
- 速率为 3/4 的代码的回溯深度为 10
- 速率为 5/6 的代码的回溯深度为 15
# 版本历史记录
在 2024a 之前推出
2025a SP1:当操作模式为 term 和 trunc 时,输出参数个数从 4 修改为 1
当操作模式为 term 和 trunc 时,默认输出一个参数,当操作模式为 cont 时,默认输出四个参数,且解码输出类型由 Matrix{Int} 修改为 BitVector。
using TyCommunication
using TyMath
rng = MT19937ar(1234)
tr = poly2trellis([4 3], [4 5 17; 7 4 2])
x = convert.(Int64, rand(rng, 0:1, 100))
code, = convenc(x, tr)
tb = 2
旧版用法:
decoded, = vitdec(code, tr, tb, "trunc", "hard")
旧版输出:
decoded
100×1 Matrix{Int64}:
0
1
0
⋮
1
0
1
2025a SP1 更新后代码,注意 decoded 输出不需要再加逗号了,因为操作模式为 trunc,只会输出单个参数。
decoded = vitdec(code, tr, tb, "trunc", "hard")
2025a SP1 更新后输出:
100-element BitVector:
0
1
0
⋮
1
0
1