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:
2024-09-10 16:00:04 +02:00
committed by GitHub
parent c6477e0ed6
commit 5b61ff24bb
93 changed files with 25399 additions and 7292 deletions

View File

@ -0,0 +1,2 @@
from .utils.defines import Defines as defs
from .utils import bit_utils, alias_utility

View File

@ -0,0 +1,401 @@
import logging
import typing
from functools import partial
from pathlib import Path
import numpy as np
from PyQt5 import QtWidgets, uic
import pyqtgraph as pg
from pyqtgraph import LegendItem
from pyctbgui.utils import decoder
from pyctbgui.utils.bit_utils import bit_is_set, manipulate_bit
from pyctbgui.utils.defines import Defines
import pyctbgui.utils.pixelmap as pm
from pyctbgui.utils.recordOrApplyPedestal import recordOrApplyPedestal
if typing.TYPE_CHECKING:
from pyctbgui.services import AcquisitionTab, PlotTab
class AdcTab(QtWidgets.QWidget):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
uic.loadUi(Path(__file__).parent.parent / 'ui' / "adc.ui", parent)
self.view = parent
self.mainWindow = None
self.det = None
self.plotTab: PlotTab | None = None
self.acquisitionTab: AcquisitionTab | None = None
self.legend: LegendItem | None = None
self.logger = logging.getLogger('AdcTab')
def setup_ui(self):
self.plotTab = self.mainWindow.plotTab
self.acquisitionTab = self.mainWindow.acquisitionTab
for i in range(Defines.adc.count):
self.setADCButtonColor(i, self.plotTab.getRandomColor())
self.initializeAllAnalogPlots()
self.legend = self.mainWindow.plotAnalogWaveform.getPlotItem().legend
self.legend.clear()
# subscribe to toggle legend
self.plotTab.subscribeToggleLegend(self.updateLegend)
def initializeAllAnalogPlots(self):
self.mainWindow.plotAnalogWaveform = pg.plot()
self.mainWindow.plotAnalogWaveform.addLegend(colCount=Defines.colCount)
self.mainWindow.verticalLayoutPlot.addWidget(self.mainWindow.plotAnalogWaveform, 1)
self.mainWindow.analogPlots = {}
waveform = np.zeros(1000)
for i in range(Defines.adc.count):
pen = pg.mkPen(color=self.getADCButtonColor(i), width=1)
legendName = getattr(self.view, f"labelADC{i}").text()
self.mainWindow.analogPlots[i] = self.mainWindow.plotAnalogWaveform.plot(waveform,
pen=pen,
name=legendName)
self.mainWindow.analogPlots[i].hide()
self.mainWindow.plotAnalogImage = pg.ImageView()
self.mainWindow.nAnalogRows = 0
self.mainWindow.nAnalogCols = 0
self.mainWindow.analog_frame = np.zeros((self.mainWindow.nAnalogRows, self.mainWindow.nAnalogCols))
self.mainWindow.plotAnalogImage.getView().invertY(False)
self.mainWindow.plotAnalogImage.setImage(self.mainWindow.analog_frame)
self.mainWindow.verticalLayoutPlot.addWidget(self.mainWindow.plotAnalogImage, 2)
def connect_ui(self):
for i in range(Defines.adc.count):
getattr(self.view, f"checkBoxADC{i}Inv").stateChanged.connect(partial(self.setADCInv, i))
getattr(self.view, f"checkBoxADC{i}En").stateChanged.connect(partial(self.setADCEnable, i))
getattr(self.view, f"checkBoxADC{i}Plot").stateChanged.connect(partial(self.setADCEnablePlot, i))
getattr(self.view, f"pushButtonADC{i}").clicked.connect(partial(self.selectADCColor, i))
self.view.checkBoxADC0_15En.stateChanged.connect(partial(self.setADCEnableRange, 0, Defines.adc.half))
self.view.checkBoxADC16_31En.stateChanged.connect(
partial(self.setADCEnableRange, Defines.adc.half, Defines.adc.count))
self.view.checkBoxADC0_15Plot.stateChanged.connect(partial(self.setADCEnablePlotRange, 0, Defines.adc.half))
self.view.checkBoxADC16_31Plot.stateChanged.connect(
partial(self.setADCEnablePlotRange, Defines.adc.half, Defines.adc.count))
self.view.checkBoxADC0_15Inv.stateChanged.connect(partial(self.setADCInvRange, 0, Defines.adc.half))
self.view.checkBoxADC16_31Inv.stateChanged.connect(
partial(self.setADCInvRange, Defines.adc.half, Defines.adc.count))
self.view.lineEditADCInversion.editingFinished.connect(self.setADCInvReg)
self.view.lineEditADCEnable.editingFinished.connect(self.setADCEnableReg)
def refresh(self):
self.updateADCNames()
self.updateADCInv()
self.updateADCEnable()
# ADCs Tab functions
def getEnabledPlots(self):
"""
return plots that are shown (checkBoxADC#Plot is checked)
"""
enabledPlots = []
self.legend.clear()
for i in range(Defines.adc.count):
if getattr(self.view, f'checkBoxADC{i}Plot').isChecked():
plotName = getattr(self.view, f"labelADC{i}").text()
enabledPlots.append((self.mainWindow.analogPlots[i], plotName))
return enabledPlots
def updateLegend(self):
"""
update the legend for the ADC waveform plot
should be called after checking or unchecking plot checkbox
"""
if not self.mainWindow.showLegend:
self.legend.clear()
else:
for plot, name in self.getEnabledPlots():
self.legend.addItem(plot, name)
def updateADCNames(self):
"""
get adc names from detector and update them in the UI
"""
for i, adc_name in enumerate(self.det.getAdcNames()):
getattr(self.view, f"labelADC{i}").setText(adc_name)
def processWaveformData(self, data: bytes, aSamples: int) -> dict[str, np.ndarray]:
"""
view function
plots processed waveform data
@param data: raw waveform data
@param aSamples: analog samples
@return: waveform dict returned to handle it for saving the output
"""
waveforms = {}
analog_array = self._processWaveformData(data, aSamples, self.mainWindow.nADCEnabled)
idx = 0
for i in range(Defines.adc.count):
checkBoxPlot = getattr(self.view, f"checkBoxADC{i}Plot")
checkBoxEn = getattr(self.view, f"checkBoxADC{i}En")
if checkBoxEn.isChecked() and checkBoxPlot.isChecked():
waveform = analog_array[:, idx]
idx += 1
self.mainWindow.analogPlots[i].setData(waveform)
plotName = getattr(self.view, f"labelADC{i}").text()
waveforms[plotName] = waveform
return waveforms
@recordOrApplyPedestal
def _processWaveformData(self, data: bytes, aSamples: int, nADCEnabled: int) -> np.ndarray:
"""
model function
processes raw waveform data
@param data: raw waveform data
@param aSamples: analog samples
@param nADCEnabled: number of enabled ADCs
@return: processed waveform data
"""
analog_array = np.array(np.frombuffer(data, dtype=np.uint16, count=nADCEnabled * aSamples))
return analog_array.reshape(-1, nADCEnabled)
def processImageData(self, data, aSamples):
"""
process the raw receiver data for analog image
data: raw analog image
aSamples: analog samples
"""
# get zoom state
viewBox = self.mainWindow.plotAnalogImage.getView()
state = viewBox.getState()
try:
self.mainWindow.analog_frame = self._processImageData(data, aSamples, self.mainWindow.nADCEnabled)
self.plotTab.ignoreHistogramSignal = True
self.mainWindow.plotAnalogImage.setImage(self.mainWindow.analog_frame.T)
except Exception:
self.logger.exception('Exception Caught')
self.mainWindow.statusbar.setStyleSheet("color:red")
message = f'Warning: Invalid size for Analog Image. Expected' \
f' {self.mainWindow.nAnalogRows * self.mainWindow.nAnalogCols} ' \
f'size, got {self.mainWindow.analog_frame.size} instead.'
self.acquisitionTab.updateCurrentFrame('Invalid Image')
self.mainWindow.statusbar.showMessage(message)
print(message)
self.plotTab.setFrameLimits(self.mainWindow.analog_frame)
# keep the zoomed in state (not 1st image)
if self.mainWindow.firstAnalogImage:
self.mainWindow.firstAnalogImage = False
else:
viewBox.setState(state)
return self.mainWindow.analog_frame.T
@recordOrApplyPedestal
def _processImageData(self, data, aSamples, nADCEnabled):
analog_array = np.array(np.frombuffer(data, dtype=np.uint16, count=nADCEnabled * aSamples))
return decoder.decode(analog_array, pm.moench04_analog())
def getADCEnableReg(self):
retval = self.det.adcenable
if self.det.tengiga:
retval = self.det.adcenable10g
self.view.lineEditADCEnable.editingFinished.disconnect()
self.view.lineEditADCEnable.setText("0x{:08x}".format(retval))
self.view.lineEditADCEnable.editingFinished.connect(self.setADCEnableReg)
return retval
def setADCEnableReg(self):
self.view.lineEditADCEnable.editingFinished.disconnect()
try:
mask = int(self.mainWindow.lineEditADCEnable.text(), 16)
if self.det.tengiga:
self.det.adcenable10g = mask
else:
self.det.adcenable = mask
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "ADC Enable Fail", str(e), QtWidgets.QMessageBox.Ok)
pass
# TODO: handling double event exceptions
self.view.lineEditADCEnable.editingFinished.connect(self.setADCEnableReg)
self.updateADCEnable()
def getADCEnable(self, i, mask):
checkBox = getattr(self.view, f"checkBoxADC{i}En")
checkBox.stateChanged.disconnect()
checkBox.setChecked(bit_is_set(mask, i))
checkBox.stateChanged.connect(partial(self.setADCEnable, i))
def updateADCEnable(self):
retval = self.getADCEnableReg()
self.mainWindow.nADCEnabled = bin(retval).count('1')
for i in range(Defines.adc.count):
self.getADCEnable(i, retval)
self.getADCEnablePlot(i)
self.getADCEnableColor(i)
self.plotTab.addSelectedAnalogPlots(i)
self.getADCEnableRange(retval)
self.getADCEnablePlotRange()
def setADCEnable(self, i):
checkBox = getattr(self.view, f"checkBoxADC{i}En")
try:
if self.det.tengiga:
enableMask = manipulate_bit(checkBox.isChecked(), self.det.adcenable10g, i)
self.det.adcenable10g = enableMask
else:
enableMask = manipulate_bit(checkBox.isChecked(), self.det.adcenable, i)
self.det.adcenable = enableMask
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "ADC Enable Fail", str(e), QtWidgets.QMessageBox.Ok)
self.updateADCEnable()
def getADCEnableRange(self, mask):
self.view.checkBoxADC0_15En.stateChanged.disconnect()
self.view.checkBoxADC16_31En.stateChanged.disconnect()
self.view.checkBoxADC0_15En.setChecked((mask & Defines.adc.BIT0_15_MASK) == Defines.adc.BIT0_15_MASK)
self.view.checkBoxADC16_31En.setChecked((mask & Defines.adc.BIT16_31_MASK) == Defines.adc.BIT16_31_MASK)
self.view.checkBoxADC0_15En.stateChanged.connect(partial(self.setADCEnableRange, 0, Defines.adc.half))
self.view.checkBoxADC16_31En.stateChanged.connect(
partial(self.setADCEnableRange, Defines.adc.half, Defines.adc.count))
def setADCEnableRange(self, start_nr, end_nr):
mask = self.getADCEnableReg()
checkBox = getattr(self.view, f"checkBoxADC{start_nr}_{end_nr - 1}En")
for i in range(start_nr, end_nr):
mask = manipulate_bit(checkBox.isChecked(), mask, i)
try:
if self.det.tengiga:
self.det.adcenable10g = mask
else:
self.det.adcenable = mask
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "ADC Enable Fail", str(e), QtWidgets.QMessageBox.Ok)
pass
self.updateADCEnable()
def getADCEnablePlot(self, i):
checkBox = getattr(self.view, f"checkBoxADC{i}En")
checkBoxPlot = getattr(self.view, f"checkBoxADC{i}Plot")
checkBoxPlot.setEnabled(checkBox.isChecked())
def setADCEnablePlot(self, i):
pushButton = getattr(self.view, f"pushButtonADC{i}")
checkBox = getattr(self.view, f"checkBoxADC{i}Plot")
pushButton.setEnabled(checkBox.isChecked())
self.getADCEnablePlotRange()
self.plotTab.addSelectedAnalogPlots(i)
self.updateLegend()
def getADCEnablePlotRange(self):
self.view.checkBoxADC0_15Plot.stateChanged.disconnect()
self.view.checkBoxADC16_31Plot.stateChanged.disconnect()
self.view.checkBoxADC0_15Plot.setEnabled(
all(getattr(self.view, f"checkBoxADC{i}Plot").isEnabled() for i in range(Defines.adc.half)))
self.view.checkBoxADC16_31Plot.setEnabled(
all(
getattr(self.view, f"checkBoxADC{i}Plot").isEnabled()
for i in range(Defines.adc.half, Defines.adc.count)))
self.view.checkBoxADC0_15Plot.setChecked(
all(getattr(self.view, f"checkBoxADC{i}Plot").isChecked() for i in range(Defines.adc.half)))
self.view.checkBoxADC16_31Plot.setChecked(
all(
getattr(self.view, f"checkBoxADC{i}Plot").isChecked()
for i in range(Defines.adc.half, Defines.adc.count)))
self.view.checkBoxADC0_15Plot.stateChanged.connect(partial(self.setADCEnablePlotRange, 0, Defines.adc.half))
self.view.checkBoxADC16_31Plot.stateChanged.connect(
partial(self.setADCEnablePlotRange, Defines.adc.half, Defines.adc.count))
def setADCEnablePlotRange(self, start_nr, end_nr):
checkBox = getattr(self.view, f"checkBoxADC{start_nr}_{end_nr - 1}Plot")
enable = checkBox.isChecked()
for i in range(start_nr, end_nr):
checkBox = getattr(self.view, f"checkBoxADC{i}Plot")
checkBox.setChecked(enable)
self.plotTab.addAllSelectedAnalogPlots()
def getADCEnableColor(self, i):
checkBox = getattr(self.view, f"checkBoxADC{i}Plot")
pushButton = getattr(self.view, f"pushButtonADC{i}")
pushButton.setEnabled(checkBox.isEnabled() and checkBox.isChecked())
def selectADCColor(self, i):
pushButton = getattr(self.view, f"pushButtonADC{i}")
self.plotTab.showPalette(pushButton)
pen = pg.mkPen(color=self.getADCButtonColor(i), width=1)
self.mainWindow.analogPlots[i].setPen(pen)
def getADCButtonColor(self, i):
pushButton = getattr(self.view, f"pushButtonADC{i}")
return self.plotTab.getActiveColor(pushButton)
def setADCButtonColor(self, i, color):
pushButton = getattr(self.view, f"pushButtonADC{i}")
return self.plotTab.setActiveColor(pushButton, color)
def getADCInvReg(self):
retval = self.det.adcinvert
self.view.lineEditADCInversion.editingFinished.disconnect()
self.view.lineEditADCInversion.setText("0x{:08x}".format(retval))
self.view.lineEditADCInversion.editingFinished.connect(self.setADCInvReg)
return retval
def setADCInvReg(self):
self.view.lineEditADCInversion.editingFinished.disconnect()
try:
self.det.adcinvert = int(self.mainWindow.lineEditADCInversion.text(), 16)
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "ADC Inversion Fail", str(e), QtWidgets.QMessageBox.Ok)
pass
# TODO: handling double event exceptions
self.view.lineEditADCInversion.editingFinished.connect(self.setADCInvReg)
self.updateADCInv()
def getADCInv(self, i, inv):
checkBox = getattr(self.view, f"checkBoxADC{i}Inv")
checkBox.stateChanged.disconnect()
checkBox.setChecked(bit_is_set(inv, i))
checkBox.stateChanged.connect(partial(self.setADCInv, i))
def updateADCInv(self):
retval = self.getADCInvReg()
for i in range(Defines.adc.count):
self.getADCInv(i, retval)
self.getADCInvRange(retval)
def setADCInv(self, i):
out = self.det.adcinvert
checkBox = getattr(self.view, f"checkBoxADC{i}Inv")
mask = manipulate_bit(checkBox.isChecked(), out, i)
self.det.adcinvert = mask
retval = self.getADCInvReg()
self.getADCInv(i, retval)
self.getADCInvRange(retval)
def getADCInvRange(self, inv):
self.view.checkBoxADC0_15Inv.stateChanged.disconnect()
self.view.checkBoxADC16_31Inv.stateChanged.disconnect()
self.view.checkBoxADC0_15Inv.setChecked((inv & Defines.adc.BIT0_15_MASK) == Defines.adc.BIT0_15_MASK)
self.view.checkBoxADC16_31Inv.setChecked((inv & Defines.adc.BIT16_31_MASK) == Defines.adc.BIT16_31_MASK)
self.view.checkBoxADC0_15Inv.stateChanged.connect(partial(self.setADCInvRange, 0, Defines.adc.half))
self.view.checkBoxADC16_31Inv.stateChanged.connect(
partial(self.setADCInvRange, Defines.adc.half, Defines.adc.count))
def setADCInvRange(self, start_nr, end_nr):
out = self.det.adcinvert
checkBox = getattr(self.view, f"checkBoxADC{start_nr}_{end_nr - 1}Inv")
mask = getattr(Defines.adc, f"BIT{start_nr}_{end_nr - 1}_MASK")
if checkBox.isChecked():
self.det.adcinvert = out | mask
else:
self.det.adcinvert = out & ~mask
self.updateADCInv()
def saveParameters(self) -> list[str]:
return [
f"adcenable {self.view.lineEditADCEnable.text()}",
f"adcinvert {self.view.lineEditADCInversion.text()}",
]

View File

