最近项目中用到了PTP做时间同步,基于linuxptp做的改进和开发。刚入门对很多东西不是很清楚,用wireshark在测试设备的slave这边抓了个包,对照源码和官方文档学习一波,请各位大佬多多批评指正。
目前设备上采用的是主从同步,oneStep模式,所以重点先看看这部分的实现逻辑。
PTP报文
设备在PTP这块采用的传输层协议是UDP。PTP协议使用的端口号是319和320,其中319对应事件消息(EVENT),需要网口记录收发时间戳,320对应普通消息(GENERAL),无需记录收发时间戳。
Signaling报文
Signaling报文负责传输搭载的TLV消息。一个Signaling报文可以携带多个TLV消息。可携带的TLV类型messageType对应官方文档14节。
Signaling报文占用空间大小与携带的TLV相关,为头部(34字节)+targetPortIdentity(10字节)+每个TLV所占空间。TLV消息所占空间与其携带数据有关,携带的数据大小为lengthField字段的值,总大小为TLV类型(2字节)+TLV数据长度(2字节)+数据。

对应代码:
port_signaling.c中的port_signaling_construct,生成初始不带TLV的Signaling报文。
port_signaling.c中的process_signaling,处理收到的Signaling报文:
- 判断当前端口状态,状态正常即可(除INITIALIZING、FAULTY和DISABLED外);
- 遍历消息的TLV链表,根据TLV类型以及TLV带的
messageType执行对应的操作。
如果收到的TLV是Request Unicast Transmission,需要解析其中的messageType。如果messageType是Announce或Sync,需要进一步添加或更新源端client。最后调用unicast_service_reply返回对应Grant Unicast Transmission报文。
如果收到的TLV是Grant Unicast Transmission,对应函数unicast_client_grant。需要判断源端是否是自己发送Request的master,然后判断对方的grant对自己的messageType类型,最后根据Unicast状态执行对应操作。
request unicast transmission TLV
功能:
Slave端口→Master端口,Slave端口请求Master端口发送对应TLV中指定的报文(包括Announce、Sync、Delay_Resp或Pdelay_Resp),并约定发送间隔。这部分主从的约定功能是可选的,对应官方文档16.1节。
logInterMessagePeriod字段用于约定后续消息发送间隔,用以2为底的对数表示。
例如,开始时Slave端口发送了一个Signaling报文,如下所示。

可以看到,该Signaling报文搭载了request unicast transmission的TLV、请求的报文类型是Announce报文,logInterMessagePeriod字段为1。说明Master端口在grant该请求后,需要每2秒(212^121)发送一条Announce报文给Slave端口。后续抓包结果中的Announce报文如下。

抓包结果显示master端基本以2秒间隔定期发送Announce报文。最后一个字段durationField指的是如果在这段时间内都没有收到来自master端的Announce报文,就认为主时钟失效。
这个TLV占用空间大小为10字节,为TLV类型(2字节)+TLV数据长度(2字节)+消息类型(1字节)+消息发送间隔(1字节)+请求返回时间(4字节)。

所以也可以看到搭载了这个TLV的Signaling报文的messageLength是54。
对应代码:
unicast_client.c中的attach_request,将request unicast transmission的TLV添加到Signaling报文后面。
unicast_client.c中的unicast_client_announce,生成Signaling报文,并调用attach_request添加request信息,最后发送。
grant unicast transmission TLV
功能:
Master端口→Slave端口,应答Slave端口的request unicast transmission请求。对应官方文档16.1节。

占用空间大小为12字节,除了上述request TLV的数据均有,还有预留空间(1字节)+是否更新grant(1字节)。

对应代码:
unicast_service.c中的attach_grant,将grant unicast transmission的TLV添加到Signaling报文后面。
unicast_service.c中的unicast_service_reply,生成Signaling报文,并调用attach_grant添加grant信息,最后发送。
Announce报文
功能:
Master端口向Slave端口定期发送,发送间隔取决于对应Signaling报文的约定。若一段时间内Slave端口未收到Master端口发来的Announce报文,则认为Master端口失效。

占用空间大小为64字节。

对应代码:
port.c中的port_tx_announce,生成Announce报文并发送。
unicast_service.c中的unicast_service_clients,检查client队列中保持连接的Slave端口(已经grant了对应了Announce请求且未过期的),并调用port_tx_announce。
port.c中的process_announce,处理收到的Announce报文:
- 判断消息地址是否是master的地址;
- 判断当前端口状态,根据状态不同选择执行是添加外部master时钟/更新当前master时钟。
Sync报文
功能:
Master端口向Slave端口定期发送,发送间隔取决于对应Signaling报文的约定。报文中带当前Master端口的时间戳(config中设定了twoStepFlag=0,即单步模式,时间戳随Sync报文发送;两步模式中,Sync报文的originTimestamp为0或估计的出端时间戳,不重要)。
占用空间大小为44字节。

