动态fec参数决策策略
This commit is contained in:
237
fec_parameter_decision.md
Normal file
237
fec_parameter_decision.md
Normal file
@@ -0,0 +1,237 @@
|
||||
# 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 参数。
|
||||
Reference in New Issue
Block a user