ntpshmviz 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. #!/usr/bin/env python3
  2. '''
  3. ntpshmviz - graph the drift of NTP servers
  4. Written by Keane Wolter <daemoneye2@gmail.com>
  5. '''
  6. #
  7. # To do:
  8. #
  9. # 1. Add exit button so the user does not need to do <Ctrl>-w
  10. # 2. Allow for a continuous stream of data to be graphed
  11. #
  12. # This file is Copyright (c) 2010-2018 by the GPSD project
  13. # SPDX-License-Identifier: BSD-2-clause
  14. from __future__ import absolute_import, print_function, division
  15. import argparse
  16. import sys
  17. # need numpy for float128, normal python floats are too small to
  18. # hold a timespec
  19. import numpy
  20. try:
  21. import matplotlib.pyplot as PLT
  22. except ImportError:
  23. print("Please make sure matplotlib is installed properly:",
  24. sys.exc_info()[0])
  25. sys.exit(1)
  26. gps_version = '3.19.1~dev'
  27. class ntpOffset(object):
  28. "The master Class"
  29. def __init__(self, stream):
  30. "Initialize class ntpOffset"
  31. # get the data
  32. self.read_data(stream)
  33. # display the data
  34. self.display()
  35. def display(self):
  36. "display the graphs"
  37. # Alert the user that closing the graphs can be done with "Ctrl-w"
  38. print ("Please note that the graph can be closed with the "
  39. "key combination of <Ctrl-w>")
  40. PLT.figure()
  41. subplot_value = 211
  42. for ntp_item in self.ntp_data:
  43. # create the subplot for the data
  44. PLT.subplot(subplot_value)
  45. # setup and create the vlines graph
  46. t = numpy.arange(0, self.line_counts[ntp_item], 1)
  47. PLT.vlines(t, 0, self.ntp_data[ntp_item], color='r')
  48. # add labels
  49. PLT.title("NTP drift for " + ntp_item)
  50. PLT.xlabel('sample')
  51. PLT.ylabel('drift')
  52. # increment the subplot by 1.
  53. # this allows for each data group to have it's own graph
  54. subplot_value += 1
  55. # make sure there is no graph or data overlapping each other and
  56. # display the graph
  57. PLT.tight_layout()
  58. PLT.show()
  59. def read_data(self, stream):
  60. "Read data from a ntp log file."
  61. # Layout is:
  62. #
  63. # - The keyword "sample"
  64. # - The NTP unit from which it was collected.
  65. # - Collection time of day, expressed in seconds
  66. # - Receiver time of day, expressed in seconds
  67. # - Clock time of day, expressed in seconds
  68. # - Leep-second notification status
  69. # - Source precision (log(2) of source jitter)
  70. self.ntp_data = {} # data sets for each ntp unit
  71. self.line_counts = {} # Count of total lines for each ntp unit
  72. record = [] # A single record in the file or data stream
  73. offset = 0 # offset value to add to the array
  74. self.lines = 0 # width of graph
  75. for line in stream:
  76. if len(line.split(' ')) > 6:
  77. if line[:1] != '#':
  78. line = line.lstrip()
  79. record = line.split(' ')
  80. try:
  81. offset = (numpy.longdouble(record[3]) -
  82. numpy.longdouble(record[4]))
  83. except:
  84. print ("Invalid data: ", sys.exc_info()[0],
  85. ". Data was: ", line)
  86. continue
  87. # If the NTP unit is in the dictionary
  88. # append the offset to the list
  89. # Otherwise, create a new list in the dictionary
  90. # and add the offset.
  91. if record[1] not in self.ntp_data:
  92. self.ntp_data[record[1]] = []
  93. self.line_counts[record[1]] = 0
  94. self.ntp_data[record[1]].append(offset)
  95. self.line_counts[record[1]] += 1
  96. stream.close()
  97. def args():
  98. parser = argparse.ArgumentParser(description='NTP drift visualizer')
  99. parser.add_argument('-V', '--version',
  100. action='version',
  101. help='Get version of GPS project',
  102. version = "ntpshmviz: version" + str(gps_version))
  103. return parser.parse_args()
  104. if __name__ == "__main__":
  105. if len(sys.argv) >= 2:
  106. arguments = args()
  107. ntpOffset(sys.stdin)
  108. sys.exit()