对应代码:
port.c中的port_tx_sync,生成Sync报文并发送。
unicast_service.c中的unicast_service_clients,检查client队列中保持连接的Slave端口(已经grant了对应了Sync请求且未过期的),并调用port_tx_sync。
prot.c中的process_sync,处理收到的Sync报文:
- 判断当前端口状态,只有uncalibrated和slave两种状态需要处理Sync报文;
- 判断端口的父时钟端口ID和消息源端口ID是否一致,只有一致才需要处理;
- 如果消息不是单播,且消息中的
logMessageInterval字段与端口的log_sync_interval不同,说明是父时钟端口更新Sync消息间隔,则对应更新端口的Sync消息间隔; - 在消息头的
correction中修正不对称性(若不对称性已知,即主-从和从-主的时延不同); - 若当前是单步模式,调用
port_synchronize,计算修正后的t1并记录t2;
若当前是两步模式,判断本次Sync消息和在收到本次Sync消息之前端口收到的上一个Follow_Up消息是否是同一个SequenceID,并更新端口的syfu状态。若ID匹配,调用port_synchronize计算修正后的t1并记录t2。(这是因为虽然Master端会先发Sync后发Follow_Up报文,但由于网络原因可能同一次的Follow_Up报文会比Sync报文先到达Slave端,因此需要记录并判断SequenceID)
Follow_Up报文
功能:Master端口→Slave端口,在两步模式(twoStepFlag=1)中,Master端口在发送完Sync报文后发送相应的Follow_Up报文,携带有对应Sync报文发送时的精确时间戳。官方文档建议Follow_Up报文应在Sync报文发送后尽快发送,源码中也是紧随Sync报文发出的。
Follow_Up报文占用空间大小为44字节,与Sync报文一致。

对应代码:
port.c中的port_tx_sync,在生成Sync报文后立即生成Follow_Up报文并发送。
port.c中的process_follow_up,处理收到的Follow_Up报文:
- 判断当前端口状态,只有UNCALIBRATED和SLAVE两种状态需要处理Follow_Up报文;
- 判断端口的父时钟端口ID和消息源端口ID是否一致,只有一致才需要处理;
- 提取Follow_Up报文中的信息;
- 判断本次Follow_Up消息和在收到本次Follow_Up消息之前端口收到的上一个Sync消息是否是同一个
SequenceID,并更新端口的syfu状态。若ID匹配,调用port_synchronize计算修正后的t1并记录t2。(这是因为由于网络原因,Follow_Up报文有可能先于Sync到达端口)
Delay_Req报文
功能:
Slave端口→Master端口,请求Master端口发送Delay_Resp报文。
占用空间大小为44字节。报文格式与Sync报文相同。
对应代码:
port.c中的port_delay_request,生成Delay_Req报文并发送。
port.c中的bc_event,调用port_delay_request。
port.c中的process_delay_req,处理收到的Delay_Req报文:
- 判断当前端口状态,只有MASTER和GRANDMASTER两种状态需要处理Delay_Req报文;
- 创建Delay_Resp消息,在
receiveTimestamp中填上收到Delay_Req的时间戳,并设置好其他部分封装成Delay_Resp报文; - 向发送Delay_Req的端口发送Delay_Resp报文。
Delay_Resp报文
功能:Master端口→Slave端口,Master端口回复Slave端口自己收到Delay_Resp报文的精确时间戳。

占用空间大小为54字节。

对应代码:
port.c中的process_delay_req,生成Delay_Resp报文并发送。
port.c中的bc_event,调用process_delay_req。
port.c中的process_delay_resp,处理收到的Delay_Resp报文:
- 判断当前端口状态,只有UNCALIBRATED和SLAVE两种状态需要处理Delay_Resp报文;
- 依次检查:
①Delay_Resp携带的requesting端口是否为当前端口;
②Delay_Resp是否来自当前端口对应的父时钟端口,只有均一致才需要处理; - 遍历当前端口已发送的Delay_Req队列,查找到该Resp对应的Req,只有找到才进一步处理;
- 记录接收时间t4并修正不对称性误差,记录对应Req的发送时间t3;
- 在Delay_Req队列中删除对应的Req。
目前主要看了这些,P2P的时间同步还没开始看,后面看了再更。希望各位大佬不吝赐教~
本文介绍了PTP(Precision Time Protocol)在项目中的应用,重点关注基于linuxptp的实现。讨论了PTP报文的种类,如Signaling、Announce、Sync、Follow_Up等,以及它们的功能和在主从同步模式下的工作原理。同时,详细分析了request unicast transmission TLV和grant unicast transmission TLV的使用,强调了它们在主从通信中的作用。文章还涵盖了PTP报文的生成、处理流程及源码解析。
8858

被折叠的 条评论
为什么被折叠?



