summaryrefslogtreecommitdiff
path: root/contrib/ntpshmviz
diff options
context:
space:
mode:
authorKeane Wolter <daemoneye2@gmail.com>2015-03-02 14:44:32 -0500
committerEric S. Raymond <esr@thyrsus.com>2015-03-02 14:44:32 -0500
commit85227fede4320e67b824693060e62b6203ed377a (patch)
treedbf7b9737c644ef6db0089fd8749b13b378555ed /contrib/ntpshmviz
parente461672f3b73957fd83877169134fa88ce87d067 (diff)
downloadgpsd-85227fede4320e67b824693060e62b6203ed377a.tar.gz
First cut at NTP offset visualization utility.
Diffstat (limited to 'contrib/ntpshmviz')
-rwxr-xr-xcontrib/ntpshmviz178
1 files changed, 178 insertions, 0 deletions
diff --git a/contrib/ntpshmviz b/contrib/ntpshmviz
new file mode 100755
index 00000000..7969772a
--- /dev/null
+++ b/contrib/ntpshmviz
@@ -0,0 +1,178 @@
+#!/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
+#
+
+import array, gtk, stripchart, sys
+
+class ntpDrift:
+ def __init__(self, filename):
+ # Initialize the class
+
+ # get the data
+ self.read_data(filename)
+
+ # create the GUI for the application
+ self.create_GUI()
+
+ # 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 Drift")
+ 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 StripTableau and add the data to it
+ self.create_StripTableau()
+
+ # 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, 1, 1, 1, self.lines)
+ sel = gtk.Adjustment(-1)
+ self.striptableau = stripchart.StripTableau(hadj, sel)
+ self.striptableau.metawidth = 120
+ self.striptableau.gradewidth = 100
+ self.vbox.pack_end(self.striptableau.widget, gtk.TRUE, gtk.TRUE)
+
+ # Add the channel for NTP2
+ # adjust the size of the graph for NTP2 to allow all the data
+ # to fit within the graph
+ vadj_ntp2 = gtk.Adjustment(self.ntp2_lower, self.ntp2_lower-0.1, self.ntp2_upper+0.1, 0.1, 0, self.ntp2_upper+0.1)
+ ntp2_item = self.striptableau.addChannel(self.ntp2, vadj_ntp2)
+ ntp2_item.name = "NTP2"
+ ntp2_item.meta = self.create_text("NTP2 Drift Values")
+
+ # add the channel for NTP3
+ # adjust the size of the graph for NTP2 to allow all the data
+ # to fit within the graph
+ vadj_ntp3 = gtk.Adjustment(self.ntp3_lower-0.1, self.ntp3_lower-0.1, self.ntp3_upper+0.1, 0.1, 0, self.ntp3_upper+0.1)
+ ntp3_item = self.striptableau.addChannel(self.ntp3, vadj_ntp3)
+ ntp3_item.name = "NTP3"
+ ntp3_item.meta = self.create_text("NTP3 Drift Values")
+
+ def create_text(self, text):
+ # Creates a text widget to contain a description of a channel.
+ scrolled_window = gtk.ScrolledWindow()
+ scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scrolled_window.set_shadow_type(gtk.SHADOW_IN)
+
+ textview = gtk.TextView()
+ buffer = gtk.TextBuffer()
+ iter = buffer.get_iter_at_offset(0)
+ buffer.insert(iter, text)
+ textview.set_buffer(buffer)
+ textview.set_editable(gtk.TRUE)
+ textview.set_cursor_visible(gtk.TRUE)
+ textview.set_wrap_mode(gtk.WRAP_WORD)
+ textview.set_left_margin(5)
+ textview.set_right_margin(5)
+ textview.set_pixels_above_lines(5)
+ textview.set_pixels_below_lines(5)
+
+ scrolled_window.add(textview)
+ scrolled_window.show()
+
+ return scrolled_window
+
+ def create_toolbar(self):
+ # Create the toolbar
+ self.toolbar = gtk.Toolbar()
+ self.toolbar.show()
+
+ # add buttons for zoom and wire them to the StripTableau widget
+ 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)
+
+ # sit the toolbar inside a HandleBox so that it can be detacjed
+ 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 get_drift(self, data):
+ # get the difference between the clock time and receiver time of day
+ return (float(data.split(' ')[3]) - float(data.split(' ')[4]))
+
+ def read_data(self, filename):
+ # 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.ntp2 = array.array("d") # ntp2 array
+ self.ntp3 = array.array("d") # ntp3 array
+ self.lines = 0 # width of graph - set to the size of the largest array
+ self.ntp2_upper = 0 # highest value in ntp2 array
+ self.ntp2_lower = 0 # lowest value in ntp2 array
+ self.ntp3_upper = 0 # highest value in ntp3 array
+ self.ntp3_lower = 0 # lowest value in ntp3 array
+ drift = 0 # drift value to add to the array and to check the upper and lower bounds of the graph
+
+ with open(filename) as input_file:
+ for line in input_file:
+ if len(line.split(' ')) > 6:
+ if 'NTP2' in line:
+ drift = self.get_drift(line)
+ self.ntp2.append(drift)
+ if drift > self.ntp2_upper:
+ self.ntp2_upper = round(drift, 5)
+ if drift < self.ntp2_lower:
+ self.ntp2_lower = round(drift, 5)
+ if 'NTP3' in line:
+ drift = self.get_drift(line)
+ self.ntp3.append(drift)
+ if drift > self.ntp3_upper:
+ self.ntp3_upper = round(drift, 5)
+ if drift < self.ntp3_lower:
+ self.ntp3_lower = round(drift, 5)
+ input_file.close()
+
+ # Get the line count for the larger of the two arrays.
+ # This will set the width of the graph when it is displayed.
+ if len(self.ntp2) > len(self.ntp3):
+ self.lines = len(self.ntp2)
+ else:
+ self.lines = len(self.ntp3)
+
+# Run the class
+if __name__ == "__main__":
+ # If the user passes a file name, use that.
+ # Otherwise use the samples file.
+ if (len(sys.argv) < 2):
+ filename = "SAMPLES.txt"
+ else:
+ filename = sys.argv[1]
+
+ # instantiate the application
+ app = ntpDrift(filename)