news 2026/8/22 3:30:14

多路 Sniff 模式协商与 Deep 模式窗口对齐全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
多路 Sniff 模式协商与 Deep 模式窗口对齐全解析

单链路的 Sniff 入门不难,难的是一条设备上同时跑多条 Sniff 链路——多路窗口怎么协商、怎么错峰、进入 deep 省电后怎么保证窗口不漂、不重叠。这是 Multi-Central / Multi-Peripheral 场景下,调度器设计的核心难题。本文拆解:Sniff 协商的完整流程、建立用的全套参数、以及 deep 模式下多路窗口对齐的设计思路。

TL;DR

  • 多路 Sniff 的本质:一个设备(Master 或 Slave)同时维护 N 条 ACL 链路,每条链路有独立的 D_SNIFF / T_SNIFF 锚点,调度器必须让 N 个窗口"错峰"且"不漂"。
  • 协商流程LMP_sniff协商基础参数(D_SNIFF / T_SNIFF / Attempt / Timeout),LMP_sniff_subrating协商 deep 模式的子速率(Max Latency / Min Timeout)。
  • Deep 模式的窗口对齐精髓锚点 D_SNIFF 永远不变,deep 只是把实际唤醒周期放大为 T_SNIFF × N,双方用同一个锚点独立推算,天然对齐。
  • 多路错峰的关键:把多条链路的 T_SNIFF 设计成倍数关系,让窗口在时域上天然不重叠,配合调度器的优先级仲裁兜底。
  • 时钟漂移是最大敌人:deep 模式下休眠久、漂移累积大,必须靠每次收包更新 clk_offset 持续校准。

目录

  1. 多路 Sniff 的场景与挑战
  2. Sniff 协商全流程
  3. 建立用的全套参数
  4. Deep 模式:子速率与窗口对齐
  5. 多路窗口对齐的调度设计
  6. 实战坑与调优
  7. 抓包验证
  8. 总结

1. 多路 Sniff 的场景与挑战

1.1 什么是"多路 Sniff"

单路 Sniff 是一条 ACL 链路进入省电模式。多路 Sniff 是同一个设备同时维护多条进入 Sniff 的 ACL 链路。典型场景:

场景拓扑多路压力
Multi-Central一个中央设备连 3 个外围,3 条 ACL 都进 Sniff中央设备作为 Master,要错峰调度 3 个 Sniff 窗口
Central + Peripheral设备同时是链路 A 的 Master、链路 B 的 Slave既要推算自己的 Slave 窗口,又要给别人的 Slave 排窗口
TWS 主耳主耳对手机是 Slave(Sniff),对副耳是 Master(TPT)两条链路的 Sniff 节奏必须对齐,否则副耳监听丢包

1.2 多路带来的三个新问题

单路 Sniff 只要"对齐一条链路的窗口",多路则多了三个维度的问题:

问题 1:窗口重叠(调度冲突) 链路 A: ┃窗┃──────┃窗┃──────┃窗┃ 链路 B: ──────┃窗┃──────┃窗┃── ↑ 如果 A/B 窗口撞在一起,同一个 RF 前端无法同时服务两条链路 问题 2:功耗峰值 三条链路同时唤醒 → 瞬时电流叠加 → 电源纹波 → 影响 RF 性能 问题 3:deep 模式下漂移放大 T_SNIFF 拉大后,休眠时间变长,clk_offset 漂移累积, 窗口对不上的概率随休眠时长指数上升

核心结论:多路 Sniff 的难点不在"协商"(协议已定义),而在调度器如何把 N 个窗口在时域上排开,并让它们在 deep 模式下依然对齐


2. Sniff 协商全流程

2.1 协商的参与者与方向

Sniff 由Master 发起协商(LMP 规范里LMP_sniff方向是 M→S),但从设备可以主动请求(HCI 层的HCI_Sniff_Mode命令,Controller 内部再转成 LMP_sniff)。

HCI 层: Host → Controller: HCI_Sniff_Mode(handle, max_interval, min_interval, attempt, timeout) LMP 层: Master → Slave: LMP_sniff(D_sniff, T_sniff, attempt, timeout) Slave → Master: LMP_sniff_accepted / LMP_not_accepted

2.2 LMP_sniff 协商时序

