Files
bachelor-thesis/fec_parameter_decision.md

238 lines
6.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# FEC 参数决策说明
本文的 FEC 参数决策可以理解为一个反馈闭环:解码端统计近期丢包模式,控制器估计三状态丢包模型参数,再在延迟约束和残余丢包率约束下搜索编码参数。
下面用 `d` 表示交织深度,也就是代码里的 `gap` 和论文中的交织深度;用 `k` 表示每列保护的数据包数,也就是代码搜索时的 `n`,最终下发为 `protection_num`
## 1. 从测量值估计 p21、p23、p33
解码端通过全局序列号检测丢包,并在滑动窗口内把丢包段按长度分成三类:
- `total`:统计窗口内的数据包总数。
- `loss_1`:长度为 1 的丢包段数量,即孤立丢包。
- `loss_2`:长度为 2 的连续丢包段数量。
- `loss_3`:长度大于等于 3 的连续丢包段数量。
控制器用这些统计量估计三状态模型中的三个概率:
- `p21`:从正常状态进入孤立丢包状态的概率。
- `p23`:从正常状态进入连续突发丢包状态的概率。
- `p33`:突发丢包继续的概率。
代码首先估计 `p33`
```text
loss_3_cal = max(loss_3, 1)
p33 = loss_3_cal / (loss_2 + loss_3_cal)
```
直观上,`p33` 表示突发丢包是否容易继续。如果长度大于等于 3 的丢包段越多,说明突发丢包越容易延续,因此 `p33` 越大。代码中把 `loss_3` 至少取为 1是为了避免除零并让低样本场景下的估计更保守。
然后估计 `p23`。代码分别从长度为 2 的突发段和长度大于等于 3 的突发段反推 `p23`
```text
p23_a = loss_2 / (total * p33 * (1 - p33))
p23_b = loss_3_cal / (total * p33 * p33)
p23 = (p23_a + p23_b) / 2
```
对应的直觉是:
```text
loss_2 / total ≈ p23 * p33 * (1 - p33)
loss_3 / total ≈ p23 * p33^2
```
也就是说,长度为 2 或更长的连续丢包都来自“触发突发丢包”这件事,只是后续持续次数不同。控制器用两个观测量分别反推 `p23`,再取平均。
最后估计 `p21`
```text
p21 = (loss_1 + loss_2 + loss_3_cal) / total - p23
```
其中:
```text
(loss_1 + loss_2 + loss_3_cal) / total
```
表示丢包事件段的总发生率。模型认为丢包事件要么是孤立丢包,要么是突发丢包,因此:
```text
总丢包事件率 ≈ p21 + p23
```
所以可以用总丢包事件率减去突发丢包触发概率 `p23`,得到孤立丢包触发概率 `p21`
代码中还对概率做了截断,避免估计值在样本较少时过大或越界:
```text
p33 ∈ [0, 0.8]
p23_a ∈ [0, 0.8]
p23_b ∈ [0, 0.8]
p21 ∈ [0, 1]
```
## 2. lambda 与这些概率的关系
估计出 `p21``p23` 后,代码定义:
```text
lambda = p21 + p23
```
代码中变量名是:
```text
loss_happen_rate = p21 + p23
```
`lambda` 的意义是:在当前统计窗口和三状态模型下,一个新的丢包事件发生的概率。这里的“丢包事件”不是单个丢包包,而是一次丢包段的开始:
- 如果进入 `S1`,表示发生一次孤立丢包。
- 如果进入 `S3`,表示发生一次连续突发丢包。
因此:
```text
lambda = 孤立丢包触发概率 + 突发丢包触发概率
= p21 + p23
```
在后续参数搜索中,`lambda` 被用来估计一个保护单元里出现多个丢包事件的概率。因为 XOR FEC 每列只有一个冗余包,所以每列最多恢复一个丢包;如果同一列里出现两个或更多丢包事件,就可能产生残余丢包。
## 3. 有了 lambda 后如何搜索最高的 d、k
FEC 编码矩阵由两个参数决定:
- `d`:交织深度,决定有多少列。
- `k`:每列保护的数据包数,决定每列有多少个数据包参与 XOR。
每个编码组包含:
```text
d * k 个数据包 + d 个冗余包
```
代码会枚举:
```text
d ∈ {1, 2, 3, 4}
```
对每个 `d`,搜索满足约束的最大 `k`
### 3.1 延迟约束
代码设置延迟阈值:
```text
delay_th_ms = 20ms
```
对于给定的交织深度 `d`,每列保护的数据包数 `k` 不能太大,否则要等更多数据包到齐才能生成和接收冗余包,恢复等待时间会变长。
代码用吞吐量 `rx_thpt` 和当前丢包率 `loss_rate` 估计 `k` 的上限:
```text
up_limit = delay_th_ms / 1000 * rx_thpt / (1 - loss_rate) / d
```
含义是:在 20ms 的等待时间预算里,按照当前接收速率最多能容纳多少个被保护的数据包。`d` 越大,同一个编码组跨越的包越多,所以每列允许的 `k` 越小。
### 3.2 残余丢包率约束
代码设置目标残余丢包阈值:
```text
loss_th = 0.01
```
也就是目标残余丢包率为 1%。
恢复失败风险分成两部分:
第一部分是长突发风险。交织深度 `d` 可以把长度不超过 `d` 的连续丢包打散;如果突发长度超过 `d`,就可能仍然无法恢复。代码用下面的式子估计这部分风险:
```text
p_burst_fail = p23 * p33^d
```
第二部分是随机多丢包风险。对于一列中 `k` 个受保护数据包,如果发生 0 次或 1 次丢包事件XOR 可以恢复;如果发生 2 次及以上,就可能恢复失败。代码用下面的近似式估计:
```text
alpha_random = 1
- (1 - lambda)^k
- k * lambda * (1 - lambda)^k
```
它的含义是:
```text
alpha_random ≈ 1 - P(0 次丢包事件) - P(1 次丢包事件)
≈ P(至少 2 次丢包事件)
```
因此,对于每个候选 `d`,代码给随机多丢包风险留下的预算是:
```text
alpha_random_th = loss_th / 2 - p23 * p33^d
```
如果:
```text
alpha_random < alpha_random_th
```
则说明当前 `k` 在残余丢包约束下是可接受的。
### 3.3 搜索过程
整体搜索过程可以写成:
```text
for d in 1..=4:
根据 20ms 延迟阈值计算 up_limit
根据长突发风险计算 alpha_random_th
如果 alpha_random_th < 0:
当前 d 不可行
否则从 up_limit 向下枚举 k:
计算 alpha_random
如果 alpha_random < alpha_random_th:
记录当前 d 下最大的 k
break
```
最后,代码在所有可行的 `(d, k)` 中选择 `k` 最大的组合:
```text
interleave_depth = d
protection_num = k
```
如果所有候选都不可行,代码会进入兜底逻辑,使用:
```text
d = 5
k = clamp(20ms * rx_thpt / (1 - loss_rate) / 5, 1, 10)
```
如果估计出的丢包事件发生率非常低:
```text
lambda < 0.001
```
则认为链路质量足够好,直接关闭 FEC
```text
d = 0
k = 0
```
## 答辩页一句话版本
解码端先把近期丢包压缩成孤立丢包、短突发丢包和长突发丢包三类统计量;控制器据此估计三状态丢包模型中的 `p21``p23``p33`,并得到丢包事件发生率 `lambda = p21 + p23`。随后,控制器枚举交织深度 `d`,在 20ms 延迟约束和 1% 残余丢包率约束下搜索最大的保护长度 `k`,最终将 `(d, k)` 下发给编码端作为下一组 FEC 参数。