Files
RiedelScripts/PTPLogger/PTPLogger.sh
T

175 lines
4.5 KiB
Bash

import re
import datetime as dt
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.lines import Line2D
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext
def parse_log(file_path):
t, rms, freq = [], [], []
state_events = []
master_events = []
last_timestamp = None
with open(file_path, encoding='utf-8', errors='ignore') as f:
for line in f:
ts_match = re.match(r'^(\w+ \d+ \d+:\d+:\d+)', line)
if ts_match:
try:
timestr = ts_match.group(1) + " 2026"
last_timestamp = dt.datetime.strptime(timestr, "%b %d %H:%M:%S %Y")
except:
pass
timestamp = last_timestamp
if not timestamp:
continue
m = re.search(r'rms\s+(\d+).*freq\s+([+-]?\d+)', line)
if m:
t.append(timestamp)
rms.append(int(m.group(1)))
freq.append(int(m.group(2)))
m = re.search(r'port \d+: (\w+) to (\w+)', line)
if m:
state_events.append((timestamp, f"{m.group(1)}→{m.group(2)}"))
m = re.search(
r'(selected best master clock|new foreign master)\s+([0-9a-fA-F\.:]+)',
line
)
if m:
master_events.append((timestamp, m.group(2)))
return t, rms, freq, state_events, master_events
def plot_data(t, rms, freq, state_events, master_events, detail):
if not t:
messagebox.showerror("Error", "No valid RMS/freq data found.")
return
fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True)
ax1.plot(t, rms, color='black')
ax1.set_ylabel("RMS (ns)")
ax1.grid()
ax2.plot(t, freq, color='black')
ax2.set_ylabel("Frequency deviation")
ax2.set_xlabel("Time")
ax2.grid()
# event lines
for ts, _ in state_events:
ax1.axvline(ts, color='blue', linestyle='--', alpha=0.7)
ax2.axvline(ts, color='blue', linestyle='--', alpha=0.7)
for ts, _ in master_events:
ax1.axvline(ts, color='red', linestyle='--', alpha=0.7)
ax2.axvline(ts, color='red', linestyle='--', alpha=0.7)
legend = [
Line2D([0], [0], color='black', label='RMS/Freq'),
Line2D([0], [0], color='blue', linestyle='--', label='State'),
Line2D([0], [0], color='red', linestyle='--', label='Master'),
]
ax1.legend(handles=legend)
multi_day = any(d.date() != t[0].date() for d in t)
if multi_day:
formatter = mdates.DateFormatter('%d %H:%M')
else:
formatter = mdates.DateFormatter('%H:%M:%S')
if detail == "Seconds":
locator = mdates.SecondLocator(interval=1)
elif detail == "5 sec":
locator = mdates.SecondLocator(interval=5)
elif detail == "10 sec":
locator = mdates.SecondLocator(interval=10)
elif detail == "30 sec":
locator = mdates.SecondLocator(interval=30)
elif detail == "1 min":
locator = mdates.MinuteLocator(interval=1)
else:
locator = mdates.AutoDateLocator(maxticks=20)
ax2.xaxis.set_major_locator(locator)
ax2.xaxis.set_major_formatter(formatter)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
def open_file():
file_path = filedialog.askopenfilename(
title="Select log",
filetypes=[("Log files", "*.log *.txt"), ("All files", "*.*")]
)
if not file_path:
return
detail = detail_var.get()
t, rms, freq, state_events, master_events = parse_log(file_path)
plot_data(t, rms, freq, state_events, master_events, detail)
event_box.delete(1.0, tk.END)
events = []
for ts, label in state_events:
events.append((ts, f"{ts} | STATE {label}"))
for ts, label in master_events:
events.append((ts, f"{ts} | MASTER {label}"))
events.sort(key=lambda x: x[0])
if events:
for _, line in events:
event_box.insert(tk.END, line + "\n")
else:
event_box.insert(tk.END, "No events found.\n")
# GUI
root = tk.Tk()
root.title("PTP Log Viewer")
root.geometry("750x550")
frame = tk.Frame(root)
frame.pack(pady=5)
tk.Button(frame, text="Open Log", command=open_file).pack(side=tk.LEFT, padx=10)
detail_var = tk.StringVar(value="Auto")
tk.OptionMenu(
frame,
detail_var,
"Auto",
"Seconds",
"5 sec",
"10 sec",
"30 sec",
"1 min"
).pack(side=tk.LEFT)
event_box = scrolledtext.ScrolledText(root)
event_box.pack(fill=tk.BOTH, expand=True)
root.mainloop()