@ -0,0 +1,719 @@
import json
import typing
from pathlib import Path
import numpy as np
import time
import zmq
from PyQt5 import QtWidgets, uic
import logging
from slsdet import readoutMode, runStatus
from pyctbgui.utils.defines import Defines
from pyctbgui.utils.numpyWriter.npy_writer import NumpyFileManager
from pyctbgui.utils.numpyWriter.npz_writer import NpzFileWriter
if typing.TYPE_CHECKING:
# only used for type hinting. To avoid circular dependencies these
# won't be imported in runtime
from pyctbgui.services import SignalsTab, TransceiverTab, AdcTab, PlotTab
class AcquisitionTab(QtWidgets.QWidget):
def __init__(self, parent):
self.__isWaveform = None
super().__init__(parent)
self.currentMeasurement = None
self.dsamples = None
self.stoppedFlag = None
self.asamples = None
self.tsamples = None
uic.loadUi(Path(__file__).parent.parent / 'ui' / "acquisition.ui", parent)
self.view = parent
self.mainWindow = None
self.det = None
self.signalsTab: SignalsTab = None
self.transceiverTab: TransceiverTab = None
self.adcTab: AdcTab = None
self.plotTab: PlotTab = None
self.writeNumpy: bool = False
self.outputDir: Path = Path('/')
self.outputFileNamePrefix: str = ''
self.numpyFileManagers: dict[str, NumpyFileManager] = {}
self.logger = logging.getLogger('AcquisitionTab')
def setup_ui(self):
self.signalsTab = self.mainWindow.signalsTab
self.transceiverTab = self.mainWindow.transceiverTab
self.adcTab = self.mainWindow.adcTab
self.plotTab = self.mainWindow.plotTab
self.toggleStartButton(False)
def connect_ui(self):
# For Acquistions Tab
self.view.comboBoxROMode.currentIndexChanged.connect(self.setReadOut)
self.view.spinBoxRunF.editingFinished.connect(self.setRunFrequency)
self.view.spinBoxTransceiver.editingFinished.connect(self.setTransceiver)
self.view.spinBoxAnalog.editingFinished.connect(self.setAnalog)
self.view.spinBoxDigital.editingFinished.connect(self.setDigital)
self.view.spinBoxADCF.editingFinished.connect(self.setADCFrequency)
self.view.spinBoxADCPhase.editingFinished.connect(self.setADCPhase)
self.view.spinBoxADCPipeline.editingFinished.connect(self.setADCPipeline)
self.view.spinBoxDBITF.editingFinished.connect(self.setDBITFrequency)
self.view.spinBoxDBITPhase.editingFinished.connect(self.setDBITPhase)
self.view.spinBoxDBITPipeline.editingFinished.connect(self.setDBITPipeline)
self.view.checkBoxFileWriteRaw.stateChanged.connect(self.setFileWrite)
self.view.checkBoxFileWriteNumpy.stateChanged.connect(self.setFileWriteNumpy)
self.view.lineEditFileName.editingFinished.connect(self.setFileName)
self.view.lineEditFilePath.editingFinished.connect(self.setFilePath)
self.view.pushButtonFilePath.clicked.connect(self.browseFilePath)
self.view.spinBoxAcquisitionIndex.editingFinished.connect(self.setAccquisitionIndex)
self.view.spinBoxFrames.editingFinished.connect(self.setFrames)
self.view.spinBoxPeriod.editingFinished.connect(self.setPeriod)
self.view.comboBoxPeriod.currentIndexChanged.connect(self.setPeriod)
self.view.spinBoxTriggers.editingFinished.connect(self.setTriggers)
def refresh(self):
self.getReadout()
self.getRunFrequency()
self.getTransceiver()
self.getAnalog()
self.getDigital()
self.getADCFrequency()
self.getADCPhase()
self.getADCPipeline()
self.getDBITFrequency()
self.getDBITPhase()
self.getDBITPipeline()
self.getFileWrite()
self.getFileName()
self.getFilePath()
self.getAccquisitionIndex()
self.getFrames()
self.getTriggers()
self.getPeriod()
self.updateDetectorStatus(self.det.status)
# Acquisition Tab functions
def getReadout(self):
self.view.comboBoxROMode.currentIndexChanged.disconnect()
self.view.spinBoxAnalog.editingFinished.disconnect()
self.view.spinBoxDigital.editingFinished.disconnect()
self.view.spinBoxTransceiver.editingFinished.disconnect()
self.mainWindow.romode = self.det.romode
self.view.comboBoxROMode.setCurrentIndex(self.mainWindow.romode.value)
match self.mainWindow.romode:
case readoutMode.ANALOG_ONLY:
self.view.spinBoxAnalog.setEnabled(True)
self.view.labelAnalog.setEnabled(True)
self.view.spinBoxDigital.setDisabled(True)
self.view.labelDigital.setDisabled(True)
self.view.labelTransceiver.setDisabled(True)
self.view.spinBoxTransceiver.setDisabled(True)
case readoutMode.DIGITAL_ONLY:
self.view.spinBoxAnalog.setDisabled(True)
self.view.labelAnalog.setDisabled(True)
self.view.spinBoxDigital.setEnabled(True)
self.view.labelDigital.setEnabled(True)
self.view.labelTransceiver.setDisabled(True)
self.view.spinBoxTransceiver.setDisabled(True)
case readoutMode.ANALOG_AND_DIGITAL:
self.view.spinBoxAnalog.setEnabled(True)
self.view.labelAnalog.setEnabled(True)
self.view.spinBoxDigital.setEnabled(True)
self.view.labelDigital.setEnabled(True)
self.view.labelTransceiver.setDisabled(True)
self.view.spinBoxTransceiver.setDisabled(True)
case readoutMode.TRANSCEIVER_ONLY:
self.view.spinBoxAnalog.setDisabled(True)
self.view.labelAnalog.setDisabled(True)
self.view.spinBoxDigital.setDisabled(True)
self.view.labelDigital.setDisabled(True)
self.view.labelTransceiver.setEnabled(True)
self.view.spinBoxTransceiver.setEnabled(True)
case _:
self.view.spinBoxAnalog.setDisabled(True)
self.view.labelAnalog.setDisabled(True)
self.view.spinBoxDigital.setEnabled(True)
self.view.labelDigital.setEnabled(True)
self.view.labelTransceiver.setEnabled(True)
self.view.spinBoxTransceiver.setEnabled(True)
self.view.comboBoxROMode.currentIndexChanged.connect(self.setReadOut)
self.view.spinBoxAnalog.editingFinished.connect(self.setAnalog)
self.view.spinBoxDigital.editingFinished.connect(self.setDigital)
self.view.spinBoxTransceiver.editingFinished.connect(self.setTransceiver)
self.getAnalog()
self.getDigital()
self.plotTab.showPlot()
def plotReferesh(self):
self.read_zmq()
def setReadOut(self):
self.view.comboBoxROMode.currentIndexChanged.disconnect()
try:
if self.view.comboBoxROMode.currentIndex() == 0:
self.det.romode = readoutMode.ANALOG_ONLY
elif self.view.comboBoxROMode.currentIndex() == 1:
self.det.romode = readoutMode.DIGITAL_ONLY
elif self.view.comboBoxROMode.currentIndex() == 2:
self.det.romode = readoutMode.ANALOG_AND_DIGITAL
elif self.view.comboBoxROMode.currentIndex() == 3:
self.det.romode = readoutMode.TRANSCEIVER_ONLY
else:
self.det.romode = readoutMode.DIGITAL_AND_TRANSCEIVER
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "Readout Mode Fail", str(e), QtWidgets.QMessageBox.Ok)
# TODO: handling double event exceptions
self.view.comboBoxROMode.currentIndexChanged.connect(self.setReadOut)
self.getReadout()
def getRunFrequency(self):
self.view.spinBoxRunF.editingFinished.disconnect()
self.view.spinBoxRunF.setValue(self.det.runclk)
self.view.spinBoxRunF.editingFinished.connect(self.setRunFrequency)
def setRunFrequency(self):
self.view.spinBoxRunF.editingFinished.disconnect()
try:
self.det.runclk = self.view.spinBoxRunF.value()
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "Run Frequency Fail", str(e), QtWidgets.QMessageBox.Ok)
# TODO: handling double event exceptions
self.view.spinBoxRunF.editingFinished.connect(self.setRunFrequency)
self.getRunFrequency()
def getTransceiver(self):
self.view.spinBoxTransceiver.editingFinished.disconnect()
self.tsamples = self.det.tsamples
self.view.spinBoxTransceiver.setValue(self.tsamples)
self.view.spinBoxTransceiver.editingFinished.connect(self.setTransceiver)
def setTransceiver(self):
self.view.spinBoxTransceiver.editingFinished.disconnect()
try:
self.det.tsamples = self.view.spinBoxTransceiver.value()
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "Transceiver Samples Fail", str(e),
QtWidgets.QMessageBox.Ok)
# TODO: handling double event exceptions
self.view.spinBoxTransceiver.editingFinished.connect(self.setTransceiver)
self.getTransceiver()
def getAnalog(self):
self.view.spinBoxAnalog.editingFinished.disconnect()
self.asamples = self.det.asamples
self.view.spinBoxAnalog.setValue(self.asamples)
self.view.spinBoxAnalog.editingFinished.connect(self.setAnalog)
def setAnalog(self):
self.view.spinBoxAnalog.editingFinished.disconnect()
try:
self.det.asamples = self.view.spinBoxAnalog.value()
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "Digital Samples Fail", str(e), QtWidgets.QMessageBox.Ok)
# TODO: handling double event exceptions
self.view.spinBoxAnalog.editingFinished.connect(self.setAnalog)
self.getAnalog()
def getDigital(self):
self.view.spinBoxDigital.editingFinished.disconnect()
self.dsamples = self.det.dsamples
self.view.spinBoxDigital.setValue(self.dsamples)
self.view.spinBoxDigital.editingFinished.connect(self.setDigital)
def setDigital(self):
self.view.spinBoxDigital.editingFinished.disconnect()
try:
self.det.dsamples = self.view.spinBoxDigital.value()
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "Digital Samples Fail", str(e), QtWidgets.QMessageBox.Ok)
# TODO: handling double event exceptions
self.view.spinBoxDigital.editingFinished.connect(self.setDigital)
self.getDigital()
def getADCFrequency(self):
self.view.spinBoxADCF.editingFinished.disconnect()
self.view.spinBoxADCF.setValue(self.det.adcclk)
self.view.spinBoxADCF.editingFinished.connect(self.setADCFrequency)
def setADCFrequency(self):
self.view.spinBoxADCF.editingFinished.disconnect()
try:
self.det.adcclk = self.view.spinBoxADCF.value()
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "ADC Frequency Fail", str(e), QtWidgets.QMessageBox.Ok)
# TODO: handling double event exceptions
self.view.spinBoxADCF.editingFinished.connect(self.setADCFrequency)
self.getADCFrequency()
def getADCPhase(self):
self.view.spinBoxADCPhase.editingFinished.disconnect()
self.view.spinBoxADCPhase.setValue(self.det.adcphase)
self.view.spinBoxADCPhase.editingFinished.connect(self.setADCPhase)
def setADCPhase(self):
self.view.spinBoxADCPhase.editingFinished.disconnect()
try:
self.det.adcphase = self.view.spinBoxADCPhase.value()
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "ADC Phase Fail", str(e), QtWidgets.QMessageBox.Ok)
# TODO: handling double event exceptions
self.view.spinBoxADCPhase.editingFinished.connect(self.setADCPhase)
self.getADCPhase()
def getADCPipeline(self):
self.view.spinBoxADCPipeline.editingFinished.disconnect()
self.view.spinBoxADCPipeline.setValue(self.det.adcpipeline)
self.view.spinBoxADCPipeline.editingFinished.connect(self.setADCPipeline)
def setADCPipeline(self):
self.view.spinBoxADCPipeline.editingFinished.disconnect()
try:
self.det.adcpipeline = self.view.spinBoxADCPipeline.value()
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "ADC Pipeline Fail", str(e), QtWidgets.QMessageBox.Ok)
# TODO: handling double event exceptions
self.view.spinBoxADCPipeline.editingFinished.connect(self.setADCPipeline)
self.getADCPipeline()
def getDBITFrequency(self):
self.view.spinBoxDBITF.editingFinished.disconnect()
self.view.spinBoxDBITF.setValue(self.det.dbitclk)
self.view.spinBoxDBITF.editingFinished.connect(self.setDBITFrequency)
def setDBITFrequency(self):
self.view.spinBoxDBITF.editingFinished.disconnect()
try:
self.det.dbitclk = self.view.spinBoxDBITF.value()
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "DBit Frequency Fail", str(e), QtWidgets.QMessageBox.Ok)
# TODO: handling double event exceptions
self.view.spinBoxDBITF.editingFinished.connect(self.setDBITFrequency)
self.getDBITFrequency()
def getDBITPhase(self):
self.view.spinBoxDBITPhase.editingFinished.disconnect()
self.view.spinBoxDBITPhase.setValue(self.det.dbitphase)
self.view.spinBoxDBITPhase.editingFinished.connect(self.setDBITPhase)
def setDBITPhase(self):
self.view.spinBoxDBITPhase.editingFinished.disconnect()
try:
self.det.dbitphase = self.view.spinBoxDBITPhase.value()
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "DBit Phase Fail", str(e), QtWidgets.QMessageBox.Ok)
# TODO: handling double event exceptions
self.view.spinBoxDBITPhase.editingFinished.connect(self.setDBITPhase)
self.getDBITPhase()
def getDBITPipeline(self):
self.view.spinBoxDBITPipeline.editingFinished.disconnect()
self.view.spinBoxDBITPipeline.setValue(self.det.dbitpipeline)
self.view.spinBoxDBITPipeline.editingFinished.connect(self.setDBITPipeline)
def setDBITPipeline(self):
self.view.spinBoxDBITPipeline.editingFinished.disconnect()
try:
self.det.dbitpipeline = self.view.spinBoxDBITPipeline.value()
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "DBit Pipeline Fail", str(e), QtWidgets.QMessageBox.Ok)
# TODO: handling double event exceptions
self.view.spinBoxDBITPipeline.editingFinished.connect(self.setDBITPipeline)
self.getDBITPipeline()
def getFileWrite(self):
self.view.checkBoxFileWriteRaw.stateChanged.disconnect()
self.view.checkBoxFileWriteRaw.setChecked(self.det.fwrite)
self.view.checkBoxFileWriteRaw.stateChanged.connect(self.setFileWrite)
def setFileWrite(self):
self.det.fwrite = self.view.checkBoxFileWriteRaw.isChecked()
self.getFileWrite()
def setFileWriteNumpy(self):
"""
slot for saving the data in numpy (.npy) format
"""
self.writeNumpy = not self.writeNumpy
def getFileName(self):
"""
set the lineEditFilePath input widget to the filename value from the detector
"""
self.view.lineEditFileName.editingFinished.disconnect()
fileName = self.det.fname
self.view.lineEditFileName.setText(fileName)
self.outputFileNamePrefix = fileName
self.view.lineEditFileName.editingFinished.connect(self.setFileName)
def setFileName(self):
"""
slot for setting the filename from the widget to the detector
"""
self.det.fname = self.view.lineEditFileName.text()
self.getFileName()
def getFilePath(self):
"""
set the lineEditFilePath input widget to the path value from the detector
"""
self.view.lineEditFilePath.editingFinished.disconnect()
path = self.det.fpath
self.view.lineEditFilePath.setText(str(path))
self.view.lineEditFilePath.editingFinished.connect(self.setFilePath)
self.outputDir = path
def setFilePath(self):
"""
slot to set the directory of the output for the detector
"""
self.det.fpath = Path(self.view.lineEditFilePath.text())
self.getFilePath()
def saveNumpyFile(self, data: np.ndarray | dict[str, np.ndarray], jsonHeader):
"""
save the acquisition data (waveform or image) in the specified path
save waveform in multiple .npy files
save image as npy file format
@note: frame number can be up to 100,000 so the data arrays cannot be fully loaded to memory
"""
if not self.writeNumpy:
return
if self.outputDir == Path('/'):
self.outputDir = Path('./')
if self.outputFileNamePrefix == '':
self.outputFileNamePrefix = 'run'
for device in data:
if device not in self.numpyFileManagers:
tmpPath = self.outputDir / f'{self.outputFileNamePrefix}_{device}_{jsonHeader["fileIndex"]}.npy'
self.numpyFileManagers[device] = NumpyFileManager(tmpPath, 'w', data[device].shape, data[device].dtype)
self.numpyFileManagers[device].writeOneFrame(data[device])
if 'progress' in jsonHeader and jsonHeader['progress'] >= 100:
# close opened files after saving the last frame
self.closeOpenedNumpyFiles(jsonHeader)
def closeOpenedNumpyFiles(self, jsonHeader):
"""
create npz file for waveform plots and close opened numpy files to persist their data
"""
if not self.writeNumpy:
return
if len(self.numpyFileManagers) == 0:
return
oneFile: bool = len(self.numpyFileManagers) == 1
filepaths = [npw.file.name for device, npw in self.numpyFileManagers.items()]
filenames = list(self.numpyFileManagers.keys())
ext = 'npy' if oneFile else 'npz'
newPath = self.outputDir / f'{self.outputFileNamePrefix}_{jsonHeader["fileIndex"]}.{ext}'
if not oneFile:
# if there is multiple .npy files group them in an .npz file
self.numpyFileManagers.clear()
NpzFileWriter.zipNpyFiles(newPath, filepaths, filenames, deleteOriginals=True, compressed=False)
else:
# rename files from "run_ADC0_0.npy" to "run_0.npy" if it is a single .npy file
oldPath = self.outputDir / f'{self.outputFileNamePrefix}_' \
f'{self.numpyFileManagers.popitem()[0]}_{jsonHeader["fileIndex"]}.{ext}'
Path.rename(oldPath, newPath)
self.logger.info(f'Saving numpy data in {newPath} Finished')
def browseFilePath(self):
response = QtWidgets.QFileDialog.getExistingDirectory(parent=self.mainWindow,
caption="Select Path to Save Output File",
directory=str(Path.cwd()),
options=(QtWidgets.QFileDialog.ShowDirsOnly
| QtWidgets.QFileDialog.DontResolveSymlinks)
# filter='README (*.md *.ui)'
)
if response:
self.view.lineEditFilePath.setText(response)
self.setFilePath()
def getAccquisitionIndex(self):
self.view.spinBoxAcquisitionIndex.editingFinished.disconnect()
self.view.spinBoxAcquisitionIndex.setValue(self.det.findex)
self.view.spinBoxAcquisitionIndex.editingFinished.connect(self.setAccquisitionIndex)
def setAccquisitionIndex(self):
self.det.findex = self.view.spinBoxAcquisitionIndex.value()
self.getAccquisitionIndex()
def getFrames(self):
self.view.spinBoxFrames.editingFinished.disconnect()
self.view.spinBoxFrames.setValue(self.det.frames)
self.view.spinBoxFrames.editingFinished.connect(self.setFrames)
def setFrames(self):
self.det.frames = self.view.spinBoxFrames.value()
self.getFrames()
def getPeriod(self):
self.view.spinBoxPeriod.editingFinished.disconnect()
self.view.comboBoxPeriod.currentIndexChanged.disconnect()
# Converting to right time unit for period
tPeriod = self.det.period
if tPeriod < 100e-9:
self.view.comboBoxPeriod.setCurrentIndex(3)
self.view.spinBoxPeriod.setValue(tPeriod / 1e-9)
elif tPeriod < 100e-6:
self.view.comboBoxPeriod.setCurrentIndex(2)
self.view.spinBoxPeriod.setValue(tPeriod / 1e-6)
elif tPeriod < 100e-3:
self.view.comboBoxPeriod.setCurrentIndex(1)
self.view.spinBoxPeriod.setValue(tPeriod / 1e-3)
else:
self.view.comboBoxPeriod.setCurrentIndex(0)
self.view.spinBoxPeriod.setValue(tPeriod)
self.view.spinBoxPeriod.editingFinished.connect(self.setPeriod)
self.view.comboBoxPeriod.currentIndexChanged.connect(self.setPeriod)
def setPeriod(self):
if self.view.comboBoxPeriod.currentIndex() == 0:
self.det.period = self.view.spinBoxPeriod.value()
elif self.view.comboBoxPeriod.currentIndex() == 1:
self.det.period = self.view.spinBoxPeriod.value() * (1e-3)
elif self.view.comboBoxPeriod.currentIndex() == 2:
self.det.period = self.view.spinBoxPeriod.value() * (1e-6)
else:
self.det.period = self.view.spinBoxPeriod.value() * (1e-9)
self.getPeriod()
def getTriggers(self):
self.view.spinBoxTriggers.editingFinished.disconnect()
self.view.spinBoxTriggers.setValue(self.det.triggers)
self.view.spinBoxTriggers.editingFinished.connect(self.setTriggers)
def setTriggers(self):
self.det.triggers = self.view.spinBoxTriggers.value()
self.getTriggers()
def updateDetectorStatus(self, status):
self.mainWindow.labelDetectorStatus.setText(status.name)
def updateCurrentMeasurement(self):
self.mainWindow.labelCurrentMeasurement.setText(str(self.currentMeasurement))
# print(f"Meausrement {self.currentMeasurement}")
def updateCurrentFrame(self, val):
self.mainWindow.labelFrameNumber.setText(str(val))
def updateAcquiredFrames(self, val):
self.mainWindow.labelAcquiredFrames.setText(str(val))
def toggleAcquire(self):
if self.mainWindow.pushButtonStart.isChecked():
self.plotTab.showPatternViewer(False)
self.acquire()
else:
self.stopAcquisition()
def toggleStartButton(self, started):
if started:
self.mainWindow.pushButtonStart.setChecked(True)
self.mainWindow.pushButtonStart.setText('Stop')
else:
self.mainWindow.pushButtonStart.setChecked(False)
self.mainWindow.pushButtonStart.setText('Start')
def stopAcquisition(self):
self.det.stop()
self.stoppedFlag = True
def checkBeforeAcquire(self):
if self.plotTab.view.radioButtonImage.isChecked():
# matterhorn image
if self.plotTab.view.comboBoxPlot.currentText() == "Matterhorn":
if self.mainWindow.romode not in [readoutMode.TRANSCEIVER_ONLY, readoutMode.DIGITAL_AND_TRANSCEIVER]:
QtWidgets.QMessageBox.warning(self.mainWindow, "Plot type",
"To read Matterhorn image, please enable transceiver readout mode",
QtWidgets.QMessageBox.Ok)
return False
if self.transceiverTab.getTransceiverEnableReg() != Defines.Matterhorn.tranceiverEnable:
QtWidgets.QMessageBox.warning(
self.mainWindow, "Plot type", "To read Matterhorn image, please set transceiver enable to " +
str(Defines.Matterhorn.tranceiverEnable), QtWidgets.QMessageBox.Ok)
return False
# moench04 image
elif self.plotTab.view.comboBoxPlot.currentText() == "Moench04":
if self.mainWindow.romode not in [readoutMode.ANALOG_ONLY, readoutMode.ANALOG_AND_DIGITAL]:
QtWidgets.QMessageBox.warning(self.mainWindow, "Plot type",
"To read Moench 04 image, please enable analog readout mode",
QtWidgets.QMessageBox.Ok)
return False
if self.mainWindow.nADCEnabled != 32:
QtWidgets.QMessageBox.warning(self.mainWindow, "Plot type",
"To read Moench 04 image, please enable all 32 adcs",
QtWidgets.QMessageBox.Ok)
return False
return True
def acquire(self):
if not self.checkBeforeAcquire():
self.toggleStartButton(False)
return
self.stoppedFlag = False
self.toggleStartButton(True)
self.currentMeasurement = 0
# ensure zmq streaming is enabled
if self.det.rx_zmqstream == 0:
self.det.rx_zmqstream = 1
# some functions that must be updated for local values
self.getTransceiver()
self.getAnalog()
self.getDigital()
self.getReadout()
self.signalsTab.getDBitOffset()
self.adcTab.getADCEnableReg()
self.signalsTab.updateDigitalBitEnable()
self.transceiverTab.getTransceiverEnableReg()
self.startMeasurement()
def startMeasurement(self):
try:
self.updateCurrentMeasurement()
self.updateCurrentFrame(0)
self.updateAcquiredFrames(0)
self.mainWindow.progressBar.setValue(0)
self.det.rx_start()
self.det.start()
time.sleep(Defines.Time_Wait_For_Packets_ms)
self.checkEndofAcquisition()
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "Acquire Fail", str(e), QtWidgets.QMessageBox.Ok)
self.checkEndofAcquisition()
def checkEndofAcquisition(self):
caught = self.det.rx_framescaught[0]
self.updateAcquiredFrames(caught)
status = self.det.getDetectorStatus()[0]
self.updateDetectorStatus(status)
measurementDone = False
# print(f'status:{status}')
match status:
case runStatus.RUNNING:
pass
case runStatus.WAITING:
pass
case runStatus.TRANSMITTING:
pass
case _:
measurementDone = True
# check for 500ms for no packets
# needs more time for 1g streaming out done
if measurementDone:
time.sleep(Defines.Time_Wait_For_Packets_ms)
if self.det.rx_framescaught[0] != caught:
measurementDone = False
numMeasurments = self.view.spinBoxMeasurements.value()
if measurementDone:
if self.det.rx_status == runStatus.RUNNING:
self.det.rx_stop()
if self.view.checkBoxFileWriteRaw.isChecked() or self.view.checkBoxFileWriteNumpy.isChecked():
self.view.spinBoxAcquisitionIndex.stepUp()
self.setAccquisitionIndex()
# next measurement
self.currentMeasurement += 1
if self.currentMeasurement < numMeasurments and not self.stoppedFlag:
self.startMeasurement()
else:
self.mainWindow.statusTimer.stop()
self.toggleStartButton(False)
else:
self.mainWindow.statusTimer.start(Defines.Time_Status_Refresh_ms)
# For other functios
# Reading data from zmq and decoding it
def read_zmq(self):
# print("in readzmq")
try:
msg = self.socket.recv_multipart(flags=zmq.NOBLOCK)
if len(msg) != 2:
if len(msg) != 1:
print(f'len(msg) = {len(msg)}')
return
header, data = msg
jsonHeader = json.loads(header)
self.mainWindow.progressBar.setValue(int(jsonHeader['progress']))
self.updateCurrentFrame(jsonHeader['frameIndex'])
# waveform
waveforms = {}
if self.plotTab.view.radioButtonWaveform.isChecked():
# analog
if self.mainWindow.romode.value in [0, 2]:
waveforms |= self.adcTab.processWaveformData(data, self.asamples)
# digital
if self.mainWindow.romode.value in [1, 2, 4]:
waveforms |= self.signalsTab.processWaveformData(data, self.asamples, self.dsamples)
# transceiver
if self.mainWindow.romode.value in [3, 4]:
waveforms |= self.transceiverTab.processWaveformData(data, self.dsamples)
# image
else:
# analog
if self.mainWindow.romode.value in [0, 2]:
waveforms['analog_image'] = self.adcTab.processImageData(data, self.asamples)
# transceiver
if self.mainWindow.romode.value in [3, 4]:
waveforms['tx_image'] = self.transceiverTab.processImageData(data, self.dsamples)
self.saveNumpyFile(waveforms, jsonHeader)
except zmq.ZMQError:
pass
except Exception:
self.logger.exception("Exception caught")
def setup_zmq(self):
self.det.rx_zmqstream = 1
self.zmqIp = self.det.rx_zmqip
self.zmqport = self.det.rx_zmqport
self.zmq_stream = self.det.rx_zmqstream
self.context = zmq.Context()
self.socket = self.context.socket(zmq.SUB)
self.socket.connect(f"tcp://{self.zmqIp}:{self.zmqport}")
self.socket.subscribe("")
def saveParameters(self) -> list[str]:
return [
f'romode {self.view.comboBoxROMode.currentText().lower()}',
f'runclk {self.view.spinBoxRunF.value()}',
f'adcclk {self.view.spinBoxADCF.value()}',
f'adcphase {self.view.spinBoxADCPhase.value()}',
f'adcpipeline {self.view.spinBoxADCPipeline.value()}',
f'dbitclk {self.view.spinBoxDBITF.value()}',
f'dbitphase {self.view.spinBoxDBITPhase.value()}',
f'dbitpipeline {self.view.spinBoxDBITPipeline.value()}',
f'fwrite {int(self.view.checkBoxFileWriteRaw.isChecked())}',
f'fname {self.view.lineEditFileName.text()}',
f'fpath {self.view.lineEditFilePath.text()}',
f'findex {self.view.spinBoxAcquisitionIndex.value()}',
f'frames {self.view.spinBoxFrames.value()}',
f'triggers {self.view.spinBoxTriggers.value()}',
f'period {self.view.spinBoxPeriod.value()} {self.view.comboBoxPeriod.currentText().lower()}',
f'asamples {self.view.spinBoxAnalog.value()}',
f'dsamples {self.view.spinBoxDigital.value()}',
f'tsamples {self.view.spinBoxTransceiver.value()}',
]

