From a6163888f3c56123b1db313743c6147ba498732c Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Fri, 8 Aug 2014 14:42:07 +0300 Subject: Add third_party libs --- third_party/ptpd-1.1.0/tools/filter_response.m | 29 ++++ third_party/ptpd-1.1.0/tools/offset_stats.m | 102 ++++++++++++ third_party/ptpd-1.1.0/tools/ptp_plot.py | 170 +++++++++++++++++++ third_party/ptpd-1.1.0/tools/quality_correlate.py | 190 ++++++++++++++++++++++ 4 files changed, 491 insertions(+) create mode 100644 third_party/ptpd-1.1.0/tools/filter_response.m create mode 100644 third_party/ptpd-1.1.0/tools/offset_stats.m create mode 100644 third_party/ptpd-1.1.0/tools/ptp_plot.py create mode 100644 third_party/ptpd-1.1.0/tools/quality_correlate.py (limited to 'third_party/ptpd-1.1.0/tools') diff --git a/third_party/ptpd-1.1.0/tools/filter_response.m b/third_party/ptpd-1.1.0/tools/filter_response.m new file mode 100644 index 0000000..5c20163 --- /dev/null +++ b/third_party/ptpd-1.1.0/tools/filter_response.m @@ -0,0 +1,29 @@ +#!/usr/bin/octave -qf + +# the IIR filter +s = 2^4; +a = [ s -(s-1) ]; +b = [ 1/2 1/2 ]; + +[h w] = freqz(b, a, 100000); + +subplot(211); +plot(w/pi,abs(h),";;"); +axis(); +#plot(w/pi,20*log(abs(h))); +#axis([0 1 -120 0]); +#semilogx(w/pi,20*log(abs(h)),";;"); +#axis([1e-3 1 -120 0]); +ylabel("gain"); + +subplot(212); +plot(w/pi,unwrap(angle(h)),";;"); +axis([0 1 -pi 0]); +#semilogx(w/pi,unwrap(angle(h)),";;"); +#axis([1e-3 1 -pi 0]); +ylabel("phase (rad)"); +xlabel("frequency"); +replot; + +pause; + diff --git a/third_party/ptpd-1.1.0/tools/offset_stats.m b/third_party/ptpd-1.1.0/tools/offset_stats.m new file mode 100644 index 0000000..26af4c2 --- /dev/null +++ b/third_party/ptpd-1.1.0/tools/offset_stats.m @@ -0,0 +1,102 @@ +#!/usr/bin/octave -qf + +printf("start\n"); + +load t; +load tr; + +# time window to analyze +t_beg = 1; +t_fin = length(t); + +# parameters for synthetic reference time +#start_time = round(t(t_beg)); +#sample_interval = 1; + +# parameters for histogram +h_beg = -15e-6; +h_fin = 15e-6; +h_sz = 1e-6; + +# parameters for allan variance +# tune for your computational power +tau_beg = 1; +tau_fin = length(t)/10; +tau_maxsamps = 100; + +# ---------- + +printf("data loaded\n"); + +# create synthetic reference time +#tr = t; +#tr(t_beg) = start_time; +#for k = (t_beg+1):t_fin +# +# tr(k) = tr(k-1) + sample_interval; +# +#endfor + +# time offset computation +o = t - tr; + +o_min = min(o(t_beg:t_fin)); +o_max = max(o(t_beg:t_fin)); +o_mean = mean(o(t_beg:t_fin)); + +# relative tick rate computation +r = t; +for k = (t_beg+1):t_fin + + r(k) = o(k) - o(k-1); + +endfor + +r_min = min(r((t_beg+1):t_fin)); +r_max = max(r((t_beg+1):t_fin)); +r_mean = mean(r((t_beg+1):t_fin)); + +figure; +h_bins = h_beg:h_sz:h_fin; +hist( o(t_beg:t_fin), h_bins, 1); + +printf("histogram plotted\n"); + +figure; +subplot(211); +plot( t_beg:t_fin, o(t_beg:t_fin), "r;time offset;", + [t_beg t_fin], [o_min o_min], "g;;", + [t_beg t_fin], [o_max o_max], "g;;", + [t_beg t_fin], [o_mean o_mean], "b;;"); +subplot(212); +plot( (t_beg+1):t_fin, r((t_beg+1):t_fin), "r;relative tick rate;", + [(t_beg+1) t_fin], [r_min r_min], "g;;", + [(t_beg+1) t_fin], [r_max r_max], "g;;", + [(t_beg+1) t_fin], [r_mean r_mean], "b;;") +replot; + +printf("offset plotted\n"); + +# the allan vaiance computation from the IEEE 1588 spec +a = t; +for tau = tau_beg:tau_fin + + beg = t_beg; + if (t_fin-2*tau) > tau_maxsamps + fin = tau_maxsamps; + else + fin = (t_fin-2*tau); + end + + a(tau) = sum((t(beg:fin) - 2*t((beg+tau):(fin+tau)) + t((beg+2*tau):(fin+2*tau))).^2); + a(tau) /= 2*(fin-beg)*(tau^2); + +endfor + +figure; +loglog( tau_beg:tau_fin, a(tau_beg:tau_fin), ";allan variance;"); +replot; + +printf("variance plotted\n"); +input("done\n"); + diff --git a/third_party/ptpd-1.1.0/tools/ptp_plot.py b/third_party/ptpd-1.1.0/tools/ptp_plot.py new file mode 100644 index 0000000..7c43dec --- /dev/null +++ b/third_party/ptpd-1.1.0/tools/ptp_plot.py @@ -0,0 +1,170 @@ +#! /usr/bin/env python2.6 +# Copyright (c) 2010, Neville-Neil Consulting +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# Neither the name of Neville-Neil Consulting nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Author: George V. Neville-Neil +# +# Description: + +"""ptp_plot.py -- Plot PTP delays reported by the slave. + +This program takes a ptp log file generated by the slave +and plots various times on a graph + +This program requires at least python2.6 as well as numpy +and gnuplot support. + +""" + +import csv +import datetime +import subprocess +import sys +import tempfile + +from numpy import * + +import Gnuplot, Gnuplot.funcutils + +def usage(): + sys.exit() + +def main(): + + from optparse import OptionParser + + parser = OptionParser() + parser.add_option("-a", "--all", dest="all", default=0, + help="show all entries") + parser.add_option("-t", "--type", dest="type", default="delay", + help="plot the delay or offset") + parser.add_option("-l", "--logfile", dest="logfile", default=None, + help="logfile to use") + parser.add_option("-s", "--start", dest="start", default="09:30:00", + help="start time") + parser.add_option("-e", "--end", dest="end", default="16:30:00", + help="end time") + parser.add_option("-r", "--roll", dest="roll", type=int, default=0, + help="number of days to roll at the start") + parser.add_option("-p", "--print", dest="png", default=None, + help="file to print the graph to") + parser.add_option("-y", "--ymin", dest="ymin", default="0.000000", + help="minimum y value") + parser.add_option("-Y", "--ymax", dest="ymax", default="0.001000", + help="maximum y value") + parser.add_option("-S", "--save", dest="save", default=None, + help="save file name") + + + (options, args) = parser.parse_args() + + if ((options.type != "delay") and (options.type != "offset")): + print "You must choose either delay or offset." + usage() + + try: + logfile = csv.reader(open(options.logfile, "rb")) + except: + print "Could not open %s" % options.logfile + sys.exit() + + # + # This is an ugly hack, but it turns out that gnuplot + # is better able to plot time data if we write it out + # in the familiar format to a temporary file and + # then plot from the file rather than building up + # arrays of data. + # + tmpfile = tempfile.NamedTemporaryFile() + + savefile = None + + if (options.save != None): + savefile = open(options.save, "w") + + + first = True + for line in logfile: + # Split off the microseconds + try: + dt = line[0].rpartition(':')[0] + except: + continue + now = datetime.datetime.strptime(dt, "%Y-%m-%d %H:%M:%S") + if (first == True): + if (options.all == 0): + start = datetime.datetime.strptime(options.start, "%H:%M:%S") + else: + start = now + start = start.replace(year=now.year, month=now.month, + day=now.day + options.roll) + end = datetime.datetime.strptime(options.end, "%H:%M:%S") + end = end.replace(year=now.year, month=now.month, + day=now.day + options.roll) + first = False + if ((now > end) and (options.all == 0)): + break + if ((now > start) or (options.all != 0)): + if (options.type == "delay"): + tmpfile.write("%s %f\n" % (dt, float(line[3]))) + if (savefile != None): + savefile.write("%s %f\n" % (dt, float(line[3]))) + else: + tmpfile.write("%s %f\n" % (dt, float(line[4]))) + if (savefile != None): + savefile.write("%s %f\n" % (dt, float(line[4]))) + + plotter = Gnuplot.Gnuplot(debug=1) + plotter('set data style dots') + if (options.type == "delay"): + plotter.set_range('yrange', [options.ymin, options.ymax]) + plotter.ylabel('Seconds\\nOne Way Delay') + else: + plotter.set_range('yrange', [options.ymin, options.ymax]) + plotter.ylabel('Seconds\\nOffset') + if (options.all == 0): + plotter.xlabel(options.logfile + " " + options.start + " - " + options.end) + else: + plotter.xlabel(options.logfile + " " + str(start) + " - " + str(now)) + plotter('set xdata time') + plotter('set timefmt "%Y-%m-%d %H:%M:%S"') + + tmpfile.flush() + plotter.plot(Gnuplot.File(tmpfile.name, using='1:3')) + + if (options.png != None): + plotter.hardcopy(options.logfile + "-" + options.type + ".png", terminal='png') + raw_input('Press return to exit') + else: + raw_input('Press return to exit') + + +if __name__ == "__main__": + main() diff --git a/third_party/ptpd-1.1.0/tools/quality_correlate.py b/third_party/ptpd-1.1.0/tools/quality_correlate.py new file mode 100644 index 0000000..cea5dd0 --- /dev/null +++ b/third_party/ptpd-1.1.0/tools/quality_correlate.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python2.6 +# Copyright (c) 2010, Neville-Neil Consulting +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# Neither the name of Neville-Neil Consulting nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Author: George V. Neville-Neil +# +# Description: This program reads a pair of PTPd quality files, +# generated with the -R option, correlates and graphs the differences +# between the time each host saw a SYNC which is an easy way to +# measure the quality of time synchronization between two clients +# a timing service. + +import sys + +from numpy import * + +import Gnuplot, Gnuplot.funcutils + +import gzip +import os + +def main(): + + from optparse import OptionParser + + parser = OptionParser() + parser.add_option("-y", "--ymin", dest="ymin", default=0, + help="minimum y value") + parser.add_option("-Y", "--ymax", dest="ymax", default=1000000, + help="maximum y value") + parser.add_option("-N", "--Names", dest="hosts", nargs=2, default=None, + help="host list for sync graph") + parser.add_option("-s", "--start", dest="start", type="int", default=0, + help="starting sequence number") + parser.add_option("-p", "--print", dest="png", default=None, + help="print the graph to a file") + parser.add_option("-o", "--output", dest="output", default=None, + help="save the correlated data to a file.") + parser.add_option("-d", "--debug", dest="debug", type="int", default=0, + help="print debugging info (verbose)") + (options, args) = parser.parse_args() + + if (options.output != None): + try: + outfile = open(options.output, "w") + except: + print "cannot open %s for writing" % options.output + + files = [] + for filename in options.hosts: + if (os.path.splitext(filename)[1] == '.gz'): + file = gzip.open(filename) + else: + file = open(filename) + + trace = {} + done = False + while not done: + try: + (seq, ts) = file.readline().split() + sequence = int(seq) + timestamp = long(ts) + except StopIteration: + done = True + except: + break + + trace[sequence] = timestamp + + files.append(trace) + + sequences = [] + sequences.append(sorted(files[0].keys())) + sequences.append(sorted(files[1].keys())) + + if ((min(sequences[0]) > max(sequences[1])) or + (min(sequences[1]) > max(sequences[1]))): + print "sequences do not overlap" + sys.exit(1) + + # Trim the dictionaries of non overlapping elements + + index = 0 + if ((min(sequences[0]) < min(sequences[1]))): + start = min(sequences[0]) + end = min(sequences[1]) + index = 0 + else: + start = min(sequences[1]) + end = min(sequences[0]) + index = 1 + + for i in range(start, end): + del files[index][i] + + if ((max(sequences[0]) > max(sequences[1]))): + start = max(sequences[1]) + end = max(sequences[0]) + index = 0 + else: + start = max(sequences[0]) + end = max(sequences[1]) + index = 1 + + for i in range(start, end): + del files[index][i] + + if options.start == 0: + options.start = min(files[0].keys()) + + graph = [] + + minimum = sys.maxint + maximum = -sys.maxint -1 + + for i in range(options.start,options.start + len(files[0])): + try: + delta = abs(files[1][i] - files[0][i]) + except KeyError: + print "9:99:99.000900" + print "missing packet %d" % i + continue + + if delta > maximum: + maximum = delta + if delta < minimum: + minimum = delta + + if (options.output != None): + outfile.write(("%d %d\n" % (delta, (files[0][i] / 1000000000)))) + + graph.append(delta) + + if (options.output != None): + outfile.close() + + print "min %d, max %d" % (minimum, maximum) + + # if (minimum.seconds > 1): + # print "Time difference exceeded one second maximum, " \ + # "cannot graph differences" + # sys.exit(1) + + plotter = Gnuplot.Gnuplot(debug=1) + + if ((options.ymin != 0) or (options.ymax != 10)): + plotter.set_range('yrange', [options.ymin, options.ymax]) + + plotter.ylabel('Time Difference\\nNanoseconds') + plotter.xlabel('Sample Number') + plotter.plot(graph) + + if (options.png != None): + plotter.hardcopy(options.png + ".png", terminal='png') + raw_input('Press return to exit') + else: + raw_input('Press return to exit') + +# The canonical way to make a python module into a script. +# Remove if unnecessary. + +if __name__ == "__main__": + main() -- cgit v1.3.1