Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
The MIT License (MIT)

Copyright (c) {{{2014}}} {{{ChenLi}}}
{{{2019}}} {{{Jacques Samoun - Schneider Electric}}}

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
121 changes: 74 additions & 47 deletions ntpserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@
import socket
import struct
import time
import Queue
import mutex
import queue
import threading
import select

taskQueue = Queue.Queue()
taskQueue = queue.Queue()
stopFlag = False

def stop_ntp_server():
global stopFlag
stopFlag = True

def start_ntp_server():
global stopFlag
stopFlag = False

def system_to_ntp_time(timestamp):
"""Convert a system time to a NTP time.

Expand Down Expand Up @@ -55,8 +62,8 @@ def _to_time(integ, frac, n=32):
Retuns:
timestamp
"""
return integ + float(frac)/2**n
return integ + float(frac)/2**n



class NTPException(Exception):
Expand Down Expand Up @@ -119,7 +126,7 @@ class NTPPacket:

This represents an NTP packet.
"""

_PACKET_FORMAT = "!B B B b 11I"
"""packet format to pack/unpack"""

Expand Down Expand Up @@ -161,7 +168,7 @@ def __init__(self, version=2, mode=3, tx_timestamp=0):
self.tx_timestamp_high = 0
self.tx_timestamp_low = 0
"""tansmit timestamp"""

def to_data(self):
"""Convert this NTPPacket to a buffer that can be sent over a socket.

Expand Down Expand Up @@ -234,28 +241,36 @@ def GetTxTimeStamp(self):
def SetOriginTimeStamp(self,high,low):
self.orig_timestamp_high = high
self.orig_timestamp_low = low


class RecvThread(threading.Thread):
def __init__(self,socket):
def __init__(self,socket, delay=0):
threading.Thread.__init__(self)
self.socket = socket
# Customizable delay, can be used for testing or simulation an offset
# NTP server - default to Zero delay
self.delay = delay
def run(self):
global taskQueue,stopFlag
while True:
if stopFlag == True:
print "RecvThread Ended"
print ("RecvThread Ended")
break
rlist,wlist,elist = select.select([self.socket],[],[],1);
if len(rlist) != 0:
print "Received %d packets" % len(rlist)
print ("Received %d packets" % len(rlist))
for tempSocket in rlist:
try:
data,addr = tempSocket.recvfrom(1024)
recvTimestamp = recvTimestamp = system_to_ntp_time(time.time())
recvTimestamp = system_to_ntp_time(time.time()+self.delay)
print ('RT = %d' % recvTimestamp) # Receive Timestamp
taskQueue.put((data,addr,recvTimestamp))
except socket.error,msg:
print msg;
except socket.error as msg:
print ("Socket error: %s" % msg)
else:
# debug print if needed
# print ("Tout on Rx")
pass

class WorkThread(threading.Thread):
def __init__(self,socket):
Expand All @@ -265,14 +280,14 @@ def run(self):
global taskQueue,stopFlag
while True:
if stopFlag == True:
print "WorkThread Ended"
print ("WorkThread Ended")
break
try:
data,addr,recvTimestamp = taskQueue.get(timeout=1)
recvPacket = NTPPacket()
data,addr,recvTimestamp = taskQueue.get(timeout=10) # will generate an exception

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? it make Ctrl+C respond slower.

recvPacket = NTPPacket(version=4, mode=3) # we know this is version 4, client mode
recvPacket.from_data(data)
timeStamp_high,timeStamp_low = recvPacket.GetTxTimeStamp()
sendPacket = NTPPacket(version=3,mode=4)
sendPacket = NTPPacket(version=4,mode=4) # version 4, server mode
sendPacket.stratum = 2
sendPacket.poll = 10
'''
Expand All @@ -281,35 +296,47 @@ def run(self):
sendPacket.root_dispersion = 0x0aa7
sendPacket.ref_id = 0x808a8c2c
'''
sendPacket.ref_timestamp = recvTimestamp-5
sendPacket.ref_timestamp = recvTimestamp-5 # pretend the clock was updated slightly before
sendPacket.SetOriginTimeStamp(timeStamp_high,timeStamp_low)
sendPacket.recv_timestamp = recvTimestamp
sendPacket.tx_timestamp = system_to_ntp_time(time.time())
socket.sendto(sendPacket.to_data(),addr)
print "Sended to %s:%d" % (addr[0],addr[1])
except Queue.Empty:

# for testing: we base the tx_timestamp on the rcvTimestamp, which was purposedly offset
# so we simulate an NTP server which is offset from the client
# Then we expect the client to gradually align
# 1 second of processing delay
sendPacket.tx_timestamp = recvTimestamp + 1 # system_to_ntp_time(time.time()+self.delay)

print ('TT = %d' % sendPacket.tx_timestamp) # Transmit Timestamp
self.socket.sendto(sendPacket.to_data(),addr)
print ("Sent packet to %s:%d" % (addr[0],addr[1]))


except queue.Empty:
continue


listenIp = "0.0.0.0"
listenPort = 123
socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
socket.bind((listenIp,listenPort))
print "local socket: ", socket.getsockname();
recvThread = RecvThread(socket)
recvThread.start()
workThread = WorkThread(socket)
workThread.start()

while True:
try:
time.sleep(0.5)
except KeyboardInterrupt:
print "Exiting..."
stopFlag = True
recvThread.join()
workThread.join()
#socket.close()
print "Exited"
break



if __name__=="__main__":


listenIp = "0.0.0.0"
listenPort = 123
socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
socket.bind((listenIp,listenPort))
print ("local socket: %s " % str(socket.getsockname()))

recvThread = RecvThread(socket, 240) # Use 240 secs offset for testing

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a breaking change here? testing code should not appear in pr IMHO,

recvThread.start()
workThread = WorkThread(socket)
workThread.start()

while True:
try:
time.sleep(0.5)
except KeyboardInterrupt:
print ("Exiting...")
stopFlag = True
recvThread.join()
workThread.join()

print ("Exited")
break