View File

@ -0,0 +1,170 @@
from functools import partial
from pathlib import Path
from PyQt5 import QtWidgets, uic
from pyctbgui.utils.defines import Defines
from slsdet import dacIndex
class DacTab(QtWidgets.QWidget):
def __init__(self, parent):
super().__init__(parent)
uic.loadUi(Path(__file__).parent.parent / 'ui' / "Dacs.ui", parent)
self.view = parent
def setup_ui(self):
for i in range(Defines.dac.count):
dac = getattr(dacIndex, f"DAC_{i}")
getattr(self.view, f"spinBoxDAC{i}").setValue(self.det.getDAC(dac)[0])
if self.det.highvoltage == 0:
self.view.spinBoxHighVoltage.setDisabled(True)
self.view.checkBoxHighVoltage.setChecked(False)
def connect_ui(self):
n_dacs = len(self.det.daclist)
for i in range(n_dacs):
getattr(self.view, f"spinBoxDAC{i}").editingFinished.connect(partial(self.setDAC, i))
getattr(self.view, f"checkBoxDAC{i}").stateChanged.connect(partial(self.setDACTristate, i))
getattr(self.view, f"checkBoxDAC{i}mV").stateChanged.connect(partial(self.getDAC, i))
self.view.comboBoxADCVpp.currentIndexChanged.connect(self.setADCVpp)
self.view.spinBoxHighVoltage.editingFinished.connect(self.setHighVoltage)
self.view.checkBoxHighVoltage.stateChanged.connect(self.setHighVoltage)
def refresh(self):
self.updateDACNames()
for i in range(Defines.dac.count):
self.getDACTristate(i)
self.getDAC(i)
self.getADCVpp()
self.getHighVoltage()
def updateDACNames(self):
for i, name in enumerate(self.det.getDacNames()):
getattr(self.view, f"checkBoxDAC{i}").setText(name)
def getDACTristate(self, i):
checkBox = getattr(self.view, f"checkBoxDAC{i}")
dac = getattr(dacIndex, f"DAC_{i}")
checkBox.stateChanged.disconnect()
if (self.det.getDAC(dac)[0]) == -100:
checkBox.setChecked(False)
else:
checkBox.setChecked(True)
checkBox.stateChanged.connect(partial(self.setDACTristate, i))
def setDACTristate(self, i):
checkBox = getattr(self.view, f"checkBoxDAC{i}")
if not checkBox.isChecked():
self.setDAC(i)
self.getDAC(i)
def getDAC(self, i):
checkBox = getattr(self.view, f"checkBoxDAC{i}")
checkBoxmV = getattr(self.view, f"checkBoxDAC{i}mV")
spinBox = getattr(self.view, f"spinBoxDAC{i}")
label = getattr(self.view, f"labelDAC{i}")
dac = getattr(dacIndex, f"DAC_{i}")
checkBox.stateChanged.disconnect()
checkBoxmV.stateChanged.disconnect()
spinBox.editingFinished.disconnect()
# do not uncheck automatically
if self.det.getDAC(dac)[0] != -100:
checkBox.setChecked(True)
if checkBox.isChecked():
spinBox.setEnabled(True)
checkBoxmV.setEnabled(True)
else:
spinBox.setDisabled(True)
checkBoxmV.setDisabled(True)
in_mv = checkBoxmV.isChecked() and checkBox.isChecked()
dacValue = self.det.getDAC(dac, in_mv)[0]
unit = "mV" if in_mv else ""
label.setText(f"{dacValue} {unit}")
spinBox.setValue(dacValue)
checkBox.stateChanged.connect(partial(self.setDACTristate, i))
checkBoxmV.stateChanged.connect(partial(self.getDAC, i))
spinBox.editingFinished.connect(partial(self.setDAC, i))
def setDAC(self, i):
checkBoxDac = getattr(self.view, f"checkBoxDAC{i}")
checkBoxmV = getattr(self.view, f"checkBoxDAC{i}mV")
spinBox = getattr(self.view, f"spinBoxDAC{i}")
dac = getattr(dacIndex, f"DAC_{i}")
value = -100
if checkBoxDac.isChecked():
value = spinBox.value()
in_mV = checkBoxDac.isChecked() and checkBoxmV.isChecked()
self.det.setDAC(dac, value, in_mV)
self.getDAC(i)
def getADCVpp(self):
retval = self.det.adcvpp
self.view.labelADCVpp.setText(f'Mode: {str(retval)}')
self.view.comboBoxADCVpp.currentIndexChanged.disconnect()
self.view.comboBoxADCVpp.setCurrentIndex(retval)
self.view.comboBoxADCVpp.currentIndexChanged.connect(self.setADCVpp)
def setADCVpp(self):
self.det.adcvpp = self.view.comboBoxADCVpp.currentIndex()
self.getADCVpp()
def getHighVoltage(self):
retval = self.det.highvoltage
self.view.labelHighVoltage.setText(str(retval))
self.view.spinBoxHighVoltage.editingFinished.disconnect()
self.view.checkBoxHighVoltage.stateChanged.disconnect()
self.view.spinBoxHighVoltage.setValue(retval)
if retval:
self.view.checkBoxHighVoltage.setChecked(True)
if self.view.checkBoxHighVoltage.isChecked():
self.view.spinBoxHighVoltage.setEnabled(True)
self.view.spinBoxHighVoltage.editingFinished.connect(self.setHighVoltage)
self.view.checkBoxHighVoltage.stateChanged.connect(self.setHighVoltage)
def setHighVoltage(self):
value = 0
if self.view.checkBoxHighVoltage.isChecked():
value = self.view.spinBoxHighVoltage.value()
try:
self.det.highvoltage = value
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "High Voltage Fail", str(e), QtWidgets.QMessageBox.Ok)
pass
self.getHighVoltage()
def saveParameters(self) -> list:
"""
save parameters for the current tab
@return: list of commands
"""
commands = []
for i in range(Defines.dac.count):
# if checkbox disabled put -100 in dac units
enabled = getattr(self.view, f"checkBoxDAC{i}").isChecked()
if not enabled:
commands.append(f"dac {i} -100")
# else put the value in dac or mV units
else:
value = getattr(self.view, f"spinBoxDAC{i}").value()
inMV = getattr(self.view, f"checkBoxDAC{i}mV").isChecked()
unit = " mV" if inMV else ""
commands.append(f"dac {i} {value}{unit}")
commands.append(f"adcvpp {self.view.comboBoxADCVpp.currentText()} mV")
commands.append(f"highvoltage {self.view.spinBoxHighVoltage.value()}")
return commands

View File

