summaryrefslogtreecommitdiff
path: root/contrib/ntpshmviz
blob: 17c754d4cd488f4be406b3a690be8749ac431d53 (plain)
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
#!/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

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]
            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 = (float(record[3]) - float(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, 5)
                        self.ntp_lower[record[1]] = round(offset, 5)

                    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, 5)
                    if offset < self.ntp_lower[record[1]]:
                        self.ntp_lower[record[1]] = round(offset, 5)

                    # 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()