主设备(Master) 从设备(Slave) │ │ │ ① 决定进 Sniff │ ├── LMP_sniff ──────────────────────────→ │ │ D_sniff = 1000(主设备 CLKN 锚点) │ │ T_sniff = 32(周期,时隙) │ │ Attempt = 4(强制监听时隙) │ │ Timeout = 16(延伸监听时隙) │ │ │ │ ② 从设备评估参数是否可接受 │ │ ←────────────── LMP_sniff_accepted ──────┤ │ (或 LMP_not_accepted 拒绝) │ │ │ │ ③ 双方在 D_sniff 时隙开始 Sniff │ │ 窗口 k = D_sniff + k·T_sniff │ │ │

2.3 D_sniff 是怎么算出来的

D_sniff 是主设备 CLKN 的一个未来时隙号,主设备选取时要保证从设备有足够时间处理 LMP 命令:

D_sniff = 当前主设备 CLKN + 安全余量(通常 8~16 时隙) 主设备当前 CLKN = 1000 D_sniff = 1000 + 16 = 1016 从设备本地换算: D_sniff_local = 1016 - clk_offset

关键:D_sniff 必须是未来的时隙,否则协商完成后窗口已经"过期"。余量要覆盖 LMP 传输 + 从设备处理的时隙数。

2.4 退出 Sniff:LMP_unsniff

需要回到 Active 时(如开始大流量传输),Master 发LMP_unsniff

LMP_unsniff 与 LMP_sniff 不同——它不需要协商新参数, 只需要双方约定一个"退出时刻"(Instant),到期回到 Active 全时监听。 退出 Sniff 后,从设备恢复每个时隙监听,功耗回升。

3. 建立用的全套参数

3.1 基础 Sniff 四参数(LMP_sniff)

参数字段含义范围对功耗/延迟的影响
D_sniffD_sniffSniff 锚点(主设备 CLKN 时隙号)未来时隙决定窗口的"绝对位置",用于多路对齐
T_sniffT_sniff嗅探周期(时隙数)0x00040x4000(2.5ms10.24s)越大越省电、延迟越高
Sniff_AttemptAttempt强制监听时隙数1~255越大越稳、越耗电
Sniff_TimeoutTimeout延伸监听时隙数0~255越大有数据时越久才休眠

3.2 时钟偏移 clk_offset

虽然不是 LMP_sniff 的参数,但是窗口对齐的隐含前提

参数来源作用
clk_offsetLMP_clk_offset_req/rsp(连接建立时)从设备换算主设备 CLKN 到本地时钟
从设备本地时钟 = 主设备 CLKN - clk_offset 窗口 k 的从设备本地时刻 = (D_sniff + k·T_sniff) - clk_offset

3.3 Sniff Subrating 三参数(deep 模式)

Deep 省电通过LMP_sniff_subrating_req/rsp协商,核心参数是:

参数字段含义
Max LatencyMax_Latency最大可容忍延迟(时隙),从设备据此计算子速率 N
Min Remote TimeoutMin_Remote_Timeout最小远端(对端)Timeout
Min Local TimeoutMin_Local_Timeout最小本地 Timeout

子速率 N 的计算:从设备根据 Max Latency 和基础 T_sniff,算出实际唤醒周期 = T_sniff × N,使实际周期不超过 Max Latency:

实际周期 = T_sniff × N ≤ Max_Latency N = floor(Max_Latency / T_sniff) 示例: T_sniff = 8 时隙 (5ms), Max_Latency = 800 时隙 (500ms) N = floor(800 / 8) = 100 实际周期 = 8 × 100 = 800 时隙 (500ms)

3.4 参数全景表(一张表记住所有参数)

┌─────────────────────────────────────────────────────────┐ │ Sniff 参数全景 │ ├─────────────────────────────────────────────────────────┤ │ 基础 Sniff(LMP_sniff): │ │ D_sniff —— 锚点(绝对位置,多路对齐的基准) │ │ T_sniff —— 周期(唤醒频率) │ │ Attempt —— 强制监听窗口宽度 │ │ Timeout —— 延伸监听窗口宽度 │ │ │ │ 隐含前提: │ │ clk_offset —— 主从时钟偏移(窗口换算的前提) │ │ │ │ Deep 模式(LMP_sniff_subrating): │ │ Max_Latency —— 最大容忍延迟 → 决定子速率 N │ │ Min_Remote_Timeout —— 最小对端 Timeout │ │ Min_Local_Timeout —— 最小本地 Timeout │ └─────────────────────────────────────────────────────────┘

4. Deep 模式:子速率与窗口对齐

4.1 什么是 deep 模式

