Skip to content
Merged
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
10 changes: 10 additions & 0 deletions packs/configs/process_lecroy_csv.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[required]

process = 'decode'
lecroy_oscilloscope_model = 'LECROYWS4054HD' # currently only one model implemented
Comment thread
jwaiton marked this conversation as resolved.
file_path = '/path/to/file.csv'
save_path = '/path/to/file.h5'

[optional]

overwrite = True
24 changes: 17 additions & 7 deletions packs/proc/proc.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import traceback

from packs.core.io import read_config_file
from packs.proc.processing_utils import process_csv_lecroy
from packs.proc.processing_utils import process_bin_WD2
from packs.proc.processing_utils import process_bin_WD1
from packs.proc.calibration_utils import calibrate
Expand All @@ -24,13 +25,22 @@ def proc(config_file):
match conf_dict.pop('process'):
case 'decode':
# removing the first two components so that the other arguments are passed correctly
match conf_dict.pop('wavedump_edition'):
case 1:
process_bin_WD1(**conf_dict)
case 2:
process_bin_WD2(**conf_dict)
case other:
raise RuntimeError(f"wavedump edition {other} decoding isn't currently implemented.")
if 'lecroy_oscilloscope_model' in conf_dict:
Comment thread
jwaiton marked this conversation as resolved.
match conf_dict.pop('lecroy_oscilloscope_model'):
case 'LECROYWS4054HD':
process_csv_lecroy(**conf_dict)
case other:
raise RuntimeError(f"Lecroy model {other} decoding isn't currently implemented.")
elif 'wavedump_edition' in conf_dict:
match conf_dict.pop('wavedump_edition'):
case 1:
process_bin_WD1(**conf_dict)
case 2:
process_bin_WD2(**conf_dict)
case other:
raise RuntimeError(f"wavedump edition {other} decoding isn't currently implemented.")
else:
raise RuntimeError('No valid decoding method selected.')
case 'calibrate':
calibrate(**conf_dict)
case other:
Expand Down
157 changes: 157 additions & 0 deletions packs/proc/processing_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
import tables as tb
import pandas as pd
import warnings
import csv

import h5py

from typing import BinaryIO
from typing import Generic
from typing import Optional
from datetime import datetime
from typing import List