@ -0,0 +1,456 @@
import os
from functools import partial
from pathlib import Path
from PyQt5 import QtWidgets, uic
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from pyctbgui.utils.defines import Defines
from pyctbgui.utils.plotPattern import PlotPattern
class PatternTab(QtWidgets.QWidget):
def __init__(self, parent):
super().__init__(parent)
uic.loadUi(Path(__file__).parent.parent / 'ui' / "pattern.ui", parent)
self.view = parent
self.mainWindow = None
self.det = None
self.plotTab = None
def setup_ui(self):
# Pattern Tab
self.plotTab = self.mainWindow.plotTab
for i in range(len(Defines.Colors)):
self.view.comboBoxPatColor.addItem(Defines.Colors[i])
self.view.comboBoxPatWaitColor.addItem(Defines.Colors[i])
self.view.comboBoxPatLoopColor.addItem(Defines.Colors[i])
for i in range(len(Defines.LineStyles)):
self.view.comboBoxPatWaitLineStyle.addItem(Defines.LineStyles[i])
self.view.comboBoxPatLoopLineStyle.addItem(Defines.LineStyles[i])
self.updateDefaultPatViewerParameters()
self.view.comboBoxPatColorSelect.setCurrentIndex(0)
self.view.comboBoxPatWait.setCurrentIndex(0)
self.view.comboBoxPatLoop.setCurrentIndex(0)
self.view.spinBoxPatClockSpacing.setValue(self.clock_vertical_lines_spacing)
self.view.checkBoxPatShowClockNumber.setChecked(self.show_clocks_number)
self.view.doubleSpinBoxLineWidth.setValue(self.line_width)
self.view.lineEditPatternFile.setText(self.det.patfname[0])
# rest gets updated after connecting to slots
# pattern viewer plot area
self.figure, self.ax = plt.subplots()
self.canvas = FigureCanvas(self.figure)
self.toolbar = NavigationToolbar(self.canvas, self.view)
self.mainWindow.gridLayoutPatternViewer.addWidget(self.toolbar)
self.mainWindow.gridLayoutPatternViewer.addWidget(self.canvas)
self.figure.clear()
def connect_ui(self):
# For Pattern Tab
self.view.lineEditStartAddress.editingFinished.connect(self.setPatLimitAddress)
self.view.lineEditStopAddress.editingFinished.connect(self.setPatLimitAddress)
for i in range(Defines.pattern.loops_count):
getattr(self.view,
f"lineEditLoop{i}Start").editingFinished.connect(partial(self.setPatLoopStartStopAddress, i))
getattr(self.view,
f"lineEditLoop{i}Stop").editingFinished.connect(partial(self.setPatLoopStartStopAddress, i))
getattr(self.view, f"lineEditLoop{i}Wait").editingFinished.connect(partial(self.setPatLoopWaitAddress, i))
getattr(self.view,
f"spinBoxLoop{i}Repetition").editingFinished.connect(partial(self.setPatLoopRepetition, i))
getattr(self.view, f"spinBoxLoop{i}WaitTime").editingFinished.connect(partial(self.setPatLoopWaitTime, i))
self.view.pushButtonCompiler.clicked.connect(self.setCompiler)
self.view.pushButtonUncompiled.clicked.connect(self.setUncompiledPatternFile)
self.view.pushButtonPatternFile.clicked.connect(self.setPatternFile)
self.view.pushButtonLoadPattern.clicked.connect(self.loadPattern)
self.view.comboBoxPatColorSelect.currentIndexChanged.connect(self.getPatViewerColors)
self.view.comboBoxPatWait.currentIndexChanged.connect(self.getPatViewerWaitParameters)
self.view.comboBoxPatLoop.currentIndexChanged.connect(self.getPatViewerLoopParameters)
self.view.comboBoxPatColor.currentIndexChanged.connect(self.updatePatViewerParameters)
self.view.comboBoxPatWaitColor.currentIndexChanged.connect(self.updatePatViewerParameters)
self.view.comboBoxPatLoopColor.currentIndexChanged.connect(self.updatePatViewerParameters)
self.view.comboBoxPatWaitLineStyle.currentIndexChanged.connect(self.updatePatViewerParameters)
self.view.comboBoxPatLoopLineStyle.currentIndexChanged.connect(self.updatePatViewerParameters)
self.view.doubleSpinBoxWaitAlpha.editingFinished.connect(self.updatePatViewerParameters)
self.view.doubleSpinBoxLoopAlpha.editingFinished.connect(self.updatePatViewerParameters)
self.view.doubleSpinBoxWaitAlphaRect.editingFinished.connect(self.updatePatViewerParameters)
self.view.doubleSpinBoxLoopAlphaRect.editingFinished.connect(self.updatePatViewerParameters)
self.view.spinBoxPatClockSpacing.editingFinished.connect(self.updatePatViewerParameters)
self.view.checkBoxPatShowClockNumber.stateChanged.connect(self.updatePatViewerParameters)
self.view.doubleSpinBoxLineWidth.editingFinished.connect(self.updatePatViewerParameters)
self.view.pushButtonViewPattern.clicked.connect(self.viewPattern)
def refresh(self):
self.getPatLimitAddress()
for i in range(Defines.pattern.loops_count):
self.getPatLoopStartStopAddress(i)
self.getPatLoopWaitAddress(i)
self.getPatLoopRepetition(i)
self.getPatLoopWaitTime(i)
# Pattern Tab functions
def getPatLimitAddress(self):
retval = self.det.patlimits
self.view.lineEditStartAddress.editingFinished.disconnect()
self.view.lineEditStopAddress.editingFinished.disconnect()
self.view.lineEditStartAddress.setText("0x{:04x}".format(retval[0]))
self.view.lineEditStopAddress.setText("0x{:04x}".format(retval[1]))
self.view.lineEditStartAddress.editingFinished.connect(self.setPatLimitAddress)
self.view.lineEditStopAddress.editingFinished.connect(self.setPatLimitAddress)
def setPatLimitAddress(self):
self.view.lineEditStartAddress.editingFinished.disconnect()
self.view.lineEditStopAddress.editingFinished.disconnect()
try:
start = int(self.view.lineEditStartAddress.text(), 16)
stop = int(self.view.lineEditStopAddress.text(), 16)
self.det.patlimits = [start, stop]
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "Pattern Limit Address Fail", str(e),
QtWidgets.QMessageBox.Ok)
pass
# TODO: handling double event exceptions
self.view.lineEditStartAddress.editingFinished.connect(self.setPatLimitAddress)
self.view.lineEditStopAddress.editingFinished.connect(self.setPatLimitAddress)
self.getPatLimitAddress()
def getPatLoopStartStopAddress(self, level):
retval = self.det.patloop[level]
lineEditStart = getattr(self.view, f"lineEditLoop{level}Start")
lineEditStop = getattr(self.view, f"lineEditLoop{level}Stop")
lineEditStart.editingFinished.disconnect()
lineEditStop.editingFinished.disconnect()
lineEditStart.setText("0x{:04x}".format(retval[0]))
lineEditStop.setText("0x{:04x}".format(retval[1]))
lineEditStart.editingFinished.connect(partial(self.setPatLoopStartStopAddress, level))
lineEditStop.editingFinished.connect(partial(self.setPatLoopStartStopAddress, level))
def setPatLoopStartStopAddress(self, level):
lineEditStart = getattr(self.view, f"lineEditLoop{level}Start")
lineEditStop = getattr(self.view, f"lineEditLoop{level}Stop")
lineEditStart.editingFinished.disconnect()
lineEditStop.editingFinished.disconnect()
try:
start = int(lineEditStart.text(), 16)
stop = int(lineEditStop.text(), 16)
self.det.patloop[level] = [start, stop]
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "Pattern Loop Start Stop Address Fail", str(e),
QtWidgets.QMessageBox.Ok)
pass
# TODO: handling double event exceptions
lineEditStart.editingFinished.connect(partial(self.setPatLoopStartStopAddress, level))
lineEditStop.editingFinished.connect(partial(self.setPatLoopStartStopAddress, level))
self.getPatLoopStartStopAddress(level)
def getPatLoopWaitAddress(self, level):
retval = self.det.patwait[level]
lineEdit = getattr(self.view, f"lineEditLoop{level}Wait")
lineEdit.editingFinished.disconnect()
lineEdit.setText("0x{:04x}".format(retval))
lineEdit.editingFinished.connect(partial(self.setPatLoopWaitAddress, level))
def setPatLoopWaitAddress(self, level):
lineEdit = getattr(self.view, f"lineEditLoop{level}Wait")
lineEdit.editingFinished.disconnect()
try:
addr = int(lineEdit.text(), 16)
self.det.patwait[level] = addr
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "Pattern Wait Address Fail", str(e),
QtWidgets.QMessageBox.Ok)
pass
# TODO: handling double event exceptions
lineEdit.editingFinished.connect(partial(self.setPatLoopWaitAddress, level))
self.getPatLoopWaitAddress(level)
def getPatLoopRepetition(self, level):
retval = self.det.patnloop[level]
spinBox = getattr(self.view, f"spinBoxLoop{level}Repetition")
spinBox.editingFinished.disconnect()
spinBox.setValue(retval)
spinBox.editingFinished.connect(partial(self.setPatLoopRepetition, level))
def setPatLoopRepetition(self, level):
spinBox = getattr(self.view, f"spinBoxLoop{level}Repetition")
self.det.patnloop[level] = spinBox.value()
self.getPatLoopRepetition(level)
def getPatLoopWaitTime(self, level):
retval = self.det.patwaittime[level]
spinBox = getattr(self.view, f"spinBoxLoop{level}WaitTime")
spinBox.editingFinished.disconnect()
spinBox.setValue(retval)
spinBox.editingFinished.connect(partial(self.setPatLoopWaitTime, level))
def setPatLoopWaitTime(self, level):
spinBox = getattr(self.view, f"spinBoxLoop{level}WaitTime")
self.det.patwaittime[level] = spinBox.value()
self.getPatLoopWaitTime(level)
def setCompiler(self):
response = QtWidgets.QFileDialog.getOpenFileName(
parent=self.mainWindow,
caption="Select a compiler file",
directory=str(Path.cwd()),
# filter='README (*.md *.ui)'
)
if response[0]:
self.view.lineEditCompiler.setText(response[0])
def setUncompiledPatternFile(self):
filt = 'Pattern code(*.py *.c)'
folder = Path(self.det.patfname[0]).parent
if not folder:
folder = Path.cwd()
response = QtWidgets.QFileDialog.getOpenFileName(parent=self.mainWindow,
caption="Select an uncompiled pattern file",
directory=str(folder),
filter=filt)
if response[0]:
self.view.lineEditUncompiled.setText(response[0])
def setPatternFile(self):
filt = 'Pattern file(*.pyat *.pat)'
folder = Path(self.det.patfname[0]).parent
if not folder:
folder = Path.cwd()
response = QtWidgets.QFileDialog.getOpenFileName(parent=self.mainWindow,
caption="Select a compiled pattern file",
directory=str(folder),
filter=filt)
if response[0]:
self.view.lineEditPatternFile.setText(response[0])
def compilePattern(self):
compilerFile = self.view.lineEditCompiler.text()
if not compilerFile:
QtWidgets.QMessageBox.warning(self.mainWindow, "Compile Fail", "No compiler selected. Please select one.",
QtWidgets.QMessageBox.Ok)
return ""
pattern_file = self.view.lineEditUncompiled.text()
# if old compile file exists, backup and remove to ensure old copy not loaded
oldFile = Path(pattern_file + 'at')
if oldFile.is_file():
print("Moving old compiled pattern file to _bck")
exit_status = os.system('mv ' + str(oldFile) + ' ' + str(oldFile) + '_bkup')
if exit_status != 0:
retval = QtWidgets.QMessageBox.question(
self.mainWindow, "Backup Fail",
"Could not make a backup of old compiled code. Proceed anyway to compile and overwrite?",
QtWidgets.QMessageBox.Yes, QtWidgets.QMessageBox.No)
if retval == QtWidgets.QMessageBox.No:
return ""
compileCommand = compilerFile + ' ' + pattern_file
print(compileCommand)
print("Compiling pattern code to .pat file")
exit_status = os.system(compileCommand)
if exit_status != 0:
QtWidgets.QMessageBox.warning(self.mainWindow, "Compile Fail", "Could not compile pattern.",
QtWidgets.QMessageBox.Ok)
return ""
pattern_file += 'at'
return pattern_file
def getCompiledPatFname(self):
if self.view.checkBoxCompile.isChecked():
pattern_file = self.compilePattern()
# pat name from pattern field
else:
pattern_file = self.view.lineEditPatternFile.text()
if not pattern_file:
QtWidgets.QMessageBox.warning(self.mainWindow, "Pattern Fail",
"No pattern file selected. Please select one.", QtWidgets.QMessageBox.Ok)
return ""
return pattern_file
def loadPattern(self):
pattern_file = self.getCompiledPatFname()
if not pattern_file:
return
# load pattern
self.det.pattern = pattern_file
self.view.lineEditPatternFile.setText(self.det.patfname[0])
def getPatViewerColors(self):
colorLevel = self.view.comboBoxPatColorSelect.currentIndex()
color = self.colors_plot[colorLevel]
self.view.comboBoxPatColor.currentIndexChanged.disconnect()
self.view.comboBoxPatColor.setCurrentIndex(Defines.Colors.index(color))
self.view.comboBoxPatColor.currentIndexChanged.connect(self.updatePatViewerParameters)
def getPatViewerWaitParameters(self):
waitLevel = self.view.comboBoxPatWait.currentIndex()
color = self.colors_wait[waitLevel]
line_style = self.linestyles_wait[waitLevel]
alpha = self.alpha_wait[waitLevel]
alpha_rect = self.alpha_wait_rect[waitLevel]
self.view.comboBoxPatWaitColor.currentIndexChanged.disconnect()
self.view.comboBoxPatWaitLineStyle.currentIndexChanged.disconnect()
self.view.doubleSpinBoxWaitAlpha.editingFinished.disconnect()
self.view.doubleSpinBoxWaitAlphaRect.editingFinished.disconnect()
self.view.comboBoxPatWaitColor.setCurrentIndex(Defines.Colors.index(color))
self.view.comboBoxPatWaitLineStyle.setCurrentIndex(Defines.LineStyles.index(line_style))
self.view.doubleSpinBoxWaitAlpha.setValue(alpha)
self.view.doubleSpinBoxWaitAlphaRect.setValue(alpha_rect)
self.view.comboBoxPatWaitColor.currentIndexChanged.connect(self.updatePatViewerParameters)
self.view.comboBoxPatWaitLineStyle.currentIndexChanged.connect(self.updatePatViewerParameters)
self.view.doubleSpinBoxWaitAlpha.editingFinished.connect(self.updatePatViewerParameters)
self.view.doubleSpinBoxWaitAlphaRect.editingFinished.connect(self.updatePatViewerParameters)
def getPatViewerLoopParameters(self):
loopLevel = self.view.comboBoxPatLoop.currentIndex()
color = self.colors_loop[loopLevel]
line_style = self.linestyles_loop[loopLevel]
alpha = self.alpha_loop[loopLevel]
alpha_rect = self.alpha_loop_rect[loopLevel]
self.view.comboBoxPatLoopColor.currentIndexChanged.disconnect()
self.view.comboBoxPatLoopLineStyle.currentIndexChanged.disconnect()
self.view.doubleSpinBoxLoopAlpha.editingFinished.disconnect()
self.view.doubleSpinBoxLoopAlphaRect.editingFinished.disconnect()
self.view.comboBoxPatLoopColor.setCurrentIndex(Defines.Colors.index(color))
self.view.comboBoxPatLoopLineStyle.setCurrentIndex(Defines.LineStyles.index(line_style))
self.view.doubleSpinBoxLoopAlpha.setValue(alpha)
self.view.doubleSpinBoxLoopAlphaRect.setValue(alpha_rect)
self.view.comboBoxPatLoopColor.currentIndexChanged.connect(self.updatePatViewerParameters)
self.view.comboBoxPatLoopLineStyle.currentIndexChanged.connect(self.updatePatViewerParameters)
self.view.doubleSpinBoxLoopAlpha.editingFinished.connect(self.updatePatViewerParameters)
self.view.doubleSpinBoxLoopAlphaRect.editingFinished.connect(self.updatePatViewerParameters)
# only at start up
def updateDefaultPatViewerParameters(self):
self.colors_plot = Defines.Colors_plot.copy()
self.colors_wait = Defines.Colors_wait.copy()
self.linestyles_wait = Defines.Linestyles_wait.copy()
self.alpha_wait = Defines.Alpha_wait.copy()
self.alpha_wait_rect = Defines.Alpha_wait_rect.copy()
self.colors_loop = Defines.Colors_loop.copy()
self.linestyles_loop = Defines.Linestyles_loop.copy()
self.alpha_loop = Defines.Alpha_loop.copy()
self.alpha_loop_rect = Defines.Alpha_loop_rect.copy()
self.clock_vertical_lines_spacing = Defines.Clock_vertical_lines_spacing
self.show_clocks_number = Defines.Show_clocks_number
self.line_width = Defines.Line_width
# print('default')
# self.printPatViewerParameters()
def updatePatViewerParameters(self):
colorLevel = self.view.comboBoxPatColorSelect.currentIndex()
color = self.view.comboBoxPatColor.currentIndex()
# self.colors_plot[colorLevel] = f'tab:{Defines.Colors[color].lower()}'
self.colors_plot[colorLevel] = Defines.Colors[color]
waitLevel = self.view.comboBoxPatWait.currentIndex()
color = self.view.comboBoxPatWaitColor.currentIndex()
line_style = self.view.comboBoxPatWaitLineStyle.currentIndex()
alpha = self.view.doubleSpinBoxWaitAlpha.value()
alpha_rect = self.view.doubleSpinBoxWaitAlphaRect.value()
self.colors_wait[waitLevel] = Defines.Colors[color]
self.linestyles_wait[waitLevel] = Defines.LineStyles[line_style]
self.alpha_wait[waitLevel] = alpha
self.alpha_wait_rect[waitLevel] = alpha_rect
loopLevel = self.view.comboBoxPatLoop.currentIndex()
color = self.view.comboBoxPatLoopColor.currentIndex()
line_style = self.view.comboBoxPatLoopLineStyle.currentIndex()
alpha = self.view.doubleSpinBoxLoopAlpha.value()
alpha_rect = self.view.doubleSpinBoxLoopAlphaRect.value()
self.colors_loop[loopLevel] = Defines.Colors[color]
self.linestyles_loop[loopLevel] = Defines.LineStyles[line_style]
self.alpha_loop[loopLevel] = alpha
self.alpha_loop_rect[loopLevel] = alpha_rect
self.clock_vertical_lines_spacing = self.view.spinBoxPatClockSpacing.value()
self.show_clocks_number = self.view.checkBoxPatShowClockNumber.isChecked()
self.line_width = self.view.doubleSpinBoxLineWidth.value()
# for debugging
# 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 viewPattern(self):
self.plotTab.showPatternViewer(True)
pattern_file = self.getCompiledPatFname()
if not pattern_file:
return
signalNames = self.det.getSignalNames()
p = PlotPattern(
pattern_file,
signalNames,
self.colors_plot,
self.colors_wait,
self.linestyles_wait,
self.alpha_wait,
self.alpha_wait_rect,
self.colors_loop,
self.linestyles_loop,
self.alpha_loop,
self.alpha_loop_rect,
self.clock_vertical_lines_spacing,
self.show_clocks_number,
self.line_width,
)
plt.close(self.figure)
self.mainWindow.gridLayoutPatternViewer.removeWidget(self.canvas)
self.canvas.close()
self.mainWindow.gridLayoutPatternViewer.removeWidget(self.toolbar)
self.toolbar.close()
try:
self.figure = p.patternPlot()
self.canvas = FigureCanvas(self.figure)
self.toolbar = NavigationToolbar(self.canvas, self.view)
self.mainWindow.gridLayoutPatternViewer.addWidget(self.toolbar)
self.mainWindow.gridLayoutPatternViewer.addWidget(self.canvas)
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "Pattern Viewer Fail", str(e), QtWidgets.QMessageBox.Ok)
pass
def saveParameters(self) -> list[str]:
commands = []
for i in range(Defines.pattern.loops_count):
commands.append(f"patnloop {i} {getattr(self.view, f'spinBoxLoop{i}Repetition').text()}")
commands.append(f"patloop {i} {getattr(self.view, f'lineEditLoop{i}Start').text()}, "
f"{getattr(self.view, f'lineEditLoop{i}Stop').text()}")
commands.append(f"patwait {i} {getattr(self.view, f'lineEditLoop{i}Wait').text()}")
commands.append(f"patwaittime {i} {getattr(self.view, f'spinBoxLoop{i}WaitTime').text()}")
commands.append(f"patlimits {self.view.lineEditStartAddress.text()}, {self.view.lineEditStopAddress.text()}")
# commands.append(f"patfname {self.view.lineEditPatternFile.text()}")
return commands

View File

