Files
2025-10-30 00:56:31 -06:00

103 lines
3.5 KiB
Python

# stolen from https://austinsnerdythings.com/2025/02/14/revisiting-microsecond-accurate-ntp-for-raspberry-pi-with-gps-pps-in-2025/
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetime
def parse_chrony_stats(file_path):
"""
Parse chrony statistics log file and return a pandas DataFrame
"""
# read file contents first
with open(file_path, 'r') as f:
file_contents = f.readlines()
# for each line, if it starts with '=' or ' ', skip it
file_contents = [line for line in file_contents if not line.startswith('=') and not line.startswith(' ')]
# exclude lines that include 'PPS'
file_contents = [line for line in file_contents if 'PPS' not in line]
# Use StringIO to create a file-like object from the filtered contents
from io import StringIO
csv_data = StringIO(''.join(file_contents))
# Read the filtered data using pandas
df = pd.read_csv(csv_data,
delim_whitespace=True,
names=['Date', 'Time', 'IP_Address', 'Std_dev', 'Est_offset', 'Offset_sd',
'Diff_freq', 'Est_skew', 'Stress', 'Ns', 'Bs', 'Nr', 'Asym'])
# Combine Date and Time columns into a datetime column
df['timestamp'] = pd.to_datetime(df['Date'] + ' ' + df['Time'])
return df
def plot_est_offset(df):
"""
Create a plot of Est_offset vs time for each IP address
"""
plt.figure(figsize=(12, 6))
# Plot each IP address as a separate series
for ip in df['IP_Address'].unique():
ip_data = df[df['IP_Address'] == ip]
plt.plot(ip_data['timestamp'], ip_data['Est_offset'],
marker='o', label=ip, linestyle='-', markersize=4)
plt.xlabel('Time')
plt.ylabel('Estimated Offset (seconds)')
plt.title('Chrony Estimated Offset Over Time by IP Address')
plt.legend()
plt.grid(True)
# Rotate x-axis labels for better readability
plt.xticks(rotation=45)
# Adjust layout to prevent label cutoff
plt.tight_layout()
return plt
def analyze_chrony_stats(file_path):
"""
Main function to analyze chrony statistics
"""
# Parse the data
df = parse_chrony_stats(file_path)
# Create summary statistics
summary = {
'IP Addresses': df['IP_Address'].nunique(),
'Time Range': f"{df['timestamp'].min()} to {df['timestamp'].max()}",
'Average Est Offset by IP': df.groupby('IP_Address')['Est_offset'].mean().to_dict(),
'Max Est Offset by IP': df.groupby('IP_Address')['Est_offset'].max().to_dict(),
'Min Est Offset by IP': df.groupby('IP_Address')['Est_offset'].min().to_dict(),
'Median Est Offset by IP': df.groupby('IP_Address')['Est_offset'].median().to_dict()
}
# Create the plot
plot = plot_est_offset(df)
return df, summary, plot
# Example usage
if __name__ == "__main__":
file_path = "chrony_statistics.log" # Replace with your file path
df, summary, plot = analyze_chrony_stats(file_path)
# Print summary statistics
print("\nChrony Statistics Summary:")
print("-" * 30)
print(f"Number of IP Addresses: {summary['IP Addresses']}")
print(f"Time Range: {summary['Time Range']}")
print("\nAverage Estimated Offset by IP:")
for ip, avg in summary['Average Est Offset by IP'].items():
print(f"{ip}: {avg:.2e}")
print("\nMedian Estimated Offset by IP:")
for ip, median in summary['Median Est Offset by IP'].items():
print(f"{ip}: {median:.2e}")
# Show the plot
plt.show()