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,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