Devuan fork of gpsd
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

136 lines
4.2 KiB

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