Deep 模式 = 深度省电 = 实际唤醒周期被大幅拉长。有两种方式:

方式机制特点
大 T_sniff直接协商很大的 T_sniff(如 1s)简单,但改参数要重新协商
Sniff Subrating保持 T_sniff 不变,用 N 放大实际周期推荐,切换快、锚点不变

实际项目里,deep 模式几乎都用Subrating——因为它保留了基础 T_sniff,只是"跳过"部分窗口,锚点不动,切换瞬时完成。

4.2 Deep 模式窗口对齐的精髓:锚点不变

Subrating 最精妙的设计是:deep 模式不改变 D_sniff 和 T_sniff,只引入 N 跳过窗口

基础 Sniff (D_sniff=100, T_sniff=8): 窗口: 100 108 116 124 132 140 148 156 ... │←8→│←8→│←8→│←8→│←8→│←8→│←8→│ Deep 模式 (N=4, 实际周期 = 8×4 = 32): 窗口: 100 132 164 ... │←────── 32 ──────→│←────── 32 ──────→│ 每 4 个基础窗口才唤醒一次

为什么锚点不变就能对齐

  1. 双方都从同一个 D_sniff 开始推算
  2. 实际窗口 = D_sniff + k·(T_sniff × N),k = 0,1,2…
  3. 因为 D_sniff 是同一个值,双方的推算结果天然一致
  4. 不需要任何额外的同步信令——这是 Subrating 的核心价值

4.3 从设备侧的 deep 窗口推算

