mirror of
https://github.com/slsdetectorgroup/slsDetectorPackage.git
synced 2025-06-14 13:57:13 +02:00
409 lines
18 KiB
Python
409 lines
18 KiB
Python
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_dbitreorder = None
|
|
self.rx_dbitlist = None
|
|
|
|
def refresh(self):
|
|
self.updateSignalNames()
|
|
self.updateDigitalBitEnable()
|
|
self.updateIOOut()
|
|
self.getDBitOffset()
|
|
self.getDBitReorder()
|
|
|
|
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)
|
|
self.view.checkBoxDBitReorder.stateChanged.connect(self.setDbitReorder)
|
|
|
|
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_dbitreorder, rx_dbitlist, isPlottedArray, romode,
|
|
nADCEnabled):
|
|
|
|
#transform raw waveform data into a processed numpy array
|
|
#@param data: raw waveform data
|
|
|
|
start_digital_data = 0
|
|
if romode == 2:
|
|
start_digital_data += nADCEnabled * 2 * aSamples
|
|
digital_array = np.array(np.frombuffer(data, offset=start_digital_data, dtype=np.uint8))
|
|
if rx_dbitreorder:
|
|
samples_per_bit = np.empty((len(rx_dbitlist), dSamples), dtype=np.uint8) #stored per row all the corresponding signals of all samples
|
|
nbitsPerDBit = dSamples
|
|
if nbitsPerDBit % 8 != 0:
|
|
nbitsPerDBit += (8 - (dSamples % 8))
|
|
bit_index = 0
|
|
for idx, i in enumerate(rx_dbitlist):
|
|
# where numbits * numsamples is not a multiple of 8
|
|
if bit_index % 8 != 0:
|
|
bit_index += (8 - (bit_index % 8))
|
|
if not isPlottedArray[i]:
|
|
bit_index += nbitsPerDBit
|
|
samples_per_bit[idx, :] = np.nan
|
|
continue
|
|
for iSample in range(dSamples):
|
|
# all samples for digital bit together from slsReceiver
|
|
index = int(bit_index / 8)
|
|
iBit = bit_index % 8
|
|
bit = (digital_array[index] >> iBit) & 1
|
|
samples_per_bit[idx,iSample] = bit
|
|
bit_index += 1
|
|
return samples_per_bit
|
|
else:
|
|
nbitsPerSample = len(rx_dbitlist) if len(rx_dbitlist) % 8 == 0 else len(rx_dbitlist) + (8 - (len(rx_dbitlist) % 8))
|
|
bits_per_sample = np.empty((dSamples, len(rx_dbitlist)), dtype=np.uint8) #store per row all selected bits of a sample
|
|
for iSample in range(dSamples):
|
|
bit_index = nbitsPerSample * iSample
|
|
for idx, i in enumerate(rx_dbitlist):
|
|
if not isPlottedArray[i]:
|
|
bit_index += 1
|
|
bits_per_sample[iSample, idx] = np.nan
|
|
|
|
index = int(bit_index/8)
|
|
iBit = idx % 8
|
|
bit = (digital_array[index] >> iBit) & 1
|
|
bits_per_sample[iSample, idx] = bit
|
|
bit_index += 1
|
|
return bits_per_sample.T.copy()
|
|
|
|
|
|
|
|
def processWaveformData(self, data, aSamples, dSamples):
|
|
|
|
#view function
|
|
#plots processed waveform data
|
|
#data: raw waveform data
|
|
#dsamples: digital samples
|
|
#asamples: analog samples
|
|
|
|
self.refresh()
|
|
|
|
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_dbitreorder, self.rx_dbitlist, isPlottedArray,
|
|
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 np.isnan(waveform[0]):
|
|
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 getDBitReorder(self):
|
|
self.view.checkBoxDBitReorder.stateChanged.disconnect()
|
|
self.rx_dbitreorder = self.det.rx_dbitreorder
|
|
self.view.checkBoxDBitReorder.setChecked(self.rx_dbitreorder)
|
|
self.view.checkBoxDBitReorder.stateChanged.connect(self.setDbitReorder)
|
|
|
|
|
|
def setDbitOffset(self):
|
|
self.det.rx_dbitoffset = self.view.spinBoxDBitOffset.value()
|
|
|
|
def setDbitReorder(self):
|
|
self.det.rx_dbitreorder = self.view.checkBoxDBitReorder.isChecked()
|
|
|
|
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"rx_dbitreorder {self.view.checkBoxDBitReorder.isChecked()}")
|
|
commands.append(f"patioctrl {self.view.lineEditPatIOCtrl.text()}")
|
|
return commands
|