Use the mainplot.py for interacting with the FC. type commands into the terminal and see the plot data on the screen.
This commit is contained in:
168
mainplot.py
Normal file
168
mainplot.py
Normal file
@@ -0,0 +1,168 @@
|
||||
|
||||
import Serial_decoder as SD
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
from datetime import datetime
|
||||
import time
|
||||
from collections import deque
|
||||
|
||||
# Matplotlib embedding in Tkinter
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||
|
||||
# === Serial threads (unchanged) ===
|
||||
tasks = SD.StartInputOutput() # t1: output_loop (daemon), t2: input_loop (daemon)
|
||||
FILENAME = f"SaveData/{datetime.today().strftime('%Y_%m_%d_%H_%M')}"
|
||||
|
||||
# === Plot history ===
|
||||
MAX_POINTS = 100 # rolling window length
|
||||
history_x = deque(maxlen=MAX_POINTS) # timestamps
|
||||
history_y = [deque(maxlen=MAX_POINTS) for _ in range(16)] # per-channel values
|
||||
|
||||
# === GUI setup ===
|
||||
root = tk.Tk()
|
||||
root.title("Live 4x4 Grid + Selectable Real-Time Plot")
|
||||
|
||||
# Left: 4x4 grid
|
||||
grid_frame = ttk.Frame(root)
|
||||
grid_frame.grid(row=0, column=0, padx=10, pady=10, sticky="n")
|
||||
|
||||
labels = []
|
||||
for row in range(4):
|
||||
for col in range(4):
|
||||
idx = row * 4 + col
|
||||
lbl = tk.Label(
|
||||
grid_frame, name=f"channel{idx}",
|
||||
text="0", width=10, height=3,
|
||||
borderwidth=2, relief="groove", font=("Arial", 14)
|
||||
)
|
||||
lbl.grid(row=row, column=col, padx=5, pady=5)
|
||||
labels.append(lbl)
|
||||
|
||||
# Last tick label (as you had)
|
||||
last_tick_lbl = tk.Label(
|
||||
grid_frame, text="Last Tick: -", width=20, height=3,
|
||||
borderwidth=2, relief="groove", font=("Arial", 14)
|
||||
)
|
||||
lbl = tk.Label(root, text="Last Tick:0", width=20, height=3, borderwidth=2, relief="groove", font=("Arial", 14))
|
||||
lbl.grid(row = 4, column=0, columnspan=2)
|
||||
labels.append(lbl)
|
||||
lbl = tk.Label(root, text="Delta Tick:0", width=20, height=3, borderwidth=2, relief="groove", font=("Arial", 14))
|
||||
lbl.grid(row=4, column=2, columnspan= 2)
|
||||
labels.append(lbl)
|
||||
|
||||
# Checkboxes to select plotted channels
|
||||
check_frame = ttk.LabelFrame(root, text="Plot Selection")
|
||||
check_frame.grid(row=1, column=0, padx=10, pady=(0, 10), sticky="nw")
|
||||
|
||||
check_vars = []
|
||||
for i in range(16):
|
||||
var = tk.IntVar(value=0)
|
||||
chk = ttk.Checkbutton(check_frame, text=f"Ch {i+1}", variable=var, command=lambda: update_plot())
|
||||
chk.grid(row=i//4, column=i%4, sticky="w", padx=4, pady=2)
|
||||
check_vars.append(var)
|
||||
|
||||
# Right: Matplotlib plot
|
||||
plot_frame = ttk.Frame(root)
|
||||
plot_frame.grid(row=0, column=1, rowspan=2, padx=10, pady=10, sticky="n")
|
||||
|
||||
fig = Figure(figsize=(6, 4), dpi=100)
|
||||
ax = fig.add_subplot(111)
|
||||
ax.set_title("Selected Channels (rolling)")
|
||||
ax.set_xlabel("Timestamp")
|
||||
ax.set_ylabel("Value")
|
||||
canvas = FigureCanvasTkAgg(fig, master=plot_frame)
|
||||
canvas.get_tk_widget().pack(fill="both", expand=True)
|
||||
|
||||
# Optional controls
|
||||
controls_frame = ttk.Frame(plot_frame)
|
||||
controls_frame.pack(fill="x", pady=5)
|
||||
# Clear selection button
|
||||
def clear_selection():
|
||||
for v in check_vars:
|
||||
v.set(0)
|
||||
update_plot()
|
||||
ttk.Button(controls_frame, text="Clear Selection", command=clear_selection).pack(side="left")
|
||||
|
||||
# === Update functions ===
|
||||
Last_tick: int = 0
|
||||
def update_grid(values: list,tick: int):
|
||||
global Last_tick
|
||||
with open(f"{FILENAME}_ADC.csv","+a") as file:
|
||||
file.write(f"{tick}")
|
||||
for i in range(16):
|
||||
file.write(f",{values[i]}")
|
||||
labels[i].config(text=f"{values[i]:n}")
|
||||
labels[16].config(text=f"Last Tick:{tick:n}")
|
||||
labels[17].config(text=f"Delta Tick:{(tick-Last_tick):n}")
|
||||
Last_tick = tick
|
||||
file.write("\n")
|
||||
|
||||
|
||||
def update_SI(value:SD.ReturnDecoder):
|
||||
with open(f"{FILENAME}_SI.csv","+a") as file:
|
||||
file.write(f"{value.timestamp},{value.log_message}\n")
|
||||
print(f"\033[F\033[2Ktime:{value.timestamp}, SI:{value.log_message}",end="\n")
|
||||
|
||||
def append_history(adc_values: list, timestamp: int):
|
||||
"""Store the new sample in the rolling history."""
|
||||
history_x.append(timestamp)
|
||||
for i in range(16):
|
||||
history_y[i].append(adc_values[i])
|
||||
|
||||
def update_plot():
|
||||
"""Redraw plot based on selected channels and available history."""
|
||||
ax.clear()
|
||||
ax.set_title("Selected Channels (rolling)")
|
||||
ax.set_xlabel("Timestamp")
|
||||
ax.set_ylabel("Value")
|
||||
|
||||
selected = [i for i, var in enumerate(check_vars) if var.get() == 1]
|
||||
if len(history_x) == 0 or len(selected) == 0:
|
||||
ax.text(0.5, 0.5, "No data / No channels selected", ha="center", va="center", transform=ax.transAxes)
|
||||
canvas.draw()
|
||||
return
|
||||
|
||||
x = list(history_x)
|
||||
for idx in selected:
|
||||
y = list(history_y[idx])
|
||||
ax.plot(x, y, label=f"Ch {idx+1}")
|
||||
ax.legend(loc="upper left", fontsize=8)
|
||||
ax.grid(True, linestyle="--", alpha=0.3)
|
||||
canvas.draw()
|
||||
|
||||
def update_panel():
|
||||
"""Main polling loop that consumes Serial_decoder outputs (unchanged structure)."""
|
||||
|
||||
values: SD.ReturnDecoder = SD.GetReturn(0.1) # non-blocking read with timeout
|
||||
if values is None:
|
||||
root.after(10, update_panel)
|
||||
return
|
||||
|
||||
# Stop if input thread died
|
||||
if tasks[1].is_alive() == False:
|
||||
root.quit()
|
||||
|
||||
match values.name:
|
||||
case "ADC_FULLRANK":
|
||||
# Update grid + history + plot
|
||||
update_grid(values=values.ADC_data, tick=values.timestamp)
|
||||
append_history(values.ADC_data, values.timestamp)
|
||||
update_plot() # draw right away; if too heavy, throttle with a timer
|
||||
case "SI_DECODER":
|
||||
update_SI(values)
|
||||
case "PR_DECODER":
|
||||
print(f"PRD:{values.text_respons}")
|
||||
case _:
|
||||
print(f"Error {values.name}")
|
||||
|
||||
# Continue polling
|
||||
root.after(10, update_panel) # 10 ms
|
||||
|
||||
# Start polling and GUI loop
|
||||
update_panel()
|
||||
|
||||
# FC commands
|
||||
SD.SerialPort.write(b"SET_ST_SPEED 100\n\r")
|
||||
|
||||
root.mainloop()
|
||||
Reference in New Issue
Block a user