ntpshmviz.in 4.2 KB

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