-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathanalyse_frequency.py
More file actions
103 lines (87 loc) · 3.44 KB
/
analyse_frequency.py
File metadata and controls
103 lines (87 loc) · 3.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
"""
Signal Frequency Analysis for Home Assistant histopry graph CSV files
========================================================
This script analyzes the frequency components of error signals exported from Home Assistant's history graph.
- GUI file picker for CSV selection
- Handles Home Assistant history graph CSV format (columns: entity_id, state, last_changed)
- Plots error signal and its spectrum
- Prints top 5 dominant frequency components
"""
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.fft import fft, fftfreq
import tkinter as tk
from tkinter import filedialog, messagebox
import os
def main():
# GUI to select CSV file
root = tk.Tk()
root.withdraw() # Hide the main window
file_path = filedialog.askopenfilename(
title="Select Home Assistant history graph CSV",
filetypes=[("CSV Files", "*.csv"), ("All Files", "*")],
)
# Try to use example.csv if no file is selected
if not file_path:
example_path = os.path.join(os.path.dirname(__file__), "example.csv")
if os.path.exists(example_path):
use_example = messagebox.askyesno(
"Use Example?", "No file selected. Use example.csv instead?"
)
if use_example:
file_path = example_path
else:
messagebox.showerror(
"No file selected", "No CSV file was selected. Exiting."
)
exit()
else:
messagebox.showerror(
"No file selected",
"No CSV file was selected and example.csv not found. Exiting.",
)
exit()
# Load the error signal from CSV
# Adjusted to handle CSV with columns: entity_id,state,last_changed
try:
df = pd.read_csv(file_path)
if not all(col in df.columns for col in ["state", "last_changed"]):
raise ValueError("CSV must contain 'state' and 'last_changed' columns.")
signal = df["state"].astype(float).values
timestamps = pd.to_datetime(df["last_changed"])
time_seconds = (timestamps - timestamps.iloc[0]).dt.total_seconds().values
except Exception as e:
messagebox.showerror("CSV Error", f"Failed to load or parse CSV: {e}")
exit()
# Compute average sample interval (dt)
dt = np.mean(np.diff(time_seconds))
# Remove the t=0 point from time and signal arrays
if time_seconds[0] == 0:
time_seconds = time_seconds[1:]
signal = signal[1:]
# Plot the raw error signal and frequency spectrum side by side
fig, axs = plt.subplots(1, 2, figsize=(14, 4))
axs[0].plot(time_seconds, signal)
axs[0].set_title("PID Error Signal")
axs[0].set_xlabel("Time (s)")
axs[0].set_ylabel("Error")
# Frequency analysis with correct dt
N = len(signal)
freqs = fftfreq(N, dt)
fft_values = fft(signal)
idx = np.where(freqs >= 0)
axs[1].plot(freqs[idx], np.abs(fft_values[idx]))
axs[1].set_title("Frequency Spectrum of Error Signal")
axs[1].set_xlabel("Frequency (Hz)")
axs[1].set_ylabel("Amplitude")
plt.tight_layout()
plt.show()
# Print dominant frequency components
amplitudes = np.abs(fft_values[idx])
dominant_indices = np.argsort(amplitudes)[-5:][::-1]
print("Top 5 dominant frequencies:")
for i in dominant_indices:
print(f"Frequency: {freqs[idx][i]:.4f} Hz, Amplitude: {amplitudes[i]:.2f}")
if __name__ == "__main__":
main()