Domain Time Interface Software Development Kit Documentation
Domain Time Interface Software Development Kit Documentation
I. Introduction
The Windows operating system only allows setting the time to the nearest
millisecond (0.001 seconds). The theoretical accuracy of available time
protocols can go to microseconds (0.0000001 second) or nanoseconds
(0.000000001 second).
Obtaining the time to the nearest microsecond doesn’t do much good if the
system clock can only be set to the nearest millisecond. For this reason,
Domain Time only “sets” the clock when stepping. The rest of the time,
Domain Time slews the clock (adjusts the frequency) so that time passing
on a wall clock and time passing within the computer come into
conformance with a high degree of accuracy.
This interpolation allows Domain Time to compare time received from the
network with the computer’s time at a level far below the operating
system’s native capacity, yielding more accurate corrections and more
precise time.
The SDK requires Domain Time Client or Domain Time Server version
5.1 or later. Earlier versions of Domain Time do not export the shared
memory needed by the SDK. Windows XP, Windows 2003r2, Vista,
Windows 2008, Windows 7, Windows 2008r2, Windows 8, Windows
2012, Windows 8.1, Windows 2012r2, Windows 10, Windows 2016,
Windows Server 2019, Nanoserver, and Server Core are currently
supported.
The x64 operating system allows you to run 32-bit programs in Windows-
on-Windows (WoW) mode. In WoW mode, your programs cannot see the
real system32 directory; instead, they are transparently redirected to the
syswow64 directory when they try to access the system32 directory.
Domain Time itself does not run under WoW, although the SDK will
support your programs in this configuration.
The DLLs export C-style functions for your program’s use. The demo
programs show a variety of methods for interfacing with the DLLs
(VB.NET, C#, and plain C). C-style functions are necessary to avoid the
overhead of marshalling across process boundaries. A COM or DCOM
server would suffer so much latency that it would lose the extra precision
available from Domain Time.
III. API
proper clock ticks. This should be a small enough interval, but isn’t
– in practice, the derived wall clock will move slightly ahead, then
rewind, then move slightly ahead again, all based on how much the
divisor doesn’t match reality and when the various ticks happen.
Example:
PTP_STATISTICS stats;
stats.cbSize = sizeof(PTP_STATISTICS);
DWORD error = GetPTPStats(&stats);
The remaining members of *timeptr are set with the current UTC
time of day in seconds and milliseconds since midnight January 1st
1970 UTC.
This function will operate even when Domain Time is not running,
allowing your program to monitor startup events and the initial
time check. All other functions in the SDK fail if the Domain Time
service isn’t running (although the get time functions will return
the time according to the operating system’s native resolution).
When you are finished with the handle, your program must call the
Win32 API function CloseHandle to release the resources.
This function fills in the data members with the version number of
the Domain Time Client or Server running on the machine. The
major and minor versions are simple integers. The date member is
a packed DWORD, with the high word holding the year, and the low
word holding the month and day. For example, 31 March 2019
would be represented as MAKELONG(331,2019). You can use the
HIWORD and LOWORD macros to extract the individual fields:
dwYear = HIWORD(dwDate);
dwMonthDay = LOWORD(dwDate);
printf("Version is %u.%u.b.%04u%04u",
dwMajor,
dwMinor,
dwYear,
dwMonthDay);
ROLE_UNKNOWN = 0
ROLE_MASTER = 1 (Server in the domain master role)
ROLE_SLAVE = 2 (Server in the domain slave role)
ROLE_INDIE = 3 (Server acting independently)
ROLE_CLIENT = 4 (Domain Time Full Client)
SYNC_STATUS_INVALID = 0
SYNC_STATUS_UNKNOWN = 1
SYNC_STATUS_CHECKING = 2
SYNC_STATUS_SYNCED = 3
SYNC_STATUS_UNSYNCED = 4
Each of the other API functions sets the Win32 last error. Use the
GetLastError() function to obtain the error code for any API call that
returns a BOOL whose value is FALSE. Any normal system error (such as
being out of memory) may occur, but, commonly, only the following
errors will be seen (see the Windows Platform SDK for numbers
corresponding to the defines):
situation will clear itself as soon as Domain Time has recalibrated the
interpolation counters (typically a few hundred milliseconds).
V. Technical Support
If the demo programs work on your machine, but your own programs do
not, check your syntax (especially if you are writing your own declarations
in a non-C language). Each of the API functions sets the Win32 last error
instead of throwing an exception. If you are using C++ or C#, common
practice is to wrap C-style APIs in a class where you check the last error
and throw your own exceptions as needed.
Domain Time Interface SDK Documentation Page 13 of 16
Appendix
Global Named Events
As of version 5.2.b.20140101 (January 1st, 2014), Domain Time exposes several named
events of potential interest to external applications. These events will exist whenever
Domain Time is running. Your code should use OpenEvent with SYNCHRONIZE access,
and then WaitForSingleObject (or any of the other handle-waiting functions) with a
timeout of zero to query status (see example code below). Do not try to use CreateEvent
if OpenEvent fails. You may open the events of interest once at the beginning of your
program and then close them at the end, but be sure to check for valid handles before
passing them to any of the wait functions. If Domain Time has not started yet when your
program starts, the OpenEvent call(s) will fail. However, if you have successfully opened
the event(s), they will remain valid even if the Domain Time service is restarted.
Global\domtime-sync-status-notify-event
Global\domtime-sync-status-synchronized
This event will be set (signaled) when Domain Time has successfully synchronized the
clock, and remain set unless Domain Time encounters an error synchronizing the clock.
Under normal conditions, it will be clear (unsignaled) from the moment Domain Time
starts up until after the first successful synchronization. It will then transition to signaled,
and any transition back to unsignaled indicates a problem has occurred or that the service
has been restarted.
Global\domtime-sync-status-interphase-active
This event will be set (signaled) during the application of interphase corrections. It will
be clear (unsignaled) at all other times.
Global\domtime-sync-status-ptpv2-enabled
This event will be set (signaled) if Domain Time has PTP enabled. It will be clear
(unsignaled) otherwise.
Global\domtime-sync-status-ptpv2-slave
This event will be set (signaled) if Domain Time has PTP enabled and is currently
operating as a PTP slave. It will be clear (unsignaled) otherwise.
Domain Time Interface SDK Documentation Page 14 of 16
Global\domtime-sync-status-ptpv2-master
This event will be set (signaled) if Domain Time has PTP enabled and is currently
operating as a PTP master. It will be clear (unsignaled) otherwise.
Example Code
For clarity, error handling is omitted. This sample should demonstrate how to use the
events named above in your own programs. Note that in the C language, backslashes
must be escaped (doubled) as shown.
BOOL IsPTPv2Slave()
{
BOOL bIsSlave = FALSE;
if (hEvent)
{
bIsSlave = (WAIT_OBJECT_0 == WaitForSingleObject(hEvent,0));
//
// WAIT_OBJECT_0 means event is set (signaled)
// WAIT_TIMEOUT means event is clear (unsignaled)
// any other return code is an error
//
// Don't forget to close the handle
//
CloseHandle(hEvent);
}
else
{
// use GetLastError() to find out why the open failed
//
// ERROR_FILE_NOT_FOUND means Domain Time isn't running yet
//
// ERROR_ACCESS_DENIED may happen if you try anything other
// than SYNCHRONIZE access
}
return bIsSlave;
}
As of version 5.2.20160415 (15 April 2016), Domain Time stores some extra information
in the registry for use by third-party programs, and has two new global events you can
monitor. The registry values must be treated as read-only (Domain Time never reads the
variables; it only overwrites them as conditions change.)
Global\domtime-sync-status-ptpv2-status-change
Domain Time Interface SDK Documentation Page 15 of 16
This event is auto-reset. Domain Time will set it upon a status change (change from
Listening to Slave, etc., change in Master, or change in TAI-UTC offset). When you
check the event’s status, it will change (if signaled) to unsignaled. You may therefore
wait on this event to know when to read the registry for new information.
Global\domtime-sync-status-ptpv2-offset-change
This event is auto-reset and is meaningful only if PTP is a slave. Domain Time will set it
every time a new PTP offset is calculated. When you check the event’s status, it will
change (if signaled) to unsignaled. You may therefore wait on this event to know when to
call GetPTPStats() or read the registry for new information. As of 20 September 2016,
this event will only fire if Current Offset Enabled is set to True (see below).
Current PortState (REG_SZ string). If not present, either PTP is not running, or you
have a version prior to 5.2.b.20160415. If present, it will be one of Initializing, Faulty,
Disabled, Listening, Pre-Master, Passive, Calibrating, or Slave. Other values are reserved
for future use. The status-change event will fire when this value changes.
Current Master (REG_SZ string): If not present or blank, then PTP is not a slave, and
therefore has no master. If present, it will be in the form of 00a154-fffe-125c71:1
(192.168.1.3), which is the master’s PortIdentity (clockIdentity:port) followed by the
IP address in parentheses. The status-change event will fire when this value changes.
Current Offset in the registry incurs a significant performance penalty, and may severely
impact Domain Time’s ability to keep the clock in sync with the grandmaster.
Current Offset (signed 64-bit) (REG_QWORD): This value is the offset (delta) in
hectonanoseconds (tenths of a microsecond), and must be treated as a signed value. A
positive number represents the master being ahead of the slave; a negative number
represents the slave being ahead of the master. The offset-change event will fire when
this value changes. The value is only meaningful if PTP is currently a slave. To convert
hectonanoseconds to a more friendly display format, use code like this: