Skip to content
Open
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)
110 changes: 110 additions & 0 deletions packs/vis/visualise_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
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 (ns)')
ax.set_ylabel('ADC or mV')
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)
sampling_period = 1
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