rack:自动恢复的平线判据按实测重定(p95−p05 + 中位数下限)

原判据「窗口内 max − min < 5 mA」抓不到真卡死:10-04 第 1 路平在 520 mA 五个半小时,
偶发尖峰让 590 个窗口的 max − min 都在 17 mA 以上,一个都判不出来。

- 平线改成:p95 − p05 < flat_ua(30 mA)且中位数 ≥ flat_min_ua(100 mA,新增参数)。
  下限是给空闲低功耗留的:睡着的手机平在 ~10 mA 是正常的。
- 过低不变(窗内最大值 < 30 mA):睡着的手机每 25 s 被长轮询叫醒,窗内最大值都在 213 mA 以上。
- 真实遥测回放:两次真卡死(10-04 第 1 路、10-05 09:40 第 5 路平在 382 mA)都判出;
  亮屏常醒 ~26 h × 4 路、空闲睡着 ~6 h × 5 路、试验 / OTA 期间零误判。
- 测试 +4(带尖峰的平线 / 睡着是健康的 / 睡下去再没醒 = 过低 / 低位平线不下结论),恢复页加「中位数不低于」。

依据和局限写在 recovery.py 模块头。已部署;五路的自动恢复开关仍然全关,还没真正触发过一次。

Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
zhou yang
2026-10-06 05:14:22 +08:00
共同撰写人 Claude Opus 5.5
父节点 d1fab040cc
当前提交 ca7435242f
共修改 5 个文件,包含 65 行新增和 15 行删除
+28
查看文件
@@ -237,6 +237,34 @@ def test_low_current_is_detected_and_normal_is_not():
assert feed(ph2, 0, 61, lambda s: 800 + s % 3)[-1][2][0] == "low"
# 2026-10-06 按实测改的平线判据(recovery.py 模块头的表):下面三条钉的是「改之前判错」的那几种形状。
def test_flat_tolerates_occasional_spikes():
"""真卡死(10-04 第 1 路):平在 ~520 mA,偶尔冒一个 +150 mA 的尖峰。max − min 判据一个窗口都抓不到。"""
ph = PhoneRecovery(Params(window_s=60, settle_s=30))
r = feed(ph, 0, 61, lambda s: 520000 + (s % 3) * 4000 + (150000 if s % 25 == 0 else 0))
assert r and r[-1][1] == SOFT and r[-1][2][0] == "flat"
def test_sleeping_phone_is_healthy():
"""空闲睡着:平在 ~10 mA,每 25 s 被长轮询叫醒一秒(~250 mA)。既不是平线(低)也不是过低(有尖峰)。"""
ph = PhoneRecovery(Params(window_s=300, settle_s=30))
assert feed(ph, 0, 900, lambda s: 250000 if s % 25 == 0 else 9000 + s % 4 * 1000) == []
def test_asleep_and_never_waking_is_low():
"""睡下去之后再也没醒(代理不轮询了):5 分钟里一个尖峰都没有 ⇒ 过低。"""
ph = PhoneRecovery(Params(window_s=300, settle_s=30))
r = feed(ph, 0, 301, lambda s: 9000 + s % 4 * 1000)
assert r and r[-1][2][0] == "low"
def test_flat_but_low_level_is_not_flat():
"""平在 60 mA(低于 flat_min_ua、高于 low_ua):不下结论 —— 没见过卡死的手机掉到这么低。"""
ph = PhoneRecovery(Params(window_s=60, settle_s=30))
assert feed(ph, 0, 200, lambda s: 60000 + s % 3 * 1000) == []
def test_rate_limit_gives_up():
ph = PhoneRecovery(Params(window_s=60, settle_s=30, max_per_hour=2))
t = 0