@ -0,0 +1,519 @@
import logging
from functools import partial
import random
from pathlib import Path
import numpy as np
from PyQt5 import QtWidgets, QtGui, uic
import pyqtgraph as pg
from pyctbgui.utils import recordOrApplyPedestal
from pyqtgraph import PlotWidget
from pyctbgui.utils.defines import Defines
class PlotTab(QtWidgets.QWidget):
def __init__(self, parent):
super().__init__(parent)
self.frame_min: float = 0.0
self.frame_max: float = 0.0
uic.loadUi(Path(__file__).parent.parent / 'ui' / "plot.ui", parent)
self.view = parent
self.mainWindow = None
self.det = None
self.signalsTab = None
self.transceiverTab = None
self.acquisitionTab = None
self.adcTab = None
self.cmin: float = 0.0
self.cmax: float = 0.0
self.colorRangeMode: Defines.colorRange = Defines.colorRange.all
self.ignoreHistogramSignal: bool = False
self.imagePlots: list[PlotWidget] = []
# list of callback functions to notify tabs when we should hide their legend
# follows the observer design pattern
self.hideLegendObservers = []
self.pedestalRecord: bool = False
self.pedestalApply: bool = True
self.__acqFrames = None
self.logger = logging.getLogger('PlotTab')
def setup_ui(self):
self.signalsTab = self.mainWindow.signalsTab
self.transceiverTab = self.mainWindow.transceiverTab
self.acquisitionTab = self.mainWindow.acquisitionTab
self.adcTab = self.mainWindow.adcTab
self.initializeColorMaps()
self.imagePlots = (
self.mainWindow.plotAnalogImage,
self.mainWindow.plotDigitalImage,
self.mainWindow.plotTransceiverImage,
)
def connect_ui(self):
self.view.radioButtonNoPlot.clicked.connect(self.plotOptions)
self.view.radioButtonWaveform.clicked.connect(self.plotOptions)
self.view.radioButtonDistribution.clicked.connect(self.plotOptions)
self.view.radioButtonImage.clicked.connect(self.plotOptions)
self.view.comboBoxPlot.currentIndexChanged.connect(self.setPixelMap)
self.view.comboBoxColorMap.currentIndexChanged.connect(self.setColorMap)
self.view.comboBoxZMQHWM.currentIndexChanged.connect(self.setZMQHWM)
self.view.spinBoxSerialOffset.editingFinished.connect(self.setSerialOffset)
self.view.spinBoxNCount.editingFinished.connect(self.setNCounter)
self.view.spinBoxDynamicRange.editingFinished.connect(self.setDynamicRange)
self.view.spinBoxImageX.editingFinished.connect(self.setImageX)
self.view.spinBoxImageY.editingFinished.connect(self.setImageY)
self.view.radioButtonPedestalRecord.toggled.connect(self.togglePedestalRecord)
self.view.radioButtonPedestalApply.toggled.connect(self.togglePedestalApply)
self.view.pushButtonPedestalReset.clicked.connect(self.resetPedestal)
self.view.pushButtonSavePedestal.clicked.connect(self.savePedestal)
self.view.pushButtonLoadPedestal.clicked.connect(self.loadPedestal)
self.view.checkBoxRaw.stateChanged.connect(self.setRawData)
self.view.spinBoxRawMin.editingFinished.connect(self.setRawData)
self.view.spinBoxRawMax.editingFinished.connect(self.setRawData)
self.view.checkBoxPedestal.stateChanged.connect(self.setPedestalSubtract)
self.view.spinBoxPedestalMin.editingFinished.connect(self.setPedestalSubtract)
self.view.spinBoxPedestalMax.editingFinished.connect(self.setPedestalSubtract)
self.view.spinBoxFit.editingFinished.connect(self.setFitADC)
self.view.spinBoxPlot.editingFinished.connect(self.setPlotBit)
self.view.pushButtonReferesh.clicked.connect(self.acquisitionTab.refresh)
# color range widgets
self.view.cminSpinBox.editingFinished.connect(self.setCmin)
self.view.cmaxSpinBox.editingFinished.connect(self.setCmax)
self.view.radioButtonAutomatic.clicked.connect(partial(self.setColorRangeMode, Defines.colorRange.all))
self.view.radioButtonFixed.clicked.connect(partial(self.setColorRangeMode, Defines.colorRange.fixed))
self.view.radioButtonCenter.clicked.connect(partial(self.setColorRangeMode, Defines.colorRange.center))
for plot in self.imagePlots:
plot.scene.sigMouseMoved.connect(partial(self.showPlotValues, plot))
plot.getHistogramWidget().item.sigLevelChangeFinished.connect(partial(self.handleHistogramChange, plot))
self.view.checkBoxShowLegend.stateChanged.connect(self.toggleLegend)
def refresh(self):
self.getZMQHWM()
def initializeColorMaps(self):
self.view.comboBoxColorMap.addItems(Defines.Color_map)
self.view.comboBoxColorMap.setCurrentIndex(Defines.Color_map.index(Defines.Default_Color_Map))
self.setColorMap()
def savePedestal(self):
"""
slot function to save pedestal values
"""
response = QtWidgets.QFileDialog.getSaveFileName(self.view, "Save Pedestal", str(self.det.fpath))
recordOrApplyPedestal.savePedestal(Path(response[0]))
self.logger.info(f'saved Pedestal in {response[0]}')
def loadPedestal(self):
"""
slot function to load pedestal values
"""
response = QtWidgets.QFileDialog.getOpenFileName(self.view, "Load Pedestal", str(self.det.fpath))
if response[0] == '':
return
recordOrApplyPedestal.reset(self)
try:
recordOrApplyPedestal.loadPedestal(Path(response[0]))
except (IsADirectoryError, ValueError, EOFError):
QtWidgets.QMessageBox.warning(
self.view,
"Loading Pedestal Failed",
"Loading Pedestal failed make sure the file is in the valid .npy format",
QtWidgets.QMessageBox.Ok,
)
self.logger.exception("Exception when loading pedestal")
else:
self.logger.info(f'loaded Pedestal from {response[0]}')
self.updateLabelPedestalFrames(True)
def togglePedestalRecord(self):
"""
slot function for pedestal record radio button
toggle pedestal record variable and disables the frames spinboxes in acquisition tab or plot tab depenging on
the mode
"""
self.pedestalRecord = not self.pedestalRecord
def togglePedestalApply(self):
"""
slot function for pedestal apply radio button
"""
self.pedestalApply = not self.pedestalApply
def resetPedestal(self):
"""
slot function for resetting the pedestal
"""
recordOrApplyPedestal.reset(self)
def updateLabelPedestalFrames(self, loadedPedestal=False):
"""
updates labelPedestalFrames to the length of savedFrames
"""
if loadedPedestal:
self.view.labelPedestalFrames.setText('using loaded pedestal file')
else:
self.view.labelPedestalFrames.setText(f'recorded frames: {recordOrApplyPedestal.getFramesCount()}')
def subscribeToggleLegend(self, fn_cbk):
"""
subscribe to the event of toggling the hide legend checkbox by subscribing
with a callback function
"""
self.hideLegendObservers.append(fn_cbk)
def toggleLegend(self):
"""
notify subscribers for the showLegend checkbox event by executing their callbacks
"""
self.mainWindow.showLegend = not self.mainWindow.showLegend
for notify_function in self.hideLegendObservers:
notify_function()
def setCmin(self, value=None):
"""
slot for setting cmin from cminSpinBox
also used as a normal function
"""
if value is None:
self.cmin = self.view.cminSpinBox.value()
else:
self.cmin = value
self.updateColorRangeUI()
def setCmax(self, value=None):
"""
slot for setting cmax from cmaxSpinBox
also used as a normal function
"""
if value is None:
self.cmax = self.view.cmaxSpinBox.value()
else:
self.cmax = value
self.updateColorRangeUI()
def setColorRangeMode(self, mode):
"""
slot for setting the color range mode (all,fixed,3-97%)
"""
self.colorRangeMode = mode
# disable or enable cmin/cmax spinboxes
enableSpinBoxes = mode == Defines.colorRange.fixed
self.view.cminSpinBox.setEnabled(enableSpinBoxes)
self.view.cmaxSpinBox.setEnabled(enableSpinBoxes)
self.updateColorRange()
self.updateColorRangeUI()
def handleHistogramChange(self, plot):
"""
slot called after changing the color bar
This is called even when pyqtgraph sets the image without any user intervention
the class attribute ignore_histogram_signal is set to False before setting the image
so that we can distinguish between the signal originates from pyqt or from the user
"""
if not self.ignoreHistogramSignal:
self.cmin, self.cmax = plot.getHistogramWidget().item.getLevels()
self.setCmin(self.cmin)
self.setCmax(self.cmax)
self.ignoreHistogramSignal = False
# self.setColorRangeMode(Defines.colorRange.fixed)
def setFrameLimits(self, frame):
"""
function called from AcquisitionTab::read_zmq to get the maximum and minimum
values of the decoded frame and save them in frame_min/frame_max
"""
self.frame_min = np.min(frame)
self.frame_max = np.max(frame)
self.updateColorRange()
def updateColorRange(self):
"""
for mode:
- all: sets cmin and cmax to the maximums/minimum values of the frame
- 3-97%: limits cmax and cmin so that we ignore 3% from each end of the whole range
- fixed: this function does not change cmin and cmax
"""
if self.colorRangeMode == Defines.colorRange.all:
self.cmin = self.frame_min
self.cmax = self.frame_max
elif self.colorRangeMode == Defines.colorRange.center:
self.cmin = self.frame_min + 0.03 * (self.frame_max - self.frame_min)
self.cmax = self.frame_max - 0.03 * (self.frame_max - self.frame_min)
self.updateColorRangeUI()
def updateColorRangeUI(self):
"""
updates UI views should be called after every change to cmin or cmax
"""
for plot in self.imagePlots:
plot.getHistogramWidget().item.setLevels(min=self.cmin, max=self.cmax)
self.view.cminSpinBox.setValue(self.cmin)
self.view.cmaxSpinBox.setValue(self.cmax)
def setColorMap(self):
cm = pg.colormap.getFromMatplotlib(self.view.comboBoxColorMap.currentText())
# print(f'color map:{self.comboBoxColorMap.currentText()}')
self.mainWindow.plotAnalogImage.setColorMap(cm)
self.mainWindow.plotDigitalImage.setColorMap(cm)
self.mainWindow.plotTransceiverImage.setColorMap(cm)
def getZMQHWM(self):
self.view.comboBoxZMQHWM.currentIndexChanged.disconnect()
rx_zmq_hwm = self.det.getRxZmqHwm()[0]
# ensure same value in client zmq
self.det.setClientZmqHwm(rx_zmq_hwm)
# high readout, low HWM
if -1 < rx_zmq_hwm < 25:
self.view.comboBoxZMQHWM.setCurrentIndex(1)
# low readout, high HWM
else:
self.view.comboBoxZMQHWM.setCurrentIndex(0)
self.view.comboBoxZMQHWM.currentIndexChanged.connect(self.setZMQHWM)
def setZMQHWM(self):
val = self.view.comboBoxZMQHWM.currentIndex()
# low readout, high HWM
if val == 0:
self.det.setRxZmqHwm(Defines.Zmq_hwm_low_speed)
self.det.setClientZmqHwm(Defines.Zmq_hwm_low_speed)
# high readout, low HWM
else:
self.det.setRxZmqHwm(Defines.Zmq_hwm_high_speed)
self.det.setClientZmqHwm(Defines.Zmq_hwm_high_speed)
self.getZMQHWM()
def addSelectedAnalogPlots(self, i):
enable = getattr(self.adcTab.view, f"checkBoxADC{i}Plot").isChecked()
if enable:
self.mainWindow.analogPlots[i].show()
if not enable:
self.mainWindow.analogPlots[i].hide()
def addAllSelectedAnalogPlots(self):
for i in range(Defines.adc.count):
self.addSelectedAnalogPlots(i)
def removeAllAnalogPlots(self):
for i in range(Defines.adc.count):
self.mainWindow.analogPlots[i].hide()
cm = pg.colormap.get('CET-L9') # prepare a linear color map
self.mainWindow.plotDigitalImage.setColorMap(cm)
def addSelectedDigitalPlots(self, i):
enable = getattr(self.signalsTab.view, f"checkBoxBIT{i}Plot").isChecked()
if enable:
self.mainWindow.digitalPlots[i].show()
if not enable:
self.mainWindow.digitalPlots[i].hide()
def addAllSelectedDigitalPlots(self):
for i in range(Defines.signals.count):
self.addSelectedDigitalPlots(i)
def removeAllDigitalPlots(self):
for i in range(Defines.signals.count):
self.mainWindow.digitalPlots[i].hide()
def addSelectedTransceiverPlots(self, i):
enable = getattr(self.transceiverTab.view, f"checkBoxTransceiver{i}Plot").isChecked()
if enable:
self.mainWindow.transceiverPlots[i].show()
if not enable:
self.mainWindow.transceiverPlots[i].hide()
def addAllSelectedTransceiverPlots(self):
for i in range(Defines.transceiver.count):
self.addSelectedTransceiverPlots(i)
def removeAllTransceiverPlots(self):
for i in range(Defines.transceiver.count):
self.mainWindow.transceiverPlots[i].hide()
def showPlot(self):
self.mainWindow.plotAnalogWaveform.hide()
self.mainWindow.plotDigitalWaveform.hide()
self.mainWindow.plotTransceiverWaveform.hide()
self.mainWindow.plotAnalogImage.hide()
self.mainWindow.plotDigitalImage.hide()
self.mainWindow.plotTransceiverImage.hide()
self.view.labelDigitalWaveformOption.setDisabled(True)
self.view.radioButtonOverlay.setDisabled(True)
self.view.radioButtonStripe.setDisabled(True)
if self.mainWindow.romode.value in [0, 2]:
if self.view.radioButtonWaveform.isChecked():
self.mainWindow.plotAnalogWaveform.show()
elif self.view.radioButtonImage.isChecked():
self.mainWindow.plotAnalogImage.show()
if self.mainWindow.romode.value in [1, 2, 4]:
if self.view.radioButtonWaveform.isChecked():
self.mainWindow.plotDigitalWaveform.show()
elif self.view.radioButtonImage.isChecked():
self.mainWindow.plotDigitalImage.show()
self.view.labelDigitalWaveformOption.setEnabled(True)
self.view.radioButtonOverlay.setEnabled(True)
self.view.radioButtonStripe.setEnabled(True)
if self.mainWindow.romode.value in [3, 4]:
if self.view.radioButtonWaveform.isChecked():
self.mainWindow.plotTransceiverWaveform.show()
elif self.view.radioButtonImage.isChecked():
self.mainWindow.plotTransceiverImage.show()
def plotOptions(self):
self.mainWindow.framePatternViewer.hide()
self.showPlot()
# disable plotting
self.mainWindow.read_timer.stop()
if self.view.radioButtonWaveform.isChecked():
self.mainWindow.plotAnalogWaveform.setLabel(
'left', "<span style=\"color:black;font-size:14px\">Output [ADC]</span>")
self.mainWindow.plotAnalogWaveform.setLabel(
'bottom', "<span style=\"color:black;font-size:14px\">Analog Sample [#]</span>")
self.mainWindow.plotDigitalWaveform.setLabel(
'left', "<span style=\"color:black;font-size:14px\">Digital Bit</span>")
self.mainWindow.plotDigitalWaveform.setLabel(
'bottom', "<span style=\"color:black;font-size:14px\">Digital Sample [#]</span>")
self.mainWindow.plotTransceiverWaveform.setLabel(
'left', "<span style=\"color:black;font-size:14px\">Transceiver Bit</span>")
self.mainWindow.plotTransceiverWaveform.setLabel(
'bottom', "<span style=\"color:black;font-size:14px\">Transceiver Sample [#]</span>")
self.view.stackedWidgetPlotType.setCurrentIndex(0)
elif self.view.radioButtonImage.isChecked():
self.view.stackedWidgetPlotType.setCurrentIndex(2)
self.setPixelMap()
if self.view.radioButtonNoPlot.isChecked():
self.view.labelPlotOptions.hide()
self.view.stackedWidgetPlotType.hide()
# enable plotting
else:
self.view.labelPlotOptions.show()
self.view.stackedWidgetPlotType.show()
self.mainWindow.read_timer.start(Defines.Time_Plot_Refresh_ms)
def setPixelMap(self):
if self.view.comboBoxPlot.currentText() == "Matterhorn":
self.mainWindow.nTransceiverRows = Defines.Matterhorn.nRows
self.mainWindow.nTransceiverCols = Defines.Matterhorn.nCols
elif self.view.comboBoxPlot.currentText() == "Moench04":
self.mainWindow.nAnalogRows = Defines.Moench04.nRows
self.mainWindow.nAnalogCols = Defines.Moench04.nCols
def showPatternViewer(self, enable):
if enable:
self.mainWindow.framePatternViewer.show()
self.mainWindow.framePlot.hide()
elif self.mainWindow.framePatternViewer.isVisible():
self.mainWindow.framePatternViewer.hide()
self.mainWindow.framePlot.show()
self.showPlot()
def setSerialOffset(self):
print("plot options - Not implemented yet")
# TODO:
def setNCounter(self):
print("plot options - Not implemented yet")
# TODO:
def setDynamicRange(self):
print("plot options - Not implemented yet")
# TODO:
def setImageX(self):
print("plot options - Not implemented yet")
# TODO:
def setImageY(self):
print("plot options - Not implemented yet")
# TODO:
def setRawData(self):
print("plot options - Not implemented yet")
# TODO: raw data, min, max
def setPedestalSubtract(self):
print("plot options - Not implemented yet")
# TODO: pedestal, min, max
def setFitADC(self):
print("plot options - Not implemented yet")
# TODO:
def setPlotBit(self):
print("plot options - Not implemented yet")
# TODO:
def getRandomColor(self):
'''
Returns a random color range (except white) in format string eg. "#aabbcc"
'''
randomColor = random.randrange(0, 0xffffaa, 0xaa)
return "#{:06x}".format(randomColor)
def getActiveColor(self, button):
return button.palette().color(QtGui.QPalette.Window)
def setActiveColor(self, button, str_color):
button.setStyleSheet(":enabled {background-color: %s" % str_color + "} :disabled {background-color: grey}")
def showPalette(self, button):
color = QtWidgets.QColorDialog.getColor()
if color.isValid():
self.setActiveColor(button, color.name())
# get the RGB Values
# print(color.getRgb())
def showPlotValues(self, sender, pos):
x = sender.getImageItem().mapFromScene(pos).x()
y = sender.getImageItem().mapFromScene(pos).y()
val = 0
nMaxY = self.mainWindow.nAnalogRows
nMaxX = self.mainWindow.nAnalogCols
frame = self.mainWindow.analog_frame
if sender == self.mainWindow.plotDigitalImage:
nMaxY = self.mainWindow.nDigitalRows
nMaxX = self.mainWindow.nDigitalCols
frame = self.mainWindow.digital_frame
elif sender == self.mainWindow.plotTransceiverImage:
nMaxY = self.mainWindow.nTransceiverRows
nMaxX = self.mainWindow.nTransceiverCols
frame = self.mainWindow.transceiver_frame
if 0 <= x < nMaxX and 0 <= y < nMaxY and not np.array_equal(frame, []):
val = frame[int(x), int(y)]
message = f'[{x:.2f}, {y:.2f}] = {val:.2f}'
sender.setToolTip(message)
# print(message)
else:
sender.setToolTip('')
def saveParameters(self):
commands = []
if self.view.comboBoxZMQHWM.currentIndex() == 0:
commands.append(f"zmqhwm {Defines.Zmq_hwm_low_speed}")
else:
commands.append(f"zmqhwm {Defines.Zmq_hwm_high_speed}")
return commands

View File

@ -0,0 +1,123 @@
from functools import partial
from pathlib import Path
from PyQt5 import QtWidgets, uic
from pyctbgui.utils.defines import Defines
from slsdet import dacIndex
class PowerSuppliesTab(QtWidgets.QWidget):
def __init__(self, parent):
super().__init__(parent)
uic.loadUi(Path(__file__).parent.parent / 'ui' / "powerSupplies.ui", parent)
self.view = parent
def refresh(self):
self.updateVoltageNames()
for i in Defines.powerSupplies:
self.getVoltage(i)
self.getCurrent(i)
def connect_ui(self):
for i in Defines.powerSupplies:
spinBox = getattr(self.view, f"spinBoxV{i}")
checkBox = getattr(self.view, f"checkBoxV{i}")
spinBox.editingFinished.connect(partial(self.setVoltage, i))
checkBox.stateChanged.connect(partial(self.setVoltage, i))
self.view.pushButtonPowerOff.clicked.connect(self.powerOff)
def setup_ui(self):
for i in Defines.powerSupplies:
dac = getattr(dacIndex, f"V_POWER_{i}")
spinBox = getattr(self.view, f"spinBoxV{i}")
checkBox = getattr(self.view, f"checkBoxV{i}")
retval = self.det.getPower(dac)[0]
spinBox.setValue(retval)
if retval == 0:
checkBox.setChecked(False)
spinBox.setDisabled(True)
def updateVoltageNames(self):
retval = self.det.getPowerNames()
getattr(self.view, "checkBoxVA").setText(retval[0])
getattr(self.view, "checkBoxVB").setText(retval[1])
getattr(self.view, "checkBoxVC").setText(retval[2])
getattr(self.view, "checkBoxVD").setText(retval[3])
getattr(self.view, "checkBoxVIO").setText(retval[4])
def getVoltage(self, i):
spinBox = getattr(self.view, f"spinBoxV{i}")
checkBox = getattr(self.view, f"checkBoxV{i}")
voltageIndex = getattr(dacIndex, f"V_POWER_{i}")
label = getattr(self.view, f"labelV{i}")
spinBox.editingFinished.disconnect()
checkBox.stateChanged.disconnect()
retval = self.det.getMeasuredPower(voltageIndex)[0]
# spinBox.setValue(retval)
if retval > 1:
checkBox.setChecked(True)
if checkBox.isChecked():
spinBox.setEnabled(True)
else:
spinBox.setDisabled(True)
label.setText(f'{str(retval)} mV')
spinBox.editingFinished.connect(partial(self.setVoltage, i))
checkBox.stateChanged.connect(partial(self.setVoltage, i))
self.getVChip()
# TODO: handle multiple events when pressing enter (twice)
def setVoltage(self, i):
checkBox = getattr(self.view, f"checkBoxV{i}")
spinBox = getattr(self.view, f"spinBoxV{i}")
voltageIndex = getattr(dacIndex, f"V_POWER_{i}")
spinBox.editingFinished.disconnect()
value = 0
if checkBox.isChecked():
value = spinBox.value()
try:
self.det.setPower(voltageIndex, value)
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "Voltage Fail", str(e), QtWidgets.QMessageBox.Ok)
pass
# TODO: (properly) disconnecting and connecting to handle multiple events (out of focus and pressing enter).
spinBox.editingFinished.connect(partial(self.setVoltage, i))
self.getVoltage(i)
self.getCurrent(i)
def getCurrent(self, i):
label = getattr(self.view, f"labelI{i}")
currentIndex = getattr(dacIndex, f"I_POWER_{i}")
retval = self.det.getMeasuredCurrent(currentIndex)[0]
label.setText(f'{str(retval)} mA')
def getVChip(self):
self.view.spinBoxVChip.setValue(self.det.getPower(dacIndex.V_POWER_CHIP)[0])
def powerOff(self):
for i in Defines.powerSupplies:
# set all voltages to 0
checkBox = getattr(self.view, f"checkBoxV{i}")
checkBox.stateChanged.disconnect()
checkBox.setChecked(False)
checkBox.stateChanged.connect(partial(self.setVoltage, i))
self.setVoltage(i)
def saveParameters(self) -> list:
commands = []
for i in Defines.powerSupplies:
enabled = getattr(self.view, f"checkBoxV{i}").isChecked()
if enabled:
value = getattr(self.view, f"spinBoxV{i}").value()
commands.append(f"v_{i.lower()} {value}")
else:
commands.append(f"v_{i.lower()} 0")
return commands

View File

