1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
#!/usr/bin/env python
#
# ntpshmviz - graph the drift of NTP servers
# Written by Keane Wolter <daemoneye2@gmail.com>
#
# pystripchart can be found at
# https://sourceforge.net/projects/jstripchart
#
# To do:
#
# 1. Try using an impulse rather than line plot - this is bursty noise, not
# really a contour.
#
import gtk, stripchart, sys
# need numpy for float128, normal python floats are too small to hold a timespec
import numpy
class ntpOffset:
def __init__(self, stream):
# Initialize the class
# create the GUI for the application
self.create_GUI()
# get the data
self.read_data(stream)
self.create_StripTableau()
self.display_StripTableau()
# enter the GTK main loop
gtk.main()
def create_GUI(self):
# Creates the gui for the class
# create a standard top-level GTK window
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_title("NTP Offset")
self.window.connect("destroy", gtk.mainquit)
self.window.set_default_size(700, 400)
self.window.show()
# create a VBox to hold all the top-level GUI items
self.vbox = gtk.VBox()
self.vbox.show()
self.window.add(self.vbox)
# create the toolbar
self.create_toolbar()
def create_StripTableau(self):
# create the striptable widget
# gtk.Adjustment(value, lower, upper, step_incr, page_incr, page_size)
hadj = gtk.Adjustment(0, 0, self.lines, 1, 1, self.lines * 0.5)
sel = gtk.Adjustment(0)
self.striptableau = stripchart.StripTableau(hadj, sel)
self.striptableau.metawidth = 80
self.striptableau.gradewidth = 80
self.vbox.pack_end(self.striptableau.widget, gtk.TRUE, gtk.TRUE)
def create_toolbar(self):
# Create toolbar for zoom and quit buttons
self.toolbar = gtk.Toolbar()
self.toolbar.show()
# Zoom buttons
self.toolbar.insert_stock(gtk.STOCK_ZOOM_IN, "Zoom in", None,
lambda b, w: self.striptableau.zoomIn(), self.window, -1)
self.toolbar.insert_stock(gtk.STOCK_ZOOM_OUT, "Zoom out", None,
lambda b, w: self.striptableau.zoomOut(), self.window, -1)
self.toolbar.insert_stock(gtk.STOCK_ZOOM_FIT, "Zoom fit", None,
lambda b, w: self.striptableau.zoomSel(), self.window, -1)
# Quit button
self.toolbar.insert_stock(gtk.STOCK_QUIT, "Quit", None,
lambda b, w: gtk.mainquit(), self.window, -1)
# Put the toolbar into a HandleBox
self.handlebox = gtk.HandleBox()
self.handlebox.show()
self.handlebox.add(self.toolbar)
# Pack the toolbar into the main window
self.vbox.pack_start(self.handlebox, gtk.FALSE)
def display_StripTableau(self):
# display the graph of each ntp_unit
for ntp_unit in self.ntp_data:
# gtk.Adjustment(value, lower, upper, step_incr, page_incr,
# page_size)
spread = self.ntp_upper[ntp_unit] - self.ntp_lower[ntp_unit]
# prevent divide by zero
if spread == 0:
spread = 1
vadj_ntp = gtk.Adjustment(
self.ntp_lower[ntp_unit], # initial value
self.ntp_lower[ntp_unit] - 0.001, # lower extreme
self.ntp_upper[ntp_unit] + 0.001, # upper extreme
1 / spread, # step_incr
spread, # page_incr
spread) # page size
ntp_item = self.striptableau.addChannel(self.ntp_data[ntp_unit],
vadj_ntp)
ntp_item.name = ntp_unit
def read_data(self, stream):
# Reads data from a ntp log file. Layout is:
#
# - The keyword "sample"
# - The NTP unit from which it was collected.
# - Collection time of day, expressed in seconds
# - Receiver time of day, expressed in seconds
# - Clock time of day, expressed in seconds
# - Leep-second notification status
# - Source precision (log(2) of source jitter)
self.ntp_data = {} # data sets for each ntp unit
record = [] # A single record in the file or data stream
line_counts = {} # Count of total lines for each ntp unit
self.lines = 0 # width of graph
self.ntp_upper = {} # Upper limit of the data set
self.ntp_lower = {} # Lower limit of the data set
offset = 0 # offset value to add to the array
self.ntp_vadj = {} # vertical adjustment for each graph
for line in stream:
if len(line.split(' ')) > 6:
if line[:1] != '#':
line = line.lstrip()
record = line.split(' ')
offset = numpy.float128(record[3]) - numpy.float128(record[4])
# If the NTP unit is in the dictionary
# append the offset to the list
# Otherwise, create a new list in the dictionary
# and add the offset.
if record[1] not in self.ntp_data:
self.ntp_data[record[1]] = []
line_counts[record[1]] = 0
self.ntp_upper[record[1]] = round(offset, 9)
self.ntp_lower[record[1]] = round(offset, 9)
self.ntp_data[record[1]].append(offset)
line_counts[record[1]] += 1
# Update the bounds of the NTP unit if needed
if offset > self.ntp_upper[record[1]]:
self.ntp_upper[record[1]] = round(offset, 9)
if offset < self.ntp_lower[record[1]]:
self.ntp_lower[record[1]] = round(offset, 9)
# Update the max record count if needed
if line_counts[record[1]] > self.lines:
self.lines = line_counts[record[1]]
stream.close()
if __name__ == "__main__":
ntpOffset(sys.stdin)
sys.exit()
|