.NET Framework

Synchronize Time with a PTP Server in C#

Precision Time Protocol (PTP) is a network protocol used to synchronize clocks in a distributed system. In this tutorial, we will create a PTP client that can synchronize the time with a PTP server using the Simple Network Time Protocol (SNTP) as the PTP transport protocol.

Before we start, you need to have the following installed on your computer:

  • Visual Studio (or any other C# IDE)
  • .NET Framework (version 4.6.1 or later)

Let’s get started

  1. Create a new C# console application in Visual Studio.
  2. Add the System.Net and System.Net.Sockets namespaces to your project.

The code

Here’s the code for the PTP client:

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

class PtpClient
{
    private const int PtpServerPort = 319;
    private const int PtpVersionNumber = 2;

    private UdpClient udpClient;
    private IPEndPoint ptpServerEndPoint;
    private byte[] buffer;

    public PtpClient(string ptpServerIp)
    {
        udpClient = new UdpClient();
        ptpServerEndPoint = new IPEndPoint(IPAddress.Parse(ptpServerIp), PtpServerPort);
        buffer = new byte[48];
        buffer[0] = (byte)((PtpVersionNumber << 4) | 3); // PTP version number and message type
    }

    public DateTime SynchronizeTime()
    {
        udpClient.Send(buffer, buffer.Length, ptpServerEndPoint);
        IPEndPoint senderEndPoint = new IPEndPoint(IPAddress.Any, 0);
        byte[] receiveBuffer = udpClient.Receive(ref senderEndPoint);

        // Get the transmit timestamp from the PTP server response
        long transmitTimestamp = BitConverter.ToInt64(receiveBuffer, 40);

        // Convert the transmit timestamp to a DateTime object
        DateTime dateTime = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        dateTime = dateTime.AddSeconds(transmitTimestamp >> 32);
        dateTime = dateTime.AddSeconds((transmitTimestamp & 0xffffffffL) / 4294967296.0 * 1000000000);

        return dateTime;
    }

    public void Dispose()
    {
        udpClient?.Close();
    }
}

Let’s go through the code step-by-step:

  1. The PtpClient class is defined with the following private fields:
    • udpClient – an instance of the UdpClient class used to send and receive PTP messages.
    • ptpServerEndPoint – an instance of the IPEndPoint class that represents the IP address and port number of the PTP server.
    • buffer – a byte array used to store the PTP message.
  2. The PtpClient constructor takes a string argument that represents the IP address of the PTP server. The constructor initializes the udpClient and ptpServerEndPoint fields and sets the first byte of the buffer to the PTP version number and message type.
  3. The SynchronizeTime method is used to send a PTP message to the server and receive the response. It does the following:
    • Sends the PTP message using the Send method of the udpClient object.
    • Receives the PTP response using the Receive method of the udpClient object.
  4. The transmitTimestamp is extracted from the PTP response by getting the last 8 bytes of the response.
  5. The transmitTimestamp is converted to a DateTime object using the following steps:
    • The DateTime object is initialized with the Unix epoch time (January 1, 1900).
    • The seconds component of the DateTime object is set to the high 32 bits of the transmitTimestamp.
    • The fractional seconds component of the DateTime object is set to the low 32 bits of the transmitTimestamp divided by the maximum value of a 32-bit integer (4294967296) multiplied by 1000000000 (the number of nanoseconds in a second).
  6. The SynchronizeTime method returns the DateTime object representing the current time on the PTP server.
  7. The Dispose method is used to close the udpClient object when the PtpClient object is no longer needed.

Usage

Here’s an example of how to use the PtpClient class to synchronize the time with a PTP server:

using System;

class Program
{
    static void Main(string[] args)
    {
        PtpClient client = new PtpClient("ptp.example.com");

        try
        {
            DateTime ptpTime = client.SynchronizeTime();
            Console.WriteLine("PTP server time: " + ptpTime.ToString());
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error synchronizing time: " + ex.Message);
        }
        finally
        {
            client.Dispose();
        }

        Console.ReadLine();
    }
}

Conclusion

In this tutorial, we created a PTP client that can synchronize the time with a PTP server using the Simple Network Time Protocol (SNTP) as the PTP transport protocol. The PtpClient class sends a PTP message to the server and receives the response, extracts the transmit timestamp from the response, and converts it to an DateTime object representing the current time on the PTP server. You can use the PtpClient class to synchronize the time with a PTP server in your own C# applications.