diff options
author | Keane Wolter <daemoneye2@gmail.com> | 2015-03-02 14:44:32 -0500 |
---|---|---|
committer | Eric S. Raymond <esr@thyrsus.com> | 2015-03-02 14:44:32 -0500 |
commit | 85227fede4320e67b824693060e62b6203ed377a (patch) | |
tree | dbf7b9737c644ef6db0089fd8749b13b378555ed /contrib/ntpshmviz | |
parent | e461672f3b73957fd83877169134fa88ce87d067 (diff) | |
download | gpsd-85227fede4320e67b824693060e62b6203ed377a.tar.gz |
First cut at NTP offset visualization utility.
Diffstat (limited to 'contrib/ntpshmviz')
-rwxr-xr-x | contrib/ntpshmviz | 178 |
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) |