#!/usr/bin/env python3 # # Copyright (c) 2018, Intel Corporation # # SPDX-License-Identifier: BSD-3-Clause # # Expected input file format is a CSV file with: # # <FRAME_NUMBER, FRAME_ARRIVAL_TIME, FRAME_PAYLOAD_BYTES> # E.g.: # 1,1521534608.000000456,00:38:89:bd:a1:93:1d:15:(...) # 2,1521534608.001000480,00:38:89:bd:a1:93:1d:15:(...) # # Frame number: sequence number for each frame # Frame arrival time: Rx HW timestamp for each frame # Frame Payload: payload starting with 64bit timestamp (txtime) # # This can be easily generated with tshark with the following command line: # $ tshark -r CAPTURE.pcap -t e -E separator=, -T fields -e frame.number \ # -e frame.time_epoch \ # -e data.data > DATA.out # import argparse import csv import struct import math import sys # TAI to UTC offset. Currently that is 37 seconds. TAI_OFFSET = 37000000000 def compute_offsets_stats(file_path): with open(file_path) as f: count = mean = total_sqr_dist = 0.0 min_t = sys.maxsize max_t = -sys.maxsize for line in csv.reader(f): arrival_tstamp = int(line[1].replace('.', '')) data = line[2].split(':') txtime = ''.join(data[0:8]) txtime = bytearray.fromhex(txtime) txtime = struct.unpack('<Q', txtime) val = float(arrival_tstamp - txtime[0]) val = (val - TAI_OFFSET) if val > TAI_OFFSET else val # Update statistics. # Compute the mean and variance online using Welford's algorithm. count += 1 min_t = val if val < min_t else min_t max_t = val if val > max_t else max_t delta = val - mean mean = mean + (delta / count) new_delta = val - mean total_sqr_dist += delta * new_delta if count != 0.0: variance = total_sqr_dist / (count - 1) std_dev = math.sqrt(variance) print("min:\t\t%e" % min_t) print("max:\t\t%e" % max_t) print("jitter (pk-pk):\t%e" % (max_t - min_t)) print("avg:\t\t%e" % mean) print("std dev:\t%e" % std_dev) print("count:\t\t%d" % count) def main(): parser = argparse.ArgumentParser() parser.add_argument( '-f', dest='file_path', default=None, type=str, help='Path to input file (e.g. DATA.out) generated by tshark with:\ tshark -r CAPTURE.pcap -t e -E separator=, -T\ fields -e frame.number -e frame.time_epoch\ -e data.data > DATA.out') args = parser.parse_args() if args.file_path is not None: compute_offsets_stats(args.file_path) else: parser.print_help() if __name__ == "__main__": main()