@ -0,0 +1,373 @@
from functools import partial
from pathlib import Path
import numpy as np
from PyQt5 import QtWidgets, uic
import pyqtgraph as pg
from pyqtgraph import LegendItem
from pyctbgui.utils.bit_utils import bit_is_set, manipulate_bit
from pyctbgui.utils.defines import Defines
from pyctbgui.utils.recordOrApplyPedestal import recordOrApplyPedestal
class SignalsTab(QtWidgets.QWidget):
def __init__(self, parent):
super().__init__(parent)
uic.loadUi(Path(__file__).parent.parent / 'ui' / "signals.ui", parent)
self.view = parent
self.mainWindow = None
self.det = None
self.plotTab = None
self.legend: LegendItem | None = None
self.rx_dbitoffset = None
self.rx_dbitlist = None
def refresh(self):
self.updateSignalNames()
self.updateDigitalBitEnable()
self.updateIOOut()
self.getDBitOffset()
def connect_ui(self):
for i in range(Defines.signals.count):
getattr(self.view, f"checkBoxBIT{i}DB").stateChanged.connect(partial(self.setDigitalBitEnable, i))
getattr(self.view, f"checkBoxBIT{i}Out").stateChanged.connect(partial(self.setIOOut, i))
getattr(self.view, f"checkBoxBIT{i}Plot").stateChanged.connect(partial(self.setEnableBitPlot, i))
getattr(self.view, f"pushButtonBIT{i}").clicked.connect(partial(self.selectBitColor, i))
self.view.checkBoxBIT0_31DB.stateChanged.connect(
partial(self.setDigitalBitEnableRange, 0, Defines.signals.half))
self.view.checkBoxBIT32_63DB.stateChanged.connect(
partial(self.setDigitalBitEnableRange, Defines.signals.half, Defines.signals.count))
self.view.checkBoxBIT0_31Plot.stateChanged.connect(partial(self.setEnableBitPlotRange, 0,
Defines.signals.half))
self.view.checkBoxBIT32_63Plot.stateChanged.connect(
partial(self.setEnableBitPlotRange, Defines.signals.half, Defines.signals.count))
self.view.checkBoxBIT0_31Out.stateChanged.connect(partial(self.setIOOutRange, 0, Defines.signals.half))
self.view.checkBoxBIT32_63Out.stateChanged.connect(
partial(self.setIOOutRange, Defines.signals.half, Defines.signals.count))
self.view.lineEditPatIOCtrl.editingFinished.connect(self.setIOOutReg)
self.view.spinBoxDBitOffset.editingFinished.connect(self.setDbitOffset)
def setup_ui(self):
self.plotTab = self.mainWindow.plotTab
for i in range(Defines.signals.count):
self.setDBitButtonColor(i, self.plotTab.getRandomColor())
self.initializeAllDigitalPlots()
self.legend = self.mainWindow.plotDigitalWaveform.getPlotItem().legend
self.legend.clear()
# subscribe to toggle legend
self.plotTab.subscribeToggleLegend(self.updateLegend)
def getEnabledPlots(self):
"""
return plots that are shown (checkBoxTransceiver{i}Plot is checked)
"""
enabledPlots = []
self.legend.clear()
for i in range(Defines.signals.count):
if getattr(self.view, f'checkBoxBIT{i}Plot').isChecked():
plotName = getattr(self.view, f"labelBIT{i}").text()
enabledPlots.append((self.mainWindow.digitalPlots[i], plotName))
return enabledPlots
def updateLegend(self):
"""
update the legend for the signals waveform plot
should be called after checking or unchecking plot checkbox
"""
if not self.mainWindow.showLegend:
self.legend.clear()
else:
for plot, name in self.getEnabledPlots():
self.legend.addItem(plot, name)
@recordOrApplyPedestal
def _processWaveformData(self, data, aSamples, dSamples, rx_dbitlist, isPlottedArray, rx_dbitoffset, romode,
nADCEnabled):
"""
transform raw waveform data into a processed numpy array
@param data: raw waveform data
"""
dbitoffset = rx_dbitoffset
if romode == 2:
dbitoffset += nADCEnabled * 2 * aSamples
digital_array = np.array(np.frombuffer(data, offset=dbitoffset, dtype=np.uint8))
nbitsPerDBit = dSamples
if nbitsPerDBit % 8 != 0:
nbitsPerDBit += (8 - (dSamples % 8))
offset = 0
arr = []
for i in rx_dbitlist:
# where numbits * numsamples is not a multiple of 8
if offset % 8 != 0:
offset += (8 - (offset % 8))
if not isPlottedArray[i]:
offset += nbitsPerDBit
return None
waveform = np.zeros(dSamples)
for iSample in range(dSamples):
# all samples for digital bit together from slsReceiver
index = int(offset / 8)
iBit = offset % 8
bit = (digital_array[index] >> iBit) & 1
waveform[iSample] = bit
offset += 1
arr.append(waveform)
return np.array(arr)
def processWaveformData(self, data, aSamples, dSamples):
"""
view function
plots processed waveform data
data: raw waveform data
dsamples: digital samples
asamples: analog samples
"""
waveforms = {}
isPlottedArray = {i: getattr(self.view, f"checkBoxBIT{i}Plot").isChecked() for i in self.rx_dbitlist}
digital_array = self._processWaveformData(data, aSamples, dSamples, self.rx_dbitlist, isPlottedArray,
self.rx_dbitoffset, self.mainWindow.romode.value,
self.mainWindow.nADCEnabled)
irow = 0
for idx, i in enumerate(self.rx_dbitlist):
# bits enabled but not plotting
waveform = digital_array[idx]
if waveform is None:
continue
self.mainWindow.digitalPlots[i].setData(waveform)
plotName = getattr(self.view, f"labelBIT{i}").text()
waveforms[plotName] = waveform
# TODO: left axis does not show 0 to 1, but keeps increasing
if self.plotTab.view.radioButtonStripe.isChecked():
self.mainWindow.digitalPlots[i].setY(irow * 2)
irow += 1
else:
self.mainWindow.digitalPlots[i].setY(0)
return waveforms
def initializeAllDigitalPlots(self):
self.mainWindow.plotDigitalWaveform = pg.plot()
self.mainWindow.plotDigitalWaveform.addLegend(colCount=Defines.colCount)
self.mainWindow.verticalLayoutPlot.addWidget(self.mainWindow.plotDigitalWaveform, 3)
self.mainWindow.digitalPlots = {}
waveform = np.zeros(1000)
for i in range(Defines.signals.count):
pen = pg.mkPen(color=self.getDBitButtonColor(i), width=1)
legendName = getattr(self.view, f"labelBIT{i}").text()
self.mainWindow.digitalPlots[i] = self.mainWindow.plotDigitalWaveform.plot(waveform,
pen=pen,
name=legendName,
stepMode="left")
self.mainWindow.digitalPlots[i].hide()
self.mainWindow.plotDigitalImage = pg.ImageView()
self.mainWindow.nDigitalRows = 0
self.mainWindow.nDigitalCols = 0
self.mainWindow.digital_frame = np.zeros((self.mainWindow.nDigitalRows, self.mainWindow.nDigitalCols))
self.mainWindow.plotDigitalImage.setImage(self.mainWindow.digital_frame)
self.mainWindow.verticalLayoutPlot.addWidget(self.mainWindow.plotDigitalImage, 4)
def updateSignalNames(self):
for i, name in enumerate(self.det.getSignalNames()):
getattr(self.view, f"labelBIT{i}").setText(name)
def getDigitalBitEnable(self, i, dbitList):
checkBox = getattr(self.view, f"checkBoxBIT{i}DB")
checkBox.stateChanged.disconnect()
checkBox.setChecked(i in list(dbitList))
checkBox.stateChanged.connect(partial(self.setDigitalBitEnable, i))
def updateDigitalBitEnable(self):
retval = self.det.rx_dbitlist
self.rx_dbitlist = list(retval)
self.mainWindow.nDBitEnabled = len(list(retval))
for i in range(Defines.signals.count):
self.getDigitalBitEnable(i, retval)
self.getEnableBitPlot(i)
self.getEnableBitColor(i)
self.plotTab.addSelectedDigitalPlots(i)
self.getDigitalBitEnableRange(retval)
self.getEnableBitPlotRange()
def setDigitalBitEnable(self, i):
bitList = self.det.rx_dbitlist
checkBox = getattr(self.view, f"checkBoxBIT{i}DB")
if checkBox.isChecked():
bitList.append(i)
else:
bitList.remove(i)
self.det.rx_dbitlist = bitList
self.updateDigitalBitEnable()
def getDigitalBitEnableRange(self, dbitList):
self.view.checkBoxBIT0_31DB.stateChanged.disconnect()
self.view.checkBoxBIT32_63DB.stateChanged.disconnect()
self.view.checkBoxBIT0_31DB.setChecked(all(x in list(dbitList) for x in range(Defines.signals.half)))
self.view.checkBoxBIT32_63DB.setChecked(
all(x in list(dbitList) for x in range(Defines.signals.half, Defines.signals.count)))
self.view.checkBoxBIT0_31DB.stateChanged.connect(
partial(self.setDigitalBitEnableRange, 0, Defines.signals.half))
self.view.checkBoxBIT32_63DB.stateChanged.connect(
partial(self.setDigitalBitEnableRange, Defines.signals.half, Defines.signals.count))
def setDigitalBitEnableRange(self, start_nr, end_nr):
bitList = self.det.rx_dbitlist
checkBox = getattr(self.view, f"checkBoxBIT{start_nr}_{end_nr - 1}DB")
for i in range(start_nr, end_nr):
if checkBox.isChecked():
if i not in list(bitList):
bitList.append(i)
else:
if i in list(bitList):
bitList.remove(i)
self.det.rx_dbitlist = bitList
self.updateDigitalBitEnable()
def getEnableBitPlot(self, i):
checkBox = getattr(self.view, f"checkBoxBIT{i}DB")
checkBoxPlot = getattr(self.view, f"checkBoxBIT{i}Plot")
checkBoxPlot.setEnabled(checkBox.isChecked())
def setEnableBitPlot(self, i):
pushButton = getattr(self.view, f"pushButtonBIT{i}")
checkBox = getattr(self.view, f"checkBoxBIT{i}Plot")
pushButton.setEnabled(checkBox.isChecked())
self.getEnableBitPlotRange()
self.plotTab.addSelectedDigitalPlots(i)
self.updateLegend()
def getEnableBitPlotRange(self):
self.view.checkBoxBIT0_31Plot.stateChanged.disconnect()
self.view.checkBoxBIT32_63Plot.stateChanged.disconnect()
self.view.checkBoxBIT0_31Plot.setEnabled(
all(getattr(self.view, f"checkBoxBIT{i}Plot").isEnabled() for i in range(Defines.signals.half)))
self.view.checkBoxBIT32_63Plot.setEnabled(
all(
getattr(self.view, f"checkBoxBIT{i}Plot").isEnabled()
for i in range(Defines.signals.half, Defines.signals.count)))
self.view.checkBoxBIT0_31Plot.setChecked(
all(getattr(self.view, f"checkBoxBIT{i}Plot").isChecked() for i in range(Defines.signals.half)))
self.view.checkBoxBIT32_63Plot.setChecked(
all(
getattr(self.view, f"checkBoxBIT{i}Plot").isChecked()
for i in range(Defines.signals.half, Defines.signals.count)))
self.view.checkBoxBIT0_31Plot.stateChanged.connect(partial(self.setEnableBitPlotRange, 0,
Defines.signals.half))
self.view.checkBoxBIT32_63Plot.stateChanged.connect(
partial(self.setEnableBitPlotRange, Defines.signals.half, Defines.signals.count))
def setEnableBitPlotRange(self, start_nr, end_nr):
checkBox = getattr(self.view, f"checkBoxBIT{start_nr}_{end_nr - 1}Plot")
enable = checkBox.isChecked()
for i in range(start_nr, end_nr):
checkBox = getattr(self.view, f"checkBoxBIT{i}Plot")
checkBox.setChecked(enable)
self.plotTab.addAllSelectedDigitalPlots()
def getEnableBitColor(self, i):
checkBox = getattr(self.view, f"checkBoxBIT{i}Plot")
pushButton = getattr(self.view, f"pushButtonBIT{i}")
pushButton.setEnabled(checkBox.isEnabled() and checkBox.isChecked())
def selectBitColor(self, i):
pushButton = getattr(self.view, f"pushButtonBIT{i}")
self.plotTab.showPalette(pushButton)
pen = pg.mkPen(color=self.getDBitButtonColor(i), width=1)
self.mainWindow.digitalPlots[i].setPen(pen)
def getDBitButtonColor(self, i):
pushButton = getattr(self.view, f"pushButtonBIT{i}")
return self.plotTab.getActiveColor(pushButton)
def setDBitButtonColor(self, i, color):
pushButton = getattr(self.view, f"pushButtonBIT{i}")
return self.plotTab.setActiveColor(pushButton, color)
def getIOOutReg(self):
retval = self.det.patioctrl
self.view.lineEditPatIOCtrl.editingFinished.disconnect()
self.view.lineEditPatIOCtrl.setText("0x{:016x}".format(retval))
self.view.lineEditPatIOCtrl.editingFinished.connect(self.setIOOutReg)
return retval
def setIOOutReg(self):
self.view.lineEditPatIOCtrl.editingFinished.disconnect()
try:
self.det.patioctrl = int(self.view.lineEditPatIOCtrl.text(), 16)
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "IO Out Fail", str(e), QtWidgets.QMessageBox.Ok)
pass
# TODO: handling double event exceptions
self.view.lineEditPatIOCtrl.editingFinished.connect(self.setIOOutReg)
self.updateIOOut()
def updateCheckBoxIOOut(self, i, out):
checkBox = getattr(self.view, f"checkBoxBIT{i}Out")
checkBox.stateChanged.disconnect()
checkBox.setChecked(bit_is_set(out, i))
checkBox.stateChanged.connect(partial(self.setIOOut, i))
def updateIOOut(self):
retval = self.getIOOutReg()
for i in range(Defines.signals.count):
self.updateCheckBoxIOOut(i, retval)
self.getIOoutRange(retval)
def setIOOut(self, i):
out = self.det.patioctrl
checkBox = getattr(self.view, f"checkBoxBIT{i}Out")
mask = manipulate_bit(checkBox.isChecked(), out, i)
self.det.patioctrl = mask
retval = self.getIOOutReg()
self.updateCheckBoxIOOut(i, retval)
self.getIOoutRange(retval)
def getIOoutRange(self, out):
self.view.checkBoxBIT0_31Out.stateChanged.disconnect()
self.view.checkBoxBIT32_63Out.stateChanged.disconnect()
self.view.checkBoxBIT0_31Out.setChecked((out & Defines.signals.BIT0_31_MASK) == Defines.signals.BIT0_31_MASK)
self.view.checkBoxBIT32_63Out.setChecked((out
& Defines.signals.BIT32_63_MASK) == Defines.signals.BIT32_63_MASK)
self.view.checkBoxBIT0_31Out.stateChanged.connect(partial(self.setIOOutRange, 0, Defines.signals.half))
self.view.checkBoxBIT32_63Out.stateChanged.connect(
partial(self.setIOOutRange, Defines.signals.half, Defines.signals.count))
def setIOOutRange(self, start_nr, end_nr):
out = self.det.patioctrl
checkBox = getattr(self.view, f"checkBoxBIT{start_nr}_{end_nr - 1}Out")
mask = getattr(Defines.signals, f"BIT{start_nr}_{end_nr - 1}_MASK")
if checkBox.isChecked():
self.det.patioctrl = out | mask
else:
self.det.patioctrl = out & ~mask
self.updateIOOut()
def getDBitOffset(self):
self.view.spinBoxDBitOffset.editingFinished.disconnect()
self.rx_dbitoffset = self.det.rx_dbitoffset
self.view.spinBoxDBitOffset.setValue(self.rx_dbitoffset)
self.view.spinBoxDBitOffset.editingFinished.connect(self.setDbitOffset)
def setDbitOffset(self):
self.det.rx_dbitoffset = self.view.spinBoxDBitOffset.value()
def saveParameters(self) -> list:
commands = []
dblist = [str(i) for i in range(Defines.signals.count) if getattr(self.view, f"checkBoxBIT{i}DB").isChecked()]
if len(dblist) > 0:
commands.append(f"rx_dbitlist {', '.join(dblist)}")
commands.append(f"rx_dbitoffset {self.view.spinBoxDBitOffset.value()}")
commands.append(f"patioctrl {self.view.lineEditPatIOCtrl.text()}")
return commands

View File

@ -0,0 +1,45 @@
from functools import partial
from pathlib import Path
from PyQt5 import uic, QtWidgets
from pyctbgui.utils.defines import Defines
from slsdet import dacIndex
class SlowAdcTab(QtWidgets.QWidget):
def __init__(self, parent):
super().__init__(parent)
uic.loadUi(Path(__file__).parent.parent / 'ui' / "slowAdcs.ui", parent)
self.view = parent
self.mainWindow = None
self.det = None
def setup_ui(self):
pass
def connect_ui(self):
for i in range(Defines.slowAdc.count):
getattr(self.view, f"pushButtonSlowAdc{i}").clicked.connect(partial(self.updateSlowAdc, i))
self.view.pushButtonTemp.clicked.connect(self.updateTemperature)
def refresh(self):
self.updateSlowAdcNames()
for i in range(Defines.slowAdc.count):
self.updateSlowAdc(i)
self.updateTemperature()
def updateSlowAdcNames(self):
for i, name in enumerate(self.mainWindow.det.getSlowADCNames()):
getattr(self.view, f"labelSlowAdc{i}").setText(name)
def updateSlowAdc(self, i):
slowADCIndex = getattr(dacIndex, f"SLOW_ADC{i}")
label = getattr(self.view, f"labelSlowAdcValue{i}")
slowadc = (self.det.getSlowADC(slowADCIndex))[0] / 1000
label.setText(f'{slowadc:.2f} mV')
def updateTemperature(self):
slowadc = self.det.getTemperature(dacIndex.SLOW_ADC_TEMP)
self.view.labelTempValue.setText(f'{str(slowadc[0])} °C')

View File

