summaryrefslogtreecommitdiff
path: root/contrib/ntpshmviz
blob: 684798f23701e9358a662aaf315ca74a3a4fcfc5 (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
#!/usr/bin/env python
'''
ntpshmviz - graph the drift of NTP servers
Written by Keane Wolter <daemoneye2@gmail.com>
'''
#
# To do:
#
# 1. Add exit button so the user does not need to do <Ctrl>-w
# 2. Allow for a continuous stream of data to be graphed
#
# This file is Copyright (c) 2010-2018 by the GPSD project
# SPDX-License-Identifier: BSD-2-clause

from __future__ import absolute_import, print_function, division

import sys
# need numpy for float128, normal python floats are too small to
# hold a timespec
import numpy
try:
    import matplotlib.pyplot as PLT
except ImportError:
    print("Please make sure matplotlib is installed properly:",
          sys.exc_info()[0])
    sys.exit(1)

gps_version = '3.19-dev'


class ntpOffset(object):
    "The master Class"

    def __init__(self, stream):
        "Initialize class ntpOffset"

        # get the data
        self.read_data(stream)

        # display the data
        self.display()

    def display(self):
        "display the graphs"

        # Alert the user that closing the graphs can be done with "Ctrl-w"
        print ("Please note that the graph can be closed with the "
               "key combination of <Ctrl-w>")

        PLT.figure()
        subplot_value = 211
        for ntp_item in self.ntp_data:
            # create the subplot for the data
            PLT.subplot(subplot_value)
            # setup and create the vlines graph
            t = numpy.arange(0, self.line_counts[ntp_item], 1)
            PLT.vlines(t, 0, self.ntp_data[ntp_item], color='r')
            # add labels
            PLT.title("NTP drift for " + ntp_item)
            PLT.xlabel('sample')
            PLT.ylabel('drift')
            # increment the subplot by 1.
            # this allows for each data group to have it's own graph
            subplot_value += 1

        # make sure there is no graph or data overlapping each other and
        # display the graph
        PLT.tight_layout()
        PLT.show()

    def read_data(self, stream):
        "Read 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
        self.line_counts = {}   # Count of total lines for each ntp unit
        record = []             # A single record in the file or data stream
        offset = 0              # offset value to add to the array
        self.lines = 0          # width of graph

        for line in stream:
            if len(line.split(' ')) > 6:
                if line[:1] != '#':
                    line = line.lstrip()
                    record = line.split(' ')
                    try:
                        offset = (numpy.longdouble(record[3]) -
                                  numpy.longdouble(record[4]))
                    except:
                        print ("Invalid data: ", sys.exc_info()[0],
                               ". Data was: ", line)
                        continue

                    # 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]] = []
                        self.line_counts[record[1]] = 0

                    self.ntp_data[record[1]].append(offset)
                    self.line_counts[record[1]] += 1
        stream.close()


if __name__ == "__main__":
    if len(sys.argv) >= 2:
        if (sys.argv[1] == "-V" or sys.argv[1] == "--version"):
            sys.stderr.write("ntpshmviz: Version %s\n" % gps_version)
            sys.exit(0)

        if (sys.argv[1] == "-h" or sys.argv[1] == "--help"):
            print("""
Usage: <input stream> | ntpshmviz [-h|--help] [-V|--version]

Example: ntpshmmon -n 100 | ntpshmviz

Options:
\t-h: help message
\t-V: version information

Report ntpshmviz bugs to Keane Wolter, daemoneye2@gmail.com
                      or gpsd-users@nongnu.org
gpsd homepage: www.catb.org/gpsd/
""")
            sys.exit()

    ntpOffset(sys.stdin)
    sys.exit()