# imports start from MULE/
from packs.core.core_utils import flatten
Expand Down Expand Up @@ -581,3 +583,158 @@ def process_bin_WD2(file_path : str,
save_data(event_info, rwf, save_path, counter)
counter += (counts)

def read_header_lecroy(file_obj : io.TextIOWrapper):
'''
Reads and parses the header of an oscilloscope binary file, extracting
metadata such as the number of segments, segment size, and time step.

Parameters
----------
file_obj : BinaryIO
Opened binary file object pointing to the start of the oscilloscope data file.

Returns
-------
tuple[np.ndarray, int, int]
A tuple containing:
- dt : np.ndarray
Time step between consecutive samples, computed as np.diff([time2, time1]).
- segments : int
Number of waveform segments (waveforms) stored in the file.
- segment_size : int
Number of samples per segment.

Makeup of csv header by line next(file_obj).split(',')[n],
- Oscilloscope model, instrument id, Object saved
- Segments, number of segments, SegmentSize, number of points in each segment
- Segment, TrigTime, TimeSinceSegment1
- Segment number, date and time, time since first sample recorded
- ...
'''

oscilloscope_model = int((next(file_obj).split(','))[1])

file_heading = next(file_obj).split(',')
segments = int(file_heading[1])
segment_size = int(file_heading[3])

evt_info_heading = next(file_obj).split(',')
for evt_info_line_idx in range(segments):
_ = next(file_obj).split(',')


data_heading = next(file_obj).split(',')


time1 = float((next(file_obj).split(','))[0])
time2 = float((next(file_obj).split(','))[0])

return ((np.diff([time1, time2]))[0], segments, segment_size)

def get_batch(reader : '_csv.reader',
batch_size : int) -> List:
'''
Outputs a list of all the second elements of a row for each batch
then goes to the next row
Parameters
----------
reader (_csv.reader) : Opened file object

Returns
-------
data (generator) : Generator object containing one waveforms's worth of data
'''
return [float(row[1]) for _ in range(batch_size) if (row := next(reader, None))]

def process_event_lazy_lecroy(file_obj : io.TextIOWrapper):
'''
Lecroy Oscilloscope LECROYWS4054HD: Generator that outputs each event iteratively from an opened csv file
Parameters
----------
file_object (obj) : Opened file object

Returns
-------
data (generator) : Generator object containing one event's worth of data
across each event

Makeup of csv header by line next(file_obj).split(',')[n],
- Oscilloscope model, instrument id, Object saved
- Segments, number of segments, SegmentSize, number of points in each segment
- Segment, TrigTime, TimeSinceSegment1
- Segment number, date and time, time since first sample recorded
- ...
'''

# start of header
oscilloscope_model = int((next(file_obj).split(','))[1])

file_heading = next(file_obj).split(',')
segments = int(file_heading[1])
segment_size = int(file_heading[3])

evt_info_heading = next(file_obj).split(',')

evt_info_times = np.empty(segments, dtype=np.float64)
for evt_info_line_idx in range(segments):
evt_info_line = next(file_obj).split(',')
# time since first sample recorded
evt_info_times[evt_info_line_idx] = evt_info_line[2]
# end of header

# start of data
data_heading = next(file_obj).split(',')
reader = csv.reader(file_obj)
wf_num = 0
while batch := get_batch(reader, segment_size):

yield (batch, evt_info_times[wf_num])
wf_num += 1
Comment thread
jwaiton marked this conversation as resolved.
# end of data

print("Processing Finished!")

def process_csv_lecroy(file_path : str,
save_path : str,
overwrite : Optional[bool] = False,
print_mod : Optional[int] = -1):
"""
Process a Lecroy CSV waveform file and write the parsed events to a structured output file.
This only works for individual channels at the moment, as Lecroy oscilloscopes save one file per channel.

Reads waveform data lazily from a Lecroy-format CSV, structures each event into
typed NumPy arrays, and writes them to the output file using the writer.
Parameters
----------
file_path (str) : Path to the input Lecroy CSV file to be read.
save_path (str) : Path to the output file where processed waveform data will be saved.
overwrite (bool) : If True, overwrite the output file if it already exists. Defaults to False.
print_mod (int) : Print progress every N events. Set to -1 to disable printing. Defaults to -1.
Returns
-------
None
"""

with open(file_path, 'r') as file_object:

(sample_size, num_of_events, samples) = read_header_lecroy(file_object)
print('wfs: ', num_of_events, '; samples: ', samples, '; sample size: ', sample_size)
file_object.seek(0)

with writer(save_path, 'RAW', overwrite) as write:

for i, (waveform, timestamp) in enumerate(process_event_lazy_lecroy(file_object)):

if (i % print_mod == 0) and (print_mod != -1):
print(f"Event {i}")

# enforce stucture upon data
e_dtype = types.event_info_type
wf_dtype = types.rwf_type(samples)

event_info = np.array((i, timestamp, samples, sample_size, 1), dtype = e_dtype)
waveforms = np.array((i, 0, waveform), dtype = wf_dtype)

# add data to df
write('event_info', event_info, (True, num_of_events, i))
write('rwf', waveforms, (True, num_of_events, i))
10 changes: 10 additions & 0 deletions packs/tests/data/configs/process_lecroy_csv.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[required]

process = 'decode'
lecroy_oscilloscope_model = 'LECROYWS4054HD' # currently only one model implemented
file_path = '/path/to/file.csv'
save_path = '/path/to/file.h5'

[optional]

overwrite = True
Comment thread
jwaiton marked this conversation as resolved.
Loading
Loading