Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are all the fields in a Python ntplib response, and how are they used?

Tags:

python

ntp

I'd like to make a measurement of the difference between a local clock, and a remote processor running an NTP server.

I can get the tx_time of the response, as shown below, but a better estimate would include some estimate of the network delays. There are other fields in the NTP response message which should be used, as well.

import ntplib
from time import ctime,time

addr_remote = '128.128.204.207'

c = ntplib.NTPClient()
remote = c.request(addr_remote)
local = time()
print("REMOTE: " + ctime(remote.tx_time) + "   <reference clock>   ") 
print("LOCAL:  " + ctime(local)        + "   delta: " + str(local - remote.tx_time ))

If I look at "remote":

for attr in dir(remote):
    print("remote.%s = %r" % (attr, getattr(remote, attr)))

I see:

remote.delay = 0.0
remote.dest_time = 1531863145.9309998
remote.dest_timestamp = 3740851945.9309998
remote.from_data = <bound method NTPPacket.from_data of <ntplib.NTPStats object at 0x000000000265B2E8>>
remote.leap = 0
remote.mode = 4
remote.offset = -1.8789582252502441
remote.orig_time = 1531863145.9309998
remote.orig_timestamp = 3740851945.9309998
remote.poll = 0
remote.precision = -7
remote.recv_time = 1531863144.0520415
remote.recv_timestamp = 3740851944.0520415
remote.ref_id = 0
remote.ref_time = 0.0
remote.ref_timestamp = 2208988800.0
remote.root_delay = 0.0
remote.root_dispersion = 0.0
remote.stratum = 9
remote.to_data = <bound method NTPPacket.to_data of <ntplib.NTPStats object at 0x000000000265B2E8>>
remote.tx_time = 1531863144.0520415
remote.tx_timestamp = 3740851944.0520415

So, how do I use these:

  • dest_time
  • orig_time
  • recv_time
  • tx_time

to remove the network delays, and get a better estimate of the clock difference?

like image 883
Matt Avatar asked Jul 17 '18 21:07

Matt


2 Answers

The NTP client sends a packet with its local time orig_time, which the NTP server receives at the server time recv_time. The server then replies at server time tx_time and the client receives that reply at local time dest_time.

The round trip delay is calculated as recv_time - orig_time + dest_time - tx_time, and the offset between the clocks is offset = (recv_time - orig_time + tx_time - dest_time) / 2.

Assuming the two NTP packets take consistent paths, the correct adjusted time is simply dest_time + offset, which is equivalent to tx_time + delay/2.

like image 146
Vic Avatar answered Sep 22 '22 21:09

Vic


I found this answer while trying to syncing my client computer to NTP time and checking that it actually synced. Based on @Vic, this means that the client clock (compared to the server NTP clock) is always off by:

dest_time + offset = tx_time + delay/2
dest_time - tx_time = delay/2 - offset

i.e.

correction = delay/2 - offset

As @Vic said, packets may take different routes going to and coming from, but in average correction should give you your clock offset (I think). Even by syncing with time.nist.gov with os.system('w32tm /resync/nowait'), my computer is always off by 40ms, somehow. Comments welcome!

This is my code.

import ntplib
from datetime import datetime, timezone
def get_ntp_time():
    ntp_pool = ['pool.ntp.org', 'time.nist.gov']
    def call_ntp(serverAddress):
        call = ntplib.NTPClient()
        return call.request(server, version=3)
    for server in ntp_pool:
        response = call_ntp(server)
        print(f"server: {server}")
        print(f"request packet sent (as LOCAL client time, orig_time): {datetime.fromtimestamp(response.orig_time, timezone.utc)}")
        print(f"request packet received (as NTP server time, recv_time): {datetime.fromtimestamp(response.recv_time, timezone.utc)}")
        print(f"response packet sent (as NTP server time, tx_time): {datetime.fromtimestamp(response.tx_time, timezone.utc)}")
        print(f"response packet received (as LOCAL client time, dest_time): {datetime.fromtimestamp(response.dest_time, timezone.utc)}")
        print(f'round trip duration: {response.delay} s')
        print(f'* adjusted time, tx_time + delay/2: {datetime.fromtimestamp(response.tx_time + response.delay/2, timezone.utc)}')
        print(f'* adjusted time, dest_time + offset: {datetime.fromtimestamp(response.dest_time + response.offset, timezone.utc)}')
        print(f'correction to client: {response.delay/2 - response.offset} s\n')
        # for attr in dir(response):
            # if not attr .startswith('_'):
                # print("response.%s = %r" % (attr, getattr(response, attr)))
        print('-')
get_ntp_time()

Response:

server: pool.ntp.org
request packet sent (as LOCAL client time, orig_time): 2021-04-23 16:14:46.544797+00:00
request packet received (as NTP server time, recv_time): 2021-04-23 16:14:46.535852+00:00
response packet sent (as NTP server time, tx_time): 2021-04-23 16:14:46.535862+00:00
response packet received (as LOCAL client time, dest_time): 2021-04-23 16:14:46.579710+00:00
round trip duration: 0.03490257263183594 s
* adjusted time, tx_time + delay/2: 2021-04-23 16:14:46.553314+00:00
* adjusted time, dest_time + offset: 2021-04-23 16:14:46.553314+00:00
correction to client: 0.04384756088256836 s

-
server: time.nist.gov
request packet sent (as LOCAL client time, orig_time): 2021-04-23 16:14:46.642192+00:00
request packet received (as NTP server time, recv_time): 2021-04-23 16:14:46.641157+00:00
response packet sent (as NTP server time, tx_time): 2021-04-23 16:14:46.641158+00:00
response packet received (as LOCAL client time, dest_time): 2021-04-23 16:14:46.689054+00:00
round trip duration: 0.04686117172241211 s
* adjusted time, tx_time + delay/2: 2021-04-23 16:14:46.664588+00:00
* adjusted time, dest_time + offset: 2021-04-23 16:14:46.664588+00:00
correction to client: 0.0478968620300293 s

-
like image 25
Alfred Wallace Avatar answered Sep 22 '22 21:09

Alfred Wallace