使用gPTP =================== | `原文链接 `__ | `讲解视频 `__ 目标 ~~~~~ 通用精确时间协议(Generic Precision Time Protocol,gPTP)由IEEE 802.1AS 标准所规定,是一种可以高精度地同步时钟的网络协议。\ 这对于时间敏感网络(TSN)等应用非常有效。 在此示例中,我们将演示如何配置gPTP主时钟、网桥和终端站,以在整个网络上建立可靠的时间同步。 INET version: ``4.5`` 源地址链接: `inet/showcases/tsn/timesynchronization/gptp `__ 关于 gPTP ~~~~~~~~~~ 概述 ^^^^^^^^ 实际上,不同网络设备上的时钟时间可能相互之间产生偏差。如果以一个网络设备作为参考的时间基准,那么另外一个产生相对时间偏差我们一般称之为漂移。 INET可以仿真这种时钟漂移(有关更多信息,请参考 `Clock Drift `__ )。 针对时间敏感的应用,可以使用时间同步来减少这种漂移。 将任意两个时钟之间的时间同步意味着它们之间的时间差有一个上限。这可以通过在时间差变得太大之前定期同步时钟之间的时间来实现。\ 在gPTP中,一些从时钟的时间与gPTP时间域内的主时钟的时间同步。一个网络可以有多个gPTP时间域,即节点可以将多个时钟与多个主时钟同步以实现冗余\ (例如,如果一个主时钟故障或由于链路中断而离线,节点仍然可以拥有同步的时间)。\ 每个时间域包含一个主时钟和任意数量的从时钟。该协议通过从主时钟节点向从时钟节点发送同步消息来将从时钟与主时钟同步。 根据IEEE 802.1AS标准,主时钟可以通过最佳主时钟算法(BCMA)自动选择。BCMA还确定时钟生成树,即在网络中将同步消息传播到从时钟的路径。\ INET目前不支持BCMA;每个gPTP时间域的主时钟和生成树需要手动指定。 gPTP的操作总结: - 所有节点计算驻留时间(即数据包在转发之前在节点中停留的时间)和上游链路的时延。 - gPTP同步消息向下传播。 - 节点根据同步消息计算精确时间,以及接收同步消息的链路的驻留时间和时延。 INET 中的 gPTP ^^^^^^^^^^^^ 在INET中协议由应用模块实现。这是几个网络节点中的可选模块,例如TSN主机和交换机。可以通过ini文件中的 ``hasTimeSynchronization`` 参数启用可选的 \ `Gptp `__ 模块,例如: .. code:: ini *.tsnHost.hasTimeSynchronization = true 节点可以通过拥有多个 `Gptp `__ 子模块,成为多个gPTP时间域的一部分,其中每个域对应一个子模块。 `Gptp `__ 模块根据其在生成树中的位置,扮演三种角色之一:主节点、桥节点或从节点。\ 包含主节点gPTP模块的节点拥有时间域的主时钟,生成gPTP同步消息,并向下级的桥节点和从节点广播。桥节点也会将同步消息转发给桥节点和从节点。 \ 可以通过 `Gptp `__ 模块的 ``gptpNodeType`` 参数来选择模块类型。 ``gptpNodeType`` 参数可以是 ``MASTER_NODE`` 、 ``BRIDGE_NODE`` 或 ``SLAVE_NODE`` 。 .. note:: 如果有多个时间域,网络节点可能在不同的时间域中扮演不同的gPTP角色。 生成生成树是通过将节点的接口(interfaces)标记为主端口或从端口来创建的,接口(interfaces)一般称之为称为端口(ports)。 \ 这个过程使用 ``slavePort`` 和 ``masterPorts`` 参数。同步消息在主端口上发送,并在从端口上接收。主节点只有主端口,从节点只有从端口,而桥节点两者都有。\ 因此,由于这些端口定义了生成树,每个节点只能有一个从端口。 模块具有以下独特的机制: - 对等延迟测量:从节点和桥节点定期通过发送上行的对等延迟请求消息( ``pDelayReq`` )来测量链路延迟;它们接收对等延迟响应消息( ``pDelayResp`` )。 - 时间同步:主节点定期广播带有本地时间的gPTP同步消息( ``GptpSync`` ),这些消息向下传播。从节点的时间被设置为主时钟的时间,根据链路和处理延迟进行修正。 \ 此外,通过设置振荡器补偿因子,从节点的漂移率与主时钟的漂移率对齐。补偿因子是根据当前和前一次同步事件中主时钟和从时钟的时间估计得出的。 .. note:: 这些消息的接收者需要知道消息发送的时间戳,以便能够计算出“正确”的时间。目前,只支持两步同步,即 ``pDelayResp`` 和 ``GptpSync`` 消息紧接着会有后续消息,其中包含原始 ``pDelayResp``/ ``GptpSync`` 消息发送的精确时间。当接收到后续消息时,时钟会被设置为新的时间。 节点定期发送同步和对等延迟测量消息。同步和对等延迟测量消息的周期和偏移量可以由( ``syncInterval`` , ``pDelayInterval`` , ``syncInitialOffset`` , ``pDelayInitialOffset`` )这些参数指定。 当节点具有多个gPTP时间域时,每个时间域都有一个相应的 `Gptp `__ 模块。\ `MultiDomainGptp `__ 模块使其变得方便,因为它包含多个模块。 \ 此外,每个域可以有一个相应的时钟模块。 `MultiClock `__ 模块可以用于此目的,因为它包含多个时钟子模块。 `MultiClock `__ 模块中的一个子时钟被指定为活动时钟。 \ 使用 `MultiClock `__ 模块的用户使用活动时钟的时间, \ 也就是说其他模块从 `MultiClock `__ 模块获取时间。 \ 例如,如果一个域中的时间同步失败,场景管理脚本(scenario manager script)可以将活动时钟更改为另一个子时钟。 .. note:: 使用 `MultiClock `__ 和 `MultiDomainGptp `__ 时,\ 只需将 `MultiClock `__ 模块指定给 `MultiDomainGptp `__ 模块作为时钟即可。 \ 每个域的相应子时钟将自动选择,即 `Gptp `__ 子模块与 `MultiClock `__ 中的时钟子模块进行基于索引的配对。 有关 `Gptp `__ 、 `MultiDomainGptp `__ 和 `MultiClock `__ 参数的更多信息,请查阅NED文档。 模型 ~~~~~~~~~ 在本示例中,我们展示了在三个仿真中设置和操作gPTP的过程: - **一个主时钟**:通过一个时间域和一个主时钟进行简单设置。 - **主时钟和热备份主时钟**:更复杂的设置,使用两个时间域来分别设置主时钟和热备份主时钟。如果主时钟节点离线,备份时钟可以接管并成为新的主时钟。 - **两个利用网络冗余的主时钟**:一个包含主节点和热备份主节点的较大网络,每个节点有两个时间域。时间同步在主节点和网络中的任何链接故障时都能得到保护。 在 ``General`` 配置中,我们在所有网络节点中启用 `Gptp `__ 模块,并为主时钟配置一个随机的时钟漂移率。同时,还为从节点和桥节点的时钟配置一个恒定的时钟漂移率(每个节点都对应一个随机分布)。 .. code:: ini # enable time synchronization in all network nodes *.*.hasTimeSynchronization = true # all oscillators have a constant drift rate (specified with a random distribution for each one) # except for the master clocks, which have a random drift rate **.tsnClock*.clock.oscillator.typename = "RandomDriftOscillator" **.oscillator.changeInterval = 12.5ms **.oscillator.driftRate = uniform(-100ppm, 100ppm) 我们在下面的部分详细说明每个仿真。 仿真1: 一个主时钟 One Master Clock ~~~~~~~~~~~~~~~~ 在这个仿真设置中网络拓扑是一个简单的树形结构。网络包含一个主时钟节点 (`TsnClock `__), \ 一个桥接器和两个终端设备 (`TsnDevice `__), \ 通过一个 `TsnSwitch `__ 连接。 .. image:: Pic/OneMasterClockNetwork.png :alt: OneMasterClockNetwork.png :align: center 我们通过在 ``tsnClock`` 和 ``tsnSwitch`` 中设置主端口来配置生成树 .. code:: ini # TSN clock gPTP master ports *.tsnClock.gptp.masterPorts = ["eth0"] # TSN switch gPTP bridge master ports *.tsnSwitch.gptp.masterPorts = ["eth1", "eth2"] .. note:: TsnDevice和TsnSwitch中的从端口默认设置为 ``eth0`` ,因此不需要再单独的明确设置。 这是一个同步机制的视频(显示了主时钟的时间以及其他节点与此时间的差异): .. figure:: Pic/onemasterclock.mp4 注意:目前视频无法直接播放,需要右键,选择【显示所有控件】才可以播放 我们需要注意的是:时钟是在接收到后续消息之后被设置的。 这是由gPTP同步消息的方向指示的生成树: .. image:: Pic/OneMasterClock_tree.png :alt: OneMasterClock_tree.png :align: center 我们通过将时钟时间差与仿真时间绘制成图来检查所有时钟的时钟漂移 .. image:: Pic/OneMasterClock.png :alt: OneMasterClock.png :align: center 主时钟按照随机游走过程发生漂移。从属时钟的时间周期性地与主时钟同步。在第二次时间同步事件发生时,即在0.25秒时,从属时钟的漂移率被补偿,从而让其与主时钟的漂移率进行对齐。 所有这些图表在开始时都有两个大的锯齿状图案,在漂移率被补偿之前。从现在开始,我们通常会省略这些,集中关注时钟漂移在时间同步后稳定的细节。 .. note:: 可以通过绘制 ``timeChanged:vector`` 统计数据,并应用线性趋势操作(参数为-1),轻松制作出一个时钟时间差与模拟时间的图表。 仿真2:主时钟和热备份主时钟 Primary and Hot-standby Master Clocks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 在本仿真中,配置文件中树形网络拓扑进一步扩展。网络包含一个主主时钟节点和一个热备主时钟节点。两个主时钟节点都有自己的时间同步域。 \ 交换机和设备节点有两个时钟,每个时钟分别与一个主时钟同步。两个时间域之间唯一的连接是热备主时钟,它也与主主时钟同步。这种连接有效地使两个时间域完全同步,并可以在主时钟故障的情况下无缝切换。 .. note:: 此设置只包含故障转移的可能性,但本文没有实际演示。主时钟故障在 `Effects of Time Synchronization on Time-Aware Shaping `__ 中得到了演示。 本仿真案例中的网络包含两个 `TsnClock `__ 作为时钟节点和四个 `TsnDevice `__ 作为TSN设备节点, \ 由两个 `TsnSwitch `__ 作为TSN交换机进行连接: .. image:: Pic/PrimaryAndHotStandbyNetwork.png :alt: PrimaryAndHotStandbyNetwork.png :align: center 我们的目标是为两个时间域配置两个gPTP生成树。在这个设置中,时钟节点有一个时钟,其他节点有两个(每个域一个)。 - ``tsnClock1`` (主要的主时钟节点)有一个时钟和一个gPTP域,并将时间信息传播给0域中所有其他节点。 - ``tsnClock2`` (热备用主节点)有一个时钟和两个gPTP域,将其域1的时间信息传播给除 ``tsnClock1`` 之外的域1所有其他节点。 - ``tsnClock2`` 中的时钟与域0中的主要主时钟节点进行同步。 让我们看一下omnetpp.ini中的设置,从时钟节点的设置开始: .. code:: ini *.tsnClock2.clock.typename = "SettableClock" *.tsnClock1.gptp.typename = "Gptp" *.tsnClock1.gptp.clockModule = "tsnClock1.clock" *.tsnClock1.gptp.masterPorts = ["eth0"] *.tsnClock2.gptp.typename = "MultiDomainGptp" *.tsnClock2.gptp.numDomains = 2 *.tsnClock2.gptp.domain[*].clockModule = "tsnClock2.clock" *.tsnClock2.gptp.domain[0].gptpNodeType = "SLAVE_NODE" *.tsnClock2.gptp.domain[0].slavePort = "eth0" *.tsnClock2.gptp.domain[1].gptpNodeType = "MASTER_NODE" *.tsnClock2.gptp.domain[1].masterPorts = ["eth0"] 我们为 ``tsnClock2`` 设置一个 `SettableClock `__ ,因此可以设置其时间。 \ 我们为 ``tsnClock1`` 设置一个 `Gptp `__ 模块,并将其设置为主节点。 \ 此外,我们指定它应该使用自己的时钟,并将唯一的接口 ``eth0`` 设置为主端口(节点将在该端口上发送gPTP同步消息)。 在 ``tsnClock2`` 中,我们需要两个 `Gptp `__ 模块(一个是树中的叶子,另一个是根), \ 因此我们将 ``gptp`` 模块的类型设置为具有两个域的 `MultiDomainGptp `__ 模块。 \ 两个域都使用节点中唯一的时钟,但其中一个充当gPTP主节点,另一个充当gPTP从节点(使用相同的端口 ``eth0`` )。 下面这段代码是交换机的对应设置: .. code:: ini *.tsnSwitch*.clock.typename = "MultiClock" *.tsnSwitch*.clock.numClocks = 2 # TSN switches have multiple gPTP time synchronization domains *.tsnSwitch*.gptp.typename = "MultiDomainGptp" *.tsnSwitch*.gptp.numDomains = 2 *.tsnSwitch1.gptp.domain[0].slavePort = "eth0" *.tsnSwitch1.gptp.domain[0].masterPorts = ["eth1", "eth2", "eth3"] *.tsnSwitch1.gptp.domain[1].slavePort = "eth1" *.tsnSwitch1.gptp.domain[1].masterPorts = ["eth2", "eth3"] # eth1 is omitted because no sync needed towards primary master *.tsnSwitch2.gptp.domain[0].slavePort = "eth1" *.tsnSwitch2.gptp.domain[0].masterPorts = ["eth0", "eth2", "eth3"] *.tsnSwitch2.gptp.domain[1].slavePort = "eth0" *.tsnSwitch2.gptp.domain[1].masterPorts = ["eth1", "eth2", "eth3"] 我们配置交换机具有两个时钟和两个 `Gptp `__ 模块(每个域一个)。 \ 然后,我们通过设置端口来指定生成树,这里需要注意的是:在 `TsnSwitch `__ 中,默认情况下 ``gptpModuleType`` 是 ``BRIDGE_NODE`` ,所以我们不需要指定)。 \ 在两个域中,连接到时钟节点的接口是从属端口,其他接口是主端口。唯一的例外是 ``tsnSwitch1`` 不应该向 ``tsnClock1`` 发送同步消息(因为我们不希望它与任何东西同步),所以 ``eth1`` 接口不被设置为主端口。 下面这段代码是对应的设置: .. code:: ini *.tsnDevice*.clock.typename = "MultiClock" *.tsnDevice*.clock.numClocks = 2 # TSN devices have multiple gPTP time synchronization domains *.tsnDevice*.gptp.typename = "MultiDomainGptp" *.tsnDevice*.gptp.numDomains = 2 *.tsnDevice1.gptp.clockModule = "tsnDevice1.clock" *.tsnDevice2.gptp.clockModule = "tsnDevice2.clock" *.tsnDevice3.gptp.clockModule = "tsnDevice3.clock" *.tsnDevice4.gptp.clockModule = "tsnDevice4.clock" *.tsnDevice*.gptp.domain[*].slavePort = "eth0" 就像在交换机中一样,设备中也需要两个时钟和两个 `Gptp `__ 模块,因此我们使用 `MultiClock `__ 和 `MultiDomainGptp `__\ 与两个子模块。我们将每个设备的 ``gptp`` 模块配置为使用设备中的 `MultiClock `__\ 模块;自动选择适当的子时钟用于该域。我们将所有 ``gptp`` 模块设置为将唯一接口用作从端口(在\ `TsnDevice `__\ 中,默认情况下Gptp模块类型为 ``SLAVE_NODE`` ,因此我们不需要进行配置)。 我们还为不同域中的 pDelay 测量和 gPTP同步消息配置了一些偏移量,以避免它们同时传输产生的竞争问题以及排队时延。 .. code:: ini **.pdelayInitialOffset = 100μs *.*.gptp.domain[0].syncInitialOffset = syncInterval * 1 / 2 *.*.gptp.domain[1].syncInitialOffset = syncInterval * 2 / 2 以下是仿真开始时的时间同步过程的视频。显示了主节点的时钟时间以及其他节点与该时钟时间的时间差。gPTP的消息以箭头形式可视化。可视化根据域进行了颜色编码。 .. figure:: Pic/PrimaryAndHotStandbyMasterClocks.mp4 注意:目前视频无法直接播放,需要右键,选择【显示所有控件】才可以播放。 首先,桥设备节点和从节点通过交换pDelay消息来测量链路延迟。然后,主时钟发送gPTP同步消息。请注意,在接收到gPTP跟随消息后,时钟设置为新时间时,时间差会发生跳变。 这种设置可防止主时钟发生故障。在这种情况下,场景管理脚本可以将网络中的节点切换到gPTP域1,即将MultiClock中的活动时钟切换到 ``clock[1]`` 子模块,而不会影响时间同步。 生成树以数据链路层的gPTP消息传输形式进行可视化。这描绘了网络中来自主时钟的时间信息流动。 .. image:: Pic/PrimaryAndHotStandbyMasterClocks_tree-1708860651544-41.png :alt: PrimaryAndHotStandbyMasterClocks_tree-1708860651544-41.png :align: center 让我们来看一些时钟漂移图表。与其在一张图表中绘制所有时钟的时钟漂移图,不如使用三张图表,这样就不会那么杂乱了。这是两个主时钟的时钟漂移(时钟时间与模拟时间的差异): .. image:: Pic/PrimaryAndHotStandBy_masterclocks.png :alt: PrimaryAndHotStandBy_masterclocks.png :align: center 两个主时钟都有随机漂移率,但热备份主时钟的时间和时钟漂移率会定期与主时钟同步。 这是时间域0(主要主机)中所有时钟的时钟漂移 .. image:: Pic/PrimaryAndHotStandBy_timedomain0_zoomed.png :alt: PrimaryAndHotStandBy_timedomain0_zoomed.png :align: center 每个从时钟都有一个独特但恒定的漂移速率,而主时钟的漂移速率则随机波动。从属时钟定期与主时钟同步。 \ 在最初的两次同步事件之后(未在图表上显示),从属时钟的漂移速率将被调整以与主时钟对齐。 \ 然而,每个从时钟中的振荡器补偿因子是由当前和前一个同步点的漂移速率确定的,随着主时钟的漂移速率继续变化,从属时钟可能会偏离主时钟。 \ 值得注意的是,在第一次速率补偿之后,所有从属时钟具有相同的漂移速率。 让我们看看时间域1(热备份主机)中所有时钟的时钟漂移 .. image:: Pic/PrimaryAndHotStandBy_timedomain1_zoomed.png :alt: PrimaryAndHotStandBy_timedomain1_zoomed.png :align: center 这些时钟具有不同的漂移率,并定期与热备主时钟(用蓝色粗线显示)同步。热备用主时钟本身也会从主主时钟漂移,并定期同步。图中可以明显看出时差的上限。 请注意,在域1中的从时钟进行时间同步,是在在域0中的热备份主时钟的时间更新之前。与前几种情况一样,从时钟的漂移率会得到补偿,以便与主时钟的速率保持加一致。 .. note:: 从主时钟偏离的角度来看,从这些图表上看,从时钟的差异可能看起来很大,但实际上只有微秒级别(y轴的刻度是x轴的百万分之一)。 .. warning:: 从主时钟偏离的角度来看,从这些图表上看,从时钟的差异可能看起来很大,但实际上只有微秒级别(y轴的刻度是x轴的百万分之一)。 在下一节中,我们将增加网络的冗余度,使主时钟和网络中的任何链路都能在不中断时间同步的情况下发生故障。 仿真3:两个利用网络冗余的主时钟 Two Master Clocks Exploiting Network Redundancy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 在本仿真案例的设置中,网络拓扑是一个环形结构。主时钟和热备份主时钟各自具有两个独立的时间域。 \ 一个时间域使用顺时针方向,另一个时间域使用逆时针方向在环形拓扑中传播时钟时间。 \ 这种方法可以保护主时钟节点的故障以及环中单个链路的故障,因为所有桥接器都可以通过两个主时钟的时间同步域之一在两个方向上到达。 下图是网络示意图,它使用与之前相同的节点类型, \ 包括 `TsnClock `__ 、 \ `TsnSwitch `__ 和 \ `TsnDevice `__ 。 .. image:: Pic/TwoMasterClocksNetwork.png :alt: TwoMasterClocksNetwork.png :align: center 时间同步冗余是通过以下方式实现的: - 主要的主节点有一个时钟和两个主gPTP时间域。这些域在时钟里以顺时针和逆时针的方向发送时间信息。 - 热备份主节点有两个从节点和两个主节点的gPTP域,以及两个子时钟。域0和域1将两个时钟通过两个域与主要的主节点进行同步。同时,域2和域3在环中双向发送两个时钟的定时信息。 - 交换机和设备节点有四个域(和四个子时钟),域0和域1与主要的主节点进行同步,域2和域3与热备份主节点同步。 - 因此,交换机中的gPTP模块是gPTP桥设备,终端设备中的gPTP模块是gPTP从设备。 在主要的主节点故障和环中的一个链接故障的情况下,交换机和终端设备将至少有一个同步的时钟可以切换到。 接下来看一下如何配置这个方案。 通过添加所需的gPTP域和时钟,并设置上述的生成树。在设置端口和时钟时,有一些重要的方面: - 我们不想将任何时间信息转发给主要的主节点,因此我们对应的设置了 ``tsnSwitch1`` 中的主端口。 - 需要注意的是,我们不将定时消息转发给最初发送它的交换机(以避免同步消息无限循环)。例如,tsnSwitch6不应将同步消息发送到域0中的 ``tsnSwitch1`` 。 - 热备份的主节点只有两个时钟,被四个域使用。从域0和域1传递给域2和域3的定时信息在这里进行。所以我们设置域0和域2使用 ``clock[0]`` ,域1和域3使用 ``clock[1]`` 。 下面是时钟节点的配置代码: .. code:: ini [Config TwoMasterClocksExploitingNetworkRedundancy] network = TwoMasterClocksRingGptpShowcase description = "Ring topology with redundant time synchronization domains" # clock visualization note: bridge and slave nodes display difference from corresponding master clock # TSN clock2 has multiple clocks *.tsnClock2.clock.typename = "MultiClock" *.tsnClock2.clock.numClocks = 2 # TSN clocks have multiple gPTP time synchronization domains *.tsnClock*.gptp.typename = "MultiDomainGptp" *.tsnClock1.gptp.numDomains = 2 *.tsnClock1.gptp.domain[0..1].clockModule = "tsnClock1.clock" *.tsnClock1.gptp.domain[0].masterPorts = ["eth0"] *.tsnClock1.gptp.domain[1].masterPorts = ["eth0"] *.tsnClock2.gptp.numDomains = 4 *.tsnClock2.gptp.domain[2..3].clockModule = "tsnClock2.clock" *.tsnClock2.gptp.domain[0].gptpNodeType = "SLAVE_NODE" *.tsnClock2.gptp.domain[0].slavePort = "eth0" *.tsnClock2.gptp.domain[1].gptpNodeType = "SLAVE_NODE" *.tsnClock2.gptp.domain[1].slavePort = "eth0" *.tsnClock2.gptp.domain[2].gptpNodeType = "MASTER_NODE" *.tsnClock2.gptp.domain[2].masterPorts = ["eth0"] *.tsnClock2.gptp.domain[3].gptpNodeType = "MASTER_NODE" *.tsnClock2.gptp.domain[3].masterPorts = ["eth0"] 我们设置 ``tsnClock1`` 有两个 `Gptp `__ 模块, \ 每个模块使用主机中唯一的时钟。时钟网络节点的类型是 `TsnClock `__ ; \ 在这些节点中,默认情况下, `Gptp `__ 模块被设置为主节点。 \ 我们在两个模块中设置了主端口,因此它们通过它们唯一的以太网接口传播时间信息。 ``tsnClock2`` 被设置为具有四个gPTP域。由于 ``tsnClock2`` 只有两个子时钟, \ 我们需要在 `MultiClock `__ 模块中指定域2和域3使用 ``clock[0]`` 和 ``clock[1]`` , \ 只需设置 `MultiClock `__ 模块的 ``clockModule`` 参数即可,因为它会自动将子时钟分配给对应域。 因此,在 ``tsnClock2`` 中,域0和域1是gPTP从端,同步到主要主站的两个域。域2和域3是gPTP主端,并传播由前两个域设置的时钟时间。 交换机的配置如下: .. code:: ini # TSN switches have multiple clocks *.tsnSwitch*.clock.typename = "MultiClock" *.tsnSwitch*.clock.numClocks = 4 # TSN switches have multiple gPTP time synchronization domains *.tsnSwitch*.gptp.typename = "MultiDomainGptp" *.tsnSwitch*.gptp.numDomains = 4 # TSN switch 1 *.tsnSwitch1.gptp.domain[0].masterPorts = ["eth1"] *.tsnSwitch1.gptp.domain[0].slavePort = "eth0" *.tsnSwitch1.gptp.domain[1].masterPorts = ["eth2"] *.tsnSwitch1.gptp.domain[1].slavePort = "eth0" *.tsnSwitch1.gptp.domain[2].masterPorts = ["eth1"] *.tsnSwitch1.gptp.domain[2].slavePort = "eth2" *.tsnSwitch1.gptp.domain[3].masterPorts = ["eth2"] *.tsnSwitch1.gptp.domain[3].slavePort = "eth1" # TSN switch 2 *.tsnSwitch2.gptp.domain[0].masterPorts = ["eth1", "eth2"] *.tsnSwitch2.gptp.domain[0].slavePort = "eth0" *.tsnSwitch2.gptp.domain[1].masterPorts = ["eth2"] *.tsnSwitch2.gptp.domain[1].slavePort = "eth1" *.tsnSwitch2.gptp.domain[2].masterPorts = ["eth1", "eth2"] *.tsnSwitch2.gptp.domain[2].slavePort = "eth0" *.tsnSwitch2.gptp.domain[3].masterPorts = ["eth0", "eth2"] *.tsnSwitch2.gptp.domain[3].slavePort = "eth1" # TSN switch 3 *.tsnSwitch3.gptp.domain[0].masterPorts = ["eth1", "eth2"] *.tsnSwitch3.gptp.domain[0].slavePort = "eth0" *.tsnSwitch3.gptp.domain[1].masterPorts = ["eth0", "eth2"] *.tsnSwitch3.gptp.domain[1].slavePort = "eth1" *.tsnSwitch3.gptp.domain[2].masterPorts = ["eth2"] *.tsnSwitch3.gptp.domain[2].slavePort = "eth0" *.tsnSwitch3.gptp.domain[3].masterPorts = ["eth0", "eth2"] *.tsnSwitch3.gptp.domain[3].slavePort = "eth1" # TSN switch 4 *.tsnSwitch4.gptp.domain[0].masterPorts = ["eth0", "eth2"] *.tsnSwitch4.gptp.domain[0].slavePort = "eth1" *.tsnSwitch4.gptp.domain[1].masterPorts = ["eth0", "eth1"] *.tsnSwitch4.gptp.domain[1].slavePort = "eth2" *.tsnSwitch4.gptp.domain[2].masterPorts = ["eth2"] *.tsnSwitch4.gptp.domain[2].slavePort = "eth0" *.tsnSwitch4.gptp.domain[3].masterPorts = ["eth1"] *.tsnSwitch4.gptp.domain[3].slavePort = "eth0" # TSN switch 5 *.tsnSwitch5.gptp.domain[0].masterPorts = ["eth1", "eth2"] *.tsnSwitch5.gptp.domain[0].slavePort = "eth0" *.tsnSwitch5.gptp.domain[1].masterPorts = ["eth0", "eth2"] *.tsnSwitch5.gptp.domain[1].slavePort = "eth1" *.tsnSwitch5.gptp.domain[2].masterPorts = ["eth1", "eth2"] *.tsnSwitch5.gptp.domain[2].slavePort = "eth0" *.tsnSwitch5.gptp.domain[3].masterPorts = ["eth2"] *.tsnSwitch5.gptp.domain[3].slavePort = "eth1" # TSN switch 6 *.tsnSwitch6.gptp.domain[0].masterPorts = ["eth2"] *.tsnSwitch6.gptp.domain[0].slavePort = "eth0" *.tsnSwitch6.gptp.domain[1].masterPorts = ["eth0", "eth2"] *.tsnSwitch6.gptp.domain[1].slavePort = "eth1" *.tsnSwitch6.gptp.domain[2].masterPorts = ["eth1", "eth2"] *.tsnSwitch6.gptp.domain[2].slavePort = "eth0" *.tsnSwitch6.gptp.domain[3].masterPorts = ["eth0", "eth2"] *.tsnSwitch6.gptp.domain[3].slavePort = "eth1" 终端设备的配置如下: .. code:: ini *.tsnDevice*.clock.typename = "MultiClock" *.tsnDevice*.clock.numClocks = 2 # TSN devices have multiple gPTP time synchronization domains *.tsnDevice*.gptp.typename = "MultiDomainGptp" *.tsnDevice*.gptp.numDomains = 2 *.tsnDevice1.gptp.clockModule = "tsnDevice1.clock" *.tsnDevice2.gptp.clockModule = "tsnDevice2.clock" *.tsnDevice3.gptp.clockModule = "tsnDevice3.clock" *.tsnDevice4.gptp.clockModule = "tsnDevice4.clock" *.tsnDevice*.gptp.domain[*].slavePort = "eth0" 最后,我们为这四个域配置偏移量,以便它们不会同时发送同步消息: .. code:: ini **.pdelayInitialOffset = 0.1ms *.*.gptp.domain[0].syncInitialOffset = syncInterval * 1 / 4 *.*.gptp.domain[1].syncInitialOffset = syncInterval * 2 / 4 *.*.gptp.domain[2].syncInitialOffset = syncInterval * 3 / 4 *.*.gptp.domain[3].syncInitialOffset = syncInterval * 4 / 4 下图是由gPTP消息可视化的生成树: .. image:: Pic/ExploitingNetworkRedundancy_tree.png :alt: ExploitingNetworkRedundancy_tree.png :align: center 就像之前的内容一样,让我们来检查网络中不同时钟的时钟漂移。下图中是主时钟的时钟漂移: .. image:: Pic/ExploitingNetworkRedundancy_masterclocks_zoomed.png :alt: ExploitingNetworkRedundancy_masterclocks_zoomed.png :align: center 热备份主节点的时钟定期与主要主节点的时间同步。需要注意的是,同步时间具有我们配置的偏移量。让我们看一下域0中的时钟漂移(主要主节点的时钟用较粗的线绘制): .. image:: Pic/ExploitingNetworkRedundancy_domain0_zoomed.png :alt: ExploitingNetworkRedundancy_domain0_zoomed.png :align: center 在域0中,所有时钟都与主要主时钟同步。它们同时同步,因为偏移量是在域之间的。域1中的时钟漂移类似,所以我们不在这里包括它。 \ 让我们看看域2(主要主时钟以虚线显示作为参考,因为它不是该域的一部分;该域中的热备份主时钟以粗线显示): .. image:: Pic/ExploitingNetworkRedundancy_domain2_zoomed.png :alt: ExploitingNetworkRedundancy_domain2_zoomed.png :align: center 所有交换机和设备都与热备份主时钟同步(该时钟本身定期与主要主时钟同步)。 .. note:: 所有领域的图表都可以在展示文件夹中的.anf文件中找到。 | 源代码: | `omnetpp.ini `__ | `GptpShowcase.ned `__ 讨论 ---------- 如果您对这个示例有任何疑问或讨论,请在 `此页面 `__ 分享您的想法。