mirror of
https://github.com/slsdetectorgroup/slsDetectorPackage.git
synced 2026-02-17 03:18:42 +01:00
Dev/pyctbgui merge (#960)
* added empty c extension * added rotation to the decoding * added color map, options and findex * minor * move checks to before acquisition * added pixel map based decoder * cleanup * no thread creation for single thread processing * added rotation and test to compare * allow high and low water mark for zmq (also buffer size) for fast readouts * removed roatation during decoding * added Transpose to image and invert Y False to invert it * retains the zoomed state after the first image of gui, catch and display exception if no detector connected * moved start frame to dockable widget, removed extra frame number label, moved current measurement also to dockable widget, hide frame plot entirely when showing patternviewer * first image dependin on which plot * remember settings of main window size and position, dockewidget if docked, its size and posisiotn as well, then update it next time the gui is opened * change in comment * using c decoder for moench 04 and matterhorn * catch exception from invalid image from decoder * clean up * update row and col when choosing image type, neeeded to show values * fix for previous PR * disable resetting colormap values keep the range selected for every new acquisition * fix typos + tested on virtual matterhorn * minor print * refactored Slow ADCs Tab * refactored DAC tab * refactored power supplies * refactored signals tab * refactored transceiver tab * fix typo * fix typo2 * remove commented code * delete commented code * delete commented code * delete commented signals code * remove commented code for transceiver tab * refactor adc tab * refactor Pattern Tab * Refactor transceivers tab (PR#5) (#118) * refactored transceiver tab * remove commented code for transceiver tab --------- Co-authored-by: Erik Frojdh <erik.frojdh@gmail.com> * refactor adc tab (PR#6) (#119) * refactor adc tab * refactored Plot and Acquisition Tabs * fix the regression issue * restructure project files * applying singleton and renaming tabs to services * working install using pip * applies singleton to tab classes and remove CI erros * added pyzmq and pillow * remove the singleton implementation and keep changes * fix merge errors in mainWindow * moved misplaced init file * rename service to tab * reorganize imports * iterate over tabs * reorder tabs * add slowadc to the list * saving changes (buggy) power supply ui not showing in the gui * split power supply tab * fixed tests * add hardcoded values to defines file * fix error * separate power supply * fix errors for powerSuppliesTab * split dacs * split slow adcs * split signals tab * added tests for bit_utils * add slowAdc class to defines * split transceiver ui file * split adc.ui * split pattern ui file * split plot and acquisition ui file * added basic test for parsing bit names * removed redundant code in read_alias_file * fix dacs ui position * testing for correct exception * cmd and args at split * group radio buttons * fix comments from PR#1 * show legend * added python version and dev requirements to setup.py * fix dac issue * moved _decoder into pkg * added inplace build * removed clear * fixed dependencies * make tests run without slsdet * updated name of action * define colcount * fixed loading of alias file * add yapf and ruff * apply formatting * fix E and F rules * add more ruff rules * change variable name * squashing gh debugging commits and add pre-commit * update label values to mv units * add hook for yapf * reconfigure yapf precommit hook * add format and check_format to makefile * change gh actions * update readme * added check_format * WIP * added linting in github action * updated readme] * add more control for color choice * remove useless file * fix un-updated line after refactoring Defines BIT0_31_MASK is not found in Defines.signals * visually improve the interface * fix last commit * add only selected plots for legend * add hide legend button * change hide legend to show legend checkbox show legend is checked by default * add support for saving in numpy * solve conversations * fix acq index offset * fix browse button in pattern error * fix other browse button errors * finish tests and add usage.md * remove buffer * add file,numpy-like interface and tests * remove useless .npy files * subscriptible npz files * remove useless files * remove repetetive tests * save changes * add mode r+, add with support,remove logging * remove offset of acqIndex between raw and numpy saving * fix only saving last frame * save signals of multiple devices * add comments and move condition for clearer code * fix bug when vieweing pattern file * iterate over enabled and plotted plots * add padestal substraction to transceiver and analog data * init pedestal frames to detector.frames * restore old exception * add pedestal substraction for digital signals * remove frames spinbox from plotTab * remove comments and use str instead of Path * avoid saving all frames * correct exception and log error's trace * add gui tests * add waveform test * add pedestal test * refactor by using fixtures * add tests for moench analog and pattern * add pytest-qt to dependencies * add save and load gui parameters * remove nohup file * fix old bug IndexError * save plot type * a * handle canceling load, loading matterhorn pedestal for moench * remove comparing .png files for pattern test * save plot type * red error on status bar when shape mismatch for loaded pedestal * fix makefile and docstrings * fix PRs conversation * move code into different function * fix wrong function names for power supply * removed old ctbgui * removed unnecessary files --------- Co-authored-by: Erik Frojdh <erik.frojdh@gmail.com> Co-authored-by: Braham Bechir <braham_b@pc11979.psi.ch> Co-authored-by: Bechir <bechir.braham@psi.ch> Co-authored-by: Bechir <bechir.brahem420@gmail.com>
This commit is contained in:
0
pyctbgui/pyctbgui/utils/__init__.py
Normal file
0
pyctbgui/pyctbgui/utils/__init__.py
Normal file
101
pyctbgui/pyctbgui/utils/alias_utility.py
Normal file
101
pyctbgui/pyctbgui/utils/alias_utility.py
Normal file
@@ -0,0 +1,101 @@
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def read_alias_file(alias_file):
|
||||
with open(alias_file) as fp:
|
||||
lines_alias = fp.readlines()
|
||||
return parse_alias_lines(lines_alias)
|
||||
|
||||
|
||||
def parse_alias_lines(lines_alias):
|
||||
bit_names = [None] * 64
|
||||
bit_plots = [None] * 64
|
||||
bit_colors = [None] * 64
|
||||
adc_names = [None] * 32
|
||||
adc_plots = [None] * 32
|
||||
adc_colors = [None] * 32
|
||||
dac_names = [None] * 18
|
||||
sense_names = [None] * 8
|
||||
power_names = [None] * 5
|
||||
pat_file_name = None
|
||||
|
||||
for line_nr, line in enumerate(lines_alias):
|
||||
ignore_list = ['PATCOMPILER']
|
||||
|
||||
# skip empty lines
|
||||
if line == '\n' or len(line) == 0:
|
||||
continue
|
||||
# skip comments
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
|
||||
cmd, *args = line.split()
|
||||
|
||||
if not args:
|
||||
raise Exception(
|
||||
f"Alias file parsing failed: Require atleast one argument in addition to command. ({line_nr}:{line})")
|
||||
|
||||
if cmd.startswith("BIT"):
|
||||
process_alias_bit_or_adc(cmd, args, bit_names, bit_plots, bit_colors)
|
||||
|
||||
elif cmd.startswith("ADC"):
|
||||
process_alias_bit_or_adc(cmd, args, adc_names, adc_plots, adc_colors)
|
||||
|
||||
elif cmd.startswith("DAC"):
|
||||
if len(args) > 1:
|
||||
raise Exception(f"Too many arguments {len(args)} (expected max: 1) for this type. ({line_nr}:{line})")
|
||||
i = int(cmd[3:])
|
||||
dac_names[i] = args[0]
|
||||
|
||||
elif cmd.startswith("SENSE"):
|
||||
if len(args) > 1:
|
||||
raise Exception(f"Too many arguments {len(args)} (expected max: 1) for this type. ({line_nr}:{line})")
|
||||
i = int(cmd[5:])
|
||||
sense_names[i] = args[0]
|
||||
|
||||
elif cmd in ["VA", "VB", "VC", "VD", "VIO"]:
|
||||
if len(args) > 1:
|
||||
raise Exception(f"Too many arguments {len(args)} (expected max: 1) for this type. ({line_nr}:{line})")
|
||||
|
||||
match cmd:
|
||||
case "VA":
|
||||
i = 0
|
||||
case "VB":
|
||||
i = 1
|
||||
case "VC":
|
||||
i = 2
|
||||
case "VD":
|
||||
i = 3
|
||||
case "VIO":
|
||||
i = 4
|
||||
power_names[i] = args[0]
|
||||
|
||||
elif cmd == "PATFILE":
|
||||
if len(args) > 1:
|
||||
raise Exception(f"Too many arguments {len(args)} (expected max: 1) for this type. ({line_nr}:{line})")
|
||||
|
||||
pat_file_name = args[0]
|
||||
path = Path(pat_file_name)
|
||||
if not path.is_file():
|
||||
raise Exception("Pattern file provided in alias file does not exist.<br><br>Pattern file:" +
|
||||
pat_file_name)
|
||||
elif cmd in ignore_list:
|
||||
pass
|
||||
|
||||
else:
|
||||
raise Exception(f"Command: {cmd} not supported. Line {line_nr}:{line}")
|
||||
|
||||
return bit_names, bit_plots, bit_colors, adc_names, adc_plots, adc_colors, dac_names, sense_names, power_names,\
|
||||
pat_file_name
|
||||
|
||||
|
||||
def process_alias_bit_or_adc(cmd, args, names, plots, colors):
|
||||
n_args = len(args)
|
||||
i = int(cmd[3:])
|
||||
names[i] = args[0]
|
||||
if n_args > 1:
|
||||
plots[i] = bool(int(args[1]))
|
||||
if n_args > 2:
|
||||
colors[i] = args[2]
|
||||
if n_args > 3:
|
||||
raise Exception(f"Too many arguments {args} (expected max: 3) for this type in line.")
|
||||
16
pyctbgui/pyctbgui/utils/bit_utils.py
Normal file
16
pyctbgui/pyctbgui/utils/bit_utils.py
Normal file
@@ -0,0 +1,16 @@
|
||||
def set_bit(value, bit_nr):
|
||||
return value | 1 << bit_nr
|
||||
|
||||
|
||||
def remove_bit(value, bit_nr):
|
||||
return value & ~(1 << bit_nr)
|
||||
|
||||
|
||||
def bit_is_set(value, bit_nr):
|
||||
return (value >> bit_nr) & 1 == 1
|
||||
|
||||
|
||||
def manipulate_bit(is_set, value, bit_nr):
|
||||
if is_set:
|
||||
return set_bit(value, bit_nr)
|
||||
return remove_bit(value, bit_nr)
|
||||
51
pyctbgui/pyctbgui/utils/decoder.py
Normal file
51
pyctbgui/pyctbgui/utils/decoder.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from pyctbgui.utils.defines import Defines
|
||||
from pyctbgui._decoder import * #bring in the function from the compiled extension
|
||||
import numpy as np
|
||||
"""
|
||||
Python implementation, keep as a reference. Change name and replace
|
||||
with C version to swap it out in the GUI
|
||||
"""
|
||||
|
||||
|
||||
def moench04(analog_buffer):
|
||||
nAnalogCols = Defines.Moench04.nCols
|
||||
nAnalogRows = Defines.Moench04.nRows
|
||||
adcNumbers = Defines.Moench04.adcNumbers
|
||||
nPixelsPerSC = Defines.Moench04.nPixelsPerSuperColumn
|
||||
scWidth = Defines.Moench04.superColumnWidth
|
||||
|
||||
analog_frame = np.zeros((nAnalogCols, nAnalogRows), dtype=analog_buffer.dtype)
|
||||
|
||||
for iPixel in range(nPixelsPerSC):
|
||||
for iSC, iAdc in enumerate(adcNumbers):
|
||||
col = ((iAdc % 16) * scWidth) + (iPixel % scWidth)
|
||||
if iSC < 16:
|
||||
row = 199 - int(iPixel / scWidth)
|
||||
else:
|
||||
row = 200 + int(iPixel / scWidth)
|
||||
index_min = iPixel * 32 + iSC
|
||||
pixel_value = analog_buffer[index_min]
|
||||
analog_frame[row, col] = pixel_value
|
||||
|
||||
return analog_frame
|
||||
|
||||
|
||||
def matterhorn(trans_buffer):
|
||||
nTransceiverRows = Defines.Matterhorn.nRows
|
||||
nTransceiverCols = Defines.Matterhorn.nCols
|
||||
|
||||
transceiver_frame = np.zeros((nTransceiverCols, nTransceiverRows), dtype=trans_buffer.dtype)
|
||||
|
||||
offset = 0
|
||||
nSamples = Defines.Matterhorn.nPixelsPerTransceiver
|
||||
for row in range(Defines.Matterhorn.nRows):
|
||||
for col in range(Defines.Matterhorn.nHalfCols):
|
||||
#print(f'row:{row} col:{col} offset: {offset}')
|
||||
for iTrans in range(Defines.Matterhorn.nTransceivers):
|
||||
transceiver_frame[iTrans * Defines.Matterhorn.nHalfCols + col,
|
||||
row] = trans_buffer[offset + nSamples * iTrans]
|
||||
offset += 1
|
||||
if (col + 1) % nSamples == 0:
|
||||
offset += nSamples
|
||||
|
||||
return transceiver_frame
|
||||
111
pyctbgui/pyctbgui/utils/defines.py
Normal file
111
pyctbgui/pyctbgui/utils/defines.py
Normal file
@@ -0,0 +1,111 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Defines:
|
||||
Time_Wait_For_Packets_ms = 0.5
|
||||
Time_Status_Refresh_ms = 100
|
||||
Time_Plot_Refresh_ms = 20
|
||||
|
||||
Zmq_hwm_high_speed = 2
|
||||
Zmq_hwm_low_speed = -1
|
||||
|
||||
Acquisition_Tab_Index = 7
|
||||
Max_Tabs = 9
|
||||
|
||||
class adc:
|
||||
tabIndex = 5
|
||||
count = 32
|
||||
half = 16
|
||||
BIT0_15_MASK = 0x0000FFFF
|
||||
BIT16_31_MASK = 0xFFFF0000
|
||||
|
||||
class dac:
|
||||
tabIndex = 0
|
||||
count = 18
|
||||
|
||||
class signals:
|
||||
tabIndex = 3
|
||||
count = 64
|
||||
half = 32
|
||||
BIT0_31_MASK = 0x00000000FFFFFFFF
|
||||
BIT32_63_MASK = 0xFFFFFFFF00000000
|
||||
|
||||
class pattern:
|
||||
tabIndex = 6
|
||||
loops_count = 6
|
||||
|
||||
class transceiver:
|
||||
count = 4
|
||||
tabIndex = 4
|
||||
|
||||
class slowAdc:
|
||||
tabIndex = 2
|
||||
count = 8
|
||||
|
||||
colCount = 4
|
||||
|
||||
powerSupplies = ('A', 'B', 'C', 'D', 'IO')
|
||||
|
||||
class ImageIndex(Enum):
|
||||
Matterhorn = 0
|
||||
Moench04 = 1
|
||||
|
||||
class Matterhorn:
|
||||
nRows = 48
|
||||
nHalfCols = 24
|
||||
nCols = 48
|
||||
nTransceivers = 2
|
||||
tranceiverEnable = 0x3
|
||||
nPixelsPerTransceiver = 4
|
||||
|
||||
class Moench04:
|
||||
nRows = 400
|
||||
nCols = 400
|
||||
adcNumbers = [
|
||||
9, 8, 11, 10, 13, 12, 15, 14, 1, 0, 3, 2, 5, 4, 7, 6, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27,
|
||||
26, 25, 24
|
||||
]
|
||||
nPixelsPerSuperColumn = 5000
|
||||
superColumnWidth = 25
|
||||
|
||||
Color_map = [
|
||||
'viridis', 'plasma', 'inferno', 'magma', 'cividis', 'binary', 'gist_yarg', 'gist_gray', 'gray', 'bone', 'pink',
|
||||
'spring', 'summer', 'autumn', 'winter', 'cool', 'Wistia', 'hot', 'afmhot', 'gist_heat', 'copper',
|
||||
'gist_rainbow', 'rainbow', 'jet', 'turbo'
|
||||
]
|
||||
Default_Color_Map = 'viridis'
|
||||
|
||||
# pattern viewer defines
|
||||
|
||||
# pattern plot
|
||||
Colors_plot = ['Blue', 'Orange']
|
||||
|
||||
# Wait colors and line styles (6 needed from 0 to 5)
|
||||
# Colors_wait = ['b', 'g', 'r', 'c', 'm', 'y']
|
||||
Colors_wait = ['Blue', 'Green', 'Red', 'Cyan', 'Magenta', 'Yellow']
|
||||
Linestyles_wait = ['--', '--', '--', '--', '--', '--']
|
||||
Alpha_wait = [0.5, 0.5, 0.5, 0.5, 0.5, 0.5]
|
||||
Alpha_wait_rect = [0.2, 0.2, 0.2, 0.2, 0.2, 0.2]
|
||||
|
||||
# Loop colors and line styles (6 needed from 0 to 5)
|
||||
Colors_loop = ['Green', 'Red', 'Purple', 'Brown', 'Pink', 'Grey']
|
||||
Linestyles_loop = ['-.', '-.', '-.', '-.', '-.', '-.']
|
||||
Alpha_loop = [0.5, 0.5, 0.5, 0.5, 0.5, 0.5]
|
||||
Alpha_loop_rect = [0.2, 0.2, 0.2, 0.2, 0.2, 0.2]
|
||||
|
||||
# Display the count of clocks
|
||||
Clock_vertical_lines_spacing = 50
|
||||
Show_clocks_number = True
|
||||
Line_width = 2.0
|
||||
|
||||
Colors = [
|
||||
'Blue', 'Orange', 'Green', 'Red', 'Purple', 'Brown', 'Pink', 'Gray', 'Olive', 'Cyan', 'Magenta', 'Yellow',
|
||||
'Black', 'White'
|
||||
]
|
||||
|
||||
LineStyles = ['-', '--', '-.', ':']
|
||||
|
||||
class colorRange(Enum):
|
||||
all = 0
|
||||
center = 1
|
||||
fixed = 2
|
||||
0
pyctbgui/pyctbgui/utils/numpyWriter/__init__.py
Normal file
0
pyctbgui/pyctbgui/utils/numpyWriter/__init__.py
Normal file
224
pyctbgui/pyctbgui/utils/numpyWriter/npy_writer.py
Normal file
224
pyctbgui/pyctbgui/utils/numpyWriter/npy_writer.py
Normal file
@@ -0,0 +1,224 @@
|
||||
"""
|
||||
Wrapper to be able to append frames to a numpy file
|
||||
|
||||
numpy header v1
|
||||
|
||||
- 6bytes \x93NUMPY
|
||||
- 1 byte major version number \x01
|
||||
- 1 byte minor version number \x00
|
||||
- 2 bytes (unsigned short) HEADER_LEN length of header to follow
|
||||
- Header as an ASCII dict terminated by \n padded with space \x20 to make sure
|
||||
we get len(magic string) + 2 + len(length) + HEADER_LEN divisible with 64
|
||||
Allocate enough space to allow for the data to grow
|
||||
"""
|
||||
|
||||
import ast
|
||||
import os
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
class NumpyFileManager:
|
||||
"""
|
||||
class used to read and write into .npy files that can't be loaded completely into memory
|
||||
|
||||
for read mode implements numpy like interface and file-like object function
|
||||
"""
|
||||
magic_str = np.lib.format.magic(1, 0)
|
||||
headerLength = np.uint16(128)
|
||||
FSEEK_FILE_END = 2
|
||||
BUFFER_MAX = 500
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
file: str | Path | zipfile.ZipExtFile,
|
||||
mode: str = 'r',
|
||||
frameShape: tuple = None,
|
||||
dtype=None,
|
||||
):
|
||||
"""
|
||||
initiates a NumpyFileManager class for reading or writing bytes directly to/from a .npy file
|
||||
@param file: path to the file to open or create
|
||||
@param frameShape: shape of the frame ex: (5000,) for waveforms or (400,400) for image
|
||||
@param dtype: type of the numpy array's header
|
||||
@param mode: file open mode must be in 'rwx'
|
||||
"""
|
||||
if mode not in ['r', 'w', 'x', 'r+']:
|
||||
raise ValueError('file mode should be either r,w,x,r+')
|
||||
|
||||
if isinstance(file, zipfile.ZipExtFile):
|
||||
if mode != 'r':
|
||||
raise ValueError('NumpyFileManager only supports read mode for zipfiles')
|
||||
else:
|
||||
if mode == 'x' and Path.is_file(Path(file)):
|
||||
raise FileExistsError(f'file {file} exists while given mode is x')
|
||||
|
||||
self.dtype = np.dtype(dtype) # in case we pass a type like np.float32
|
||||
self.frameShape = frameShape
|
||||
self.frameCount = 0
|
||||
self.cursorPosition = self.headerLength
|
||||
self.mode = mode
|
||||
|
||||
# if newFile frameShape and dtype should be present
|
||||
if mode == 'w' or mode == 'x':
|
||||
assert frameShape is not None
|
||||
assert dtype is not None
|
||||
# create/clear the file with mode wb+
|
||||
self.file = open(file, 'wb+')
|
||||
self.updateHeader()
|
||||
|
||||
else:
|
||||
# opens file for read and check if the header of the file corresponds to the given function
|
||||
# arguments
|
||||
if isinstance(file, zipfile.ZipExtFile):
|
||||
self.file = file
|
||||
else:
|
||||
mode = 'rb' if self.mode == 'r' else 'rb+'
|
||||
self.file = open(file, mode)
|
||||
self.file.seek(10)
|
||||
headerStr = self.file.read(np.uint16(self.headerLength - 10)).decode("UTF-8")
|
||||
header_dict = ast.literal_eval(headerStr)
|
||||
self.frameShape = header_dict['shape'][1:]
|
||||
if frameShape is not None:
|
||||
assert frameShape == self.frameShape, \
|
||||
f"shape in arguments ({frameShape}) is not the same as the shape of the stored " \
|
||||
f"file ({self.frameShape})"
|
||||
|
||||
self.dtype = np.lib.format.descr_to_dtype(header_dict['descr'])
|
||||
if dtype is not None:
|
||||
assert dtype == self.dtype, \
|
||||
f"dtype in argument ({dtype}) is not the same as the dtype of the stored file ({self.dtype})"
|
||||
|
||||
self.frameCount = header_dict['shape'][0]
|
||||
|
||||
assert not header_dict['fortran_order'], "fortran_order in the stored file is not False"
|
||||
|
||||
self.__frameSize = np.dtype(self.dtype).itemsize * np.prod(self.frameShape)
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.close()
|
||||
|
||||
def restoreCursorPosition(func):
|
||||
"""
|
||||
decorator function used to restore the file descriptors
|
||||
cursor position after using read or write functions
|
||||
"""
|
||||
|
||||
def wrapper(self, *args, **kwargs):
|
||||
tmp = self.cursorPosition
|
||||
result = func(self, *args, **kwargs)
|
||||
self.cursorPosition = tmp
|
||||
self.file.seek(tmp)
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
@restoreCursorPosition
|
||||
def updateHeader(self):
|
||||
"""
|
||||
updates the header of the .npy file with the class attributes
|
||||
@note: fortran_order is always set to False
|
||||
"""
|
||||
if self.mode == 'r':
|
||||
return
|
||||
self.file.seek(0)
|
||||
header_dict = {
|
||||
'descr': np.lib.format.dtype_to_descr(self.dtype),
|
||||
'fortran_order': False,
|
||||
'shape': (self.frameCount, *self.frameShape)
|
||||
}
|
||||
np.lib.format.write_array_header_1_0(self.file, header_dict)
|
||||
self.flush()
|
||||
|
||||
@restoreCursorPosition
|
||||
def writeOneFrame(self, frame: np.ndarray):
|
||||
"""
|
||||
write one frame without buffering
|
||||
@param frame: numpy array for a frame
|
||||
"""
|
||||
if frame.shape != self.frameShape:
|
||||
raise ValueError(f"frame shape given {frame.shape} is not the same as the file's shape {self.frameShape}")
|
||||
if frame.dtype != self.dtype:
|
||||
raise ValueError(f"frame dtype given {frame.dtype} is not the same as the file's dtype {self.dtype}")
|
||||
|
||||
self.file.seek(0, self.FSEEK_FILE_END)
|
||||
self.frameCount += 1
|
||||
self.file.write(frame.tobytes())
|
||||
|
||||
def flush(self):
|
||||
"""
|
||||
persist data into disk
|
||||
"""
|
||||
self.file.flush()
|
||||
os.fsync(self.file)
|
||||
|
||||
@restoreCursorPosition
|
||||
def readFrames(self, frameStart: int, frameEnd: int) -> np.ndarray:
|
||||
"""
|
||||
read frames from .npy file without loading the whole file to memory with np.load
|
||||
@param frameStart: number of the frame to start reading from
|
||||
@param frameEnd: index of the last frame (not inclusive)
|
||||
@return: np.ndarray of frames of the shape [frameEnd-frameStart,*self.frameShape]
|
||||
"""
|
||||
frameCount = frameEnd - frameStart
|
||||
|
||||
if frameStart < 0:
|
||||
raise NotImplementedError("frameStart must be bigger than 0")
|
||||
if frameCount < 0:
|
||||
if frameStart <= 0:
|
||||
raise NotImplementedError("frameEnd must be bigger than frameStart")
|
||||
frameCount = 0
|
||||
self.file.seek(self.headerLength + frameStart * self.__frameSize)
|
||||
data = self.file.read(frameCount * self.__frameSize)
|
||||
return np.frombuffer(data, self.dtype).reshape([-1, *self.frameShape])
|
||||
|
||||
def read(self, frameCount):
|
||||
"""
|
||||
file like interface to read frameCount frames from the already stored position
|
||||
@param frameCount: number of frames to read
|
||||
@return: numpy array containing frameCount frames
|
||||
"""
|
||||
assert frameCount > 0
|
||||
data = self.file.read(frameCount * self.__frameSize)
|
||||
self.cursorPosition += frameCount * self.__frameSize
|
||||
return np.frombuffer(data, self.dtype).reshape([-1, *self.frameShape])
|
||||
|
||||
def seek(self, frameNumber):
|
||||
"""
|
||||
file-like interface to move the file's cursor position to the frameNumber
|
||||
"""
|
||||
assert frameNumber >= 0
|
||||
self.cursorPosition = self.headerLength + frameNumber * self.__frameSize
|
||||
self.file.seek(self.cursorPosition)
|
||||
|
||||
def close(self):
|
||||
self.updateHeader()
|
||||
self.file.close()
|
||||
|
||||
def __getitem__(self, item):
|
||||
isSlice = False
|
||||
if isinstance(item, slice):
|
||||
isSlice = True
|
||||
if item.step is not None:
|
||||
raise NotImplementedError("step parameter is not implemented yet")
|
||||
if isSlice:
|
||||
return self.readFrames(item.start, item.stop)
|
||||
frame = self.readFrames(item, item + 1)
|
||||
if frame.size != 0:
|
||||
frame = frame.squeeze(0)
|
||||
return frame
|
||||
|
||||
def __del__(self):
|
||||
"""
|
||||
in case the user forgot to close the file
|
||||
"""
|
||||
if hasattr(self, 'file') and not self.file.closed:
|
||||
try:
|
||||
self.close()
|
||||
except ImportError:
|
||||
self.file.close()
|
||||
91
pyctbgui/pyctbgui/utils/numpyWriter/npz_writer.py
Normal file
91
pyctbgui/pyctbgui/utils/numpyWriter/npz_writer.py
Normal file
@@ -0,0 +1,91 @@
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import zipfile
|
||||
import io
|
||||
|
||||
import numpy as np
|
||||
from pyctbgui.utils.numpyWriter.npy_writer import NumpyFileManager
|
||||
|
||||
|
||||
class NpzFileWriter:
|
||||
"""
|
||||
Write data to npz file incrementally rather than compute all and write
|
||||
once, as in ``np.save``. This class can be used with ``contextlib.closing``
|
||||
to ensure closed after usage.
|
||||
"""
|
||||
|
||||
def __init__(self, tofile: str, mode='w', compress_file=False):
|
||||
"""
|
||||
:param tofile: the ``npz`` file to write
|
||||
:param mode: must be one of {'x', 'w', 'a'}. See
|
||||
https://docs.python.org/3/library/zipfile.html for detail
|
||||
"""
|
||||
self.__openedFiles = {}
|
||||
self.compression = zipfile.ZIP_DEFLATED if compress_file else zipfile.ZIP_STORED
|
||||
self.tofile = tofile
|
||||
self.mode = mode
|
||||
self.file = zipfile.ZipFile(self.tofile, mode=self.mode, compression=self.compression)
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.close()
|
||||
|
||||
def writeArray(self, key: str, data: np.ndarray | bytes) -> None:
|
||||
"""
|
||||
overwrite existing data of name ``key``.
|
||||
|
||||
:param key: the name of data to write
|
||||
:param data: the data
|
||||
"""
|
||||
key += '.npy'
|
||||
with io.BytesIO() as cbuf:
|
||||
np.save(cbuf, data)
|
||||
cbuf.seek(0)
|
||||
with self.file.open(key, mode='w', force_zip64=True) as outfile:
|
||||
shutil.copyfileobj(cbuf, outfile)
|
||||
|
||||
def readFrames(self, file: str, frameStart: int, frameEnd: int):
|
||||
file += '.npy'
|
||||
with self.file.open(file, mode='r') as outfile:
|
||||
npw = NumpyFileManager(outfile)
|
||||
return npw.readFrames(frameStart, frameEnd)
|
||||
|
||||
@staticmethod
|
||||
def zipNpyFiles(filename: str,
|
||||
files: list[str | Path],
|
||||
fileKeys: list[str],
|
||||
deleteOriginals=False,
|
||||
compressed=False):
|
||||
compression = zipfile.ZIP_DEFLATED if compressed else zipfile.ZIP_STORED
|
||||
|
||||
with zipfile.ZipFile(filename, mode='w', compression=compression, allowZip64=True) as zipf:
|
||||
for idx, file in enumerate(files):
|
||||
zipf.write(file, arcname=fileKeys[idx] + '.npy')
|
||||
if deleteOriginals:
|
||||
for file in files:
|
||||
Path.unlink(file)
|
||||
|
||||
def __getitem__(self, item: str) -> NumpyFileManager:
|
||||
"""
|
||||
returns NumpyFileManager file handling the .npy file under the key item inside of the .npz file
|
||||
@param item:
|
||||
@return:
|
||||
"""
|
||||
if not isinstance(item, str):
|
||||
raise TypeError('given item is not of type str')
|
||||
if item not in self.__openedFiles:
|
||||
outfile = self.file.open(item + '.npy', mode='r')
|
||||
self.__openedFiles[item] = NumpyFileManager(outfile)
|
||||
return self.__openedFiles[item]
|
||||
|
||||
def namelist(self):
|
||||
return sorted([key[:-4] for key in self.file.namelist()])
|
||||
|
||||
def close(self):
|
||||
if hasattr(self, 'file') and self.file is not None:
|
||||
self.file.close()
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
71
pyctbgui/pyctbgui/utils/numpyWriter/usage.md
Normal file
71
pyctbgui/pyctbgui/utils/numpyWriter/usage.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Using numpyWriter module
|
||||
## concept
|
||||
numpyWriter is used to write and load huge numpy arrays that can't be fully loaded in RAM.
|
||||
It is designed to write frames of a constant shape (defined by user) and incrementally add to .npy and .npz files without accessing all of its contents
|
||||
|
||||
### NumpyFileManager
|
||||
class to handle writing in .npy files frame by frame.
|
||||
its positional parameter `file` can be of type: str,pathlib.Path, zipfile.ZipExtFile. This way we can use NumpyFileManager to open files by getting their path or
|
||||
**in read mode** it can receiver file-like objects to read their data.
|
||||
|
||||
the complexity of initializing from file-like objects is added to be able to read from .npz files which are simply a zip of .npy files. Furthermore now we can save our files .npz files and read from them (even when compressed (⊙_⊙) ) without loading the whole .npy or .npz in memory.
|
||||
|
||||
### NpzFileWriter
|
||||
class used to handle .npz file functionalities. it can zip existing .npy files, write a whole array in an .npz file without loading the whole .npz in memory,
|
||||
and read frames from .npy files inside the .npz file
|
||||
|
||||
## Usage
|
||||
|
||||
```python
|
||||
# create .npy file
|
||||
npw = NumpyFileManager('file.npy', 'w', (400, 400), np.int32)
|
||||
npw.addFrame(np.ones([400, 400], dtype=np.int32))
|
||||
npw.close()
|
||||
|
||||
# read frames from existing .npy file
|
||||
npw = NumpyFileManager('file.npy')
|
||||
# if arr is stored in the .npy file this statement will return arr[50:100]
|
||||
npw.readFrames(50, 100)
|
||||
|
||||
# Numpy like interface
|
||||
# NumpyFileManager is also subscriptable
|
||||
npw[50:100] # returns the array arr[50:100]
|
||||
npw[0] # returns the array arr[0]
|
||||
|
||||
# File like interface
|
||||
# the npw class's cursors is initialized on the first frame
|
||||
npw.read(5) # reads five frames and updates the cursor
|
||||
npw.seek(99) # updates the cursor to point it to the 99-th frame
|
||||
|
||||
# to ensure that files are written to disk
|
||||
npw.flush()
|
||||
|
||||
# zip existing .npy files (stored on disk)
|
||||
# filePaths: the paths to .npy files
|
||||
# keys: name of the arrays inside of the .npz file
|
||||
NpzFileWriter.zipNpyFiles('file.npz', filePaths, keys, compressed=True)
|
||||
|
||||
# add numpy arrays incrementally to a .npz file
|
||||
with NpzFileWriter('tmp.npz', 'w', compress_file=True) as npz:
|
||||
npz.writeArray('adc', arr1)
|
||||
npz.writeArray('tx', arr2)
|
||||
|
||||
# read frames from adc.npy inside of tmp.npz
|
||||
with NpzFileWriter('tmp.npz', 'r') as npz:
|
||||
frames = npz.readFrames('adc', 5, 8)
|
||||
|
||||
# NpzFileWriter is also subscriptable and returns a NumpyFileManager initialized
|
||||
# to open the the file with the given key inside the .npz file
|
||||
npz = NpzFileWriter('tmp.npz', 'r')
|
||||
npz.writeArray('adc', arr1)
|
||||
|
||||
|
||||
npz['adc'] # returns a NumpyFileManager
|
||||
npz['adc'][50:100] # returns the array from 50 to 100
|
||||
# note once a NumpyFileManager instance is created internally NpzFileWriter stores it
|
||||
# this is done to avoid opening and closing the same file
|
||||
# also file-like interface can be used
|
||||
npz['adc'].read(5) # returns arr[:5]
|
||||
npz['adc'].seek(100) # updates the cursor
|
||||
npz['adc'].read(2) # returns arr[100:2]
|
||||
```
|
||||
61
pyctbgui/pyctbgui/utils/pixelmap.py
Normal file
61
pyctbgui/pyctbgui/utils/pixelmap.py
Normal file
@@ -0,0 +1,61 @@
|
||||
import numpy as np
|
||||
# generate pixelmaps for various CTB detectors
|
||||
|
||||
|
||||
def moench03():
|
||||
out = np.zeros((400, 400), dtype=np.uint32)
|
||||
adc_numbers = np.array(
|
||||
(12, 13, 14, 15, 12, 13, 14, 15, 8, 9, 10, 11, 8, 9, 10, 11, 4, 5, 6, 7, 4, 5, 6, 7, 0, 1, 2, 3, 0, 1, 2, 3),
|
||||
dtype=np.int_)
|
||||
for n_pixel in range(5000):
|
||||
for i_sc in range(32):
|
||||
adc_nr = adc_numbers[i_sc]
|
||||
col = ((adc_nr * 25) + (n_pixel % 25))
|
||||
row = 0
|
||||
if (i_sc // 4 % 2 == 0):
|
||||
row = 199 - (n_pixel // 25)
|
||||
else:
|
||||
row = 200 + (n_pixel // 25)
|
||||
|
||||
i_analog = n_pixel * 32 + i_sc
|
||||
out[row, col] = i_analog
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def moench04_analog():
|
||||
out = np.zeros((400, 400), dtype=np.uint32)
|
||||
adc_numbers = np.array((9, 8, 11, 10, 13, 12, 15, 14, 1, 0, 3, 2, 5, 4, 7, 6, 23, 22, 21, 20, 19, 18, 17, 16, 31,
|
||||
30, 29, 28, 27, 26, 25, 24),
|
||||
dtype=np.int_)
|
||||
|
||||
for n_pixel in range(5000):
|
||||
for i_sc in range(32):
|
||||
adc_nr = adc_numbers[i_sc]
|
||||
col = ((adc_nr % 16) * 25) + (n_pixel % 25)
|
||||
row = 0
|
||||
if i_sc < 16:
|
||||
row = 199 - (n_pixel // 25)
|
||||
else:
|
||||
row = 200 + (n_pixel // 25)
|
||||
|
||||
i_analog = n_pixel * 32 + i_sc
|
||||
out[row, col] = i_analog
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def matterhorn_transceiver():
|
||||
out = np.zeros((48, 48), dtype=np.uint32)
|
||||
|
||||
offset = 0
|
||||
nSamples = 4
|
||||
for row in range(48):
|
||||
for col in range(24):
|
||||
for iTrans in range(2):
|
||||
out[iTrans * 24 + col, row] = offset + nSamples * iTrans
|
||||
offset += 1
|
||||
if (col + 1) % nSamples == 0:
|
||||
offset += nSamples
|
||||
|
||||
return out
|
||||
726
pyctbgui/pyctbgui/utils/plotPattern.py
Executable file
726
pyctbgui/pyctbgui/utils/plotPattern.py
Executable file
@@ -0,0 +1,726 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Created on Wed May 24 09:44:53 2017
|
||||
|
||||
Plot the pattern for New Chip Test Box (.pat)
|
||||
|
||||
Changes:
|
||||
- 2017-11-21 Adapt it to python-3
|
||||
- 2017-09-25 All can be plotted
|
||||
- 2017-09-22 Can be plotted but the loop and wait not work yet
|
||||
|
||||
@author: Jiaguo Zhang and Julian Heymes
|
||||
"""
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from matplotlib.patches import Rectangle
|
||||
|
||||
|
||||
class PlotPattern:
|
||||
|
||||
def __init__(self, pattern, signalNames, colors_plot, colors_wait, linestyles_wait, alpha_wait, alpha_wait_rect,
|
||||
colors_loop, linestyles_loop, alpha_loop, alpha_loop_rect, clock_vertical_lines_spacing,
|
||||
show_clocks_number, line_width):
|
||||
self.pattern = pattern
|
||||
self.signalNames = signalNames
|
||||
self.verbose = False
|
||||
# TODO: send alias
|
||||
|
||||
self.colors_plot = colors_plot.copy()
|
||||
self.colors_wait = colors_wait.copy()
|
||||
self.linestyles_wait = linestyles_wait.copy()
|
||||
self.alpha_wait = alpha_wait.copy()
|
||||
self.alpha_wait_rect = alpha_wait_rect.copy()
|
||||
self.colors_loop = colors_loop.copy()
|
||||
self.linestyles_loop = linestyles_loop.copy()
|
||||
self.alpha_loop = alpha_loop.copy()
|
||||
self.alpha_loop_rect = alpha_loop_rect.copy()
|
||||
self.clock_vertical_lines_spacing = clock_vertical_lines_spacing
|
||||
self.show_clocks_number = show_clocks_number
|
||||
self.line_width = line_width
|
||||
|
||||
self.colors_plot[0] = f'xkcd:{colors_plot[0].lower()}'
|
||||
self.colors_plot[1] = f'xkcd:{colors_plot[1].lower()}'
|
||||
|
||||
for i in range(6):
|
||||
self.colors_wait[i] = f'xkcd:{colors_wait[i].lower()}'
|
||||
self.colors_loop[i] = f'xkcd:{colors_loop[i].lower()}'
|
||||
|
||||
if self.verbose:
|
||||
self.printPatViewerParameters()
|
||||
|
||||
def printPatViewerParameters(self):
|
||||
print('Pattern Viewer Parameters:')
|
||||
print(f'\tcolor1: {self.colors_plot[0]}, color2: {self.colors_plot[1]}')
|
||||
print(f"\twait color: {self.colors_wait}")
|
||||
print(f"\twait linestyles: {self.linestyles_wait}")
|
||||
print(f"\twait alpha: {self.alpha_wait}")
|
||||
print(f"\twait alpha rect: {self.alpha_wait_rect}")
|
||||
print(f"\tloop color: {self.colors_loop}")
|
||||
print(f"\tloop linestyles: {self.linestyles_loop}")
|
||||
print(f"\tloop alpha: {self.alpha_loop}")
|
||||
print(f"\tloop alpha rect: {self.alpha_loop_rect}")
|
||||
print(f'\tclock vertical lines spacing: {self.clock_vertical_lines_spacing}')
|
||||
print(f'\tshow clocks number: {self.show_clocks_number}')
|
||||
print(f'\tline width: {self.line_width}')
|
||||
print('\n')
|
||||
|
||||
def dec2binary(self, dec_num, width=None):
|
||||
return np.binary_repr(int(dec_num), width=width)
|
||||
|
||||
def hex2dec(self, string_num):
|
||||
return str(int(string_num.upper(), 16))
|
||||
|
||||
def hex2binary(self, string_num, width=None):
|
||||
return self.dec2binary(self.hex2dec(string_num.upper()), width=width)
|
||||
|
||||
def patternPlot(self):
|
||||
# Define a hex to binary function
|
||||
# global definition
|
||||
# base = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F]
|
||||
self.base = [str(x) for x in range(10)] + [chr(x) for x in range(ord('A'), ord('A') + 6)]
|
||||
|
||||
# Load the pattern and get all lines
|
||||
# Loop all lines
|
||||
# with open(Folder + "/" + File_pat + ".pat") as f_pat:
|
||||
with open(self.pattern) as f_pat:
|
||||
lines_pat = f_pat.readlines()
|
||||
|
||||
# number of lines for pattern file
|
||||
nlines_pat = len(lines_pat)
|
||||
# a counter
|
||||
cnt = 0
|
||||
if self.verbose:
|
||||
print("The total number of lines of pattern:", nlines_pat)
|
||||
|
||||
# Loop all lines of pattern
|
||||
waittime0 = None
|
||||
waittime1 = None
|
||||
waittime2 = None
|
||||
waittime3 = None
|
||||
waittime4 = None
|
||||
waittime5 = None
|
||||
|
||||
nloop0 = None
|
||||
nloop1 = None
|
||||
nloop2 = None
|
||||
nloop3 = None
|
||||
nloop4 = None
|
||||
nloop5 = None
|
||||
|
||||
for k in range(nlines_pat):
|
||||
# content of line
|
||||
words_line = lines_pat[k].split()
|
||||
if len(words_line) < 2:
|
||||
continue
|
||||
if words_line[0] == "patword":
|
||||
# print words_line from b0 to b63
|
||||
bits = self.hex2binary(words_line[-1], 64)[::-1]
|
||||
if self.verbose:
|
||||
print("The bits for line-", k + 1, "is:", bits)
|
||||
# convert string bits to decimal array
|
||||
num_bits = np.array(list(map(str, bits)), dtype="uint16")
|
||||
if cnt == 0:
|
||||
mat_pat = num_bits
|
||||
else:
|
||||
# add bits to matrix
|
||||
mat_pat = np.concatenate((mat_pat, num_bits), axis=0)
|
||||
cnt = cnt + 1
|
||||
# print("The matrix of pattern:", mat_pat.reshape(int(cnt), int(len(num_bits))))
|
||||
|
||||
# Look at the io: 0 for sending to ASIC, 1 for reading from ASIC
|
||||
if words_line[0] == "patioctrl":
|
||||
# print words_line
|
||||
if self.verbose:
|
||||
print(words_line[-1])
|
||||
bits = self.hex2binary(words_line[-1], 64)[::-1]
|
||||
if self.verbose:
|
||||
print(bits)
|
||||
# convert string bits to decimal array
|
||||
self.out_bits = np.array(list(map(str, bits)), dtype="uint16")
|
||||
|
||||
if self.verbose:
|
||||
print(words_line)
|
||||
# Deal with waiting point
|
||||
|
||||
# ====== WAIT ======
|
||||
if words_line[0] == "patwait" and words_line[1] == "0":
|
||||
wait0 = int(self.hex2dec(words_line[2]))
|
||||
if self.verbose:
|
||||
print("wait 0 at:", wait0)
|
||||
if words_line[0] == "patwaittime" and words_line[1] == "0":
|
||||
waittime0 = int(words_line[2])
|
||||
if self.verbose:
|
||||
print("wait 0 for:", waittime0)
|
||||
|
||||
if words_line[0] == "patwait" and words_line[1] == "1":
|
||||
wait1 = int(self.hex2dec(words_line[2]))
|
||||
if self.verbose:
|
||||
print("wait 1 at:", wait1)
|
||||
if words_line[0] == "patwaittime" and words_line[1] == "1":
|
||||
waittime1 = int(words_line[2])
|
||||
if self.verbose:
|
||||
print("wait 1 for:", waittime1)
|
||||
|
||||
if words_line[0] == "patwait" and words_line[1] == "2":
|
||||
wait2 = int(self.hex2dec(words_line[2]))
|
||||
if self.verbose:
|
||||
print("wait 2 at:", wait2)
|
||||
if words_line[0] == "patwaittime" and words_line[1] == "2":
|
||||
waittime2 = int(words_line[2])
|
||||
if self.verbose:
|
||||
print("wait 2 for:", waittime2)
|
||||
|
||||
if words_line[0] == "patwait" and words_line[1] == "3":
|
||||
wait3 = int(self.hex2dec(words_line[2]))
|
||||
if self.verbose:
|
||||
print("wait 0 at:", wait3)
|
||||
if words_line[0] == "patwaittime" and words_line[1] == "3":
|
||||
waittime3 = int(words_line[2])
|
||||
if self.verbose:
|
||||
print("wait 0 for:", waittime3)
|
||||
|
||||
if words_line[0] == "patwait" and words_line[1] == "4":
|
||||
wait4 = int(self.hex2dec(words_line[2]))
|
||||
if self.verbose:
|
||||
print("wait 1 at:", wait4)
|
||||
if words_line[0] == "patwaittime" and words_line[1] == "4":
|
||||
waittime4 = int(words_line[2])
|
||||
if self.verbose:
|
||||
print("wait 1 for:", waittime4)
|
||||
|
||||
if words_line[0] == "patwait" and words_line[1] == "5":
|
||||
wait5 = int(self.hex2dec(words_line[2]))
|
||||
if self.verbose:
|
||||
print("wait 2 at:", wait5)
|
||||
if words_line[0] == "patwaittime" and words_line[1] == "5":
|
||||
waittime5 = int(words_line[2])
|
||||
if self.verbose:
|
||||
print("wait 2 for:", waittime5)
|
||||
|
||||
# ====== LOOPS ======
|
||||
if words_line[0] == "patloop" and words_line[1] == "0":
|
||||
loop0_start = int(self.hex2dec(words_line[2]))
|
||||
loop0_end = int(self.hex2dec(words_line[3]))
|
||||
if self.verbose:
|
||||
print("loop 0 start:", loop0_start, ", end:", loop0_end)
|
||||
if words_line[0] == "patnloop" and words_line[1] == "0":
|
||||
nloop0 = int(words_line[2])
|
||||
if self.verbose:
|
||||
print("loop 0 times:", nloop0)
|
||||
|
||||
if words_line[0] == "patloop" and words_line[1] == "1":
|
||||
loop1_start = int(self.hex2dec(words_line[2]))
|
||||
loop1_end = int(self.hex2dec(words_line[3]))
|
||||
if self.verbose:
|
||||
print("loop 1 start:", loop1_start, ", end:", loop1_end)
|
||||
if words_line[0] == "patnloop" and words_line[1] == "1":
|
||||
nloop1 = int(words_line[2])
|
||||
if self.verbose:
|
||||
print("loop 1 times:", nloop1)
|
||||
|
||||
if words_line[0] == "patloop" and words_line[1] == "2":
|
||||
loop2_start = int(self.hex2dec(words_line[2]))
|
||||
loop2_end = int(self.hex2dec(words_line[3]))
|
||||
if self.verbose:
|
||||
print("loop 2 start:", loop2_start, ", end:", loop2_end)
|
||||
if words_line[0] == "patnloop" and words_line[1] == "2":
|
||||
nloop2 = int(words_line[2])
|
||||
if self.verbose:
|
||||
print("loop 2 times:", nloop2)
|
||||
|
||||
if words_line[0] == "patloop" and words_line[1] == "3":
|
||||
loop3_start = int(self.hex2dec(words_line[2]))
|
||||
loop3_end = int(self.hex2dec(words_line[3]))
|
||||
if self.verbose:
|
||||
print("loop 3 start:", loop3_start, ", end:", loop3_end)
|
||||
if words_line[0] == "patnloop" and words_line[1] == "3":
|
||||
nloop3 = int(words_line[2])
|
||||
if self.verbose:
|
||||
print("loop 3 times:", nloop3)
|
||||
|
||||
if words_line[0] == "patloop" and words_line[1] == "4":
|
||||
loop4_start = int(self.hex2dec(words_line[2]))
|
||||
loop4_end = int(self.hex2dec(words_line[3]))
|
||||
if self.verbose:
|
||||
print("loop 4 start:", loop4_start, ", end:", loop4_end)
|
||||
if words_line[0] == "patnloop" and words_line[1] == "4":
|
||||
nloop4 = int(words_line[2])
|
||||
if self.verbose:
|
||||
print("loop 4 times:", nloop4)
|
||||
|
||||
if words_line[0] == "patloop" and words_line[1] == "5":
|
||||
loop5_start = int(self.hex2dec(words_line[2]))
|
||||
loop5_end = int(self.hex2dec(words_line[3]))
|
||||
if self.verbose:
|
||||
print("loop 5 start:", loop5_start, ", end:", loop5_end)
|
||||
if words_line[0] == "patnloop" and words_line[1] == "5":
|
||||
nloop5 = int(words_line[2])
|
||||
if self.verbose:
|
||||
print("loop 5 times:", nloop5)
|
||||
|
||||
# no patioctrl commands read
|
||||
if not hasattr(self, 'out_bits'):
|
||||
raise Exception("No patioctrl command found in pattern file")
|
||||
# print(self.out_bits)
|
||||
|
||||
# internal counter
|
||||
avail_index = []
|
||||
avail_name = []
|
||||
# Remove non-used bits
|
||||
for i in range(64):
|
||||
# if self.out_bits[0][i] == 1:
|
||||
if self.out_bits[i] == 1:
|
||||
avail_index.append(i)
|
||||
avail_name.append(self.signalNames[i])
|
||||
if self.verbose:
|
||||
print(avail_index)
|
||||
print(avail_name)
|
||||
|
||||
# number of effective used bits
|
||||
nbiteff = len(avail_name)
|
||||
|
||||
# subMat = mat_ext[:,index]
|
||||
# print(mat_pat.shape)
|
||||
subMat = mat_pat.reshape(int(cnt), int(len(num_bits)))[0:, avail_index]
|
||||
# subMat = mat_pat[avail_index]
|
||||
# timing = np.linspace(0, subMat.shape[0] - 1, subMat.shape[0])
|
||||
plt.rcParams['figure.figsize'] = 15, 5
|
||||
|
||||
# ============= PLOTTING =============
|
||||
|
||||
plt.rcParams["font.weight"] = "bold"
|
||||
plt.rcParams["axes.labelweight"] = "bold"
|
||||
fig, axs = plt.subplots(nbiteff, sharex='all')
|
||||
plt.subplots_adjust(wspace=0, hspace=0)
|
||||
# axs[nbiteff - 1].set(xlabel='Timing [clk]')
|
||||
for idx, i in enumerate(range(nbiteff)):
|
||||
axs[idx].tick_params(axis='x', labelsize=6)
|
||||
|
||||
axs[idx].plot(subMat.T[i],
|
||||
"-",
|
||||
drawstyle="steps-post",
|
||||
linewidth=self.line_width,
|
||||
color=self.colors_plot[idx % 2])
|
||||
x_additional = range(len(subMat.T[i]) - 1, len(subMat.T[i]) + 2)
|
||||
additional_stuff = [subMat.T[i][-1]] * 3
|
||||
|
||||
axs[idx].plot(x_additional,
|
||||
additional_stuff,
|
||||
"--",
|
||||
drawstyle="steps-post",
|
||||
linewidth=self.line_width,
|
||||
color=self.colors_plot[idx % 2],
|
||||
alpha=0.5)
|
||||
axs[idx].yaxis.set_ticks([0.5], minor=False)
|
||||
axs[idx].xaxis.set_ticks(np.arange(0, len(subMat.T[i]) + 10, self.clock_vertical_lines_spacing))
|
||||
|
||||
axs[idx].yaxis.set_ticklabels([avail_name[i]])
|
||||
axs[idx].get_yticklabels()[0].set_color(self.colors_plot[idx % 2])
|
||||
|
||||
axs[idx].grid(1, 'both', 'both', alpha=0.5)
|
||||
axs[idx].yaxis.grid(which="both", color=self.colors_plot[idx % 2], alpha=0.2)
|
||||
if idx != nbiteff - 1:
|
||||
if not self.show_clocks_number:
|
||||
axs[idx].xaxis.set_ticklabels([])
|
||||
axs[idx].set(xlabel=' ', ylim=(-0.2, 1.2))
|
||||
else:
|
||||
axs[idx].set(xlabel='Timing [clk]', ylim=(-0.2, 1.2))
|
||||
# axs[idx].set_xlim(left=0)
|
||||
axs[idx].set_xlim(left=0, right=len(subMat.T[i]) + 1)
|
||||
axs[idx].spines['top'].set_visible(False)
|
||||
axs[idx].spines['right'].set_alpha(0.2)
|
||||
axs[idx].spines['right'].set_visible(True)
|
||||
axs[idx].spines['bottom'].set_visible(False)
|
||||
axs[idx].spines['left'].set_visible(False)
|
||||
|
||||
# =====================================================================================================
|
||||
# Plot the wait lines
|
||||
# Wait 0
|
||||
if waittime0 is not None:
|
||||
if waittime0 == 0:
|
||||
axs[idx].plot([wait0, wait0], [-10, 10],
|
||||
linestyle=self.linestyles_wait[0],
|
||||
color=self.colors_wait[0],
|
||||
alpha=self.alpha_wait[0],
|
||||
linewidth=self.line_width)
|
||||
axs[idx].plot([wait0 + 1, wait0 + 1], [-10, 10],
|
||||
linestyle=self.linestyles_wait[0],
|
||||
color=self.colors_wait[0],
|
||||
linewidth=self.line_width,
|
||||
alpha=self.alpha_wait[0])
|
||||
axs[idx].add_patch(
|
||||
Rectangle((wait0, -10),
|
||||
1,
|
||||
20,
|
||||
label="wait 0: skipped" if idx == 0 else "",
|
||||
facecolor=self.colors_wait[0],
|
||||
alpha=self.alpha_wait_rect[0],
|
||||
hatch='\\\\'))
|
||||
else:
|
||||
axs[idx].plot([wait0, wait0], [-10, 10],
|
||||
linestyle=self.linestyles_wait[0],
|
||||
color=self.colors_wait[0],
|
||||
label="wait 0: " + str(waittime0) + " clk" if idx == 0 else "",
|
||||
linewidth=self.line_width,
|
||||
alpha=self.alpha_wait[0])
|
||||
|
||||
# Wait 1
|
||||
if waittime1 is not None:
|
||||
if waittime1 == 0:
|
||||
axs[idx].plot([wait1, wait1], [-10, 10],
|
||||
linestyle=self.linestyles_wait[1],
|
||||
color=self.colors_wait[1],
|
||||
alpha=self.alpha_wait[1],
|
||||
linewidth=self.line_width)
|
||||
axs[idx].plot([wait1 + 1, wait1 + 1], [-10, 10],
|
||||
linestyle=self.linestyles_wait[1],
|
||||
color=self.colors_wait[1],
|
||||
linewidth=self.line_width,
|
||||
alpha=self.alpha_wait[1])
|
||||
axs[idx].add_patch(
|
||||
Rectangle((wait1, -10),
|
||||
1,
|
||||
20,
|
||||
label="wait 1: skipped" if idx == 0 else "",
|
||||
facecolor=self.colors_wait[1],
|
||||
alpha=self.alpha_wait_rect[1],
|
||||
hatch='\\\\'))
|
||||
else:
|
||||
axs[idx].plot([wait1, wait1], [-10, 10],
|
||||
linestyle=self.linestyles_wait[1],
|
||||
color=self.colors_wait[1],
|
||||
label="wait 1: " + str(waittime1) + " clk" if idx == 0 else "",
|
||||
linewidth=self.line_width,
|
||||
alpha=self.alpha_wait[1])
|
||||
|
||||
# Wait 2
|
||||
if waittime2 is not None:
|
||||
if waittime2 == 0:
|
||||
axs[idx].plot([wait2, wait2], [-10, 10],
|
||||
linestyle=self.linestyles_wait[2],
|
||||
color=self.colors_wait[2],
|
||||
alpha=self.alpha_wait[2],
|
||||
linewidth=self.line_width)
|
||||
axs[idx].plot([wait2 + 1, wait2 + 1], [-10, 10],
|
||||
linestyle=self.linestyles_wait[2],
|
||||
color=self.colors_wait[2],
|
||||
linewidth=self.line_width,
|
||||
alpha=self.alpha_wait[2])
|
||||
axs[idx].add_patch(
|
||||
Rectangle((wait2, -10),
|
||||
1,
|
||||
20,
|
||||
label="wait 2: skipped" if idx == 0 else "",
|
||||
facecolor=self.colors_wait[2],
|
||||
alpha=self.alpha_wait_rect[2],
|
||||
hatch='\\\\'))
|
||||
else:
|
||||
axs[idx].plot([wait2, wait2], [-10, 10],
|
||||
linestyle=self.linestyles_wait[2],
|
||||
color=self.colors_wait[2],
|
||||
label="wait 2: " + str(waittime2) + " clk" if idx == 0 else "",
|
||||
linewidth=self.line_width,
|
||||
alpha=self.alpha_wait[2])
|
||||
|
||||
# Wait 3
|
||||
if waittime3 is not None:
|
||||
if waittime3 == 0:
|
||||
axs[idx].plot([wait3, wait3], [-10, 10],
|
||||
linestyle=self.linestyles_wait[3],
|
||||
color=self.colors_wait[3],
|
||||
alpha=self.alpha_wait[3],
|
||||
linewidth=self.line_width)
|
||||
axs[idx].plot([wait3 + 1, wait3 + 1], [-10, 10],
|
||||
linestyle=self.linestyles_wait[3],
|
||||
color=self.colors_wait[3],
|
||||
linewidth=self.line_width,
|
||||
alpha=self.alpha_wait[3])
|
||||
axs[idx].add_patch(
|
||||
Rectangle((wait3, -10),
|
||||
1,
|
||||
20,
|
||||
label="wait 3: skipped" if idx == 0 else "",
|
||||
facecolor=self.colors_wait[3],
|
||||
alpha=self.alpha_wait_rect[3],
|
||||
hatch='\\\\'))
|
||||
else:
|
||||
axs[idx].plot([wait3, wait3], [-10, 10],
|
||||
linestyle=self.linestyles_wait[3],
|
||||
color=self.colors_wait[3],
|
||||
label="wait 3: " + str(waittime3) + " clk" if idx == 0 else "",
|
||||
linewidth=self.line_width,
|
||||
alpha=self.alpha_wait[3])
|
||||
|
||||
# Wait 4
|
||||
if waittime4 is not None:
|
||||
if waittime4 == 0:
|
||||
axs[idx].plot([wait4, wait4], [-10, 10],
|
||||
linestyle=self.linestyles_wait[4],
|
||||
color=self.colors_wait[4],
|
||||
alpha=self.alpha_wait[4],
|
||||
linewidth=self.line_width)
|
||||
axs[idx].plot([wait4 + 1, wait4 + 1], [-10, 10],
|
||||
linestyle=self.linestyles_wait[4],
|
||||
color=self.colors_wait[4],
|
||||
linewidth=self.line_width,
|
||||
alpha=self.alpha_wait[4])
|
||||
axs[idx].add_patch(
|
||||
Rectangle((wait4, -10),
|
||||
1,
|
||||
20,
|
||||
label="wait 4: skipped" if idx == 0 else "",
|
||||
facecolor=self.colors_wait[4],
|
||||
alpha=self.alpha_wait_rect[4],
|
||||
hatch='\\\\'))
|
||||
else:
|
||||
axs[idx].plot([wait4, wait4], [-10, 10],
|
||||
linestyle=self.linestyles_wait[4],
|
||||
color=self.colors_wait[4],
|
||||
label="wait 4: " + str(waittime4) + " clk" if idx == 0 else "",
|
||||
linewidth=self.line_width,
|
||||
alpha=self.alpha_wait[4])
|
||||
|
||||
# Wait 5
|
||||
if waittime5 is not None:
|
||||
if waittime5 == 0:
|
||||
axs[idx].plot([wait5, wait5], [-10, 10],
|
||||
linestyle=self.linestyles_wait[5],
|
||||
color=self.colors_wait[5],
|
||||
alpha=self.alpha_wait[5],
|
||||
linewidth=self.line_width)
|
||||
axs[idx].plot([wait5 + 1, wait5 + 1], [-10, 10],
|
||||
linestyle=self.linestyles_wait[5],
|
||||
color=self.colors_wait[5],
|
||||
linewidth=self.line_width,
|
||||
alpha=self.alpha_wait[5])
|
||||
axs[idx].add_patch(
|
||||
Rectangle((wait5, -10),
|
||||
1,
|
||||
20,
|
||||
label="wait 5: skipped" if idx == 0 else "",
|
||||
facecolor=self.colors_wait[5],
|
||||
alpha=self.alpha_wait_rect[5],
|
||||
hatch='\\\\'))
|
||||
else:
|
||||
axs[idx].plot([wait5, wait5], [-10, 10],
|
||||
linestyle=self.linestyles_wait[5],
|
||||
color=self.colors_wait[5],
|
||||
label="wait 5: " + str(waittime5) + " clk" if idx == 0 else "",
|
||||
linewidth=self.line_width,
|
||||
alpha=self.alpha_wait[5])
|
||||
|
||||
# =====================================================================================================
|
||||
# Plot the loop lines
|
||||
# Loop 0
|
||||
if nloop0 is not None:
|
||||
if nloop0 == 0:
|
||||
axs[idx].plot([loop0_start, loop0_start], [-10, 10],
|
||||
linestyle=self.linestyles_loop[0],
|
||||
color=self.colors_loop[0],
|
||||
alpha=self.alpha_loop[0],
|
||||
linewidth=self.line_width)
|
||||
axs[idx].plot([loop0_end + 1, loop0_end + 1], [-10, 10],
|
||||
linestyle=self.linestyles_loop[0],
|
||||
color=self.colors_loop[0],
|
||||
alpha=self.alpha_loop[0],
|
||||
linewidth=self.line_width)
|
||||
axs[idx].add_patch(
|
||||
Rectangle((loop0_start, -10),
|
||||
loop0_end + 1 - loop0_start,
|
||||
20,
|
||||
label="loop 0: skipped" if idx == 0 else "",
|
||||
facecolor=self.colors_loop[0],
|
||||
alpha=self.alpha_loop_rect[0],
|
||||
hatch='//'))
|
||||
else:
|
||||
axs[idx].plot([loop0_start, loop0_start], [-10, 10],
|
||||
linestyle=self.linestyles_loop[0],
|
||||
color=self.colors_loop[0],
|
||||
alpha=self.alpha_loop[0],
|
||||
label="loop 0: " + str(nloop0) + " times" if idx == 0 else "",
|
||||
linewidth=self.line_width)
|
||||
axs[idx].plot([loop0_end, loop0_end], [-10, 10],
|
||||
linestyle=self.linestyles_loop[0],
|
||||
color=self.colors_loop[0],
|
||||
alpha=self.alpha_loop[0],
|
||||
linewidth=self.line_width)
|
||||
|
||||
# Loop 1
|
||||
if nloop1 is not None:
|
||||
if nloop1 == 0:
|
||||
axs[idx].plot([loop1_start, loop1_start], [-10, 10],
|
||||
linestyle=self.linestyles_loop[1],
|
||||
color=self.colors_loop[1],
|
||||
alpha=self.alpha_loop[1],
|
||||
linewidth=self.line_width)
|
||||
axs[idx].plot([loop1_end + 1, loop1_end + 1], [-10, 10],
|
||||
linestyle=self.linestyles_loop[1],
|
||||
color=self.colors_loop[1],
|
||||
alpha=self.alpha_loop[1],
|
||||
linewidth=self.line_width)
|
||||
axs[idx].add_patch(
|
||||
Rectangle((loop1_start, -10),
|
||||
loop1_end + 1 - loop1_start,
|
||||
20,
|
||||
label="loop 1: skipped" if idx == 0 else "",
|
||||
facecolor=self.colors_loop[1],
|
||||
alpha=self.alpha_loop_rect[1],
|
||||
hatch='//'))
|
||||
else:
|
||||
axs[idx].plot([loop1_start, loop1_start], [-10, 10],
|
||||
linestyle=self.linestyles_loop[1],
|
||||
color=self.colors_loop[1],
|
||||
alpha=self.alpha_loop[1],
|
||||
label="loop 1: " + str(nloop1) + " times" if idx == 0 else "",
|
||||
linewidth=self.line_width)
|
||||
axs[idx].plot([loop1_end, loop1_end], [-10, 10],
|
||||
linestyle=self.linestyles_loop[1],
|
||||
color=self.colors_loop[1],
|
||||
alpha=self.alpha_loop[1],
|
||||
linewidth=self.line_width)
|
||||
|
||||
# Loop 2
|
||||
if nloop2 is not None:
|
||||
if nloop2 == 0:
|
||||
axs[idx].plot([loop2_start, loop2_start], [-10, 10],
|
||||
linestyle=self.linestyles_loop[2],
|
||||
color=self.colors_loop[2],
|
||||
alpha=self.alpha_loop[2],
|
||||
linewidth=self.line_width)
|
||||
axs[idx].plot([loop2_end + 1, loop2_end + 1], [-10, 10],
|
||||
linestyle=self.linestyles_loop[2],
|
||||
color=self.colors_loop[2],
|
||||
alpha=self.alpha_loop[2],
|
||||
linewidth=self.line_width)
|
||||
axs[idx].add_patch(
|
||||
Rectangle((loop2_start, -10),
|
||||
loop2_end + 1 - loop2_start,
|
||||
20,
|
||||
label="loop 2: skipped" if idx == 0 else "",
|
||||
facecolor=self.colors_loop[2],
|
||||
alpha=self.alpha_loop_rect[2],
|
||||
hatch='//'))
|
||||
else:
|
||||
axs[idx].plot([loop2_start, loop2_start], [-10, 10],
|
||||
linestyle=self.linestyles_loop[2],
|
||||
color=self.colors_loop[2],
|
||||
alpha=self.alpha_loop[2],
|
||||
label="loop 2: " + str(nloop2) + " times" if idx == 0 else "",
|
||||
linewidth=self.line_width)
|
||||
axs[idx].plot([loop2_end, loop2_end], [-10, 10],
|
||||
linestyle=self.linestyles_loop[2],
|
||||
color=self.colors_loop[2],
|
||||
alpha=self.alpha_loop[2],
|
||||
linewidth=self.line_width)
|
||||
|
||||
# Loop 3
|
||||
if nloop3 is not None:
|
||||
if nloop3 == 0:
|
||||
axs[idx].plot([loop3_start, loop3_start], [-10, 10],
|
||||
linestyle=self.linestyles_loop[3],
|
||||
color=self.colors_loop[3],
|
||||
alpha=self.alpha_loop[3],
|
||||
linewidth=self.line_width)
|
||||
axs[idx].plot([loop3_end + 1, loop3_end + 1], [-10, 10],
|
||||
linestyle=self.linestyles_loop[3],
|
||||
color=self.colors_loop[3],
|
||||
alpha=self.alpha_loop[3],
|
||||
linewidth=self.line_width)
|
||||
axs[idx].add_patch(
|
||||
Rectangle((loop3_start, -10),
|
||||
loop3_end + 1 - loop3_start,
|
||||
20,
|
||||
label="loop 3: skipped" if idx == 0 else "",
|
||||
facecolor=self.colors_loop[3],
|
||||
alpha=self.alpha_loop_rect[3],
|
||||
hatch='//'))
|
||||
else:
|
||||
axs[idx].plot([loop3_start, loop3_start], [-10, 10],
|
||||
linestyle=self.linestyles_loop[3],
|
||||
color=self.colors_loop[3],
|
||||
alpha=self.alpha_loop[3],
|
||||
label="loop 3: " + str(nloop3) + " times" if idx == 0 else "",
|
||||
linewidth=self.line_width)
|
||||
axs[idx].plot([loop3_end, loop3_end], [-10, 10],
|
||||
linestyle=self.linestyles_loop[3],
|
||||
color=self.colors_loop[3],
|
||||
alpha=self.alpha_loop[3],
|
||||
linewidth=self.line_width)
|
||||
|
||||
# Loop 4
|
||||
if nloop4 is not None:
|
||||
if nloop4 == 0:
|
||||
axs[idx].plot([loop4_start, loop4_start], [-10, 10],
|
||||
linestyle=self.linestyles_loop[4],
|
||||
color=self.colors_loop[4],
|
||||
alpha=self.alpha_loop[4],
|
||||
linewidth=self.line_width)
|
||||
axs[idx].plot([loop4_end + 1, loop4_end + 1], [-10, 10],
|
||||
linestyle=self.linestyles_loop[4],
|
||||
color=self.colors_loop[4],
|
||||
alpha=self.alpha_loop[4],
|
||||
linewidth=self.line_width)
|
||||
axs[idx].add_patch(
|
||||
Rectangle((loop4_start, -10),
|
||||
loop4_end + 1 - loop4_start,
|
||||
20,
|
||||
label="loop 4: skipped" if idx == 0 else "",
|
||||
facecolor=self.colors_loop[4],
|
||||
alpha=self.alpha_loop_rect[4],
|
||||
hatch='//'))
|
||||
else:
|
||||
axs[idx].plot([loop4_start, loop4_start], [-10, 10],
|
||||
linestyle=self.linestyles_loop[4],
|
||||
color=self.colors_loop[4],
|
||||
alpha=self.alpha_loop[4],
|
||||
label="loop 4: " + str(nloop4) + " times" if idx == 0 else "",
|
||||
linewidth=self.line_width)
|
||||
axs[idx].plot([loop4_end, loop4_end], [-10, 10],
|
||||
linestyle=self.linestyles_loop[4],
|
||||
color=self.colors_loop[4],
|
||||
alpha=self.alpha_loop[4],
|
||||
linewidth=self.line_width)
|
||||
|
||||
# Loop 5
|
||||
if nloop5 is not None:
|
||||
if nloop5 == 0:
|
||||
axs[idx].plot([loop5_start, loop5_start], [-10, 10],
|
||||
linestyle=self.linestyles_loop[5],
|
||||
color=self.colors_loop[5],
|
||||
alpha=self.alpha_loop[5],
|
||||
linewidth=self.line_width)
|
||||
axs[idx].plot([loop5_end + 1, loop5_end + 1], [-10, 10],
|
||||
linestyle=self.linestyles_loop[5],
|
||||
color=self.colors_loop[5],
|
||||
alpha=self.alpha_loop[5],
|
||||
linewidth=self.line_width)
|
||||
axs[idx].add_patch(
|
||||
Rectangle((loop5_start, -10),
|
||||
loop5_end + 1 - loop5_start,
|
||||
20,
|
||||
label="loop 5: skipped" if idx == 0 else "",
|
||||
facecolor=self.colors_loop[5],
|
||||
alpha=self.alpha_loop_rect[5],
|
||||
hatch='//'))
|
||||
else:
|
||||
axs[idx].plot([loop5_start, loop5_start], [-10, 10],
|
||||
linestyle=self.linestyles_loop[5],
|
||||
color=self.colors_loop[5],
|
||||
alpha=self.alpha_loop[5],
|
||||
label="loop 5: " + str(nloop5) + " times" if idx == 0 else "",
|
||||
linewidth=self.line_width)
|
||||
axs[idx].plot([loop5_end, loop5_end], [-10, 10],
|
||||
linestyle=self.linestyles_loop[5],
|
||||
color=self.colors_loop[5],
|
||||
alpha=self.alpha_loop[5],
|
||||
linewidth=self.line_width)
|
||||
|
||||
n_cols = np.count_nonzero([
|
||||
waittime0 != 0, waittime1 != 0, waittime2 != 0, waittime3 != 0, waittime4 != 0, waittime5 != 0, nloop0
|
||||
!= 0, nloop1 != 0, nloop2 != 0, nloop3 != 0, nloop4 != 0, nloop5 != 0
|
||||
])
|
||||
if n_cols > 0:
|
||||
fig.legend(loc="upper center", ncol=n_cols)
|
||||
return fig
|
||||
100
pyctbgui/pyctbgui/utils/recordOrApplyPedestal.py
Normal file
100
pyctbgui/pyctbgui/utils/recordOrApplyPedestal.py
Normal file
@@ -0,0 +1,100 @@
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
|
||||
__frameCount = 0
|
||||
__pedestalSum = np.array(0, np.float64)
|
||||
__pedestal = np.array(0, np.float32)
|
||||
__loadedPedestal = False
|
||||
|
||||
|
||||
def reset(plotTab):
|
||||
global __frameCount, __pedestalSum, __pedestal, __loadedPedestal
|
||||
__frameCount = 0
|
||||
__pedestalSum = np.array(0, np.float64)
|
||||
__pedestal = np.array(0, np.float64)
|
||||
__loadedPedestal = False
|
||||
|
||||
plotTab.updateLabelPedestalFrames()
|
||||
|
||||
|
||||
def getFramesCount():
|
||||
return __frameCount
|
||||
|
||||
|
||||
def getPedestal():
|
||||
return __pedestal
|
||||
|
||||
|
||||
def calculatePedestal():
|
||||
global __pedestalSum, __pedestal
|
||||
if __loadedPedestal:
|
||||
return __pedestal
|
||||
if __frameCount == 0:
|
||||
__pedestal = np.array(0, np.float64)
|
||||
else:
|
||||
__pedestal = __pedestalSum / __frameCount
|
||||
return __pedestal
|
||||
|
||||
|
||||
def savePedestal(path=Path('/tmp/pedestal')):
|
||||
pedestal = calculatePedestal()
|
||||
np.save(path, pedestal)
|
||||
|
||||
|
||||
def loadPedestal(path: Path):
|
||||
global __pedestal, __loadedPedestal
|
||||
__loadedPedestal = True
|
||||
__pedestal = np.load(path)
|
||||
|
||||
|
||||
__logger = logging.getLogger('recordOrApplyPedestal')
|
||||
|
||||
|
||||
def recordOrApplyPedestal(func):
|
||||
"""
|
||||
decorator function used to apply pedestal functionalities
|
||||
@param func: processing function that needs to be wrapped
|
||||
@return: wrapper function to be called
|
||||
"""
|
||||
|
||||
def wrapper(obj, *args, **kwargs):
|
||||
"""
|
||||
wrapeer that calls func (a raw data _processing function) and calculates or applies a pedestal to it
|
||||
@param obj: reference to func's class instance (self of its class)
|
||||
@return: if record mode: return frame untouched, if apply mode: return frame - pedestal
|
||||
"""
|
||||
global __frameCount, __pedestal, __pedestalSum
|
||||
|
||||
frame = func(obj, *args, **kwargs)
|
||||
if not np.array_equal(0, __pedestalSum) and __pedestalSum.shape != frame.shape:
|
||||
# check if __pedestalSum has same different shape as the frame
|
||||
__logger.info('pedestal shape mismatch. resetting pedestal...')
|
||||
reset(obj.plotTab)
|
||||
|
||||
if obj.plotTab.pedestalRecord:
|
||||
if __loadedPedestal:
|
||||
# reset loaded pedestal if we acquire in record mode
|
||||
__logger.warning('resetting loaded pedestal...')
|
||||
reset(obj.plotTab)
|
||||
__frameCount += 1
|
||||
|
||||
__pedestalSum = np.add(__pedestalSum, frame, dtype=np.float64)
|
||||
|
||||
obj.plotTab.updateLabelPedestalFrames()
|
||||
return frame
|
||||
if obj.plotTab.pedestalApply:
|
||||
# apply pedestal
|
||||
# check if pedestal is calculated
|
||||
if __loadedPedestal and frame.shape != __pedestal.shape:
|
||||
__logger.warning('pedestal shape mismatch. resetting pedestal...')
|
||||
obj.plotTab.mainWindow.statusbar.setStyleSheet("color:red")
|
||||
obj.plotTab.mainWindow.statusbar.showMessage('pedestal shape mismatch. resetting pedestal...')
|
||||
reset(obj.plotTab)
|
||||
|
||||
return frame - calculatePedestal()
|
||||
|
||||
return frame
|
||||
|
||||
return wrapper
|
||||
Reference in New Issue
Block a user