ntpshmviz.py.in 4.4 KB

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