@ -0,0 +1,273 @@
from functools import partial
from pathlib import Path
import numpy as np
from PyQt5 import QtWidgets, uic
import pyqtgraph as pg
from pyqtgraph import LegendItem
from pyctbgui.utils import decoder
from pyctbgui.utils.defines import Defines
from pyctbgui.utils.bit_utils import bit_is_set, manipulate_bit
import pyctbgui.utils.pixelmap as pm
from pyctbgui.utils.recordOrApplyPedestal import recordOrApplyPedestal
class TransceiverTab(QtWidgets.QWidget):
def __init__(self, parent):
super().__init__(parent)
uic.loadUi(Path(__file__).parent.parent / 'ui' / "transceiver.ui", parent)
self.view = parent
self.mainWindow = None
self.det = None
self.plotTab = None
self.legend: LegendItem | None = None
self.acquisitionTab = None
def setup_ui(self):
self.plotTab = self.mainWindow.plotTab
self.acquisitionTab = self.mainWindow.acquisitionTab
for i in range(Defines.transceiver.count):
self.setTransceiverButtonColor(i, self.plotTab.getRandomColor())
self.initializeAllTransceiverPlots()
self.legend = self.mainWindow.plotTransceiverWaveform.getPlotItem().legend
self.legend.clear()
# subscribe to toggle legend
self.plotTab.subscribeToggleLegend(self.updateLegend)
def connect_ui(self):
for i in range(Defines.transceiver.count):
getattr(self.view, f"checkBoxTransceiver{i}").stateChanged.connect(partial(self.setTransceiverEnable, i))
getattr(self.view,
f"checkBoxTransceiver{i}Plot").stateChanged.connect(partial(self.setTransceiverEnablePlot, i))
getattr(self.view, f"pushButtonTransceiver{i}").clicked.connect(partial(self.selectTransceiverColor, i))
self.view.lineEditTransceiverMask.editingFinished.connect(self.setTransceiverEnableReg)
def refresh(self):
self.updateTransceiverEnable()
def getEnabledPlots(self):
"""
return plots that are shown (checkBoxTransceiver{i}Plot is checked)
"""
enabledPlots = []
self.legend.clear()
for i in range(Defines.transceiver.count):
if getattr(self.view, f'checkBoxTransceiver{i}Plot').isChecked():
plotName = getattr(self.view, f"labelTransceiver{i}").text()
enabledPlots.append((self.mainWindow.transceiverPlots[i], plotName))
return enabledPlots
def updateLegend(self):
"""
update the legend for the transceiver waveform plot
should be called after checking or unchecking plot checkbox
"""
if not self.mainWindow.showLegend:
self.legend.clear()
else:
for plot, name in self.getEnabledPlots():
self.legend.addItem(plot, name)
@recordOrApplyPedestal
def _processWaveformData(self, data, dSamples, romode, nDBitEnabled, nTransceiverEnabled):
"""
model function
processes raw receiver waveform data
@param data: raw receiver waveform data
@param dSamples: digital samples
@param romode: readout mode value
@param nDBitEnabled: number of digital bits enabled
@param nTransceiverEnabled: number of transceivers enabled
@return: processed transceiver data
"""
transceiverOffset = 0
if romode == 4:
nbitsPerDBit = dSamples
if dSamples % 8 != 0:
nbitsPerDBit += (8 - (dSamples % 8))
transceiverOffset += nDBitEnabled * (nbitsPerDBit // 8)
trans_array = np.array(np.frombuffer(data, offset=transceiverOffset, dtype=np.uint16))
return trans_array.reshape(-1, nTransceiverEnabled)
def processWaveformData(self, data, dSamples):
"""
plots raw waveform data
data: raw waveform data
dsamples: digital samples
tsamples: transceiver samples
"""
waveforms = {}
trans_array = self._processWaveformData(data, dSamples, self.mainWindow.romode.value,
self.mainWindow.nDBitEnabled, self.nTransceiverEnabled)
idx = 0
for i in range(Defines.transceiver.count):
checkBoxPlot = getattr(self.view, f"checkBoxTransceiver{i}Plot")
checkBoxEn = getattr(self.view, f"checkBoxTransceiver{i}")
if checkBoxEn.isChecked() and checkBoxPlot.isChecked():
waveform = trans_array[:, idx]
idx += 1
self.mainWindow.transceiverPlots[i].setData(waveform)
plotName = getattr(self.view, f"labelTransceiver{i}").text()
waveforms[plotName] = waveform
return waveforms
@recordOrApplyPedestal
def _processImageData(self, data, dSamples, romode, nDBitEnabled):
"""
processes raw image data
@param data:
@param dSamples:
@param romode:
@param nDBitEnabled:
@return:
"""
transceiverOffset = 0
if romode == 4:
nbitsPerDBit = dSamples
if dSamples % 8 != 0:
nbitsPerDBit += (8 - (dSamples % 8))
transceiverOffset += nDBitEnabled * (nbitsPerDBit // 8)
trans_array = np.array(np.frombuffer(data, offset=transceiverOffset, dtype=np.uint16))
return decoder.decode(trans_array, pm.matterhorn_transceiver())
def processImageData(self, data, dSamples):
"""
view function
plots transceiver image
dSamples: digital samples
data: raw image data
"""
# get zoom state
viewBox = self.mainWindow.plotTransceiverImage.getView()
state = viewBox.getState()
try:
self.mainWindow.transceiver_frame = self._processImageData(data, dSamples, self.mainWindow.romode.value,
self.mainWindow.nDBitEnabled)
self.plotTab.ignoreHistogramSignal = True
self.mainWindow.plotTransceiverImage.setImage(self.mainWindow.transceiver_frame)
except Exception:
self.mainWindow.statusbar.setStyleSheet("color:red")
message = f'Warning: Invalid size for Transceiver Image. Expected' \
f' {self.mainWindow.nTransceiverRows * self.mainWindow.nTransceiverCols} size,' \
f' got {self.mainWindow.transceiver_frame.size} instead.'
self.acquisitionTab.updateCurrentFrame('Invalid Image')
self.mainWindow.statusbar.showMessage(message)
print(message)
self.plotTab.setFrameLimits(self.mainWindow.transceiver_frame)
# keep the zoomed in state (not 1st image)
if self.mainWindow.firstTransceiverImage:
self.mainWindow.firstTransceiverImage = False
else:
viewBox.setState(state)
return self.mainWindow.transceiver_frame
def initializeAllTransceiverPlots(self):
self.mainWindow.plotTransceiverWaveform = pg.plot()
self.mainWindow.plotTransceiverWaveform.addLegend(colCount=Defines.colCount)
self.mainWindow.verticalLayoutPlot.addWidget(self.mainWindow.plotTransceiverWaveform, 5)
self.mainWindow.transceiverPlots = {}
waveform = np.zeros(1000)
for i in range(Defines.transceiver.count):
pen = pg.mkPen(color=self.getTransceiverButtonColor(i), width=1)
legendName = getattr(self.view, f"labelTransceiver{i}").text()
self.mainWindow.transceiverPlots[i] = self.mainWindow.plotTransceiverWaveform.plot(waveform,
pen=pen,
name=legendName)
self.mainWindow.transceiverPlots[i].hide()
self.mainWindow.plotTransceiverImage = pg.ImageView()
self.mainWindow.nTransceiverRows = 0
self.mainWindow.nTransceiverCols = 0
self.mainWindow.transceiver_frame = np.zeros(
(self.mainWindow.nTransceiverRows, self.mainWindow.nTransceiverCols))
self.mainWindow.plotTransceiverImage.setImage(self.mainWindow.transceiver_frame)
self.mainWindow.verticalLayoutPlot.addWidget(self.mainWindow.plotTransceiverImage, 6)
cm = pg.colormap.get('CET-L9') # prepare a linear color map
self.mainWindow.plotTransceiverImage.setColorMap(cm)
def getTransceiverEnableReg(self):
retval = self.det.transceiverenable
self.view.lineEditTransceiverMask.editingFinished.disconnect()
self.view.lineEditTransceiverMask.setText("0x{:08x}".format(retval))
self.view.lineEditTransceiverMask.editingFinished.connect(self.setTransceiverEnableReg)
return retval
def setTransceiverEnableReg(self):
self.view.lineEditTransceiverMask.editingFinished.disconnect()
try:
mask = int(self.view.lineEditTransceiverMask.text(), 16)
self.det.transceiverenable = mask
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "Transceiver Enable Fail", str(e), QtWidgets.QMessageBox.Ok)
pass
# TODO: handling double event exceptions
self.view.lineEditTransceiverMask.editingFinished.connect(self.setTransceiverEnableReg)
self.updateTransceiverEnable()
def getTransceiverEnable(self, i, mask):
checkBox = getattr(self.view, f"checkBoxTransceiver{i}")
checkBox.stateChanged.disconnect()
checkBox.setChecked(bit_is_set(mask, i))
checkBox.stateChanged.connect(partial(self.setTransceiverEnable, i))
def updateTransceiverEnable(self):
retval = self.getTransceiverEnableReg()
self.nTransceiverEnabled = bin(retval).count('1')
for i in range(4):
self.getTransceiverEnable(i, retval)
self.getTransceiverEnablePlot(i)
self.getTransceiverEnableColor(i)
self.plotTab.addSelectedTransceiverPlots(i)
def setTransceiverEnable(self, i):
checkBox = getattr(self.view, f"checkBoxTransceiver{i}")
try:
enableMask = manipulate_bit(checkBox.isChecked(), self.det.transceiverenable, i)
self.det.transceiverenable = enableMask
except Exception as e:
QtWidgets.QMessageBox.warning(self.mainWindow, "Transceiver Enable Fail", str(e), QtWidgets.QMessageBox.Ok)
pass
self.updateTransceiverEnable()
def getTransceiverEnablePlot(self, i):
checkBox = getattr(self.view, f"checkBoxTransceiver{i}")
checkBoxPlot = getattr(self.view, f"checkBoxTransceiver{i}Plot")
checkBoxPlot.setEnabled(checkBox.isChecked())
def setTransceiverEnablePlot(self, i):
pushButton = getattr(self.view, f"pushButtonTransceiver{i}")
checkBox = getattr(self.view, f"checkBoxTransceiver{i}Plot")
pushButton.setEnabled(checkBox.isChecked())
self.plotTab.addSelectedTransceiverPlots(i)
self.updateLegend()
def getTransceiverEnableColor(self, i):
checkBox = getattr(self.view, f"checkBoxTransceiver{i}Plot")
pushButton = getattr(self.view, f"pushButtonTransceiver{i}")
pushButton.setEnabled(checkBox.isEnabled() and checkBox.isChecked())
def selectTransceiverColor(self, i):
pushButton = getattr(self.view, f"pushButtonTransceiver{i}")
self.plotTab.showPalette(pushButton)
pen = pg.mkPen(color=self.getTransceiverButtonColor(i), width=1)
self.mainWindow.transceiverPlots[i].setPen(pen)
def getTransceiverButtonColor(self, i):
pushButton = getattr(self.view, f"pushButtonTransceiver{i}")
return self.plotTab.getActiveColor(pushButton)
def setTransceiverButtonColor(self, i, color):
pushButton = getattr(self.view, f"pushButtonTransceiver{i}")
return self.plotTab.setActiveColor(pushButton, color)
def saveParameters(self):
return ["transceiverenable {}".format(self.view.lineEditTransceiverMask.text())]

View File

@ -0,0 +1,9 @@
from .ADC import AdcTab
from .Acquisition import AcquisitionTab
from .DACs import DacTab
from .Pattern import PatternTab
from .Plot import PlotTab
from .PowerSupplies import PowerSuppliesTab
from .Signals import SignalsTab
from .SlowADCs import SlowAdcTab
from .Transceiver import TransceiverTab

View File

@ -0,0 +1,779 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1444</width>
<height>943</height>
</rect>
</property>
<property name="windowTitle">
<string>Chip Test Board</string>
</property>
<property name="dockNestingEnabled">
<bool>false</bool>
</property>
<widget class="QWidget" name="centralwidget">
<property name="minimumSize">
<size>
<width>870</width>
<height>890</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>870</width>
<height>890</height>
</size>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>9</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="minimumSize">
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>870</width>
<height>879</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true"/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frameControl">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QGridLayout" name="gridLayout_10">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QTabWidget" name="tabWidget">
<property name="minimumSize">
<size>
<width>870</width>
<height>870</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>870</width>
<height>870</height>
</size>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tabDac">
<attribute name="title">
<string>DACs</string>
</attribute>
<widget class="DacTab" name="widgetDacs" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>851</width>
<height>841</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>711</width>
<height>791</height>
</size>
</property>
</widget>
</widget>
<widget class="QWidget" name="tabPower">
<attribute name="title">
<string>Power Supplies</string>
</attribute>
<widget class="PowerSuppliesTab" name="widgetPowerSupplies" native="true">
<property name="geometry">
<rect>
<x>-1</x>
<y>-1</y>
<width>841</width>
<height>821</height>
</rect>
</property>
</widget>
</widget>
<widget class="QWidget" name="tabSlowAdc">
<attribute name="title">
<string>Slow ADCs</string>
</attribute>
<widget class="SlowAdcTab" name="widgetSlowAdcs" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>871</width>
<height>821</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>871</width>
<height>571</height>
</size>
</property>
</widget>
</widget>
<widget class="QWidget" name="tabSignal">
<attribute name="title">
<string>Signals</string>
</attribute>
<widget class="SignalsTab" name="widgetSignals" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>831</width>
<height>821</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>831</width>
<height>821</height>
</size>
</property>
</widget>
</widget>
<widget class="QWidget" name="tabTransceivers">
<attribute name="title">
<string>Transceivers</string>
</attribute>
<widget class="TransceiverTab" name="widgetTransceiver" native="true">
<property name="geometry">
<rect>
<x>9</x>
<y>19</y>
<width>841</width>
<height>341</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>841</width>
<height>181</height>
</size>
</property>
</widget>
</widget>
<widget class="QWidget" name="tabAdc">
<attribute name="title">
<string>ADCs</string>
</attribute>
<widget class="AdcTab" name="widgetAdc" native="true">
<property name="geometry">
<rect>
<x>-1</x>
<y>-1</y>
<width>841</width>
<height>821</height>
</rect>
</property>
</widget>
</widget>
<widget class="QWidget" name="tabPattern">
<attribute name="title">
<string>Pattern</string>
</attribute>
<widget class="PatternTab" name="widgetPattern" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>851</width>
<height>831</height>
</rect>
</property>
</widget>
</widget>
<widget class="QWidget" name="tabAcquisition">
<attribute name="title">
<string>Acquisition</string>
</attribute>
<widget class="AcquisitionTab" name="widgetAcquisition" native="true">
<property name="geometry">
<rect>
<x>-10</x>
<y>0</y>
<width>860</width>
<height>800</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>860</width>
<height>800</height>
</size>
</property>
</widget>
</widget>
<widget class="QWidget" name="tabPlot">
<attribute name="title">
<string>Plot</string>
</attribute>
<widget class="PlotTab" name="widgetPlot" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>860</width>
<height>800</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>860</width>
<height>800</height>
</size>
</property>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1444</width>
<height>25</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionLoadParameters"/>
<addaction name="actionSaveParameters"/>
<addaction name="actionExit"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
</property>
<addaction name="actionInfo"/>
<addaction name="actionKeyboardShortcuts"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QDockWidget" name="dockWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>568</width>
<height>214</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>524287</width>
<height>524287</height>
</size>
</property>
<property name="features">
<set>QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable</set>
</property>
<property name="allowedAreas">
<set>Qt::RightDockWidgetArea</set>
</property>
<attribute name="dockWidgetArea">
<number>2</number>
</attribute>
<widget class="QWidget" name="dockWidgetContent">
<layout class="QGridLayout" name="gridLayout_7">
<item row="0" column="0" colspan="2">
<widget class="QFrame" name="frameAcquisition">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>100</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout_25">
<item row="1" column="2">
<widget class="QLabel" name="labelCurrentMeasurement">
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="5">
<widget class="QProgressBar" name="progressBar">
<property name="minimumSize">
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="value">
<number>24</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QPushButton" name="pushButtonStart">
<property name="minimumSize">
<size>
<width>0</width>
<height>36</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="palette">
<palette>
<active>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>85</red>
<green>170</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>85</red>
<green>170</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>85</red>
<green>170</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>85</red>
<green>170</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>85</red>
<green>170</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>85</red>
<green>170</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>85</red>
<green>170</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>85</red>
<green>170</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>85</red>
<green>170</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="toolTip">
<string>Shift + Enter is the keyboard shortcut to start/stop acquisition from any tab</string>
</property>
<property name="styleSheet">
<string notr="true">QPushButton {background-color: rgb(85, 170, 127);}
QPushButton:checked{background-color: red;}</string>
</property>
<property name="text">
<string>Start</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelDetectorStatus">
<property name="minimumSize">
<size>
<width>110</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>IDLE</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QLabel" name="label_137">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Acquired:</string>
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="QLabel" name="labelAcquiredFrames">
<property name="minimumSize">
<size>
<width>70</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>70</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_134">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Measurement:</string>
</property>
</widget>
</item>
<item row="1" column="3">
<spacer name="horizontalSpacer_24">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QFrame" name="framePatternViewer">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayoutPatternViewer"/>
</widget>
</item>
<item>
<widget class="QFrame" name="framePlot">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayoutPlot">
<item>
<widget class="QLabel" name="labelFrameNumber">
<property name="text">
<string>#</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
<action name="actionLoadParameters">
<property name="text">
<string>Load Parameters</string>
</property>
</action>
<action name="actionSave">
<property name="text">
<string>Save</string>
</property>
</action>
<action name="actionExit">
<property name="text">
<string>Exit</string>
</property>
</action>
<action name="actionInfo">
<property name="text">
<string>Info</string>
</property>
</action>
<action name="actionKeyboardShortcuts">
<property name="text">
<string>Keyboard Shortcuts</string>
</property>
</action>
<action name="actionSaveParameters">
<property name="text">
<string>Save Parameters</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
<class>PowerSuppliesTab</class>
<extends>QWidget</extends>
<header>pyctbgui.services.PowerSupplies</header>
<container>1</container>
</customwidget>
<customwidget>
<class>DacTab</class>
<extends>QWidget</extends>
<header>pyctbgui.services.DACs</header>
<container>1</container>
</customwidget>
<customwidget>
<class>SlowAdcTab</class>
<extends>QWidget</extends>
<header>pyctbgui.services.SlowADCs</header>
<container>1</container>
</customwidget>
<customwidget>
<class>SignalsTab</class>
<extends>QWidget</extends>
<header>pyctbgui.services.Signals</header>
<container>1</container>
</customwidget>
<customwidget>
<class>TransceiverTab</class>
<extends>QWidget</extends>
<header>pyctbgui.services.Transceiver</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AdcTab</class>
<extends>QWidget</extends>
<header>pyctbgui.services.ADC</header>
<container>1</container>
</customwidget>
<customwidget>
<class>PatternTab</class>
<extends>QWidget</extends>
<header>pyctbgui.services.Pattern</header>
<container>1</container>
</customwidget>
<customwidget>
<class>AcquisitionTab</class>
<extends>QWidget</extends>
<header>pyctbgui.services.Acquisition</header>
<container>1</container>
</customwidget>
<customwidget>
<class>PlotTab</class>
<extends>QWidget</extends>
<header>pyctbgui.services.Plot</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>actionExit</sender>
<signal>triggered()</signal>
<receiver>MainWindow</receiver>
<slot>close()</slot>
<hints>
<hint type="sourcelabel">
<x>-1</x>
<y>-1</y>
</hint>
<hint type="destinationlabel">
<x>753</x>
<y>496</y>
</hint>
</hints>
</connection>
</connections>
</ui>

1198
pyctbgui/pyctbgui/ui/Dacs.ui Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,369 @@
import logging
from PyQt5 import QtWidgets, QtCore, uic
import argparse
import signal
import pyqtgraph as pg
from pathlib import Path
from functools import partial
from slsdet import Detector, dacIndex
from pyctbgui.services import TransceiverTab, DacTab, AdcTab, AcquisitionTab, SignalsTab, PatternTab, \
SlowAdcTab, PlotTab, PowerSuppliesTab
from pyctbgui.utils import alias_utility
from pyctbgui.utils.defines import Defines
class MainWindow(QtWidgets.QMainWindow):
signalShortcutAcquire = QtCore.pyqtSignal()
signalShortcutTabUp = QtCore.pyqtSignal()
signalShortcutTabDown = QtCore.pyqtSignal()
def __init__(self, *args, **kwargs):
parser = argparse.ArgumentParser()
parser.add_argument('-a', '--alias', help="Alias file complete path")
arglist, __ = parser.parse_known_args()
self.alias_file = arglist.alias
pg.setConfigOption("background", (247, 247, 247))
pg.setConfigOption("foreground", "k")
pg.setConfigOption('leftButtonPan', False)
super().__init__(*args, **kwargs)
uic.loadUi(Path(__file__).parent / "CtbGui.ui", self)
logging.basicConfig(encoding='utf-8', level=logging.INFO)
self.logger = logging.getLogger(__name__)
self.det = None
self.showLegend = True
self.settings = None
try:
self.det = Detector()
# ensure detector is up
self.det.detectorserverversion[0]
except Exception as e:
QtWidgets.QMessageBox.critical(self, "Connect Fail", str(e) + "Exiting Gui...", QtWidgets.QMessageBox.Ok)
raise
# get Tab Classes
self.plotTab: PlotTab = self.widgetPlot
self.slowAdcTab: SlowAdcTab = self.widgetSlowAdcs
self.dacTab: DacTab = self.widgetDacs
self.powerSuppliesTab: PowerSuppliesTab = self.widgetPowerSupplies
self.signalsTab: SignalsTab = self.widgetSignals
self.transceiverTab: TransceiverTab = self.widgetTransceiver
self.adcTab: AdcTab = self.widgetAdc
self.patternTab: PatternTab = self.widgetPattern
self.acquisitionTab: AcquisitionTab = self.widgetAcquisition
self.tabs_list = [
self.dacTab, self.powerSuppliesTab, self.slowAdcTab, self.signalsTab, self.transceiverTab, self.adcTab,
self.patternTab, self.acquisitionTab, self.plotTab
]
self.setup_ui()
self.acquisitionTab.setup_zmq()
self.tabWidget.setCurrentIndex(Defines.Acquisition_Tab_Index)
self.tabWidget.currentChanged.connect(self.refresh_tab)
self.connect_ui()
for tab in self.tabs_list:
tab.refresh()
# also refreshes timer to start plotting
self.plotTab.plotOptions()
self.plotTab.showPlot()
self.patternTab.getPatViewerColors()
self.patternTab.getPatViewerWaitParameters()
self.patternTab.getPatViewerLoopParameters()
self.patternTab.updatePatViewerParameters()
self.plotTab.showPatternViewer(False)
if self.alias_file is not None:
self.loadAliasFile()
self.signalShortcutAcquire.connect(self.pushButtonStart.click)
self.signalShortcutTabUp.connect(partial(self.changeTabIndex, True))
self.signalShortcutTabDown.connect(partial(self.changeTabIndex, False))
# to catch the ctrl + c to abort
signal.signal(signal.SIGINT, signal.SIG_DFL)
self.firstAnalogImage = True
self.firstDigitalImage = True
self.firstTransceiverImage = True
self.updateSettingValues()
def updateSettingMainWindow(self):
self.settings.beginGroup("mainwindow")
# window size
width = self.settings.value('window_width')
height = self.settings.value('window_height')
if width is not None and height is not None:
self.resize(int(width), int(height))
# print(f'Main window resized to {width}x{height}')
# window position
pos = self.settings.value('window_pos')
if type(pos) is QtCore.QPoint:
# print(f'Moved main window to {pos}')
self.move(pos)
self.settings.endGroup()
def saveSettingMainWindow(self):
self.settings.beginGroup("mainwindow")
self.settings.setValue('window_width', self.rect().width())
self.settings.setValue('window_height', self.rect().height())
self.settings.setValue('window_pos', self.pos())
self.settings.endGroup()
def updateSettingDockWidget(self):
self.settings.beginGroup("dockwidget")
# is docked
if self.settings.contains('window_width') and self.settings.contains('window_height'):
# window size
width = self.settings.value('window_width')
height = self.settings.value('window_height')
if width is not None and height is not None:
# print(f'Plot window - Floating ({width}x{height})')
self.dockWidget.setFloating(True)
self.dockWidget.resize(int(width), int(height))
# window position
pos = self.settings.value('window_pos')
if type(pos) is QtCore.QPoint:
# print(f'Moved plot window to {pos}')
self.dockWidget.move(pos)
self.settings.endGroup()
def saveSettingDockWidget(self):
self.settings.beginGroup("dockwidget")
if self.dockWidget.isFloating():
self.settings.setValue('window_width', self.dockWidget.rect().width())
self.settings.setValue('window_height', self.dockWidget.rect().height())
self.settings.setValue('window_pos', self.dockWidget.pos())
else:
self.settings.remove('window_width')
self.settings.remove('window_height')
self.settings.remove('window_pos')
self.settings.endGroup()
def savePlotTypeAndDetector(self):
self.settings.setValue('isImage', self.plotTab.view.radioButtonImage.isChecked())
self.settings.setValue('detector', self.plotTab.view.comboBoxPlot.currentText())
def updatePlotTypeAndDetector(self):
# load plot type from qsettings
isImage = self.settings.value('isImage', True, type=bool)
self.plotTab.view.radioButtonImage.setChecked(isImage)
self.plotTab.plotOptions()
# load detector from qsettings
if isImage:
self.plotTab.view.comboBoxPlot.setCurrentText(self.settings.value('detector', 'Matterhorn'))
def updateSettingValues(self):
self.settings = QtCore.QSettings('slsdetectorgroup', 'pyctbgui')
self.updateSettingMainWindow()
self.updateSettingDockWidget()
self.updatePlotTypeAndDetector()
def saveSettings(self):
# store in ~/.config/slsdetectorgroup/pyctbgui.conf
self.saveSettingMainWindow()
self.saveSettingDockWidget()
self.savePlotTypeAndDetector()
def closeEvent(self, event):
self.saveSettings()
def loadAliasFile(self):
print(f'Loading Alias file: {self.alias_file}')
try:
bit_names, bit_plots, bit_colors, adc_names, adc_plots, adc_colors, dac_names, slowadc_names, \
voltage_names, pat_file_name = alias_utility.read_alias_file(self.alias_file)
except Exception as e:
QtWidgets.QMessageBox.warning(self, "Alias File Fail",
str(e) + "<br> " + self.alias_file, QtWidgets.QMessageBox.Ok)
return
for i in range(Defines.signals.count):
if bit_names[i]:
self.det.setSignalName(i, bit_names[i])
if bit_plots[i]:
getattr(self.signalsTab.view, f"checkBoxBIT{i}DB").setChecked(bit_plots[i])
getattr(self.signalsTab.view, f"checkBoxBIT{i}Plot").setChecked(bit_plots[i])
if bit_colors[i]:
self.signalsTab.setDBitButtonColor(i, bit_colors[i])
for i in range(Defines.adc.count):
if adc_names[i]:
self.det.setAdcName(i, adc_names[i])
if adc_plots[i]:
getattr(self.adcTab.view, f"checkBoxADC{i}En").setChecked(adc_plots[i])
getattr(self.adcTab.view, f"checkBoxADC{i}Plot").setChecked(adc_plots[i])
if adc_colors[i]:
self.adcTab.setADCButtonColor(i, adc_colors[i])
for i in range(Defines.dac.count):
if dac_names[i]:
iDac = getattr(dacIndex, f"DAC_{i}")
self.det.setDacName(iDac, dac_names[i])
for i in range(Defines.slowAdc.count):
slowadc_index = self.det.getSlowADCList()
if slowadc_names[i]:
self.det.setSlowADCName(slowadc_index[i], slowadc_names[i])
for i in range(len(Defines.powerSupplies)):
voltage_index = self.det.getVoltageList()
if voltage_names[i]:
self.det.setVoltageName(voltage_index[i], voltage_names[i])
if pat_file_name:
self.lineEditPatternFile.setText(pat_file_name)
self.signalsTab.updateSignalNames()
self.adcTab.updateADCNames()
self.slowAdcTab.updateSlowAdcNames()
self.dacTab.updateDACNames()
self.powerSuppliesTab.updateVoltageNames()
# For Action options function
# TODO Only add the components of action option+ functions
# Function to show info
def showInfo(self):
msg = QtWidgets.QMessageBox()
msg.setWindowTitle("About")
msg.setText("This Gui is for Chip Test Boards.\n Current Phase: Development")
msg.exec_()
def showKeyBoardShortcuts(self):
msg = QtWidgets.QMessageBox()
msg.setWindowTitle("Keyboard Shortcuts")
msg.setText(
"Start Acquisition (from any tab): Shift + Return<br>Move Tab Right : Ctrl + '+'<br>Move Tab Left :"
" Ctrl + '-'<br>")
msg.exec_()
def loadParameters(self):
response = QtWidgets.QFileDialog.getOpenFileName(
parent=self,
caption="Select a parameter file to open",
directory=str(Path.cwd()),
# filter='README (*.md *.ui)'
)
if response[0] == '':
return
try:
self.det.parameters = (response[0])
for tab in self.tabs_list:
tab.refresh()
QtWidgets.QMessageBox.information(self, "Load Parameter Success", "Parameters loaded successfully",
QtWidgets.QMessageBox.Ok)
except (RuntimeError, FileNotFoundError) as e:
self.logger.exception(e)
QtWidgets.QMessageBox.warning(self, "Load Parameter Fail", str(e), QtWidgets.QMessageBox.Ok)
def refresh_tab(self, tab_index):
match tab_index:
case 0:
self.dacTab.refresh()
case 1:
self.powerSuppliesTab.refresh()
case 2:
self.slowAdcTab.refresh()
case 3:
self.transceiverTab.refresh()
case 4:
self.signalsTab.refresh()
case 5:
self.adcTab.refresh()
case 6:
self.patternTab.refresh()
case 7:
self.acquisitionTab.refresh()
case 8:
self.plotTab.refresh()
def setup_ui(self):
# To check detector status
self.statusTimer = QtCore.QTimer()
self.statusTimer.timeout.connect(self.acquisitionTab.checkEndofAcquisition)
# To auto trigger the read
self.read_timer = QtCore.QTimer()
self.read_timer.timeout.connect(self.acquisitionTab.read_zmq)
for tab in self.tabs_list:
tab.mainWindow = self
tab.det = self.det
for tab in self.tabs_list:
tab.setup_ui()
def keyPressEvent(self, event):
if event.modifiers() & QtCore.Qt.ShiftModifier:
if event.key() == QtCore.Qt.Key_Return:
self.signalShortcutAcquire.emit()
if event.modifiers() & QtCore.Qt.ControlModifier:
if event.key() == QtCore.Qt.Key_Plus:
self.signalShortcutTabUp.emit()
if event.key() == QtCore.Qt.Key_Minus:
self.signalShortcutTabDown.emit()
def changeTabIndex(self, up):
ind = self.tabWidget.currentIndex()
if up:
ind += 1
if ind == Defines.Max_Tabs:
ind = 0
else:
ind -= 1
if ind == -1:
ind = Defines.Max_Tabs - 1
self.tabWidget.setCurrentIndex(ind)
def connect_ui(self):
# Show info
self.actionInfo.triggered.connect(self.showInfo)
self.actionKeyboardShortcuts.triggered.connect(self.showKeyBoardShortcuts)
self.actionLoadParameters.triggered.connect(self.loadParameters)
self.pushButtonStart.clicked.connect(self.acquisitionTab.toggleAcquire)
self.actionSaveParameters.triggered.connect(self.saveParameters)
for tab in self.tabs_list:
tab.connect_ui()
def saveParameters(self):
response = QtWidgets.QFileDialog.getSaveFileName(self, "Save Parameters", str(self.det.fpath))
if response[0] == '':
return
# save DACs
commands = self.dacTab.saveParameters()
# save signals
commands.extend(self.signalsTab.saveParameters())
# save transceiver
commands.extend(self.transceiverTab.saveParameters())
# save ADCs
commands.extend(self.adcTab.saveParameters())
# save pattern
commands.extend(self.patternTab.saveParameters())
# save acquisition
commands.extend(self.acquisitionTab.saveParameters())
# save power supplies
commands.extend(self.powerSuppliesTab.saveParameters())
# save plot
commands.extend(self.plotTab.saveParameters())
try:
with open(response[0], 'w') as fp:
fp.write('\n'.join(commands))
except Exception as e:
self.logger.exception(e)
QtWidgets.QMessageBox.warning(self, "Save Parameter Fail", str(e), QtWidgets.QMessageBox.Ok)
QtWidgets.QMessageBox.information(self, "Save Parameter Success", "Parameters saved successfully",
QtWidgets.QMessageBox.Ok)

View File

@ -0,0 +1 @@
from .MainWindow import MainWindow

View File

@ -0,0 +1,927 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>870</width>
<height>823</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>870</width>
<height>800</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="QFrame" name="frame_20">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>841</width>
<height>71</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QGridLayout" name="gridLayout_19">
<item row="1" column="5">
<widget class="QSpinBox" name="spinBoxRunF">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>31</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>125</width>
<height>31</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::UpDownArrows</enum>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_398">
<property name="text">
<string>Read Out Mode: </string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QComboBox" name="comboBoxROMode">
<property name="minimumSize">
<size>
<width>0</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>32</height>
</size>
</property>
<item>
<property name="text">
<string>Analog</string>
</property>
</item>
<item>
<property name="text">
<string>Digital</string>
</property>
</item>
<item>
<property name="text">
<string>Analog and Digital</string>
</property>
</item>
<item>
<property name="text">
<string>Transceiver</string>
</property>
</item>
<item>
<property name="text">
<string>Digital and Transceiver</string>
</property>
</item>
</widget>
</item>
<item row="1" column="4">
<widget class="QLabel" name="label_70">
<property name="text">
<string>Run Clock Frequency (MHz):</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QFrame" name="frame_13">
<property name="geometry">
<rect>
<x>10</x>
<y>90</y>
<width>841</width>
<height>51</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout_24">
<item row="0" column="1">
<widget class="QSpinBox" name="spinBoxTransceiver">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>125</width>
<height>32</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100000</number>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_16">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelTransceiver">
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Transceiver Samples:</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QFrame" name="frame_15">
<property name="geometry">
<rect>
<x>10</x>
<y>150</y>
<width>841</width>
<height>201</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<item row="2" column="3">
<widget class="QLabel" name="label_74">
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>DBIT Clock Frequency (MHz):</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_71">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>ADC Clock Frequency (MHz):</string>
</property>
</widget>
</item>
<item row="3" column="4">
<widget class="QSpinBox" name="spinBoxDBITPhase">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>31</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>125</width>
<height>31</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QLabel" name="label_76">
<property name="text">
<string>DBIT Pipeline:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="spinBoxADCF">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>31</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>125</width>
<height>31</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="spinBoxADCPipeline">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>31</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>125</width>
<height>31</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_73">
<property name="text">
<string>ADC Pipeline:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_72">
<property name="text">
<string>ADC Clock Phase (a.u.):</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QLabel" name="label_75">
<property name="text">
<string>DBIT Clock Phase (a.u.):</string>
</property>
</widget>
</item>
<item row="2" column="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="4">
<widget class="QSpinBox" name="spinBoxDBITF">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>31</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>125</width>
<height>31</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item row="4" column="4">
<widget class="QSpinBox" name="spinBoxDBITPipeline">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>31</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>125</width>
<height>31</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="spinBoxADCPhase">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>31</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>125</width>
<height>31</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelAnalog">
<property name="text">
<string>Analog Samples:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="spinBoxAnalog">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>125</width>
<height>32</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100000</number>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="labelDigital">
<property name="text">
<string>Digital Samples:</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QSpinBox" name="spinBoxDigital">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>125</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>125</width>
<height>32</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100000</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QFrame" name="frame_11">
<property name="geometry">
<rect>
<x>10</x>
<y>370</y>
<width>841</width>
<height>181</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QGridLayout" name="gridLayout_15">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_127">
<property name="font">
<font>
<pointsize>11</pointsize>
</font>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="text">
<string>Output Settings</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_129">
<property name="text">
<string>File name:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="lineEditFileName">
<property name="minimumSize">
<size>
<width>0</width>
<height>31</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>31</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(200, 219, 230);</string>
</property>
<property name="placeholderText">
<string>File Name</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_131">
<property name="text">
<string>Index:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="spinBoxAcquisitionIndex">
<property name="minimumSize">
<size>
<width>0</width>
<height>31</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>31</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="maximum">
<number>999999999</number>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QCheckBox" name="checkBoxFileWriteRaw">
<property name="text">
<string>Raw</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QPushButton" name="pushButtonFilePath">
<property name="minimumSize">
<size>
<width>0</width>
<height>36</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(199, 213, 207);</string>
</property>
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEditFilePath">
<property name="minimumSize">
<size>
<width>0</width>
<height>31</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>31</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(200, 219, 230);</string>
</property>
<property name="placeholderText">
<string>File Path</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_130">
<property name="text">
<string>File path:</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QCheckBox" name="checkBoxFileWriteNumpy">
<property name="text">
<string>Numpy</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Save format:</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QFrame" name="frame_53">
<property name="geometry">
<rect>
<x>10</x>
<y>570</y>
<width>841</width>
<height>141</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Number of frames:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="spinBoxFrames">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>120</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>32</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999999999</number>
</property>
<property name="value">
<number>999999999</number>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="3">
<widget class="QLabel" name="label_392">
<property name="text">
<string>Period (s):</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QDoubleSpinBox" name="spinBoxPeriod">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>120</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>32</height>
</size>
</property>
<property name="decimals">
<number>4</number>
</property>
<property name="maximum">
<double>99999.990000000005239</double>
</property>
</widget>
</item>
<item row="0" column="5">
<widget class="QComboBox" name="comboBoxPeriod">
<property name="minimumSize">
<size>
<width>0</width>
<height>31</height>
</size>
</property>
<item>
<property name="text">
<string>s</string>
</property>
</item>
<item>
<property name="text">
<string>ms</string>
</property>
</item>
<item>
<property name="text">
<string>μs</string>
</property>
</item>
<item>
<property name="text">
<string>ns</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_393">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Number of triggers:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="spinBoxTriggers">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>120</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>32</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999999999</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_132">
<property name="text">
<string>Number of measurements:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="spinBoxMeasurements">
<property name="minimumSize">
<size>
<width>120</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>32</height>
</size>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999999999</number>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

4212
pyctbgui/pyctbgui/ui/adc.ui Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1055
pyctbgui/pyctbgui/ui/plot.ui Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,483 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>827</width>
<height>375</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="QFrame" name="frame_3">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>841</width>
<height>381</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QGridLayout" name="gridLayout_12">
<item row="2" column="1">
<widget class="QSpinBox" name="spinBoxVC">
<property name="minimumSize">
<size>
<width>0</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>32</height>
</size>
</property>
<property name="toolTip">
<string>Only accepts value range (636 - 2468)</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="suffix">
<string> mV</string>
</property>
<property name="maximum">
<number>2468</number>
</property>
</widget>
</item>
<item row="3" column="4">
<widget class="QLabel" name="labelID">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="spinBoxVA">
<property name="minimumSize">
<size>
<width>0</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>32</height>
</size>
</property>
<property name="toolTip">
<string>Only accepts value range (636 - 2468)</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="specialValueText">
<string/>
</property>
<property name="suffix">
<string> mV</string>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>2468</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QLabel" name="labelIA">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="5" column="3" colspan="2">
<widget class="QPushButton" name="pushButtonPowerOff">
<property name="enabled">
<bool>true</bool>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>36</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">QPushButton{background-color: red;}
QPushButton:disabled{background-color: grey;}</string>
</property>
<property name="text">
<string>Power off</string>
</property>
<property name="checkable">
<bool>false</bool>
</property>
</widget>
</item>
<item row="4" column="3">
<widget class="QLabel" name="labelVIO">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QLabel" name="labelIC">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="checkBoxVC">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>VC</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="spinBoxVIO">
<property name="minimumSize">
<size>
<width>0</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>32</height>
</size>
</property>
<property name="toolTip">
<string>Only accepts value range (1200 - 2468)</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="suffix">
<string> mV</string>
</property>
<property name="maximum">
<number>2468</number>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSpinBox" name="spinBoxVChip">
<property name="minimumSize">
<size>
<width>0</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>32</height>
</size>
</property>
<property name="toolTip">
<string>Only accepts value range (1200 - 2468)</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="suffix">
<string> mV</string>
</property>
<property name="maximum">
<number>2468</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="checkBoxVB">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>VB</string>
</property>
</widget>
</item>
<item row="4" column="4">
<widget class="QLabel" name="labelIIO">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLabel" name="labelVB">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="checkBoxVA">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>VA</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QLabel" name="labelVD">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="checkBoxVD">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>VD</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_136">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>VCHIP</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QLabel" name="labelVC">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="spinBoxVD">
<property name="minimumSize">
<size>
<width>0</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>32</height>
</size>
</property>
<property name="toolTip">
<string>Only accepts value range (636 - 2468)</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="suffix">
<string> mV</string>
</property>
<property name="maximum">
<number>2468</number>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="checkBoxVIO">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>VIO</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QLabel" name="labelIB">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_12">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>80</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="3">
<widget class="QLabel" name="labelVA">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="spinBoxVB">
<property name="minimumSize">
<size>
<width>0</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>32</height>
</size>
</property>
<property name="toolTip">
<string>Only accepts value range (636 - 2468)</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="suffix">
<string> mV</string>
</property>
<property name="maximum">
<number>2468</number>
</property>
</widget>
</item>
<item row="0" column="5">
<spacer name="horizontalSpacer_22">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>80</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,472 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>841</width>
<height>571</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>841</width>
<height>571</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="QFrame" name="frame_4">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>841</width>
<height>571</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QGridLayout" name="gridLayout_11">
<item row="0" column="0">
<widget class="QLabel" name="labelSlowAdc0">
<property name="text">
<string>SENSE 0:</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="labelSlowAdcValue0">
<property name="text">
<string>***</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QPushButton" name="pushButtonSlowAdc0">
<property name="minimumSize">
<size>
<width>150</width>
<height>36</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>36</height>
</size>
</property>
<property name="font">
<font>
<family>Cantarell</family>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(199, 213, 207);</string>
</property>
<property name="text">
<string>Update</string>
</property>
<property name="icon">
<iconset theme="update">
<normaloff>../../../../.designer</normaloff>../../../../.designer</iconset>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="labelSlowAdcValue1">
<property name="text">
<string>***</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLabel" name="labelSlowAdcValue3">
<property name="text">
<string>***</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="4">
<widget class="QPushButton" name="pushButtonSlowAdc3">
<property name="minimumSize">
<size>
<width>150</width>
<height>36</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>36</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(199, 213, 207);</string>
</property>
<property name="text">
<string>Update</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="labelSlowAdc4">
<property name="text">
<string>SENSE 4:</string>
</property>
</widget>
</item>
<item row="8" column="2">
<widget class="QLabel" name="labelTempValue">
<property name="text">
<string>***</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="4" column="4">
<widget class="QPushButton" name="pushButtonSlowAdc4">
<property name="minimumSize">
<size>
<width>150</width>
<height>36</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>36</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(199, 213, 207);</string>
</property>
<property name="text">
<string>Update</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="0">
<widget class="QLabel" name="labelSlowAdc5">
<property name="text">
<string>SENSE 5:</string>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLabel" name="labelSlowAdcValue5">
<property name="text">
<string>***</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="5" column="4">
<widget class="QPushButton" name="pushButtonSlowAdc5">
<property name="minimumSize">
<size>
<width>150</width>
<height>36</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>36</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(199, 213, 207);</string>
</property>
<property name="text">
<string>Update</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="labelSlowAdc6">
<property name="text">
<string>SENSE 6:</string>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="QLabel" name="labelSlowAdcValue6">
<property name="text">
<string>***</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelSlowAdc1">
<property name="text">
<string>SENSE 1:</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QPushButton" name="pushButtonSlowAdc1">
<property name="minimumSize">
<size>
<width>150</width>
<height>36</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>36</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(199, 213, 207);</string>
</property>
<property name="text">
<string>Update</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelSlowAdc2">
<property name="text">
<string>SENSE 2:</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QLabel" name="labelSlowAdcValue4">
<property name="text">
<string>***</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="labelSlowAdcValue2">
<property name="text">
<string>***</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QPushButton" name="pushButtonSlowAdc2">
<property name="minimumSize">
<size>
<width>150</width>
<height>36</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>36</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(199, 213, 207);</string>
</property>
<property name="text">
<string>Update</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="labelSlowAdc3">
<property name="text">
<string>SENSE 3:</string>
</property>
</widget>
</item>
<item row="6" column="4">
<widget class="QPushButton" name="pushButtonSlowAdc6">
<property name="minimumSize">
<size>
<width>150</width>
<height>36</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>36</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(199, 213, 207);</string>
</property>
<property name="text">
<string>Update</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="labelSlowAdc7">
<property name="text">
<string>SENSE 7:</string>
</property>
</widget>
</item>
<item row="7" column="2">
<widget class="QLabel" name="labelSlowAdcValue7">
<property name="text">
<string>***</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="7" column="4">
<widget class="QPushButton" name="pushButtonSlowAdc7">
<property name="minimumSize">
<size>
<width>150</width>
<height>36</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>36</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(199, 213, 207);</string>
</property>
<property name="text">
<string>Update</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="labelTemp">
<property name="text">
<string>Temperature</string>
</property>
</widget>
</item>
<item row="8" column="4">
<widget class="QPushButton" name="pushButtonTemp">
<property name="minimumSize">
<size>
<width>150</width>
<height>36</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>36</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(199, 213, 207);</string>
</property>
<property name="text">
<string>Update</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer_10">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="5">
<spacer name="horizontalSpacer_11">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,542 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>841</width>
<height>239</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>841</width>
<height>181</height>
</size>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="QFrame" name="frame_37">
<property name="geometry">
<rect>
<x>10</x>
<y>20</y>
<width>841</width>
<height>181</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>841</width>
<height>181</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
<layout class="QGridLayout" name="gridLayout_45">
<item row="5" column="0" colspan="7">
<widget class="QFrame" name="frame_38">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout_46">
<item row="0" column="2">
<widget class="QLineEdit" name="lineEditTransceiverMask">
<property name="minimumSize">
<size>
<width>150</width>
<height>32</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>150</width>
<height>32</height>
</size>
</property>
<property name="font">
<font>
<family>Monospace</family>
<pointsize>10</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
</property>
<property name="text">
<string>0xFFFF</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_87">
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Enable mask:</string>
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer_43">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="2" column="2">
<widget class="QCheckBox" name="checkBoxTransceiver2Plot">
<property name="minimumSize">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Plot</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="checkBoxTransceiver1Plot">
<property name="minimumSize">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Plot</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelTransceiver0">
<property name="minimumSize">
<size>
<width>80</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Transceiver 0</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="checkBoxTransceiver2">
<property name="minimumSize">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>En</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelTransceiver2">
<property name="minimumSize">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Transceiver 2</string>
</property>
</widget>
</item>
<item row="4" column="1">
<spacer name="verticalSpacer_7">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="4">
<widget class="QPushButton" name="pushButtonTransceiver1">
<property name="minimumSize">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="checkBoxTransceiver1">
<property name="minimumSize">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>En</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="checkBoxTransceiver0">
<property name="minimumSize">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>En</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelTransceiver1">
<property name="minimumSize">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Transceiver 1</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QCheckBox" name="checkBoxTransceiver0Plot">
<property name="minimumSize">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Plot</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="labelTransceiver3">
<property name="minimumSize">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Transceiver 3</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QCheckBox" name="checkBoxTransceiver3Plot">
<property name="minimumSize">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>Plot</string>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QPushButton" name="pushButtonTransceiver2">
<property name="minimumSize">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="5">
<spacer name="horizontalSpacer_44">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="checkBoxTransceiver3">
<property name="minimumSize">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string>En</string>
</property>
</widget>
</item>
<item row="3" column="4">
<widget class="QPushButton" name="pushButtonTransceiver3">
<property name="minimumSize">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QPushButton" name="pushButtonTransceiver0">
<property name="minimumSize">
<size>
<width>0</width>
<height>16</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<pointsize>10</pointsize>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

View 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.")

View 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)

View 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

View 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

View 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()

View 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()

View 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]
```

View 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

View 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

View 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