nx_bwe.nx source
↩ module page · 73 lines · 3384 B
1// nx_bwe.nx -- SOVEREIGN bandwidth estimator (the "adapt to the pipe" rung). BENCHMARK: WebRTC's GCC
2// (Google Congestion Control) = a delay-based signal (queuing delay TREND: is one-way delay rising =
3// a queue building ahead of us?) + a loss-based signal, combined into an AIMD controller (additive
4// increase when the path is clear, multiplicative decrease the instant congestion appears). Rebuilt from
5// scratch, pure integer, no third party, transport-agnostic. Output = an available-bitrate estimate that
6// feeds nx_media_budget's uplink input, so the ladder rides the REAL pipe instead of a static guess.
7// bwe_on_arrival(send_ts, arr_ts) : per-packet inter-arrival delay variation -> EWMA trend
8// bwe_report_loss(lost, total) : windowed loss -> EWMA loss rate
9// bwe_update() : one AIMD step -> new estimate (call ~1x/RTT)
10// license_tier: ORIGINAL
11
12const BW_EST: i64 = 0
13const BW_LOSS: i64 = 1 // EWMA loss, permille
14const BW_TREND: i64 = 2 // EWMA inter-arrival delay variation (rising = queue building)
15const BW_PSEND: i64 = 3
16const BW_PARR: i64 = 4
17const BW_HAVE: i64 = 5
18const BW_MIN: i64 = 6
19const BW_MAX: i64 = 7
20const BW_INC: i64 = 8
21
22const BW_TREND_THRESH: i64 = 50 // delay-variation units above which a queue is deemed building
23const BW_LOSS_HI: i64 = 100 // 10% -> congested
24const BW_LOSS_LO: i64 = 20 // 2% -> safe to probe up
25const BW_DEC_NUM: i64 = 85 // multiplicative decrease = x0.85 (fast back-off, GCC-style)
26const BW_DEC_DEN: i64 = 100
27
28func bwe_init(bw: *i64, min_kbps: i64, max_kbps: i64, start_kbps: i64, inc_kbps: i64) -> i64 {
29 bw[BW_EST] = start_kbps
30 bw[BW_LOSS] = 0
31 bw[BW_TREND] = 0
32 bw[BW_HAVE] = 0
33 bw[BW_MIN] = min_kbps
34 bw[BW_MAX] = max_kbps
35 bw[BW_INC] = inc_kbps
36 return 0
37}
38// per-packet: d = (arr - prev_arr) - (send - prev_send) = how much EXTRA delay this packet took vs the
39// last = the queuing-delay gradient. Sustained positive => a queue is growing ahead of us (overuse).
40func bwe_on_arrival(bw: *i64, send_ts: i64, arr_ts: i64) -> i64 {
41 if bw[BW_HAVE] == 1 {
42 let d: i64 = (arr_ts - bw[BW_PARR]) - (send_ts - bw[BW_PSEND])
43 bw[BW_TREND] = bw[BW_TREND] + (d - bw[BW_TREND]) / 8 // EWMA (1/8)
44 }
45 bw[BW_PSEND] = send_ts
46 bw[BW_PARR] = arr_ts
47 bw[BW_HAVE] = 1
48 return 0
49}
50func bwe_report_loss(bw: *i64, lost: i64, total: i64) -> i64 {
51 var lp: i64 = 0
52 if total > 0 { lp = lost * 1000 / total }
53 bw[BW_LOSS] = bw[BW_LOSS] + (lp - bw[BW_LOSS]) / 4
54 return 0
55}
56// one AIMD step. Congestion = (delay trend over threshold) OR (loss over 10%). React fast down, probe slow up.
57func bwe_update(bw: *i64) -> i64 {
58 var congested: i64 = 0
59 if bw[BW_TREND] > BW_TREND_THRESH { congested = 1 }
60 if bw[BW_LOSS] > BW_LOSS_HI { congested = 1 }
61 if congested == 1 {
62 bw[BW_EST] = bw[BW_EST] * BW_DEC_NUM / BW_DEC_DEN
63 bw[BW_TREND] = 0 // reacted; re-measure the gradient
64 } else {
65 if bw[BW_LOSS] < BW_LOSS_LO { bw[BW_EST] = bw[BW_EST] + bw[BW_INC] } // clear path -> probe up
66 }
67 if bw[BW_EST] < bw[BW_MIN] { bw[BW_EST] = bw[BW_MIN] }
68 if bw[BW_EST] > bw[BW_MAX] { bw[BW_EST] = bw[BW_MAX] }
69 return bw[BW_EST]
70}
71func bwe_estimate(bw: *i64) -> i64 { return bw[BW_EST] }
72
73func main() -> i64 { return 0 }