typedefstruct{uint32_td_sniff;// 基础 Sniff 锚点(主设备 CLKN)uint32_tt_sniff;// 基础周期(时隙)uint32_tattempt;// 窗口宽度uint32_tsubrating_n;// 子速率 N(deep 模式下 N > 1)int32_tclk_offset;// 主从时钟偏移}sniff_deep_state_t;// 计算下一个实际唤醒窗口(deep 模式)uint32_tcalc_next_deep_window(sniff_deep_state_t*s,uint32_tcur_local_clk){uint32_td_sniff_local=s->d_sniff-s->clk_offset;uint32_tactual_period=s->t_sniff*s->subrating_n;// 实际周期uint32_telapsed=cur_local_clk-d_sniff_local;uint32_tactual_idx=elapsed/actual_period;// 下一个实际窗口 = 锚点 + (idx+1) × 实际周期returnd_sniff_local+(actual_idx+1)*actual_period;}

对比:普通 Sniff 的周期是t_sniff,deep 模式的周期是t_sniff * subrating_n锚点d_sniff始终不变——这是 deep 模式窗口对齐的根本。

4.4 Deep 模式下的时钟漂移问题

deep 模式休眠时间变长,clk_offset 漂移累积更大。这是 deep 模式窗口对齐最大的坑:

模式实际周期±20ppm 晶振漂移
普通 Sniff25ms1μs(可忽略)
Deep (N=40)1s40μs(需留余量)
Deep (N=400)10s400μs(接近 1 时隙,必须校准)

应对策略

  1. 每次收包更新 clk_offset:只要通信不断,偏移持续校准
  2. 扩大 Attempt 窗口:deep 模式下 Attempt 要覆盖漂移累积
  3. 粗时钟唤醒 + 精时钟锁定:用低功耗粗时钟提前唤醒,用精确时钟锁定窗口
推荐 Attempt 时隙数 = ceil(2 · 实际周期秒数 · ppm · 1e-6 / 625e-6) + 1 deep 模式实际周期 = 1s, ppm = 20: Attempt = ceil(2 · 1 · 20e-6 / 625e-6) + 1 = ceil(0.064) + 1 = 2 时隙

5. 多路窗口对齐的调度设计

5.1 调度器要解决的核心问题

多路 Sniff 下,调度器(Planner/Slice)要保证:

目标说明
窗口不重叠N 条链路的 Sniff 窗口在时域上错开,RF 前端不同时服务两个窗口
优先级仲裁窗口意外重叠时,高优先级链路(如 eSCO 通话)优先
功耗平滑避免多条链路同时唤醒造成电流尖峰
deep 模式对齐进入 deep 后,多路窗口依然错峰

5.2 核心手段一:T_sniff 倍数关系

最有效的错峰手段是让多条链路的 T_sniff 成倍数关系,窗口天然不重叠:

链路 A: T_sniff = 8 → 窗口: 100, 108, 116, 124, ... 链路 B: T_sniff = 16 → 窗口: 104, 120, 136, ... 链路 C: T_sniff = 32 → 窗口: 112, 144, ... 错峰效果(每 32 时隙一个大周期): 100 104 108 112 116 120 124 ... A B A C A B A ...

设计原则

  • 各链路 T_sniff 都是最小 T_sniff 的整数倍
  • D_sniff 错开一个"最小窗口宽度"的偏移
  • 这样窗口在时域上天然排开,调度器只需处理意外重叠

5.3 核心手段二:Planner + Slice 两级调度

对应简历里的 Schedule 调度系统,两级规划:

┌─────────────────────────────────────────────┐ │ Planner(长周期规划,ms~s 级) │ │ - 规划各链路 Sniff 窗口的"宏观节奏" │ │ - 处理 deep 模式的子速率 N │ │ - 计算各链路下一个窗口时刻 │ └──────────────────┬──────────────────────────┘ │ 下发时隙计划 ┌──────────────────▼──────────────────────────┐ │ Slice(短周期切片,时隙级) │ │ - 每个时隙决定"这个时隙服务哪条链路" │ │ - 窗口重叠时按优先级仲裁 │ │ - eSCO > ACL > 无连接广播 │ └─────────────────────────────────────────────┘
// Slice 层:每时隙的仲裁逻辑typedefstruct{uint8_tlink_id;uint8_tpriority;// eSCO=3, ACL高=2, ACL低=1bool is_sniff_window;// 是否处于该链路的 Sniff 窗口}slice_decision_t;slice_decision_tslice_arbitrate(scheduler_t*sched,uint32_tcur_clk){slice_decision_tbest={.priority=0,.is_sniff_window=false};for(inti=0;i<sched->link_count;i++){link_t*link=&sched->links[i];// 判断当前时隙是否是该链路的 Sniff 窗口if(in_sniff_window(link,cur_clk)){if(link->priority>best.priority){best.link_id=i;best.priority=link->priority;best.is_sniff_window=true;}}}returnbest;// 返回最高优先级的链路}

5.4 核心手段三:多路 deep 模式的锚点规划

进入 deep 模式后,多路窗口的错峰依然靠锚点规划

原则 1:所有链路的 D_sniff 保持"相对错峰" 链路 A 的 D_sniff = 100 链路 B 的 D_sniff = 100 + 4(错开一个最小窗口) 链路 C 的 D_sniff = 100 + 8 原则 2:deep 模式的 N 也成倍数关系 链路 A: T=8, N=4 → 实际周期 32 链路 B: T=16, N=4 → 实际周期 64 链路 C: T=32, N=4 → 实际周期 128 原则 3:锚点不变是前提 无论 N 怎么变,D_sniff 永远不动,错峰关系永不破坏

5.5 多路对齐的完整时序示例

三条链路进入 deep 模式后的对齐效果: 链路 A (T=8, N=4, 实际周期 32): ┃窗┃───────────────────────────────┃窗┃─────────── 100 132 链路 B (T=16, N=4, 实际周期 64): ──────┃窗┃─────────────────────────────────────── 104 链路 C (T=32, N=4, 实际周期 128): ────────────┃窗┃───────────────────────────────── 112 总览(128 时隙 = 80ms 一个超级周期): 100 104 112 A B C ┃窗┃ ┃窗┃ ┃窗┃ ...三个窗口完美错峰,无重叠

6. 实战坑与调优

6.1 坑 1:D_sniff 设得太近导致协商后窗口过期

症状:协商成功后第一个窗口就丢包 根因:D_sniff 余量不足,LMP 传输 + 从设备处理耗时超过了余量 修复:D_sniff = 当前 CLKN + 至少 16 时隙(10ms)

6.2 坑 2:deep 模式切换后多路窗口突然重叠

症状:从普通 Sniff 切到 deep 模式后,两条链路窗口撞在一起 根因:切换 N 时没有重新检查多路错峰关系 修复:切换 N 前,先验证新实际周期下各链路窗口是否仍错峰; 若会重叠,优先调整 N 而非 D_sniff(锚点不动是铁律)

6.3 坑 3:deep 模式长休眠后窗口漂移失准

症状:deep 模式跑几分钟后,从设备偶尔收不到主设备的包 根因:长时间无通信,clk_offset 漂移累积,窗口偏移超过 Attempt 覆盖范围 修复:① 扩大 Attempt ② 主设备周期性发 POLL 包强制校准 ③ 用粗时钟提前唤醒

6.4 多路 Sniff 调优清单

场景T_sniffNAttempt说明
多路待机(中央连 3 外设)8 时隙402实际周期 200ms,窗口错峰
多路 + 一路活跃活跃路 T=8/N=1,其余 T=16/N=82活跃路快响应,休眠路 deep
TWS 主耳手机链路 T=40/N=40,TPT 自主调度4副耳监听必须对齐手机链路

7. 抓包验证

用 HCI snoop 抓包验证多路 Sniff 对齐:

7.1 看协商

抓包过滤 LMP 命令: LMP_sniff → 看 D_sniff / T_sniff / Attempt / Timeout LMP_sniff_subrating_req → 看 Max_Latency(推断 N) LMP_sniff_subrating_rsp → 确认协商结果

7.2 看对齐

关键验证点: 1. 多条链路的 ACL 数据包是否在时间轴上"错峰"出现 2. deep 切换后,数据包间隔是否 = T_sniff × N 3. 是否有连续的 POLL/NULL 包(说明窗口失准,在互相探测)

7.3 看漂移

长时间抓包,观察数据包的实际间隔 vs 协商周期: 间隔 = 协商周期 ± 漂移量 漂移量持续增大 → clk_offset 未更新,窗口在漂

8. 总结

多路 Sniff 的设计可以浓缩成三句话:

  1. 协商LMP_sniff定锚点和周期,LMP_sniff_subrating定 deep 模式的子速率,锚点 D_sniff 永远是窗口对齐的基准。
  2. 对齐:deep 模式不改变 D_sniff,只放大实际周期为 T_sniff × N,双方从同一锚点独立推算,天然对齐,无需额外同步。
  3. 多路错峰:让 T_sniff 成倍数关系 + D_sniff 错开最小窗口,配合调度器的优先级仲裁,保证 N 个窗口在时域上不重叠、不漂。

最核心的一句话锚点不变,是 deep 模式窗口对齐的根;倍数错峰,是多路 Sniff 不冲突的根。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/22 3:28:03

APMCM选题策略:数学建模能力映射与赛题底层结构解析

1. 这不是“押题”&#xff0c;而是帮你把准竞赛脉搏的实战导航2023 APMCM亚太杯数学建模——这个名字一出来&#xff0c;很多同学第一反应是翻往年赛题、刷知乎热帖、蹲群聊“求神押题”。但我在带了七届APMCM队伍、亲手指导过42支参赛队后&#xff0c;越来越确信&#xff1a;…

作者头像 李华
网站建设 2026/8/22 3:27:18

AI简历工具:零实习背景求职者的破局利器

1. 项目概述&#xff1a;AI简历工具如何改变求职游戏规则2026年的毕业季即将到来&#xff0c;一个令人焦虑的数据正在校园里流传&#xff1a;超过37%的应届生没有任何正式实习经历。这个数字在非一线城市高校甚至更高。但有趣的是&#xff0c;头部企业的HR们发现&#xff0c;他…

作者头像 李华
网站建设 2026/8/22 3:21:32

CHI协议事务深度解析:从缓存一致性到SoC互联性能优化

1. CHI协议&#xff1a;现代SoC互联的基石与挑战在当今追求极致性能与能效比的片上系统&#xff08;SoC&#xff09;设计领域&#xff0c;处理器核心、内存控制器、各类加速器之间的高效、有序通信是决定整个芯片成败的关键。这背后&#xff0c;一套强大、灵活的片上互联协议扮…

作者头像 李华
网站建设 2026/8/22 3:16:18

AI智能体长期记忆架构设计:MEMTIER分层存储与检索优化实践

1. 项目概述&#xff1a;当AI智能体需要“长期记忆”最近在折腾一个能长期自主运行的AI智能体项目&#xff0c;比如一个能持续监控市场、自动撰写报告的分析助手&#xff0c;或者一个能记住与用户数月对话历史的个人聊天伴侣。在开发过程中&#xff0c;我遇到了一个几乎所有同类…

作者头像 李华
网站建设 2026/8/22 3:14:53

Spring Boot 容器化避坑指南

周一早上&#xff0c;负责上线的同事在群里发了条消息&#xff1a;「镜像 800MB&#xff0c;拉取花了 3 分钟&#xff0c;测试环境全阻塞了。」 底下没人接话。因为所有人都知道问题出在哪&#xff1a;Spring Boot 应用被打进 Docker 镜像时&#xff0c;大多数人只是把 JDK 和 …

作者头像 李华