2.4 时间机器的记忆:PTP数据集全解析
如果PTP设备有大脑,记忆存放在哪里?
你有没有想过,一个PTP设备是如何“记住“事情的?
它记得:
- 我是谁(clockIdentity)
- 我现在是什么状态(MASTER还是SLAVE)
- 我的主时钟是谁(grandmasterIdentity)
- 我和主时钟差多少时间(offsetFromMaster)
- 我应该每隔多久发一次Sync报文(logSyncInterval)
这些信息存在哪里?
答案就是:数据集(Data Set)。
PTP数据集就像是设备的“记忆库“,存储了协议运行所需的所有状态信息。
数据集的三种“记忆类型“
在深入每个数据集之前,我们先理解数据集成员的三种分类。
静态成员(Static):出厂时决定,永不变
定义:在设备设计时确定,运行期间不会改变。
特点:
- 通常与硬件绑定
- 不需要运行时计算
- 不会被协议修改
典型例子:
clockIdentity:设备的唯一标识,由硬件决定numberPorts:设备有多少个PTP端口,由硬件决定
比喻:就像你的DNA,出生时就决定了,终身不变。
动态成员(Dynamic):协议运行时自动更新
定义:在协议运行过程中,根据收到的报文或测量结果自动更新。
特点:
- 实时变化
- 不需要人工干预
- 由协议算法控制
典型例子:
offsetFromMaster:从时钟与主时钟的时间差,每次同步后更新portState:端口当前状态,由BMCA决定meanDelay:平均路径延迟,每次测量后更新
比喻:就像你的心跳频率,根据身体状况自动调整,不需要你控制。
可配置成员(Configurable):管理员可以改
定义:可以通过管理接口(SNMP、NETCONF、CLI等)修改。
特点:
- 需要管理员主动设置
- 通常有默认值
- 可能在运行时改变(如果管理员重新配置)
典型例子:
priority1、priority2:BMCA优先级,管理员可调整domainNumber:域编号,管理员可设置logSyncInterval:Sync报文发送间隔,管理员可调整
比喻:就像你的发型,可以自己决定怎么剪,也可以随时改变。
核心数据集详解
PTP定义了多个数据集,每个数据集负责一类信息。
一、defaultDS:默认数据集
作用:存储PTP实例的身份信息和全局配置。
所属:每个PTP实例一个。
关键成员详解
1. clockIdentity:我是谁
- 类型:ClockIdentity(8字节)
- 分类:静态
- 含义:PTP实例的唯一标识符
- 生成方式:基于MAC地址、EUI-64或其他唯一标识
为什么需要clockIdentity?
场景一:BMCA决胜
当两个设备的所有优先级属性都相同时,BMCA会比较clockIdentity。clockIdentity小的获胜。
场景二:环路检测
边界时钟在转发Announce报文时,可以在PATH_TRACE TLV中记录经过的clockIdentity列表。如果某个边界时钟发现自己的clockIdentity已经在列表中,说明形成了环路。
场景三:诊断
管理员可以通过clockIdentity快速定位问题设备。
2. numberPorts:我有几个端口
- 类型:UInteger16
- 分类:静态
- 含义:PTP实例的端口数量
- 取值:
- 普通时钟:1
- 边界时钟:≥2
- 透明时钟:≥1
为什么需要numberPorts?
在管理协议中,管理员需要知道设备有多少个端口,以便查询每个端口的状态。
3. clockQuality:我的时钟质量如何
- 类型:结构体(clockClass + clockAccuracy + offsetScaledLogVariance)
- 分类:动态
- 含义:时钟的质量指标
clockQuality的三个成员:
clockClass:
- 时钟等级,表示时钟的精度和可溯源性
- 值越小,等级越高
- 关键值:6(主参考)、7(保持模式)、248(默认)、255(仅从时钟)
clockAccuracy:
- 时钟精度,表示时钟的预期精度范围
- 值越小,精度越高
- 关键值:0x20(≤25ns)、0x22(≤250ns)、0x23(≤1μs)
offsetScaledLogVariance:
- 时钟稳定性,基于Allan方差
- 值越小,稳定性越好
clockQuality如何变化?
正常状态:
- 设备接GPS,clockClass = 6
- clockAccuracy根据GPS精度设置,如0x20
GPS信号丢失,但本地原子钟保持:
- clockClass从6变为7
- clockAccuracy可能略有下降
本地时钟漂移超出规范:
- clockClass从7变为52
- 表示不再适合做主时钟
4. priority1和priority2:我的优先级
- 类型:UInteger8(0-255)
- 分类:可配置
- 含义:BMCA选举优先级
- 规则:值越小,优先级越高
priority1 vs priority2的区别:
- priority1:第一优先级,通常用于区分不同“等级“的设备
- priority2:第二优先级,通常用于区分同一“等级“内的设备
配置策略:
| 设备类型 | priority1建议值 | priority2建议值 |
|---|---|---|
| 核心主时钟(GPS+原子钟) | 10 | 10 |
| 一级备份(GPS+铷钟) | 20 | 10-20 |
| 二级备份(GPS+TCXO) | 30 | 10-30 |
| 普通设备 | 128 | 128 |
| 仅从设备 | 200-255 | 128 |
5. domainNumber:我的域
- 类型:UInteger8(0-255)
- 分类:可配置
- 含义:PTP实例所属的域编号
- 默认值:0
6. slaveOnly:我只能是从时钟吗
- 类型:Boolean
- 分类:可配置
- 含义:该PTP实例是否只能作为从时钟
- 取值:
- TRUE:永远不能成为主时钟
- FALSE:可以成为主时钟
什么时候设置slaveOnly=TRUE?
- 终端设备(工作站、服务器)
- 不提供时间给他人的设备
- 只需要同步,不需要被同步的设备
注意:当slaveOnly=TRUE时,clockClass必须设置为255。
7. sdoId:我的Profile归属
- 类型:UInteger16(低12位有效,0-4095)
- 分类:可配置
- 含义:标识PTP实例使用的Profile
- 默认值:0x000
二、currentDS:当前数据集
作用:存储PTP实例的当前同步状态。
所属:每个PTP实例一个。
关键成员详解
1. stepsRemoved:我离主时钟有多远
- 类型:UInteger16
- 分类:动态
- 含义:到Grandmaster的边界时钟跳数
- 取值:
- Grandmaster自己:0
- 直接连接Grandmaster的从时钟:1
- 再下一级:2
- 以此类推
如何更新?
我是主时钟(M1/M2决策):
- stepsRemoved = 0
我是从时钟(S1决策):
- stepsRemoved = Ebest中的stepsRemoved + 1
示例:
Grandmaster A(stepsRemoved = 0)
↓
Boundary Clock B(stepsRemoved = 1)
↓
Boundary Clock C(stepsRemoved = 2)
↓
Slave Clock D(stepsRemoved = 3)
stepsRemoved的作用:
作用一:BMCA决策
当两个设备的grandmasterIdentity相同时,比较stepsRemoved。stepsRemoved小的优先。
作用二:防止无限传播
当stepsRemoved ≥ 255时,丢弃Announce报文,防止环路导致无限传播。
2. offsetFromMaster:我和主时钟差多少
- 类型:TimeInterval(内部单位:纳秒 × 2¹⁶,即约15.26飞秒分辨率)
- 分类:动态
- 含义:从时钟时间 - 主时钟时间
- 取值:
- 正值:从时钟比主时钟快
- 负值:从时钟比主时钟慢
- 零:完全同步
如何计算?
在1.4节中,我们已经详细推导了offsetFromMaster的计算公式:
offsetFromMaster = t₂ - originTimestamp - meanDelay - correctionField
其中:
- t₂:Sync报文的接收时间戳
- originTimestamp:Sync报文的发送时间戳
- meanDelay:平均路径延迟
- correctionField:各种校正值的总和
offsetFromMaster的用途:
用途一:相位校正
从时钟根据offsetFromMaster调整自己的时间。如果offsetFromMaster = +1μs,说明从时钟快了1μs,需要减去1μs。
用途二:同步质量监控
管理员可以监控offsetFromMaster的变化,判断同步质量。如果offsetFromMaster剧烈波动,说明网络不稳定或时钟有问题。
3. meanDelay:平均延迟
- 类型:TimeInterval(纳秒 × 2¹⁶)
- 分类:动态
- 含义:平均路径延迟或平均链路延迟
- 取决于延迟机制:
- E2E机制:meanDelay = meanPathDelay(端到端路径延迟)
- P2P机制:meanDelay = meanLinkDelay(对等链路延迟)
- 无延迟机制:meanDelay = 0
如何计算?
在1.4节中,我们已经详细推导了meanPathDelay的计算公式:
meanPathDelay = [(t₂ - t₁) + (t₄ - t₃)] / 2
其中:
- t₁:Sync报文的发送时间戳
- t₂:Sync报文的接收时间戳
- t₃:Delay_Req报文的发送时间戳
- t₄:Delay_Req报文的接收时间戳
三、parentDS:父时钟数据集
作用:存储关于父PTP实例(上游主时钟)的信息。
所属:每个PTP实例一个。
关键成员详解
1. parentPortIdentity:我的上级是谁
- 类型:PortIdentity(clockIdentity + portNumber)
- 分类:动态
- 含义:父时钟的端口标识
示例:
如果从时钟从某个边界时钟的端口2接收时间信号,那么:
- parentPortIdentity.clockIdentity = 边界时钟的clockIdentity
- parentPortIdentity.portNumber = 2
2. grandmasterIdentity:终极主时钟是谁
- 类型:ClockIdentity(8字节)
- 分类:动态
- 含义:Grandmaster的标识
parentPortIdentity vs grandmasterIdentity的区别:
Grandmaster A
↓
Boundary Clock B(端口2)
↓
Slave Clock C
对于从时钟C:
- parentPortIdentity = B的端口2的标识
- grandmasterIdentity = A的clockIdentity
3. grandmasterClockQuality:主时钟的质量
- 类型:结构体(clockClass + clockAccuracy + offsetScaledLogVariance)
- 分类:动态
- 含义:Grandmaster的时钟质量
从时钟需要知道Grandmaster的质量,以便在Announce报文中传播给下游设备。
4. grandmasterPriority1和grandmasterPriority2
- 类型:UInteger8
- 分类:动态
- 含义:Grandmaster的BMCA优先级
四、timePropertiesDS:时间属性数据集
作用:存储时间尺度和溯源信息。
所属:每个PTP实例一个。
关键成员详解
1. currentUtcOffset:TAI与UTC的差
- 类型:Integer16
- 分类:动态(主时钟可配置)
- 含义:TAI - UTC的当前值
- 截至2026年:37秒
如何更新?
- 主时钟:从GPS信号中提取,或人工配置
- 从时钟:从Announce报文中获取,或人工配置
2. currentUtcOffsetValid:UTC偏移有效吗
- 类型:Boolean
- 分类:动态
- 含义:currentUtcOffset是否有效
- 取值:
- TRUE:有效
- FALSE:无效,不应使用
什么时候是FALSE?
- 主时钟失去GPS信号,无法获取最新的闰秒信息
- 主时钟的时间源不可靠
3. leap59和leap61:闰秒预告
- 类型:Boolean
- 分类:动态
- 含义:
- leap59=TRUE:当月最后一分钟将有59秒(负闰秒)
- leap61=TRUE:当月最后一分钟将有61秒(正闰秒)
注意:leap59和leap61不能同时为TRUE。
4. ptpTimescale:使用哪种时间尺度
- 类型:Boolean
- 分类:动态(主时钟可配置)
- 含义:
- TRUE:PTP时间尺度(TAI)
- FALSE:ARB时间尺度
5. timeTraceable:时间可溯源吗
- 类型:Boolean
- 分类:动态
- 含义:时间是否可溯源到国际标准
6. frequencyTraceable:频率可溯源吗
- 类型:Boolean
- 分类:动态
- 含义:频率是否可溯源到国际标准
7. timeSource:时间源类型
- 类型:Enumeration8
- 分类:动态
- 含义:主时钟的时间来自哪种类型的源
- 典型值:
- 0x20:GNSS(GPS等)
- 0x10:ATOMIC_CLOCK(原子钟)
- 0x50:NTP
- 0xA0:INTERNAL_OSCILLATOR(内部振荡器)
五、portDS:端口数据集
作用:存储每个PTP端口的状态和配置。
所属:每个PTP端口一个。
关键成员详解
1. portIdentity:端口标识
- 类型:PortIdentity(clockIdentity + portNumber)
- 分类:静态
- 含义:端口的唯一标识
2. portState:端口状态
- 类型:Enumeration8
- 分类:动态
- 含义:端口当前状态
- 取值:
- 0x01:INITIALIZING
- 0x02:FAULTY
- 0x03:DISABLED
- 0x04:LISTENING
- 0x05:PRE_MASTER
- 0x06:MASTER
- 0x07:PASSIVE
- 0x08:UNCALIBRATED
- 0x09:SLAVE
3. logAnnounceInterval:Announce报文间隔
- 类型:Integer8
- 分类:可配置
- 含义:Announce报文的发送间隔,以log₂秒表示
- 计算:间隔 = 2^(logAnnounceInterval) 秒
- 默认值:1(即间隔 = 2秒)
- 典型范围:-3到+4(即125ms到16秒)
示例:
| logAnnounceInterval | 间隔 |
|---|---|
| -3 | 125毫秒 |
| -2 | 250毫秒 |
| -1 | 500毫秒 |
| 0 | 1秒 |
| 1 | 2秒(默认) |
| 2 | 4秒 |
| 3 | 8秒 |
| 4 | 16秒 |
4. announceReceiptTimeout:Announce超时
- 类型:UInteger8
- 分类:可配置
- 含义:未收到Announce报文的超时倍数
- 计算:超时时间 = announceReceiptTimeout × 2^(logAnnounceInterval)
- 默认值:3
- 典型范围:2-10
示例:
当logAnnounceInterval=1(间隔2秒)、announceReceiptTimeout=3时:
- 超时时间 = 3 × 2 = 6秒
如果6秒内未收到Announce报文,触发ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES事件。
5. logSyncInterval:Sync报文间隔
- 类型:Integer8
- 分类:可配置
- 含义:Sync报文的发送间隔,以log₂秒表示
- 计算:间隔 = 2^(logSyncInterval) 秒
- 默认值:0(即间隔 = 1秒)
- 典型范围:-7到+1(即7.8ms到2秒)
为什么通常Sync间隔比Announce间隔小?
Sync报文用于时间同步,频率越高,同步精度越高。
Announce报文用于BMCA,不需要那么频繁,节省带宽。
6. logMinDelayReqInterval:Delay_Req最小间隔
- 类型:Integer8
- 分类:动态(主时钟通告)
- 含义:从时钟发送Delay_Req报文的最小间隔,以log₂秒表示
- 计算:间隔 = 2^(logMinDelayReqInterval) 秒
- 默认值:0(即间隔 = 1秒)
为什么是主时钟通告?
主时钟知道自己的处理能力。如果主时钟处理能力有限,可以要求从时钟降低Delay_Req频率,避免过载。
7. delayMechanism:延迟测量机制
- 类型:Enumeration8
- 分类:可配置
- 含义:端口使用的延迟测量机制
- 取值:
- 0x01:E2E(端到端)
- 0x02:P2P(对等)
- 0x03:COMMON_P2P(公共P2P)
- 0x04:SPECIAL(特殊端口)
- 0xFE:NO_MECHANISM(无延迟机制)
关键规则:
- 同一PTP通信路径上的所有端口,必须使用相同的delayMechanism
- 混用E2E和P2P会导致无法正确测量延迟
8. versionNumber和minorVersionNumber:PTP版本
- 类型:UInteger4
- 分类:可配置
- 含义:PTP协议版本
- IEEE 1588-2019:versionNumber=2, minorVersionNumber=1
数据集之间的关系
关系图
PTP实例
├── defaultDS(身份和配置)
├── currentDS(当前同步状态)
├── parentDS(父时钟信息)
├── timePropertiesDS(时间属性)
└── portDS[](每个端口一个)
├── portDS[1]
├── portDS[2]
└── ...
数据流动
主时钟发送Announce报文:
主时钟的数据集 → Announce报文
├── defaultDS.clockIdentity → sourcePortIdentity.clockIdentity
├── defaultDS.clockQuality → grandmasterClockQuality
├── defaultDS.priority1 → grandmasterPriority1
├── defaultDS.priority2 → grandmasterPriority2
├── currentDS.stepsRemoved → stepsRemoved
├── timePropertiesDS.currentUtcOffset → currentUtcOffset
└── ...
从时钟接收Announce报文:
Announce报文 → 从时钟的数据集
├── sourcePortIdentity → parentDS.parentPortIdentity
├── grandmasterIdentity → parentDS.grandmasterIdentity
├── grandmasterClockQuality → parentDS.grandmasterClockQuality
├── grandmasterPriority1 → parentDS.grandmasterPriority1
├── stepsRemoved + 1 → currentDS.stepsRemoved
└── ...
数据集更新规则
M1/M2决策(我成为主时钟)
当BMCA决定我是主时钟时,按以下规则更新数据集:
currentDS:
- stepsRemoved = 0
- offsetFromMaster = 0
- meanDelay = 0
parentDS:
- parentPortIdentity.clockIdentity = defaultDS.clockIdentity
- parentPortIdentity.portNumber = 0
- grandmasterIdentity = defaultDS.clockIdentity
- grandmasterClockQuality = defaultDS.clockQuality
- grandmasterPriority1 = defaultDS.priority1
- grandmasterPriority2 = defaultDS.priority2
timePropertiesDS:
- 根据外部时间源(如GPS)更新各项属性
S1决策(我成为从时钟)
当BMCA决定我是从时钟时,按以下规则更新数据集:
currentDS:
- stepsRemoved = Ebest.stepsRemoved + 1
parentDS:
- parentPortIdentity = Ebest.sourcePortIdentity
- grandmasterIdentity = Ebest.grandmasterIdentity
- grandmasterClockQuality = Ebest.grandmasterClockQuality
- grandmasterPriority1 = Ebest.grandmasterPriority1
- grandmasterPriority2 = Ebest.grandmasterPriority2
timePropertiesDS:
- currentUtcOffset = Ebest.currentUtcOffset
- leap59 = Ebest.leap59
- leap61 = Ebest.leap61
- ptpTimescale = Ebest.ptpTimescale
- timeTraceable = Ebest.timeTraceable
- frequencyTraceable = Ebest.frequencyTraceable
- timeSource = Ebest.timeSource
一个完整的例子
让我们用一个完整的例子,演示数据集如何工作。
场景:
主时钟A(接GPS)
↓
边界时钟B
↓
从时钟C(5G基站)
初始状态:
主时钟A的数据集:
| 数据集 | 成员 | 值 |
|---|---|---|
| defaultDS | clockIdentity | 00-1B-19-00-00-00-00-01 |
| defaultDS | clockClass | 6 |
| defaultDS | clockAccuracy | 0x20 |
| defaultDS | priority1 | 10 |
| defaultDS | priority2 | 10 |
| defaultDS | slaveOnly | FALSE |
| currentDS | stepsRemoved | 0 |
| timePropertiesDS | ptpTimescale | TRUE |
| timePropertiesDS | currentUtcOffset | 37 |
| timePropertiesDS | timeTraceable | TRUE |
| timePropertiesDS | frequencyTraceable | TRUE |
| timePropertiesDS | timeSource | 0x20(GNSS) |
主时钟A发送Announce报文:
Announce报文内容:
| 字段 | 值 |
|---|---|
| sourcePortIdentity | 00-1B-19-00-00-00-00-01, portNumber=1 |
| grandmasterIdentity | 00-1B-19-00-00-00-00-01 |
| grandmasterClockQuality | {class:6, accuracy:0x20, variance:…} |
| grandmasterPriority1 | 10 |
| grandmasterPriority2 | 10 |
| stepsRemoved | 0 |
| currentUtcOffset | 37 |
| ptpTimescale | TRUE |
| timeTraceable | TRUE |
| frequencyTraceable | TRUE |
| timeSource | 0x20 |
边界时钟B接收Announce报文:
边界时钟B更新数据集:
| 数据集 | 成员 | 更新后的值 |
|---|---|---|
| currentDS | stepsRemoved | 0 + 1 = 1 |
| parentDS | parentPortIdentity | A的端口1 |
| parentDS | grandmasterIdentity | 00-1B-19-00-00-00-00-01 |
| parentDS | grandmasterPriority1 | 10 |
| timePropertiesDS | currentUtcOffset | 37 |
| timePropertiesDS | timeTraceable | TRUE |
边界时钟B转发Announce报文:
Announce报文内容(更新后):
| 字段 | 值 | |:—|:—|:—| | sourcePortIdentity | B的clockIdentity, portNumber=2 | | grandmasterIdentity | 00-1B-19-00-00-00-00-01 | | grandmasterClockQuality | {class:6, accuracy:0x20, variance:…} | | grandmasterPriority1 | 10 | | stepsRemoved | 1(原值0 + 1) | | currentUtcOffset | 37 |
从时钟C接收Announce报文:
从时钟C更新数据集:
| 数据集 | 成员 | 更新后的值 |
|---|---|---|
| currentDS | stepsRemoved | 1 + 1 = 2 |
| parentDS | parentPortIdentity | B的端口2 |
| parentDS | grandmasterIdentity | 00-1B-19-00-00-00-00-01 |
| timePropertiesDS | currentUtcOffset | 37 |
从时钟C的应用:
从时钟C的应用层查询时间信息:
问题1:我同步到哪个主时钟?
答案:parentDS.grandmasterIdentity = 00-1B-19-00-00-00-00-01(主时钟A)
问题2:我离主时钟多远?
答案:currentDS.stepsRemoved = 2(经过2个边界时钟)
问题3:我离上级时钟差多少?
答案:currentDS.offsetFromMaster = +125ns(假设当前测量值)
问题4:现在UTC时间是多少?
答案:PTP时间 - timePropertiesDS.currentUtcOffset
= (当前PTP时间) - 37
小结:数据集的核心要点
数据集分类:
- defaultDS:身份和全局配置
- currentDS:当前同步状态
- parentDS:父时钟信息
- timePropertiesDS:时间属性
- portDS:端口状态和配置
成员分类:
- 静态:出厂决定,不变
- 动态:协议运行时自动更新
- 可配置:管理员可以修改
关键数据集成员:
- defaultDS.clockIdentity:设备唯一标识
- defaultDS.clockQuality:时钟质量
- currentDS.offsetFromMaster:时间偏差
- currentDS.stepsRemoved:到主时钟的跳数
- parentDS.grandmasterIdentity:主时钟标识
- timePropertiesDS.currentUtcOffset:闰秒偏移
数据流动:
- 主时钟发送Announce:数据集 → 报文
- 从时钟接收Announce:报文 → 数据集
下集预告
现在,我们知道了PTP设备如何存储和管理信息。
但还有一个问题:PTP端口有9种状态,它们之间如何转换?什么事件触发状态转换?
下一节,我们将深入讲解端口状态机——PTP协议的“心脏“。你会看到,PTP定义了一套完整的状态转换规则,确保协议的正确运行。
【悬念留给2.5】
你可能好奇:为什么要有INITIALIZING、PRE_MASTER、UNCALIBRATED这些“中间状态“?直接从LISTENING跳到MASTER或SLAVE不行吗?
答案是:不行。这些中间状态是协议稳定性的关键。如果省略它们,可能导致多个设备同时声称自己是主时钟,或者从时钟在未稳定时就开始工作。
下一节,我们详细解读每个状态的作用和转换规则。