Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion bin/mule
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@ MULE pack runtime executable
Use 'mule --help' for more information
======================================''', formatter_class=argparse.RawTextHelpFormatter)

parser.add_argument("pack", choices = ['acq','proc','tests','ana'], help = '''The pack implemented:
parser.add_argument("pack", choices = ['acq','proc','tests','ana', 'vis'], help = '''The pack implemented:
acq - Acquisition of data using wavedump 1
proc - Processing of data
tests - Testing directory (IGNORE)
ana - Analysing data
vis - Visualise data
''')
parser.add_argument("config", help = 'The config file provided to the pack, this differs based on which pack youre using')
# acquire arguments
Expand Down
9 changes: 9 additions & 0 deletions packs/configs/vis_wd1_1channel.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[required]

visualise = 'waveform' # only 'waveform' as in single waveform so far
file_path = '/path/to/file.h5'
vis_params = {
'baseline_sub' : 'median',
'sidebands' : ((100, 300), (2900, 3100)),
'negative' : False}

30 changes: 30 additions & 0 deletions packs/vis/vis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import os
import sys
import traceback

from packs.core.io import read_config_file
from packs.core.core_utils import check_test
from packs.vis.visualise_utils import visualise_waveform

def vis(config_file):
print("Starting the visualisation pack...")

# checks if test, if so ends run
if check_test(config_file):
return

# take full path
full_path = os.path.expandvars(config_file)

conf_dict = read_config_file(full_path)
# check the method implemented, currently just process
try:
match conf_dict.pop('visualise'):
case 'waveform':
visualise_waveform(**conf_dict)
case other:
raise RuntimeError(f"process {other} not currently implemented.")
except KeyError as e:
print(f"\nError in the configuration file, incorrect or missing argument: {e} \n")
traceback.print_exc()
sys.exit(2)
109 changes: 109 additions & 0 deletions packs/vis/visualise_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
from tkinter import ttk

from packs.core.io import load_evt_info, load_rwf_info
from packs.proc.calibration_utils import subtract_baseline, collect_sidebands

def visualise_waveform(file_path : str,
vis_params : dict):
"""
Launch an interactive Tkinter GUI for browsing raw waveforms from a file.

Parameters
----------
file_path : str
Path to the data file. Used to load event/waveform info.
vis_params : dict
Visualisation options:
- 'negative' (bool) – invert the waveform amplitude.
- 'baseline_sub' (str) – method for determining baseline, 'median' or 'mean'
"""
# supporting functions
# ---------------------------------------------------------------------------------
def plot_waveform(wf_num : int):
"""Clear the axes and draw waveform wf_num with baseline subtraction applied."""
ax.clear()
single_wf = wf_rwf['rwf'][wf_num]
if vis_params['negative']:
single_wf = -single_wf

sideband_values = collect_sidebands(single_wf, time, vis_params)
single_wf = single_wf - subtract_baseline(sideband_values, sub_type = vis_params['baseline_sub'])
ax.plot(time, single_wf,
marker='o', markerfacecolor='None', linestyle='None', markersize=1)
ax.set_title(f'Waveform #{wf_num}')
ax.set_xlabel('Time (s)')
ax.set_ylabel('ADC')
canvas.draw()

def on_entry(event : tk.Event | None = None):
"""Check and apply the waveform index, fixing the diagram to the valid range."""
try:
val = int(entry_var.get())
val = max(0, min(val, max_wf))
entry_var.set(val)
slider_var.set(val)
plot_waveform(val)
except ValueError:
pass

def on_slider(value : str | None = None):
"""Command for ttk.Scale. Sync the entry box to the slider position and redraw the selected waveform."""
val = slider_var.get()
entry_var.set(str(val))
plot_waveform(val)
# ---------------------------------------------------------------------------------

filename = (file_path.rsplit('.')[1]).rsplit('/')[0]

# load event + waveform info
wf_evt = load_evt_info(file_path)
samples = int(wf_evt.loc[0].samples)
sampling_period = float(wf_evt.loc[0].sampling_period)
wf_rwf = load_rwf_info(file_path, samples)
print(f'file: {file_path}\nsamples: {samples}\nsampling_period: {sampling_period}')
max_wf = len(wf_rwf['rwf']) - 1
time = np.linspace(0,samples * sampling_period, num = samples)

# init GUI
root = tk.Tk()
root.title(f"Waveform Viewer — {filename}")

# generate plot
fig, ax = plt.subplots(layout='constrained', figsize=(8, 4))
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

# controls frame
ctrl = ttk.Frame(root, padding=8)
ctrl.pack(fill=tk.X)

ttk.Label(ctrl, text="Waveform #").pack(side=tk.LEFT)

# number entry
entry_var = tk.StringVar(value="0")

# controls entry
entry = ttk.Entry(ctrl, textvariable=entry_var, width=7)
entry.pack(side=tk.LEFT, padx=4)
entry.bind("<Return>", on_entry)
entry.bind("<FocusOut>", on_entry)

# slider
slider_var = tk.IntVar(value=0)

# controls slider
slider = ttk.Scale(ctrl, from_=0, to=max_wf, orient=tk.HORIZONTAL,
variable=slider_var, command=on_slider, length=400)
slider.pack(side=tk.LEFT, padx=8, fill=tk.X, expand=True)

ttk.Label(ctrl, text=f"(0 – {max_wf})").pack(side=tk.LEFT)

# draw initial waveform
plot_waveform(0)
root.mainloop()
Loading