|
@@ -0,0 +1,2036 @@
|
|
|
+#! python
|
|
|
+
|
|
|
+import sys
|
|
|
+import os
|
|
|
+import argparse
|
|
|
+import logging
|
|
|
+import time
|
|
|
+from PyQt4 import QtGui, QtCore
|
|
|
+from matplotlib.figure import Figure
|
|
|
+from matplotlib.backends.backend_qt4agg import (
|
|
|
+ FigureCanvasQTAgg as FigureCanvas,
|
|
|
+ NavigationToolbar2QTAgg as NavigationToolbar)
|
|
|
+from heb.board import write_pci
|
|
|
+import heb.io
|
|
|
+import heb.plot
|
|
|
+import heb.board
|
|
|
+import numpy as np
|
|
|
+from array import array as pArr
|
|
|
+import struct
|
|
|
+import matplotlib.cm as cm
|
|
|
+import matplotlib.colors as colors
|
|
|
+
|
|
|
+
|
|
|
+OPT_3D_MAP = "3D Map"
|
|
|
+OPT_TRAIN = "Train"
|
|
|
+OPT_COMBINED = "Combined"
|
|
|
+OPT_FFT = "Fourier transform"
|
|
|
+# OPT_RECONSTRUCTED = "Reconstructed"
|
|
|
+OPT_ADC_1 = "ADC 1"
|
|
|
+OPT_ADC_2 = "ADC 2"
|
|
|
+OPT_ADC_3 = "ADC 3"
|
|
|
+OPT_ADC_4 = "ADC 4"
|
|
|
+
|
|
|
+
|
|
|
+def enable_wait_cursor():
|
|
|
+ QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
|
|
|
+
|
|
|
+
|
|
|
+def disable_wait_cursor():
|
|
|
+ QtGui.QApplication.restoreOverrideCursor()
|
|
|
+
|
|
|
+
|
|
|
+class Spinbox(QtGui.QSpinBox):
|
|
|
+
|
|
|
+ def __init__(self, minimum, maximum, default):
|
|
|
+ QtGui.QSpinBox.__init__(self)
|
|
|
+ self.setMinimum(minimum)
|
|
|
+ self.setMaximum(maximum)
|
|
|
+ self.setValue(default)
|
|
|
+
|
|
|
+ def setValueSilent(self, value):
|
|
|
+ self.blockSignals(True)
|
|
|
+ self.setValue(value)
|
|
|
+ self.blockSignals(False)
|
|
|
+
|
|
|
+
|
|
|
+class GraphicalLED(QtGui.QWidget):
|
|
|
+
|
|
|
+ def __init__(self, parent=None, colorRGB=(255, 0, 0), height=20, width=20):
|
|
|
+ QtGui.QWidget.__init__(self, parent)
|
|
|
+ self.width = width
|
|
|
+ self.height = height
|
|
|
+ self.color = QtGui.QColor(colorRGB[0], colorRGB[1], colorRGB[2])
|
|
|
+ self.center = QtCore.QPoint(width, height)
|
|
|
+ self.setMinimumSize(2 * width, 2 * height)
|
|
|
+ self.setMaximumSize(2 * width, 2 * height)
|
|
|
+
|
|
|
+ def paintEvent(self, event):
|
|
|
+ paint = QtGui.QPainter()
|
|
|
+ paint.begin(self)
|
|
|
+ paint.setRenderHint(QtGui.QPainter.Antialiasing)
|
|
|
+
|
|
|
+ # draw a grey 'socket' for the LED
|
|
|
+ paint.setPen(QtGui.QColor(160, 160, 160))
|
|
|
+ paint.setBrush(QtGui.QColor(180, 180, 180))
|
|
|
+ paint.drawEllipse(self.center, self.width, self.height)
|
|
|
+
|
|
|
+ # draw the body of the LED
|
|
|
+ paint.setBrush(self.color)
|
|
|
+ paint.drawEllipse(self.center, self.width*0.85, self.height*0.85)
|
|
|
+
|
|
|
+ # Some versions of Qt4 differ in the way their QLinearGradients
|
|
|
+ # work... But i don't want to remove the functionality :)
|
|
|
+ if False:
|
|
|
+ self.draw_light_reflex(paint)
|
|
|
+
|
|
|
+ def draw_light_reflex(self, paint):
|
|
|
+ # set up a light reflex ellipse on the LED
|
|
|
+ paint.translate(self.center)
|
|
|
+ paint.rotate(-45.0)
|
|
|
+ paint.setPen(QtGui.QColor(255, 255, 255, 0))
|
|
|
+ highlight_center = QtCore.QPoint(0, self.width*(-0.34))
|
|
|
+
|
|
|
+ # light reflex is pure white and fades to transparent at top of LED
|
|
|
+ gradient = QtGui.QLinearGradient(highlight_center*1.5, QtCore.QPoint(0, 0))
|
|
|
+ gradient.setColorAt(0.0, QtGui.QColor(255, 255, 255, 120))
|
|
|
+ gradient.setColorAt(1.0, QtGui.QColor(255, 255, 255, 0))
|
|
|
+
|
|
|
+ # draw the light reflex using the gradient brush
|
|
|
+ paint.setBrush(gradient)
|
|
|
+ paint.drawEllipse(highlight_center, self.width*0.63, self.width*0.42)
|
|
|
+
|
|
|
+ paint.end()
|
|
|
+
|
|
|
+ def set_color(self, colorRGB=(255, 0, 0)):
|
|
|
+ self.color = QtGui.QColor(colorRGB[0], colorRGB[1], colorRGB[2])
|
|
|
+ self.update()
|
|
|
+
|
|
|
+
|
|
|
+class BitsDisplayTable(QtGui.QTableWidget):
|
|
|
+
|
|
|
+ def __init__(self, value, parent=None, optimalSize=True):
|
|
|
+ QtGui.QTableWidget.__init__(self, parent)
|
|
|
+ self.numbers = str(value)
|
|
|
+ if len(self.numbers) == 0:
|
|
|
+ raise ValueError("Cant create a table for a value of length 0.")
|
|
|
+ self.length = len(self.numbers)
|
|
|
+ self.do_style()
|
|
|
+ if optimalSize is True:
|
|
|
+ self.do_optimal_size()
|
|
|
+
|
|
|
+ def do_style(self):
|
|
|
+ self.horizontalHeader().setDefaultSectionSize(35)
|
|
|
+ self.horizontalHeader().setResizeMode(QtGui.QHeaderView.Fixed)
|
|
|
+ self.horizontalHeader().setVisible(True)
|
|
|
+ self.verticalHeader().setDefaultSectionSize(17)
|
|
|
+ self.verticalHeader().setResizeMode(QtGui.QHeaderView.Fixed)
|
|
|
+ self.verticalHeader().setVisible(False)
|
|
|
+ self.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
|
|
|
+ self.setRowCount(1)
|
|
|
+ self.setColumnCount(self.length)
|
|
|
+
|
|
|
+ # If self.length would be 5, this line would generate ('4', '3',
|
|
|
+ # '2', '1', '0')
|
|
|
+ headers = tuple([str(i) for i in reversed(range(0, self.length))])
|
|
|
+ self.setHorizontalHeaderLabels(headers)
|
|
|
+
|
|
|
+ for i in range(len(self.numbers)):
|
|
|
+ item = QtGui.QTableWidgetItem(self.numbers[i])
|
|
|
+ item.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
|
|
|
+ self.setItem(0, i, item)
|
|
|
+
|
|
|
+ def width(self):
|
|
|
+ width = 6
|
|
|
+ if self.verticalHeader().isHidden() is False:
|
|
|
+ width = self.verticalHeader().width() + 6
|
|
|
+
|
|
|
+ for i in range(self.columnCount()):
|
|
|
+ width = width + self.columnWidth(i)
|
|
|
+
|
|
|
+ return width
|
|
|
+
|
|
|
+ def height(self):
|
|
|
+ height = 6
|
|
|
+ if self.horizontalHeader().isHidden() is False:
|
|
|
+ height = self.horizontalHeader().height() + 6
|
|
|
+ for i in range(self.rowCount()):
|
|
|
+ height = height + self.rowHeight(i)
|
|
|
+
|
|
|
+ return height
|
|
|
+
|
|
|
+ def do_optimal_size(self):
|
|
|
+ size = QtCore.QSize(self.width(), self.height())
|
|
|
+ self.setMaximumSize(size)
|
|
|
+ self.setMinimumSize(size)
|
|
|
+
|
|
|
+ def stretch_to_width(self, width_in):
|
|
|
+ width = self.width()
|
|
|
+ if width >= width_in:
|
|
|
+ return
|
|
|
+
|
|
|
+ factor = width_in/float(width)
|
|
|
+ error = 0
|
|
|
+ for i in range(self.length):
|
|
|
+ current_cell_size = self.columnWidth(i)
|
|
|
+ new_cell_size = int(current_cell_size * factor)
|
|
|
+ error += new_cell_size - (current_cell_size * factor)
|
|
|
+ if (error >= 1.0) or (error <= -1.0):
|
|
|
+ new_cell_size -= int(error)
|
|
|
+ error -= int(error)
|
|
|
+ self.horizontalHeader().resizeSection(i, new_cell_size)
|
|
|
+
|
|
|
+ self.do_optimal_size()
|
|
|
+
|
|
|
+ def set_item(self, row, col, value):
|
|
|
+ item = QtGui.QTableWidgetItem(str(value))
|
|
|
+ item.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
|
|
|
+ width = self._get_table_item_width(QtGui.QTableWidgetItem(value))
|
|
|
+ if width > self.columnWidth(col):
|
|
|
+ self.horizontalHeader().resizeSection(col, width)
|
|
|
+ self.setItem(row, col, item)
|
|
|
+
|
|
|
+ def set_numbers(self, value):
|
|
|
+ new_numbers = str(value)
|
|
|
+ if len(new_numbers) == 0:
|
|
|
+ raise ValueError("Cant create a table for a value of length 0.")
|
|
|
+ if len(new_numbers) != len(self.numbers):
|
|
|
+ raise ValueError("New Values for table don't match size."
|
|
|
+ "Expected size %i but got %i" % (len(self.numbers), len(new_numbers)))
|
|
|
+ self.numbers = new_numbers
|
|
|
+ for i in range(len(self.numbers)):
|
|
|
+ item = self.item(0, i)
|
|
|
+ item.setText(self.numbers[i])
|
|
|
+
|
|
|
+ def set_label(self, start, end, label, color=None):
|
|
|
+ if (start < 0) or (end > self.columnCount()-1) or (start > end):
|
|
|
+ raise ValueError("Invalid Start and End positions for Label: %s" % label)
|
|
|
+ if self.rowCount() < 2:
|
|
|
+ self.insertRow(1)
|
|
|
+ for i in range(self.length):
|
|
|
+ self.setItem(1, i, QtGui.QTableWidgetItem(''))
|
|
|
+
|
|
|
+ span = (end-start)+1
|
|
|
+ if span > 1:
|
|
|
+ self.setSpan(1, start, 1, span)
|
|
|
+ item = QtGui.QTableWidgetItem(label)
|
|
|
+ item.setTextAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
|
|
|
+ if color:
|
|
|
+ item.setBackground(color)
|
|
|
+ self.setItem(1, start, item)
|
|
|
+
|
|
|
+ # Check if the label is larger then then cells it spans and resize the cells
|
|
|
+ # accordingly, if the label ends up larger then the cells.
|
|
|
+ label_width = self._get_table_item_width(QtGui.QTableWidgetItem(label))
|
|
|
+ cells_width = 0
|
|
|
+ for i in range(start, end+1):
|
|
|
+ cells_width = cells_width + self.columnWidth(i)
|
|
|
+
|
|
|
+ if label_width > cells_width:
|
|
|
+ new_cell_size = label_width/span
|
|
|
+ for i in range(start, end+1):
|
|
|
+ self.horizontalHeader().resizeSection(i, new_cell_size)
|
|
|
+
|
|
|
+ self.do_optimal_size()
|
|
|
+
|
|
|
+ def grey_out_column(self, column):
|
|
|
+ if (column < 0) or (column > self.length):
|
|
|
+ raise ValueError("Supplied column is out of range for this table")
|
|
|
+ for i in range(self.rowCount()):
|
|
|
+ self.item(i, column).setForeground(QtGui.QColor(120, 120, 120))
|
|
|
+ self.item(i, column).setBackground(QtGui.QColor(200, 200, 200))
|
|
|
+
|
|
|
+ def undo_grey_out_column(self, column):
|
|
|
+ if (column < 0) or (column > self.length):
|
|
|
+ raise ValueError("Supplied column is out of range for this table")
|
|
|
+ for i in range(self.rowCount()):
|
|
|
+ self.item(i, column).setForeground(QtGui.QColor(0, 0, 0))
|
|
|
+ self.item(i, column).setBackground(QtGui.QColor(255, 255, 255))
|
|
|
+
|
|
|
+ # Create a table just to insert our item and show how large its width ends up...
|
|
|
+ # We have to use this stupid workaround since QTableWidgetItem has no width() property...
|
|
|
+ def _get_table_item_width(self, item):
|
|
|
+ table = QtGui.QTableWidget()
|
|
|
+ table.horizontalHeader().setResizeMode(QtGui.QHeaderView.ResizeToContents)
|
|
|
+ table.setRowCount(1)
|
|
|
+ table.setColumnCount(1)
|
|
|
+ table.setItem(0, 0, item)
|
|
|
+ width = table.columnWidth(0)
|
|
|
+ table.deleteLater()
|
|
|
+ return width
|
|
|
+
|
|
|
+
|
|
|
+class BitsEditTable(BitsDisplayTable):
|
|
|
+
|
|
|
+ def __init__(self, value, parent=None, optimalSize=True):
|
|
|
+ BitsDisplayTable.__init__(self, value, parent, optimalSize)
|
|
|
+ self.checkboxes = []
|
|
|
+ self.populate_checkboxes()
|
|
|
+ self.verticalHeader().setDefaultSectionSize(35)
|
|
|
+ self.do_optimal_size()
|
|
|
+
|
|
|
+ def populate_checkboxes(self):
|
|
|
+ for i in range(self.length):
|
|
|
+ widget = QtGui.QWidget()
|
|
|
+ self.checkboxes += [QtGui.QCheckBox()]
|
|
|
+ layout = QtGui.QHBoxLayout(widget)
|
|
|
+ layout.addWidget(self.checkboxes[i])
|
|
|
+ layout.setAlignment(QtCore.Qt.AlignCenter)
|
|
|
+ layout.setContentsMargins(0, 0, 0, 0)
|
|
|
+ widget.setLayout(layout)
|
|
|
+ self.setCellWidget(0, i, widget)
|
|
|
+
|
|
|
+ def set_numbers(self, value):
|
|
|
+ new_numbers = str(value)
|
|
|
+ if len(new_numbers) == 0:
|
|
|
+ raise ValueError("Cant create a table for a value of length 0.")
|
|
|
+ if len(new_numbers) != len(self.numbers):
|
|
|
+ raise ValueError("New Values for table dont match size."
|
|
|
+ "Expected size %i but got %i" % (len(self.numbers), len(new_numbers)))
|
|
|
+ self.numbers = new_numbers
|
|
|
+ for i in range(len(self.numbers)):
|
|
|
+ if self.numbers[i] == '1':
|
|
|
+ self.checkboxes[i].setChecked(True)
|
|
|
+ else:
|
|
|
+ self.checkboxes[i].setChecked(False)
|
|
|
+
|
|
|
+ def get_bits(self):
|
|
|
+ bits = ''
|
|
|
+ for i in range(self.length):
|
|
|
+ if self.checkboxes[i].isChecked():
|
|
|
+ bits += '1'
|
|
|
+ else:
|
|
|
+ bits += '0'
|
|
|
+ return bits
|
|
|
+
|
|
|
+ def get_bit(self, bit):
|
|
|
+ if bit > self.length:
|
|
|
+ return None
|
|
|
+ return self.checkboxes[(self.length - bit) - 1].isChecked()
|
|
|
+
|
|
|
+ def clear_all_bits(self):
|
|
|
+ for i in range(self.length):
|
|
|
+ self.checkboxes[i].setChecked(False)
|
|
|
+
|
|
|
+
|
|
|
+class PlotCanvas(FigureCanvas):
|
|
|
+ def __init__(self, parent=None, width=5, height=4, dpi=100):
|
|
|
+ self.figure = Figure(figsize=(width, height), dpi=dpi)
|
|
|
+
|
|
|
+ FigureCanvas.__init__(self, self.figure)
|
|
|
+ self.setParent(parent)
|
|
|
+
|
|
|
+ FigureCanvas.setSizePolicy(self,
|
|
|
+ QtGui.QSizePolicy.Expanding,
|
|
|
+ QtGui.QSizePolicy.Expanding)
|
|
|
+ FigureCanvas.updateGeometry(self)
|
|
|
+
|
|
|
+ def update_figure(self, opt_widgets, data, frm, to, label=None):
|
|
|
+ adcs = [w for w in (('ADC {}'.format(i), i) for i in range(1, 5))
|
|
|
+ if opt_widgets[w[0]].isChecked()]
|
|
|
+
|
|
|
+ def is_active(mode):
|
|
|
+ return opt_widgets[mode].isChecked() and adcs
|
|
|
+
|
|
|
+ if not data or frm == to:
|
|
|
+ return
|
|
|
+
|
|
|
+ self.figure.clear()
|
|
|
+ self.figure.subplots_adjust(right=0.9)
|
|
|
+ self.figure.suptitle(os.path.basename(data.filename))
|
|
|
+
|
|
|
+ if is_active(OPT_3D_MAP):
|
|
|
+ self.plot_heatmaps(adcs, data, frm, to)
|
|
|
+
|
|
|
+ if is_active(OPT_TRAIN):
|
|
|
+ args = {'label': label} if label else {}
|
|
|
+ self.plot_train(adcs, data, frm, to, **args)
|
|
|
+
|
|
|
+ if opt_widgets[OPT_COMBINED].isChecked() and adcs:
|
|
|
+ self.plot_combined(adcs, data, frm, to, False)
|
|
|
+
|
|
|
+ if is_active(OPT_FFT):
|
|
|
+ self.plot_fft(adcs, data, frm, to)
|
|
|
+
|
|
|
+ self.draw()
|
|
|
+
|
|
|
+ def plot_heatmaps(self, adcs, data, frm, to):
|
|
|
+ n_adcs = len(adcs)
|
|
|
+ n_rows = 2 if n_adcs > 2 else 1
|
|
|
+ n_cols = 2 if n_adcs > 1 else 1
|
|
|
+
|
|
|
+ for i, (name, adc) in enumerate(adcs):
|
|
|
+ axis = self.figure.add_subplot(n_rows, n_cols, i+1)
|
|
|
+ axis.set_title(name)
|
|
|
+ heatmap = data.heatmap(adc, frm, to)
|
|
|
+ image = heb.plot.heatmap(heatmap, axis)
|
|
|
+
|
|
|
+ bar_axis = self.figure.add_axes([0.85, 0.15, 0.05, 0.7])
|
|
|
+ self.figure.subplots_adjust(right=0.8)
|
|
|
+ self.figure.colorbar(image, cax=bar_axis)
|
|
|
+
|
|
|
+ def plot_train(self, adcs, data, frm, to, **kwargs):
|
|
|
+ n_adcs = len(adcs)
|
|
|
+ n_rows = 2 if n_adcs > 2 else 1
|
|
|
+ n_cols = 2 if n_adcs > 1 else 1
|
|
|
+
|
|
|
+ for i, (name, adc) in enumerate(adcs):
|
|
|
+ axis = self.figure.add_subplot(n_rows, n_cols, i+1)
|
|
|
+ heb.plot.train(data, axis, adc, frm, to, **kwargs)
|
|
|
+
|
|
|
+ def plot_combined(self, adcs, data, frm, to, show_reconstructed):
|
|
|
+ axis = self.figure.add_subplot(111)
|
|
|
+ heb.plot.combined(data, axis, adcs, frm, to, show_reconstructed)
|
|
|
+
|
|
|
+ def plot_fft(self, adcs, data, frm, to):
|
|
|
+ n_adcs = len(adcs)
|
|
|
+ n_rows = 2 if n_adcs > 2 else 1
|
|
|
+ n_cols = 2 if n_adcs > 1 else 1
|
|
|
+
|
|
|
+ for i, (name, adc) in enumerate(adcs):
|
|
|
+ axis = self.figure.add_subplot(n_rows, n_cols, i+1)
|
|
|
+ axis.set_title(name)
|
|
|
+ image = heb.plot.fft(data, axis, adc, frm, to) # <-- This needs to also pass the 'skipped' value!
|
|
|
+
|
|
|
+ bar_axis = self.figure.add_axes([0.85, 0.15, 0.05, 0.7])
|
|
|
+ self.figure.subplots_adjust(right=0.8)
|
|
|
+ self.figure.colorbar(image, cax=bar_axis)
|
|
|
+
|
|
|
+
|
|
|
+class SimpleStatusDisplay(QtGui.QWidget):
|
|
|
+ def __init__(self, parent=None):
|
|
|
+ QtGui.QWidget.__init__(self, parent)
|
|
|
+ self.do_layout()
|
|
|
+
|
|
|
+ def do_layout(self):
|
|
|
+ self.setWindowTitle("Board Status")
|
|
|
+ status_displays = QtGui.QGridLayout()
|
|
|
+
|
|
|
+ status_displays.addWidget(QtGui.QLabel("Board Status Readouts"))
|
|
|
+
|
|
|
+ # First Label + LED Group
|
|
|
+ self.data_flow_pipeline_led = GraphicalLED(self, (128, 128, 128), 10, 10)
|
|
|
+ led_group_1_layout = QtGui.QGridLayout(self)
|
|
|
+ label_group_1 = QtGui.QLabel("DataFlow Pipeline")
|
|
|
+ label_group_1.setMinimumWidth(100)
|
|
|
+ led_group_1_layout.addWidget(label_group_1, 0, 1)
|
|
|
+ led_group_1_layout.addWidget(self.data_flow_pipeline_led, 0, 0)
|
|
|
+ led_group_1_layout.setColumnStretch(2, 1)
|
|
|
+ led_group_1_widget = QtGui.QWidget(self)
|
|
|
+ led_group_1_widget.setLayout(led_group_1_layout)
|
|
|
+ status_displays.addWidget(led_group_1_widget, 1, 0)
|
|
|
+
|
|
|
+ # Second Label + LED Group
|
|
|
+ self.master_ctrl_led = GraphicalLED(self, (128, 128, 128), 10, 10)
|
|
|
+ led_group_2_layout = QtGui.QGridLayout(self)
|
|
|
+ label_group_2 = QtGui.QLabel("Master Control")
|
|
|
+ label_group_2.setMinimumWidth(100)
|
|
|
+ led_group_2_layout.addWidget(label_group_2, 0, 1)
|
|
|
+ led_group_2_layout.addWidget(self.master_ctrl_led, 0, 0)
|
|
|
+ led_group_2_layout.setColumnStretch(2, 1)
|
|
|
+ led_group_2_widget = QtGui.QWidget(self)
|
|
|
+ led_group_2_widget.setLayout(led_group_2_layout)
|
|
|
+ status_displays.addWidget(led_group_2_widget, 2, 0)
|
|
|
+
|
|
|
+ # Third Label + LED Group
|
|
|
+ self.data_check_led = GraphicalLED(self, (128, 128, 128), 10, 10)
|
|
|
+ led_group_3_layout = QtGui.QGridLayout(self)
|
|
|
+ label_group_3 = QtGui.QLabel("Data Check")
|
|
|
+ label_group_3.setMinimumWidth(100)
|
|
|
+ led_group_3_layout.addWidget(label_group_3, 0, 1)
|
|
|
+ led_group_3_layout.addWidget(self.data_check_led, 0, 0)
|
|
|
+ led_group_3_layout.setColumnStretch(2, 1)
|
|
|
+ led_group_3_widget = QtGui.QWidget(self)
|
|
|
+ led_group_3_widget.setLayout(led_group_3_layout)
|
|
|
+ status_displays.addWidget(led_group_3_widget, 3, 0)
|
|
|
+
|
|
|
+ # Fourth Label + LED Group
|
|
|
+ self.pll_ld_led = GraphicalLED(self, (128, 128, 128), 10, 10)
|
|
|
+ led_group_4_layout = QtGui.QGridLayout(self)
|
|
|
+ label_group_4 = QtGui.QLabel("PLL_LD")
|
|
|
+ label_group_4.setMinimumWidth(100)
|
|
|
+ led_group_4_layout.addWidget(label_group_4, 0, 1)
|
|
|
+ led_group_4_layout.addWidget(self.pll_ld_led, 0, 0)
|
|
|
+ led_group_4_layout.setColumnStretch(2, 1)
|
|
|
+ led_group_4_widget = QtGui.QWidget(self)
|
|
|
+ led_group_4_widget.setLayout(led_group_4_layout)
|
|
|
+ status_displays.addWidget(led_group_4_widget, 4, 0)
|
|
|
+
|
|
|
+ status_displays.setColumnStretch(5, 2)
|
|
|
+ status_displays.setRowStretch(5, 1)
|
|
|
+ self.setLayout(status_displays)
|
|
|
+
|
|
|
+ def update_status(self, registers):
|
|
|
+ try:
|
|
|
+ bits = []
|
|
|
+ bits += ['{0:032b}'.format(registers[0])]
|
|
|
+ bits += ['{0:032b}'.format(registers[1])]
|
|
|
+ bits += ['{0:032b}'.format(registers[2])]
|
|
|
+
|
|
|
+ s1 = heb.board.get_dec_from_bits(bits[0], 2, 0)
|
|
|
+ if s1 == 0:
|
|
|
+ # Pipeline in reset mode
|
|
|
+ self.data_flow_pipeline_led.set_color((255, 255, 0))
|
|
|
+ elif s1 == 1:
|
|
|
+ # Pipeline is idle
|
|
|
+ self.data_flow_pipeline_led.set_color((0, 190, 0))
|
|
|
+ elif s1 == 6:
|
|
|
+ # Pipeline in error state
|
|
|
+ self.data_flow_pipeline_led.set_color((255, 0, 0))
|
|
|
+ else:
|
|
|
+ # Should not happen!
|
|
|
+ self.data_flow_pipeline_led.set_color((128, 128, 128))
|
|
|
+
|
|
|
+ s2 = heb.board.get_dec_from_bits(bits[0], 29, 26)
|
|
|
+ if s2 == 0:
|
|
|
+ # Master Control in reset mode
|
|
|
+ self.master_ctrl_led.set_color((255, 255, 0))
|
|
|
+ elif s2 == 1:
|
|
|
+ # Master Control is idle
|
|
|
+ self.master_ctrl_led.set_color((0, 190, 0))
|
|
|
+ elif s2 == 8:
|
|
|
+ # Master Control in error state
|
|
|
+ self.master_ctrl_led.set_color((255, 0, 0))
|
|
|
+ else:
|
|
|
+ # Should not happen!
|
|
|
+ self.master_ctrl_led.set_color((128, 128, 128))
|
|
|
+
|
|
|
+ s3 = heb.board.get_dec_from_bits(bits[2], 15, 12)
|
|
|
+ if s3 == 15:
|
|
|
+ # Data Check Idle
|
|
|
+ self.data_check_led.set_color((0, 190, 0))
|
|
|
+ else:
|
|
|
+ # Data Check Error
|
|
|
+ self.data_check_led.set_color((255, 255, 0))
|
|
|
+
|
|
|
+ s4 = int(bits[0][7])
|
|
|
+ if s4 == 0:
|
|
|
+ # PLL_LD not active
|
|
|
+ self.pll_ld_led.set_color((255, 255, 0))
|
|
|
+ elif s4 == 1:
|
|
|
+ # PLL_LD is active
|
|
|
+ self.pll_ld_led.set_color((0, 190, 0))
|
|
|
+
|
|
|
+ except IndexError:
|
|
|
+ QtGui.QMessageBox.critical(self, "Status Update Error", "Not enough registers give for status update.")
|
|
|
+ return
|
|
|
+
|
|
|
+
|
|
|
+class AdvancedBoardInterface(QtGui.QWidget):
|
|
|
+
|
|
|
+ def __init__(self, parent=None):
|
|
|
+ QtGui.QWidget.__init__(self, parent)
|
|
|
+ self.parent = parent
|
|
|
+ self.do_layout()
|
|
|
+ self.data_flow_pipeline_status = None
|
|
|
+
|
|
|
+ def do_layout(self):
|
|
|
+
|
|
|
+ table_grid = QtGui.QGridLayout()
|
|
|
+
|
|
|
+ table_grid.addWidget(QtGui.QLabel("Status1 Register 0x9050 (Readonly)"), 0, 0)
|
|
|
+ self.status1_table = BitsDisplayTable(32*'0', self)
|
|
|
+ self.do_status1_table_layout(self.status1_table)
|
|
|
+ table_grid.addWidget(self.status1_table, 1, 0)
|
|
|
+
|
|
|
+ table_grid.addWidget(QtGui.QLabel("Status2 Register 0x9054 (Readonly)"), 2, 0)
|
|
|
+ self.status2_table = BitsDisplayTable(32*'0', self)
|
|
|
+ self.do_status2_table_layout(self.status2_table)
|
|
|
+ table_grid.addWidget(self.status2_table, 3, 0)
|
|
|
+
|
|
|
+ table_grid.addWidget(QtGui.QLabel("Status3 Register 0x9058 (Readonly)"), 4, 0)
|
|
|
+ self.status3_table = BitsDisplayTable(32*'0', self)
|
|
|
+ self.do_status3_table_layout(self.status3_table)
|
|
|
+ table_grid.addWidget(self.status3_table, 5, 0)
|
|
|
+
|
|
|
+ table_grid.addItem(QtGui.QSpacerItem(1, 20), 6, 0)
|
|
|
+ table_grid.addWidget(QtGui.QLabel("Control Register 0x9040"), 7, 0)
|
|
|
+
|
|
|
+ buttons_box = QtGui.QHBoxLayout()
|
|
|
+ buttons_box.setAlignment(QtCore.Qt.AlignLeft)
|
|
|
+ self.write_control_button = QtGui.QPushButton("Write Values to board")
|
|
|
+ self.write_control_button.setMaximumWidth(200)
|
|
|
+ buttons_box.addWidget(self.write_control_button)
|
|
|
+ self.clear_control_button = QtGui.QPushButton("Clear Input")
|
|
|
+ self.clear_control_button.setMaximumWidth(200)
|
|
|
+ buttons_box.addWidget(self.clear_control_button)
|
|
|
+ self.check_status_control_button = QtGui.QPushButton("Check Status")
|
|
|
+ self.check_status_control_button.setMaximumWidth(200)
|
|
|
+ buttons_box.addWidget(self.check_status_control_button)
|
|
|
+ buttons_widget = QtGui.QWidget()
|
|
|
+ buttons_widget.setLayout(buttons_box)
|
|
|
+ table_grid.addWidget(buttons_widget, 8, 0)
|
|
|
+
|
|
|
+ self.control_table = BitsEditTable(32*'0', self)
|
|
|
+ self.do_control_table_layout(self.control_table)
|
|
|
+ table_grid.addWidget(self.control_table, 9, 0)
|
|
|
+
|
|
|
+ self.write_control_button.clicked.connect(self.send_control_to_board)
|
|
|
+ self.clear_control_button.clicked.connect(self.control_table.clear_all_bits)
|
|
|
+ self.check_status_control_button.clicked.connect(self.parent.do_status_readout)
|
|
|
+
|
|
|
+ width1 = self.status1_table.width()
|
|
|
+ width2 = self.status2_table.width()
|
|
|
+ width3 = self.status3_table.width()
|
|
|
+ width4 = self.control_table.width()
|
|
|
+ max_width = max(width1, max(width2, max(width3, width4)))
|
|
|
+ self.status1_table.stretch_to_width(max_width)
|
|
|
+ self.status2_table.stretch_to_width(max_width)
|
|
|
+ self.status3_table.stretch_to_width(max_width)
|
|
|
+ self.control_table.stretch_to_width(max_width)
|
|
|
+
|
|
|
+ self.setLayout(table_grid)
|
|
|
+
|
|
|
+ def send_control_to_board(self):
|
|
|
+ if not self.parent._check_for_no_continuous_read():
|
|
|
+ logging.log("Cant write to board while continuous readout is active")
|
|
|
+ return
|
|
|
+
|
|
|
+ bits = self.control_table.get_bits()
|
|
|
+ dec_val_bits = int(bits, 2)
|
|
|
+ self.parent.text_area.write("Writing to board Register 0x9040: %s" % ('0x{0:08x}'.format(dec_val_bits)))
|
|
|
+ try:
|
|
|
+ heb.board.write_pci(hex(dec_val_bits), '0x9040')
|
|
|
+ except heb.board.BoardError as e:
|
|
|
+ QtGui.QMessageBox.critical(self, "Board communication",
|
|
|
+ "Was unable to write value to board!\nReason: "+str(e))
|
|
|
+
|
|
|
+ self.parent.do_status_readout()
|
|
|
+
|
|
|
+ def do_status1_table_layout(self, table):
|
|
|
+ # from right to left
|
|
|
+ table.set_label(29, 31, "FSM_Data_Pipeline_Status", QtCore.Qt.green)
|
|
|
+ table.grey_out_column(28)
|
|
|
+ table.set_label(27, 27, "FULL", QtCore.Qt.green)
|
|
|
+ table.set_label(26, 26, "EMPTY", QtCore.Qt.green)
|
|
|
+ table.grey_out_column(25)
|
|
|
+ table.grey_out_column(24)
|
|
|
+ table.set_label(14, 23, "RD_data_Counts", QtCore.Qt.green)
|
|
|
+ table.set_label(13, 13, "OVR_ADC", QtGui.QColor(210, 210, 0))
|
|
|
+ table.grey_out_column(12)
|
|
|
+ table.grey_out_column(11)
|
|
|
+ table.grey_out_column(10)
|
|
|
+ table.grey_out_column(9)
|
|
|
+ table.grey_out_column(8)
|
|
|
+ table.set_label(7, 7, "PLL_LD", QtGui.QColor(210, 210, 0))
|
|
|
+ table.grey_out_column(6)
|
|
|
+ table.set_label(2, 5, "Master Control", QtCore.Qt.green)
|
|
|
+ table.grey_out_column(1)
|
|
|
+ table.set_label(0, 0, "1")
|
|
|
+
|
|
|
+ def do_status2_table_layout(self, table):
|
|
|
+ #from right to left
|
|
|
+ table.set_label(31, 31, "FIFO 128 255 empty", QtCore.Qt.green)
|
|
|
+ table.set_label(30, 30, "FIFO 128 255 full", QtCore.Qt.green)
|
|
|
+ table.grey_out_column(29)
|
|
|
+ table.grey_out_column(28)
|
|
|
+ table.set_label(17, 27, "wr data count 128 255", QtGui.QColor(210, 210, 0))
|
|
|
+ table.grey_out_column(16)
|
|
|
+ table.set_label(15, 15, "FIFO 255 64 empty", QtCore.Qt.green)
|
|
|
+ table.set_label(14, 14, "FIFO 255 64 full", QtCore.Qt.green)
|
|
|
+ table.grey_out_column(13)
|
|
|
+ table.grey_out_column(12)
|
|
|
+ table.set_label(2, 11, "rd data count 255 64", QtGui.QColor(210, 210, 0))
|
|
|
+ table.grey_out_column(1)
|
|
|
+ table.grey_out_column(0)
|
|
|
+
|
|
|
+ def do_status3_table_layout(self, table):
|
|
|
+ #from right to left
|
|
|
+ table.set_label(29, 31, "FSM_ARBITER_DDR3", QtCore.Qt.green)
|
|
|
+ table.grey_out_column(28)
|
|
|
+ table.set_label(25, 27, "FSM_WR_DDR3", QtCore.Qt.green)
|
|
|
+ table.grey_out_column(24)
|
|
|
+ table.set_label(21, 23, "FSM_R_DDR3", QtCore.Qt.green)
|
|
|
+ table.grey_out_column(20)
|
|
|
+ table.set_label(16, 19, "BC_ERROR", QtGui.QColor(210, 210, 0))
|
|
|
+ table.set_label(1, 15, "Number of wrong BC", QtGui.QColor(255, 255, 0))
|
|
|
+ table.grey_out_column(0)
|
|
|
+
|
|
|
+ def do_control_table_layout(self, table):
|
|
|
+ #from right to left
|
|
|
+ table.set_label(31, 31, "reset", QtGui.QColor(0, 255, 255))
|
|
|
+ table.grey_out_column(30)
|
|
|
+ table.grey_out_column(29)
|
|
|
+ table.grey_out_column(28)
|
|
|
+ table.set_label(27, 27, "ADC_1(A+D)", QtGui.QColor(0, 255, 255))
|
|
|
+ table.set_label(26, 26, "ADC_2(A+D)2", QtGui.QColor(0, 255, 255))
|
|
|
+ table.set_label(25, 25, "T/H_1", QtGui.QColor(210, 210, 0))
|
|
|
+ table.set_label(24, 24, "T/H_2", QtGui.QColor(210, 210, 0))
|
|
|
+ table.set_label(23, 23, "T/H_3", QtGui.QColor(210, 210, 0))
|
|
|
+ table.set_label(22, 22, "T/H_4", QtGui.QColor(210, 210, 0))
|
|
|
+ table.set_label(21, 21, "EN_data_Trans", QtCore.Qt.yellow)
|
|
|
+ table.set_label(20, 20, "EN_readout", QtCore.Qt.yellow)
|
|
|
+ table.set_label(18, 19, "ADC_1", QtCore.Qt.yellow)
|
|
|
+ table.set_label(16, 17, "ADC_2", QtCore.Qt.yellow)
|
|
|
+ table.set_label(14, 15, "ADC_3", QtCore.Qt.yellow)
|
|
|
+ table.set_label(12, 13, "ADC_4", QtCore.Qt.yellow)
|
|
|
+ table.grey_out_column(11)
|
|
|
+ table.grey_out_column(10)
|
|
|
+ table.grey_out_column(9)
|
|
|
+ table.grey_out_column(8)
|
|
|
+ table.grey_out_column(7)
|
|
|
+ table.grey_out_column(6)
|
|
|
+ table.grey_out_column(5)
|
|
|
+ table.grey_out_column(4)
|
|
|
+ table.set_label(3, 3, "Header", QtCore.Qt.green)
|
|
|
+ table.grey_out_column(2)
|
|
|
+ table.set_label(1, 1, "Pilot Bunch by FPGA", QtGui.QColor(0, 255, 255))
|
|
|
+ table.set_label(0, 0, "FPGA Temp monitor Reset", QtGui.QColor(0, 255, 255))
|
|
|
+
|
|
|
+ def update_status(self, registers):
|
|
|
+ try:
|
|
|
+ self.status1_table.set_numbers('{0:032b}'.format(registers[0]))
|
|
|
+ self.status2_table.set_numbers('{0:032b}'.format(registers[1]))
|
|
|
+ self.status3_table.set_numbers('{0:032b}'.format(registers[2]))
|
|
|
+ except:
|
|
|
+ return
|
|
|
+
|
|
|
+
|
|
|
+class PopupDialog(QtGui.QDialog):
|
|
|
+ def __init__(self, text, parent=None):
|
|
|
+ QtGui.QDialog.__init__(self, parent)
|
|
|
+ self.text = text
|
|
|
+ self.setWindowTitle("User action required")
|
|
|
+ self.do_layout()
|
|
|
+ self.return_value = False
|
|
|
+
|
|
|
+ def do_layout(self):
|
|
|
+ size = QtCore.QSize(200, 200)
|
|
|
+ #self.setMaximumSize(size)
|
|
|
+ self.setMinimumSize(size)
|
|
|
+ box = QtGui.QVBoxLayout()
|
|
|
+ self.text_label = QtGui.QLabel(self.text)
|
|
|
+ self.text_label.setAlignment(QtCore.Qt.AlignCenter)
|
|
|
+ box.addWidget(self.text_label)
|
|
|
+ self.okay_btn = QtGui.QPushButton("Okay")
|
|
|
+ self.okay_btn.setStyleSheet("padding: 15px;")
|
|
|
+ self.okay_btn.clicked.connect(self.on_okay)
|
|
|
+ box.addWidget(self.okay_btn)
|
|
|
+ box.addSpacerItem(QtGui.QSpacerItem(1, 20))
|
|
|
+ self.cancel_btn = QtGui.QPushButton("Cancel")
|
|
|
+ self.cancel_btn.setStyleSheet("padding: 15px;")
|
|
|
+ self.cancel_btn.clicked.connect(self.on_cancel)
|
|
|
+ box.addWidget(self.cancel_btn)
|
|
|
+ self.setLayout(box)
|
|
|
+
|
|
|
+ def on_okay(self):
|
|
|
+ self.return_value = True
|
|
|
+ self.close()
|
|
|
+
|
|
|
+ def on_cancel(self):
|
|
|
+ self.close()
|
|
|
+
|
|
|
+ def get_return_value(self):
|
|
|
+ return self.return_value
|
|
|
+
|
|
|
+
|
|
|
+class RegisterBitsDialog(QtGui.QDialog):
|
|
|
+ def __init__(self, n_bits, parent=None):
|
|
|
+ QtGui.QDialog.__init__(self, parent)
|
|
|
+ self.n_bits = n_bits
|
|
|
+ self.setWindowTitle("Register Bit Input Helper")
|
|
|
+ self.do_layout()
|
|
|
+ self.return_bits = None
|
|
|
+
|
|
|
+ def do_layout(self):
|
|
|
+ size = QtCore.QSize(200, 200)
|
|
|
+ #self.setMaximumSize(size)
|
|
|
+ self.setMinimumSize(size)
|
|
|
+ box = QtGui.QVBoxLayout()
|
|
|
+ self.table = BitsEditTable("0"*self.n_bits)
|
|
|
+ box.addWidget(self.table)
|
|
|
+ self.okay_btn = QtGui.QPushButton("Okay")
|
|
|
+ self.okay_btn.setStyleSheet("padding: 15px;")
|
|
|
+ self.okay_btn.clicked.connect(self.on_okay)
|
|
|
+ box.addWidget(self.okay_btn)
|
|
|
+ box.addSpacerItem(QtGui.QSpacerItem(1, 20))
|
|
|
+ self.cancel_btn = QtGui.QPushButton("Cancel")
|
|
|
+ self.cancel_btn.setStyleSheet("padding: 15px;")
|
|
|
+ self.cancel_btn.clicked.connect(self.on_cancel)
|
|
|
+ box.addWidget(self.cancel_btn)
|
|
|
+ self.setLayout(box)
|
|
|
+
|
|
|
+ def on_okay(self):
|
|
|
+ self.return_bits = self.table.get_bits()
|
|
|
+ self.close()
|
|
|
+
|
|
|
+ def on_cancel(self):
|
|
|
+ self.close()
|
|
|
+
|
|
|
+ def get_return_value(self):
|
|
|
+ return self.return_bits
|
|
|
+
|
|
|
+
|
|
|
+class FileLikeQTextEdit(QtGui.QTextEdit):
|
|
|
+ def __init__(self, parent=None):
|
|
|
+ QtGui.QTextEdit.__init__(self, parent)
|
|
|
+
|
|
|
+ def write(self, str_in):
|
|
|
+ self.append(str_in)
|
|
|
+
|
|
|
+ def flush(self):
|
|
|
+ pass
|
|
|
+
|
|
|
+ def close(self):
|
|
|
+ pass
|
|
|
+
|
|
|
+
|
|
|
+class LogHandler(logging.Handler):
|
|
|
+ def __init__(self, text_area):
|
|
|
+ logging.Handler.__init__(self)
|
|
|
+ self.text_area = text_area
|
|
|
+
|
|
|
+ def emit(self, record):
|
|
|
+ self.text_area.write(self.format(record))
|
|
|
+
|
|
|
+
|
|
|
+class ApplicationWindow(QtGui.QMainWindow):
|
|
|
+ def __init__(self, args):
|
|
|
+ QtGui.QMainWindow.__init__(self)
|
|
|
+ self.args = args
|
|
|
+ self.board_config = heb.board.BoardConfiguration(self.args.config if self.args.config else None)
|
|
|
+ self.opt_widgets = {}
|
|
|
+ self.data = None
|
|
|
+ self.continuous_read = False
|
|
|
+
|
|
|
+ self.do_layout()
|
|
|
+ self.register_observers()
|
|
|
+
|
|
|
+ if args.input:
|
|
|
+ self.read_and_update_data_from_file(args.input)
|
|
|
+
|
|
|
+ def new_checkbox(self, name, func):
|
|
|
+ checkbox = QtGui.QCheckBox(name)
|
|
|
+ checkbox.clicked.connect(func)
|
|
|
+ self.opt_widgets[name] = checkbox
|
|
|
+ return checkbox
|
|
|
+
|
|
|
+ def new_radiobutton(self, name, func):
|
|
|
+ button = QtGui.QRadioButton(name)
|
|
|
+ button.clicked.connect(func)
|
|
|
+ self.opt_widgets[name] = button
|
|
|
+ return button
|
|
|
+
|
|
|
+ def new_datawidget(self):
|
|
|
+ splitter = QtGui.QSplitter(self)
|
|
|
+
|
|
|
+ self.canvas = PlotCanvas(splitter)
|
|
|
+ self.mpl_toolbar = NavigationToolbar(self.canvas, splitter)
|
|
|
+
|
|
|
+ canvas_box = QtGui.QVBoxLayout()
|
|
|
+ canvas_box.addWidget(self.canvas)
|
|
|
+ canvas_box.addWidget(self.mpl_toolbar)
|
|
|
+ canvas_widget = QtGui.QWidget()
|
|
|
+ canvas_widget.setLayout(canvas_box)
|
|
|
+ sidebar = QtGui.QGridLayout()
|
|
|
+
|
|
|
+ data_view_groupbox = QtGui.QGroupBox()
|
|
|
+ data_view_groupbox.setTitle('Data View Settings')
|
|
|
+ data_view_layout = QtGui.QGridLayout()
|
|
|
+ data_view_layout.setVerticalSpacing(5)
|
|
|
+ data_view_layout.addWidget(self.new_radiobutton(OPT_3D_MAP, self.on_option_change), 1, 0)
|
|
|
+ data_view_layout.addWidget(self.new_radiobutton(OPT_TRAIN, self.on_option_change), 2, 0)
|
|
|
+ data_view_layout.addWidget(self.new_radiobutton(OPT_FFT, self.on_option_change), 3, 0)
|
|
|
+ data_view_layout.addWidget(self.new_radiobutton(OPT_COMBINED, self.on_option_change), 4, 0)
|
|
|
+ # sidebar.addWidget(self.new_checkbox(OPT_RECONSTRUCTED, self.on_option_change))
|
|
|
+ data_view_layout.addWidget(self.new_checkbox(OPT_ADC_1, self.on_option_change), 1, 1)
|
|
|
+ data_view_layout.addWidget(self.new_checkbox(OPT_ADC_2, self.on_option_change), 2, 1)
|
|
|
+ data_view_layout.addWidget(self.new_checkbox(OPT_ADC_3, self.on_option_change), 3, 1)
|
|
|
+ data_view_layout.addWidget(self.new_checkbox(OPT_ADC_4, self.on_option_change), 4, 1)
|
|
|
+ self.from_spinbox = Spinbox(0, 999, 0)
|
|
|
+ self.from_spinbox.valueChanged.connect(self.on_option_change)
|
|
|
+ self.to_spinbox = Spinbox(-1, 10000000, 1000)
|
|
|
+ self.to_spinbox.valueChanged.connect(self.on_to_changed)
|
|
|
+ data_view_layout.addWidget(QtGui.QLabel("Data range"), 5, 0)
|
|
|
+ data_view_layout.addWidget(self.from_spinbox, 6, 0)
|
|
|
+ data_view_layout.addWidget(self.to_spinbox, 6, 1)
|
|
|
+ self.opt_widgets[OPT_ADC_1].setChecked(True)
|
|
|
+ data_view_groupbox.setLayout(data_view_layout)
|
|
|
+ sidebar.addWidget(data_view_groupbox, 0, 0, 1, 2) # Span both columns
|
|
|
+
|
|
|
+ adc_settings_groupbox = QtGui.QGroupBox()
|
|
|
+ adc_settings_groupbox.setTitle('Timing Settings')
|
|
|
+ adc_delay_grid = QtGui.QGridLayout()
|
|
|
+ adc_delay_grid.setVerticalSpacing(5)
|
|
|
+ adc_delay_grid.addWidget(QtGui.QLabel("T/H Delay"), 0, 1, 1, 2)
|
|
|
+ self.th_delay_spinbox = Spinbox(0, 15, 0)
|
|
|
+ self.th_delay_spinbox.setMaximumWidth(80)
|
|
|
+ self.th_delay_spinbox.valueChanged.connect(self.on_adc_delay_changed)
|
|
|
+ adc_delay_grid.addWidget(self.th_delay_spinbox, 1, 1)
|
|
|
+ adc_delay_grid.addWidget(QtGui.QLabel("Coarse Delay"), 1, 0)
|
|
|
+ adc_delay_grid.addWidget(QtGui.QLabel("ADC_1"), 2, 1)
|
|
|
+ adc_delay_grid.addWidget(QtGui.QLabel("ADC_2"), 2, 2)
|
|
|
+ adc_delay_grid.addWidget(QtGui.QLabel("ADC_3"), 2, 3)
|
|
|
+ adc_delay_grid.addWidget(QtGui.QLabel("ADC_4"), 2, 4)
|
|
|
+ self.adc_1_delay_spinbox = Spinbox(0, 31, 0)
|
|
|
+ self.adc_1_delay_spinbox.setMaximumWidth(80)
|
|
|
+ self.adc_1_delay_spinbox.valueChanged.connect(self.on_adc_delay_changed)
|
|
|
+ self.adc_2_delay_spinbox = Spinbox(0, 31, 0)
|
|
|
+ self.adc_2_delay_spinbox.setMaximumWidth(80)
|
|
|
+ self.adc_2_delay_spinbox.valueChanged.connect(self.on_adc_delay_changed)
|
|
|
+ self.adc_3_delay_spinbox = Spinbox(0, 31, 0)
|
|
|
+ self.adc_3_delay_spinbox.setMaximumWidth(80)
|
|
|
+ self.adc_3_delay_spinbox.valueChanged.connect(self.on_adc_delay_changed)
|
|
|
+ self.adc_4_delay_spinbox = Spinbox(0, 31, 0)
|
|
|
+ self.adc_4_delay_spinbox.setMaximumWidth(80)
|
|
|
+ self.adc_4_delay_spinbox.valueChanged.connect(self.on_adc_delay_changed)
|
|
|
+ adc_delay_grid.addWidget(QtGui.QLabel("Fine Delay"), 3, 0)
|
|
|
+ adc_delay_grid.addWidget(self.adc_1_delay_spinbox, 3, 1)
|
|
|
+ adc_delay_grid.addWidget(self.adc_2_delay_spinbox, 3, 2)
|
|
|
+ adc_delay_grid.addWidget(self.adc_3_delay_spinbox, 3, 3)
|
|
|
+ adc_delay_grid.addWidget(self.adc_4_delay_spinbox, 3, 4)
|
|
|
+ self.adc_1_delay_text = QtGui.QLineEdit()
|
|
|
+ self.adc_1_delay_text.setReadOnly(True)
|
|
|
+ self.adc_1_delay_text.setMaximumWidth(80)
|
|
|
+ self.adc_2_delay_text = QtGui.QLineEdit()
|
|
|
+ self.adc_2_delay_text.setReadOnly(True)
|
|
|
+ self.adc_2_delay_text.setMaximumWidth(80)
|
|
|
+ self.adc_3_delay_text = QtGui.QLineEdit()
|
|
|
+ self.adc_3_delay_text.setReadOnly(True)
|
|
|
+ self.adc_3_delay_text.setMaximumWidth(80)
|
|
|
+ self.adc_4_delay_text = QtGui.QLineEdit()
|
|
|
+ self.adc_4_delay_text.setReadOnly(True)
|
|
|
+ self.adc_4_delay_text.setMaximumWidth(80)
|
|
|
+ adc_delay_grid.addWidget(QtGui.QLabel("Total Delay"), 4, 0)
|
|
|
+ adc_delay_grid.addWidget(self.adc_1_delay_text, 4, 1)
|
|
|
+ adc_delay_grid.addWidget(self.adc_2_delay_text, 4, 2)
|
|
|
+ adc_delay_grid.addWidget(self.adc_3_delay_text, 4, 3)
|
|
|
+ adc_delay_grid.addWidget(self.adc_4_delay_text, 4, 4)
|
|
|
+ adc_settings_groupbox.setLayout(adc_delay_grid)
|
|
|
+ sidebar.addWidget(adc_settings_groupbox, 1, 0, 1, 2) # Span both columns
|
|
|
+ self.set_adc_delay_spinboxes_active(False)
|
|
|
+
|
|
|
+ aquisition_settings_groupbox = QtGui.QGroupBox()
|
|
|
+ aquisition_settings_groupbox.setTitle('Acquisition Settings')
|
|
|
+ aquisition_settings_layout = QtGui.QGridLayout()
|
|
|
+ aquisition_settings_layout.setVerticalSpacing(5)
|
|
|
+ self.simulate_checkbox = QtGui.QCheckBox("Simulate Pilot Bunch")
|
|
|
+ aquisition_settings_layout.addWidget(self.simulate_checkbox, 0, 0)
|
|
|
+ self.number_of_orbits_spinbox = Spinbox(1, 10000000, 0)
|
|
|
+ self.number_of_orbits_spinbox.valueChanged.connect(self.on_number_of_orbits_changed)
|
|
|
+ aquisition_settings_layout.addWidget(self.number_of_orbits_spinbox, 1, 0)
|
|
|
+ aquisition_settings_layout.addWidget(QtGui.QLabel("Number of orbits to observe"), 1, 1)
|
|
|
+ self.number_of_skipped_orbits_spinbox = Spinbox(0, 100, 0)
|
|
|
+ self.number_of_skipped_orbits_spinbox.valueChanged.connect(self.on_number_of_skipped_orbits_changed)
|
|
|
+ aquisition_settings_layout.addWidget(self.number_of_skipped_orbits_spinbox, 2, 0)
|
|
|
+ aquisition_settings_layout.addWidget(QtGui.QLabel("Number of orbits to skip"), 2, 1)
|
|
|
+ aquisition_settings_groupbox.setLayout(aquisition_settings_layout)
|
|
|
+ sidebar.addWidget(aquisition_settings_groupbox, 2, 0, 1, 2)
|
|
|
+
|
|
|
+ data_consistency_groupbox = QtGui.QGroupBox()
|
|
|
+ data_consistency_groupbox.setTitle('Data Consistency')
|
|
|
+ data_consistency_layout = QtGui.QHBoxLayout()
|
|
|
+ self.data_consistency_led = GraphicalLED(self, (128, 128, 128), 10, 10)
|
|
|
+ data_consistency_layout.addWidget(self.data_consistency_led)
|
|
|
+ data_consistency_layout.addWidget(QtGui.QLabel("Consistency Indicator"))
|
|
|
+ data_consistency_groupbox.setLayout(data_consistency_layout)
|
|
|
+ sidebar.addWidget(data_consistency_groupbox, 3, 0, 1, 2)
|
|
|
+
|
|
|
+
|
|
|
+ read_data_toolbox = QtGui.QToolBox()
|
|
|
+
|
|
|
+ single_and_timed_read_layout = QtGui.QGridLayout()
|
|
|
+ single_and_timed_read_layout.setVerticalSpacing(10)
|
|
|
+ single_and_timed_read_layout.addWidget(QtGui.QLabel("Read every n-Milliseconds"), 0, 0)
|
|
|
+ self.continuous_read_interval_spinbox = Spinbox(0, 10000, 100)
|
|
|
+ self.continuous_read_checkbox = self.new_checkbox("Continuous Readout", self.on_continuous_read)
|
|
|
+ single_and_timed_read_layout.addWidget(self.continuous_read_interval_spinbox, 1, 0)
|
|
|
+ single_and_timed_read_layout.addWidget(self.continuous_read_checkbox, 1, 1)
|
|
|
+ self.start_button = QtGui.QPushButton("Single Read")
|
|
|
+ self.start_button.clicked.connect(self.on_single_read)
|
|
|
+ single_and_timed_read_layout.addWidget(self.start_button, 2, 0)
|
|
|
+ single_and_timed_read_layout.setRowStretch(2, 1) # Otherwise, the label in row 0 will take all the space...
|
|
|
+ single_and_timed_read_wdgt = QtGui.QWidget()
|
|
|
+ single_and_timed_read_wdgt.setLayout(single_and_timed_read_layout)
|
|
|
+ read_data_toolbox.addItem(single_and_timed_read_wdgt, 'Single and Continuous Readout')
|
|
|
+
|
|
|
+ acquisition_layout = QtGui.QGridLayout()
|
|
|
+ acquisition_layout.setVerticalSpacing(10)
|
|
|
+ self.acquisitions_spinbox = Spinbox(1, 10000000, 10)
|
|
|
+ acquisition_layout.addWidget(self.acquisitions_spinbox, 0, 0)
|
|
|
+ acquisition_layout.addWidget(QtGui.QLabel("Count"), 0, 1)
|
|
|
+ self.wait_time_spinbox = Spinbox(1, 60, 15)
|
|
|
+ acquisition_layout.addWidget(self.wait_time_spinbox, 1, 0)
|
|
|
+ acquisition_layout.addWidget(QtGui.QLabel("Wait (s)"), 1, 1)
|
|
|
+ self.spectrogram_checkbox = QtGui.QCheckBox("Build Spectrograms")
|
|
|
+ acquisition_layout.addWidget(self.spectrogram_checkbox, 2, 0)
|
|
|
+ self.acquire_button = QtGui.QPushButton("Acquire")
|
|
|
+ self.acquire_button.clicked.connect(self.on_acquire)
|
|
|
+ acquisition_layout.addWidget(self.acquire_button, 3, 0)
|
|
|
+ self.stop_button = QtGui.QPushButton("Stop")
|
|
|
+ self.stop_button.setEnabled(False)
|
|
|
+ self.stop_button.clicked.connect(self.on_stop)
|
|
|
+ acquisition_layout.addWidget(self.stop_button, 3, 1)
|
|
|
+ self.acquisition_progressbar = QtGui.QProgressBar()
|
|
|
+ acquisition_layout.addWidget(self.acquisition_progressbar, 4, 0, 1, 2)
|
|
|
+ acquisition_wdgt = QtGui.QWidget()
|
|
|
+ acquisition_wdgt.setLayout(acquisition_layout)
|
|
|
+ read_data_toolbox.addItem(acquisition_wdgt, 'Acquisition')
|
|
|
+
|
|
|
+ timescan_layout = QtGui.QGridLayout()
|
|
|
+ timescan_layout.setVerticalSpacing(10)
|
|
|
+ self.scan_button = QtGui.QPushButton("Time scan")
|
|
|
+ self.scan_button.clicked.connect(self.on_scan)
|
|
|
+ timescan_layout.addWidget(self.scan_button, 0, 0)
|
|
|
+ self.scan_stop_button = QtGui.QPushButton("Stop scan")
|
|
|
+ self.scan_stop_button.setEnabled(False)
|
|
|
+ timescan_layout.addWidget(self.scan_stop_button, 0, 1)
|
|
|
+ self.timescan_progressbar = QtGui.QProgressBar()
|
|
|
+ timescan_layout.addWidget(self.timescan_progressbar, 1, 0, 1, 2)
|
|
|
+ timescan_layout.addWidget(QtGui.QLabel('Coarse Scan Range'), 2, 0, 1, 2)
|
|
|
+ self.time_scan_coarse_from = Spinbox(0, self.board_config.get('th_delay_max'), 0)
|
|
|
+ timescan_layout.addWidget(self.time_scan_coarse_from, 3, 0)
|
|
|
+ self.time_scan_coarse_to = Spinbox(0, self.board_config.get('th_delay_max'), self.board_config.get('th_delay_max'))
|
|
|
+ timescan_layout.addWidget(self.time_scan_coarse_to, 3, 1)
|
|
|
+ timescan_layout.addWidget(QtGui.QLabel('Fine Scan Range'), 4, 0, 1, 2)
|
|
|
+ self.time_scan_fine_from = Spinbox(0, self.board_config.get('chip_delay_max'), 0)
|
|
|
+ timescan_layout.addWidget(self.time_scan_fine_from, 5, 0)
|
|
|
+ self.time_scan_fine_to = Spinbox(0, self.board_config.get('chip_delay_max'), self.board_config.get('chip_delay_max'))
|
|
|
+ timescan_layout.addWidget(self.time_scan_fine_to, 5, 1)
|
|
|
+
|
|
|
+ timescan_wdgt = QtGui.QWidget()
|
|
|
+ timescan_wdgt.setLayout(timescan_layout)
|
|
|
+ read_data_toolbox.addItem(timescan_wdgt, 'Time Scan')
|
|
|
+
|
|
|
+ read_data_toolbox.setMinimumHeight(240)
|
|
|
+
|
|
|
+ sidebar.addWidget(read_data_toolbox, 4, 0, 1, 2)
|
|
|
+ sidebar.setVerticalSpacing(10)
|
|
|
+ sidebar.setRowStretch(5, 1)
|
|
|
+
|
|
|
+ sidebar_group = QtGui.QGroupBox("sidebar")
|
|
|
+ sidebar_group.setLayout(sidebar)
|
|
|
+ sidebar_group.setFlat(True)
|
|
|
+
|
|
|
+ splitter.addWidget(canvas_widget)
|
|
|
+ splitter.addWidget(sidebar_group)
|
|
|
+ splitter.setStretchFactor(0, 4)
|
|
|
+ splitter.setStretchFactor(1, 2)
|
|
|
+ return splitter
|
|
|
+
|
|
|
+ def do_layout(self):
|
|
|
+ self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
|
|
+ self.setWindowTitle("HEB analysis")
|
|
|
+
|
|
|
+ self.file_menu = QtGui.QMenu('&File', self)
|
|
|
+ self.file_menu.addAction('&Open Datafile', self.on_open,
|
|
|
+ QtCore.Qt.CTRL + QtCore.Qt.Key_O)
|
|
|
+ self.file_menu.addAction('O&pen Config', self.on_open_config,
|
|
|
+ QtCore.Qt.CTRL + QtCore.Qt.Key_P)
|
|
|
+ self.file_menu.addAction('&Save Config', self.on_save_config,
|
|
|
+ QtCore.Qt.CTRL + QtCore.Qt.Key_S)
|
|
|
+ self.file_menu.addAction('&Quit', self.on_close,
|
|
|
+ QtCore.Qt.CTRL + QtCore.Qt.Key_Q)
|
|
|
+ self.menuBar().addMenu(self.file_menu)
|
|
|
+
|
|
|
+ self.tab_views = QtGui.QTabWidget(self)
|
|
|
+ self.setCentralWidget(self.tab_views)
|
|
|
+ # Group the board control buttons together
|
|
|
+ control_buttons = QtGui.QGridLayout()
|
|
|
+ control_buttons.addWidget(QtGui.QLabel("Board Control"), 0, 0)
|
|
|
+
|
|
|
+ self.start_board_button = QtGui.QPushButton("Start Board")
|
|
|
+ self.start_board_button.clicked.connect(self.on_start_board)
|
|
|
+ control_buttons.addWidget(self.start_board_button, 1, 0)
|
|
|
+
|
|
|
+ self.calibrate_board_button = QtGui.QPushButton("Calibrate")
|
|
|
+ self.calibrate_board_button.clicked.connect(self.on_calibrate_board)
|
|
|
+ self.calibrate_board_button.setEnabled(False)
|
|
|
+ control_buttons.addWidget(self.calibrate_board_button, 2, 0)
|
|
|
+
|
|
|
+ self.sync_board_button = QtGui.QPushButton("Synchronize")
|
|
|
+ self.sync_board_button.clicked.connect(self.on_sync_board)
|
|
|
+ self.sync_board_button.setEnabled(False)
|
|
|
+ control_buttons.addWidget(self.sync_board_button, 3, 0)
|
|
|
+
|
|
|
+ self.set_defaults_button = QtGui.QPushButton("Set Defaults")
|
|
|
+ self.set_defaults_button.clicked.connect(self.on_set_defaults)
|
|
|
+ self.set_defaults_button.setEnabled(False)
|
|
|
+ control_buttons.addWidget(self.set_defaults_button, 4, 0)
|
|
|
+
|
|
|
+ self.soft_reset_button = QtGui.QPushButton("Soft Reset")
|
|
|
+ self.soft_reset_button.clicked.connect(self.on_soft_reset)
|
|
|
+ self.soft_reset_button.setEnabled(False)
|
|
|
+ control_buttons.addWidget(self.soft_reset_button, 5, 0)
|
|
|
+
|
|
|
+ self.stop_board_button = QtGui.QPushButton("Board Off")
|
|
|
+ self.stop_board_button.clicked.connect(self.on_stop_board)
|
|
|
+ control_buttons.addWidget(self.stop_board_button, 6, 0)
|
|
|
+
|
|
|
+ #self.show_advanced_tick = QtGui.QCheckBox("Show advanced Interface")
|
|
|
+ #self.show_advanced_tick.setEnabled(True)
|
|
|
+ #self.show_advanced_tick.clicked.connect(self.do_show_advanced_interface)
|
|
|
+ #control_buttons.addWidget(self.show_advanced_tick, 6, 0)
|
|
|
+
|
|
|
+ control_buttons.setRowStretch(7, 1)
|
|
|
+ buttons_widget = QtGui.QWidget()
|
|
|
+ buttons_widget.setLayout(control_buttons)
|
|
|
+ buttons_widget.setMinimumWidth(200)
|
|
|
+ buttons_widget.setMaximumWidth(200)
|
|
|
+
|
|
|
+ # Group the Simple status display together with the "Check Status" button
|
|
|
+ status_box = QtGui.QGridLayout()
|
|
|
+ self.simple_status_display = SimpleStatusDisplay()
|
|
|
+ status_box.addWidget(self.simple_status_display, 0, 0)
|
|
|
+ self.get_status_button = QtGui.QPushButton("Check Status")
|
|
|
+ self.get_status_button.setEnabled(True)
|
|
|
+ self.get_status_button.clicked.connect(self.do_status_readout)
|
|
|
+ self.get_status_button.setMinimumWidth(150)
|
|
|
+ self.get_status_button.setMaximumWidth(150)
|
|
|
+ status_box.addWidget(self.get_status_button, 1, 0)
|
|
|
+ status_box.setRowStretch(2, 1)
|
|
|
+ simple_status_widget = QtGui.QWidget()
|
|
|
+ simple_status_widget.setLayout(status_box)
|
|
|
+
|
|
|
+ #Text Area for logging of inconsistent readouts
|
|
|
+ self.error_text_area = FileLikeQTextEdit(self)
|
|
|
+ self.error_text_area.setFont(QtGui.QFont('monospace', 9))
|
|
|
+ self.error_text_area.setTextColor(QtCore.Qt.red)
|
|
|
+ self.error_text_area.setMaximumWidth(300)
|
|
|
+ self.error_text_area.setReadOnly(True)
|
|
|
+ clear_error_text_btn = QtGui.QPushButton("Clear List")
|
|
|
+ clear_error_text_btn.clicked.connect(self.error_text_area.clear)
|
|
|
+ error_text_box = QtGui.QVBoxLayout()
|
|
|
+ error_text_box.addWidget(QtGui.QLabel("Inconsistent Files List"))
|
|
|
+ error_text_box.addWidget(self.error_text_area)
|
|
|
+ error_text_box.addWidget(clear_error_text_btn)
|
|
|
+ error_text_wdgt = QtGui.QWidget()
|
|
|
+ error_text_wdgt.setLayout(error_text_box)
|
|
|
+
|
|
|
+ #Now bundle the Board Control buttons and the Status LEDS together
|
|
|
+ top_box = QtGui.QHBoxLayout()
|
|
|
+ top_box.addWidget(buttons_widget)
|
|
|
+ top_box.addWidget(simple_status_widget)
|
|
|
+ top_box.addWidget(error_text_wdgt)
|
|
|
+ top_box.insertStretch(-1, 1)
|
|
|
+ top_box_w = QtGui.QWidget()
|
|
|
+ top_box_w.setLayout(top_box)
|
|
|
+
|
|
|
+ #Create the Timing Indication Block
|
|
|
+ self.fpga_delay_table = BitsDisplayTable('0', self)
|
|
|
+ self.fpga_delay_table.set_label(0, 0, "FPGA Delay")
|
|
|
+ self.th_delay_table = BitsDisplayTable('0', self)
|
|
|
+ self.th_delay_table.set_label(0, 0, "T/H Delay")
|
|
|
+ self.fpga_temperature = QtGui.QLineEdit()
|
|
|
+ self.fpga_temperature.setMaximumWidth(60)
|
|
|
+ self.fpga_temperature.setReadOnly(True)
|
|
|
+ self.adc_delay_table = BitsDisplayTable(4*'0', self)
|
|
|
+ self.adc_delay_table.set_label(0, 0, "ADC 1")
|
|
|
+ self.adc_delay_table.set_label(1, 1, "ADC 2")
|
|
|
+ self.adc_delay_table.set_label(2, 2, "ADC 3")
|
|
|
+ self.adc_delay_table.set_label(3, 3, "ADC 4")
|
|
|
+
|
|
|
+ timing_line_1 = QtGui.QHBoxLayout()
|
|
|
+ timing_line_1.setAlignment(QtCore.Qt.AlignLeft)
|
|
|
+ timing_line_1.addWidget(self.fpga_delay_table)
|
|
|
+ timing_line_1.addWidget(self.th_delay_table)
|
|
|
+ timing_line_1.addWidget(self.adc_delay_table)
|
|
|
+ timing_line_1_wdgt = QtGui.QWidget()
|
|
|
+ timing_line_1_wdgt.setLayout(timing_line_1)
|
|
|
+ timing_line_2 = QtGui.QGridLayout()
|
|
|
+ timing_line_2.addWidget(QtGui.QLabel("FPGA Temperature"), 0, 0)
|
|
|
+ timing_line_2.addWidget(self.fpga_temperature, 0, 1)
|
|
|
+ timing_line_2.setColumnStretch(2, 1)
|
|
|
+ timing_line_2_wdgt = QtGui.QWidget()
|
|
|
+ timing_line_2_wdgt.setLayout(timing_line_2)
|
|
|
+
|
|
|
+ timing_box = QtGui.QVBoxLayout()
|
|
|
+ timing_box.addWidget(QtGui.QLabel(" Timings in Pico Seconds"), 0, QtCore.Qt.AlignLeft)
|
|
|
+ timing_box.addWidget(timing_line_1_wdgt, 0, QtCore.Qt.AlignLeft)
|
|
|
+ timing_box.addWidget(timing_line_2_wdgt, 0, QtCore.Qt.AlignLeft)
|
|
|
+ timing_box_wdgt = QtGui.QWidget()
|
|
|
+ timing_box_wdgt.setLayout(timing_box)
|
|
|
+
|
|
|
+ #Create the the "Write Registers" block
|
|
|
+ sub_box = QtGui.QGridLayout()
|
|
|
+ sub_box.addWidget(QtGui.QLabel("Register Address"), 0, 0)
|
|
|
+ sub_box.addWidget(QtGui.QLabel("Register Value"), 0, 1)
|
|
|
+ self.adv_register_address = QtGui.QLineEdit()
|
|
|
+ self.adv_register_address.setText("0x9040")
|
|
|
+ self.adv_register_address.setInputMask(r"\0\xHhhhhhhh")
|
|
|
+ sub_box.addWidget(self.adv_register_address, 1, 0)
|
|
|
+ self.adv_register_value = QtGui.QLineEdit()
|
|
|
+ self.adv_register_value.setText("0x0")
|
|
|
+ self.adv_register_value.setInputMask(r"\0\xHhhhhhhh")
|
|
|
+ sub_box.addWidget(self.adv_register_value, 1, 1)
|
|
|
+ helper_btn = QtGui.QPushButton("Open Input Help")
|
|
|
+ helper_btn.clicked.connect(self.show_input_helper)
|
|
|
+ sub_box.addWidget(helper_btn, 2, 0)
|
|
|
+ adv_write_reg_val_btn = QtGui.QPushButton("Write Value")
|
|
|
+ adv_write_reg_val_btn.clicked.connect(self.write_register_adv)
|
|
|
+ sub_box.addWidget(adv_write_reg_val_btn, 2, 1)
|
|
|
+ sub_box.setRowStretch(3, 1)
|
|
|
+ self.sub_box_w = QtGui.QWidget()
|
|
|
+ self.sub_box_w.setLayout(sub_box)
|
|
|
+ self.sub_box_w.setVisible(False)
|
|
|
+
|
|
|
+ #Pack Board Control / Status LEDs / Timing Indication / "Write Register" together
|
|
|
+ control_block = QtGui.QGridLayout()
|
|
|
+ control_block.addWidget(top_box_w, 0, 0)
|
|
|
+ control_block.addWidget(timing_box_wdgt, 1, 0)
|
|
|
+ control_block.addWidget(self.sub_box_w, 3, 0)
|
|
|
+ control_block.setRowStretch(2, 1)
|
|
|
+ control_block_wdgt = QtGui.QWidget()
|
|
|
+ control_block_wdgt.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum)
|
|
|
+ control_block_wdgt.setLayout(control_block)
|
|
|
+
|
|
|
+ #Split the area vertically and add a text area in the bottom half
|
|
|
+ splitter = QtGui.QSplitter()
|
|
|
+ splitter.setOrientation(QtCore.Qt.Vertical)
|
|
|
+
|
|
|
+ splitter.addWidget(control_block_wdgt)
|
|
|
+ self.text_area = FileLikeQTextEdit(self)
|
|
|
+ self.text_area.setFont(QtGui.QFont('monospace', 9))
|
|
|
+ self.text_area.setReadOnly(True)
|
|
|
+ splitter.addWidget(self.text_area)
|
|
|
+
|
|
|
+ # Let log handler write to text area
|
|
|
+ log_handler = LogHandler(self.text_area)
|
|
|
+ log_handler.setFormatter(logging.Formatter('%(asctime)s - %(funcName)s(): %(message)s'))
|
|
|
+ logger = logging.getLogger()
|
|
|
+ logger.addHandler(log_handler)
|
|
|
+ logger.setLevel(logging.INFO)
|
|
|
+ self.board_config.logger = logger
|
|
|
+
|
|
|
+ # Create a widget for the Advanced Board Interface
|
|
|
+ self.advanced_interface = AdvancedBoardInterface(self)
|
|
|
+ self.advanced_interface.setVisible(False)
|
|
|
+
|
|
|
+ #set up tabs for the main window
|
|
|
+ self.tab_views.addTab(splitter, "Control")
|
|
|
+ self.tab_views.addTab(self.advanced_interface, "Registers")
|
|
|
+ self.tab_views.addTab(self.new_datawidget(), "Data")
|
|
|
+ self.tab_views.setCurrentIndex(2)
|
|
|
+
|
|
|
+ def do_show_advanced_interface(self):
|
|
|
+ show = self.show_advanced_tick.isChecked()
|
|
|
+ self.advanced_interface.setVisible(show)
|
|
|
+ self.sub_box_w.setVisible(show)
|
|
|
+
|
|
|
+ def register_observers(self):
|
|
|
+ sbo = self.board_config.observe
|
|
|
+ sbo(self.adc_1_delay_spinbox, self.adc_1_delay_spinbox.setValueSilent, 'chip_1_delay')
|
|
|
+ sbo(self.adc_2_delay_spinbox, self.adc_2_delay_spinbox.setValueSilent, 'chip_2_delay')
|
|
|
+ sbo(self.adc_3_delay_spinbox, self.adc_3_delay_spinbox.setValueSilent, 'chip_3_delay')
|
|
|
+ sbo(self.adc_4_delay_spinbox, self.adc_4_delay_spinbox.setValueSilent, 'chip_4_delay')
|
|
|
+ sbo(self.adc_1_delay_text, lambda x: self.adc_1_delay_text.setText('%i + %i' % (self.board_config.get('th_delay')*self.board_config.get('th_delay_factor'),x*self.board_config.get('chip_delay_factor'))), 'chip_1_delay')
|
|
|
+ sbo(self.adc_2_delay_text, lambda x: self.adc_2_delay_text.setText('%i + %i'%(self.board_config.get('th_delay')*self.board_config.get('th_delay_factor'),x*self.board_config.get('chip_delay_factor'))), 'chip_2_delay')
|
|
|
+ sbo(self.adc_3_delay_text, lambda x: self.adc_3_delay_text.setText('%i + %i'%(self.board_config.get('th_delay')*self.board_config.get('th_delay_factor'),x*self.board_config.get('chip_delay_factor'))), 'chip_3_delay')
|
|
|
+ sbo(self.adc_4_delay_text, lambda x: self.adc_4_delay_text.setText('%i + %i'%(self.board_config.get('th_delay')*self.board_config.get('th_delay_factor'),x*self.board_config.get('chip_delay_factor'))), 'chip_4_delay')
|
|
|
+ sbo(self.fpga_delay_table, lambda x: self.fpga_delay_table.set_item(0, 0, x*self.board_config.get('fpga_delay_factor')), 'fpga_delay')
|
|
|
+ sbo(self.th_delay_table, lambda x: self.th_delay_table.set_item(0, 0, x*self.board_config.get('th_delay_factor')), 'th_delay')
|
|
|
+ sbo(self.th_delay_spinbox, self.th_delay_spinbox.setValueSilent, 'th_delay')
|
|
|
+ sbo(self.number_of_orbits_spinbox, self.number_of_orbits_spinbox.setValueSilent, 'orbits_observe')
|
|
|
+ sbo(self.number_of_skipped_orbits_spinbox, self.number_of_skipped_orbits_spinbox.setValueSilent, 'orbits_skip')
|
|
|
+ sbo(self.adc_delay_table, lambda x: self.adc_delay_table.set_item(0, 0, x * self.board_config.get('adc_delay_factor')), 'adc_1_delay')
|
|
|
+ sbo(self.adc_delay_table, lambda x: self.adc_delay_table.set_item(0, 1, x * self.board_config.get('adc_delay_factor')), 'adc_2_delay')
|
|
|
+ sbo(self.adc_delay_table, lambda x: self.adc_delay_table.set_item(0, 2, x * self.board_config.get('adc_delay_factor')), 'adc_3_delay')
|
|
|
+ sbo(self.adc_delay_table, lambda x: self.adc_delay_table.set_item(0, 3, x * self.board_config.get('adc_delay_factor')), 'adc_4_delay')
|
|
|
+
|
|
|
+ def do_status_readout(self):
|
|
|
+ try:
|
|
|
+ registers = heb.board.read_pci(3, '0x9050', decimal=True)
|
|
|
+ self.simple_status_display.update_status(registers)
|
|
|
+ self.advanced_interface.update_status(registers)
|
|
|
+ control = heb.board.read_pci(1, '0x9040')[0]
|
|
|
+ control_bits = '{0:032b}'.format(int(control, 16))
|
|
|
+ self.advanced_interface.control_table.set_numbers(control_bits)
|
|
|
+ if control_bits[22:26] == "1111":
|
|
|
+ self.start_board_button.setEnabled(False)
|
|
|
+ self.soft_reset_button.setEnabled(True)
|
|
|
+ self.calibrate_board_button.setEnabled(True)
|
|
|
+ else:
|
|
|
+ self.start_board_button.setEnabled(True)
|
|
|
+ self.soft_reset_button.setEnabled(False)
|
|
|
+ self.calibrate_board_button.setEnabled(False)
|
|
|
+ fpga_temp_raw_hex = heb.board.read_pci(1,'0x9110')[0]
|
|
|
+ fpga_temp_raw_hex = fpga_temp_raw_hex[-3:]
|
|
|
+ fpga_temp_raw_bin = '{0:012b}'.format(int(fpga_temp_raw_hex, 16))
|
|
|
+ fpga_temp_encoded = heb.board.get_dec_from_bits(fpga_temp_raw_bin, 9, 0)
|
|
|
+ fpga_temp_celsius = '{0:2.2f}'.format(((fpga_temp_encoded*503.975)/1024)-273.15)
|
|
|
+ self.fpga_temperature.setText(fpga_temp_celsius + " C")
|
|
|
+ except heb.board.BoardError as e:
|
|
|
+ logging.error("Reading board status failed: {}".format(str(e)))
|
|
|
+
|
|
|
+ def write_register_adv(self):
|
|
|
+
|
|
|
+ # First cast both texts to integers (base 16) and let python check
|
|
|
+ # if the entered values are valid hex-numbers
|
|
|
+ try:
|
|
|
+ addr = int("%s"%self.adv_register_address.text(), 16)
|
|
|
+ except ValueError as e:
|
|
|
+ logging.error("Register address is invalid!")
|
|
|
+ self.adv_register_address.setStyleSheet("QLineEdit { background-color : #FF6666; }")
|
|
|
+ return
|
|
|
+
|
|
|
+ try:
|
|
|
+ value = int("%s"%self.adv_register_value.text(), 16)
|
|
|
+ except ValueError as e:
|
|
|
+ logging.error("Register value is invalid!")
|
|
|
+ self.adv_register_value.setStyleSheet("QLineEdit { background-color : #FF6666; }")
|
|
|
+ return
|
|
|
+
|
|
|
+ # Now use the numbers
|
|
|
+ addr = "%s"%self.adv_register_address.text()
|
|
|
+ value = "%s"%self.adv_register_value.text()
|
|
|
+ self.adv_register_address.setStyleSheet("QLineEdit { background-color : white; }")
|
|
|
+ self.adv_register_value.setStyleSheet("QLineEdit { background-color : white; }")
|
|
|
+
|
|
|
+ try:
|
|
|
+ heb.board.write_pci(value, addr)
|
|
|
+ except heb.board.BoardError as e:
|
|
|
+ QtGui.QMessageBox.critical(self, "Board communication", "'pci' " + str(e))
|
|
|
+ logging.error("Could not write values to board due to an error!")
|
|
|
+ return
|
|
|
+
|
|
|
+ # Intercept writes to the "Timing" Registers to catch the input and put it into their
|
|
|
+ # respective displays
|
|
|
+ if addr == "0x9060":
|
|
|
+ if value[:8] == "0x000501":
|
|
|
+ #We are writing a timing value. Decide which one
|
|
|
+ if value[-1:] == "0":
|
|
|
+ #FPGA
|
|
|
+ self.board_config.update('fpga_delay', int(value[8:9], 16))
|
|
|
+ elif value[-1:] == "3":
|
|
|
+ #T/H
|
|
|
+ self.board_config.update('th_delay', int(value[8:9], 16))
|
|
|
+ elif value[-1:] == "4":
|
|
|
+ #ADC_1
|
|
|
+ self.board_config.update('adc_1_delay', int(value[8:9], 16))
|
|
|
+ elif value[-1:] == "5":
|
|
|
+ #ADC_2
|
|
|
+ self.board_config.update('adc_1_delay', int(value[8:9], 16))
|
|
|
+ elif value[-1:] == "6":
|
|
|
+ #ADC_3
|
|
|
+ self.board_config.update('adc_1_delay', int(value[8:9], 16))
|
|
|
+ elif value[-1:] == "7":
|
|
|
+ #ADC_4
|
|
|
+ self.board_config.update('adc_1_delay', int(value[8:9], 16))
|
|
|
+
|
|
|
+ self.do_status_readout()
|
|
|
+ logging.info("Written '%s' to register '%s'." % (value, addr))
|
|
|
+
|
|
|
+ def show_input_helper(self):
|
|
|
+ dialog = RegisterBitsDialog(32)
|
|
|
+ dialog.exec_()
|
|
|
+ dialog.deleteLater()
|
|
|
+ bits = dialog.get_return_value()
|
|
|
+ if bits:
|
|
|
+ hex_str = hex(int(bits, 2))
|
|
|
+ self.adv_register_value.setText(hex_str)
|
|
|
+
|
|
|
+ def _check_for_no_continuous_read(self):
|
|
|
+ if self.continuous_read is True:
|
|
|
+ dialog = PopupDialog("Continuous Read is currently active!\n"
|
|
|
+ "Stop continuous read and still perform this action?")
|
|
|
+ dialog.exec_()
|
|
|
+ dialog.deleteLater()
|
|
|
+ ret = dialog.get_return_value()
|
|
|
+ if ret is False:
|
|
|
+ return False
|
|
|
+ else:
|
|
|
+ self._set_continuous_read_inactive()
|
|
|
+ self.continuous_read_checkbox.setChecked(False)
|
|
|
+ return True
|
|
|
+ else:
|
|
|
+ return True
|
|
|
+
|
|
|
+ def on_start_board(self):
|
|
|
+ enable_wait_cursor()
|
|
|
+
|
|
|
+ if not self._check_for_no_continuous_read():
|
|
|
+ disable_wait_cursor()
|
|
|
+ return
|
|
|
+
|
|
|
+ try:
|
|
|
+ if heb.board.is_active():
|
|
|
+ disable_wait_cursor()
|
|
|
+ self.do_status_readout()
|
|
|
+ return
|
|
|
+
|
|
|
+ logging.info("##### Activating Board #####")
|
|
|
+ heb.board.start_dma()
|
|
|
+ #cmd = ['pci', '--list-dma-engines']
|
|
|
+ #logging.info(heb.board.safe_call(cmd))
|
|
|
+ heb.board.write_pci('0x20001000', '0x9100')
|
|
|
+ heb.board.write_pci('0x05', '0x9040')
|
|
|
+ time.sleep(1.0)
|
|
|
+ dialog1 = PopupDialog("Switch On the power supply --> FIRST <--")
|
|
|
+ dialog1.exec_()
|
|
|
+ dialog1.deleteLater()
|
|
|
+
|
|
|
+ if not dialog1.get_return_value():
|
|
|
+ logging.error("Starting procedure canceled")
|
|
|
+ disable_wait_cursor()
|
|
|
+ return
|
|
|
+
|
|
|
+ logging.info("Switch ON T/Hs")
|
|
|
+ heb.board.write_pci('0x3C1', '0x9040')
|
|
|
+ logging.info("9040: " + str(heb.board.read_pci(1, '0x9040')[0]))
|
|
|
+ time.sleep(0.1)
|
|
|
+ dialog2 = PopupDialog("Switch On the power supply --> SECOND <--")
|
|
|
+ dialog2.exec_()
|
|
|
+ dialog2.deleteLater()
|
|
|
+
|
|
|
+ if not dialog2.get_return_value():
|
|
|
+ self.test_area.write("Starting procedure canceled")
|
|
|
+ disable_wait_cursor()
|
|
|
+ return
|
|
|
+
|
|
|
+ logging.info("Switch ON ADCs")
|
|
|
+ heb.board.write_pci('0x3F1', '0x9040')
|
|
|
+ logging.info("9040: " + str(heb.board.read_pci(1, '0x9040')[0]))
|
|
|
+ time.sleep(0.1)
|
|
|
+
|
|
|
+ heb.board.write_pci('0x3F0', '0x9040')
|
|
|
+ logging.info("9040: " + str(heb.board.read_pci(1, '0x9040')[0]))
|
|
|
+ time.sleep(1.0)
|
|
|
+
|
|
|
+ logging.info("Board started successfully!")
|
|
|
+ except heb.board.BoardError as e:
|
|
|
+ logging.error("Starting board failed: {}".format(str(e)))
|
|
|
+
|
|
|
+ disable_wait_cursor()
|
|
|
+ self.do_status_readout()
|
|
|
+
|
|
|
+
|
|
|
+ def on_calibrate_board(self):
|
|
|
+ enable_wait_cursor()
|
|
|
+ if not self._check_for_no_continuous_read():
|
|
|
+ disable_wait_cursor()
|
|
|
+ return
|
|
|
+ try:
|
|
|
+ logging.info("##### Calibrating Board #####")
|
|
|
+ logging.info("Removing Board Reset")
|
|
|
+ heb.board.write_pci('0x2000003f0', '0x9040')
|
|
|
+ time.sleep(0.5)
|
|
|
+
|
|
|
+ logging.info("SPI Fanout Programming...")
|
|
|
+ heb.board.write_pci('0x083', '0x9068')
|
|
|
+ time.sleep(0.5)
|
|
|
+ heb.board.write_pci('0x6a2', '0x9068')
|
|
|
+ time.sleep(0.5)
|
|
|
+
|
|
|
+ logging.info("PLL calibration start...")
|
|
|
+ logging.info("PLL Reset...")
|
|
|
+ heb.board.write_pci('0x80000000', '0x9060')
|
|
|
+ time.sleep(0.5)
|
|
|
+ logging.info("Set CH_0 (FPGA) clock FPGA ...")
|
|
|
+ heb.board.write_pci('0x00050000', '0x9060')
|
|
|
+ time.sleep(0.5)
|
|
|
+
|
|
|
+ logging.info("Set CH_3 clock fanout ...")
|
|
|
+ heb.board.write_pci('0x00050003', '0x9060')
|
|
|
+ time.sleep(0.5)
|
|
|
+
|
|
|
+ logging.info("Set CH_4 clock ADC 1 ...")
|
|
|
+ heb.board.write_pci('0x00050004', '0x9060')
|
|
|
+ time.sleep(0.5)
|
|
|
+
|
|
|
+ logging.info("Set CH_5 clock ADC 2 ...")
|
|
|
+ heb.board.write_pci('0x00050005', '0x9060')
|
|
|
+ time.sleep(0.5)
|
|
|
+
|
|
|
+ logging.info("Set CH_6 clock ADC 3 ...")
|
|
|
+ heb.board.write_pci('0x00050006', '0x9060')
|
|
|
+ time.sleep(0.5)
|
|
|
+
|
|
|
+ logging.info("Set CH_7 clock ADC 4 ...")
|
|
|
+ heb.board.write_pci('0x00050007', '0x9060')
|
|
|
+ time.sleep(0.5)
|
|
|
+
|
|
|
+ logging.info("Set R8 ...")
|
|
|
+ heb.board.write_pci('0x10000908', '0x9060')
|
|
|
+ time.sleep(0.5)
|
|
|
+
|
|
|
+ logging.info("Set R11 ...")
|
|
|
+ heb.board.write_pci('0x0082800B', '0x9060')
|
|
|
+ time.sleep(0.5)
|
|
|
+
|
|
|
+ logging.info("Set R13 ...")
|
|
|
+ heb.board.write_pci('0x029F400D', '0x9060')
|
|
|
+ time.sleep(0.5)
|
|
|
+
|
|
|
+ logging.info("Set R14 (F_out and Global_EN => ON) ...")
|
|
|
+ heb.board.write_pci('0x0830040E', '0x9060')
|
|
|
+ time.sleep(0.5)
|
|
|
+
|
|
|
+ logging.info("Set R15 ...")
|
|
|
+ heb.board.write_pci('0xD000100F', '0x9060')
|
|
|
+ time.sleep(0.5)
|
|
|
+
|
|
|
+ except heb.board.BoardError as e:
|
|
|
+ logging.error("Calibration failed: {}".format(str(e)))
|
|
|
+ disable_wait_cursor()
|
|
|
+ self.do_status_readout()
|
|
|
+ return
|
|
|
+
|
|
|
+ logging.info("Board Calibration successful!")
|
|
|
+ self.sync_board_button.setEnabled(True)
|
|
|
+ disable_wait_cursor()
|
|
|
+ self.do_status_readout()
|
|
|
+
|
|
|
+ def on_sync_board(self):
|
|
|
+ enable_wait_cursor()
|
|
|
+ if not self._check_for_no_continuous_read():
|
|
|
+ disable_wait_cursor()
|
|
|
+ return
|
|
|
+ try:
|
|
|
+ logging.info("##### Synchronize PLLs #####")
|
|
|
+ logging.info("Send the PLL sync signals ...")
|
|
|
+ heb.board.write_pci('0x1003f0', '0x9040')
|
|
|
+ time.sleep(1.0)
|
|
|
+ logging.info("Done!")
|
|
|
+ heb.board.write_pci('0x0003f0', '0x9040')
|
|
|
+ except heb.board.BoardError as e:
|
|
|
+ logging.error("Synchronization failed: {}".format(str(e)))
|
|
|
+ disable_wait_cursor()
|
|
|
+ self.do_status_readout()
|
|
|
+ return
|
|
|
+
|
|
|
+ logging.info("Board synchronization successful!")
|
|
|
+ self.set_defaults_button.setEnabled(True)
|
|
|
+ disable_wait_cursor()
|
|
|
+ self.do_status_readout()
|
|
|
+
|
|
|
+ def on_set_defaults(self):
|
|
|
+ enable_wait_cursor()
|
|
|
+ if not self._check_for_no_continuous_read():
|
|
|
+ disable_wait_cursor()
|
|
|
+ return
|
|
|
+ try:
|
|
|
+ logging.info("##### Setting default Values #####")
|
|
|
+ logging.info("Set Defaults delay value in the board...")
|
|
|
+ #Set_FPGA_clock_delay.sh 0
|
|
|
+ self.board_config.set_fpga_delay(self.board_config.get('fpga_delay'))
|
|
|
+ time.sleep(0.1)
|
|
|
+ #Set_Delay_chip.sh 16 16 16 16
|
|
|
+ factors = [self.board_config.get('chip_1_delay'), self.board_config.get('chip_2_delay'),
|
|
|
+ self.board_config.get('chip_3_delay'), self.board_config.get('chip_4_delay')]
|
|
|
+ self.board_config.set_chip_delay([0, 1, 2, 3], factors)
|
|
|
+ time.sleep(0.6)
|
|
|
+ #Set_TH_Delay.sh 12
|
|
|
+ self.board_config.set_th_delay(self.board_config.get('th_delay'))
|
|
|
+ time.sleep(0.1)
|
|
|
+ #Set_ADC_1_Delay.sh 4
|
|
|
+ self.board_config.set_adc_delay(0, self.board_config.get('adc_1_delay'))
|
|
|
+ time.sleep(0.1)
|
|
|
+ #Set_ADC_2_Delay.sh 4
|
|
|
+ self.board_config.set_adc_delay(1, self.board_config.get('adc_2_delay'))
|
|
|
+ time.sleep(0.1)
|
|
|
+ #Set_ADC_3_Delay.sh 4
|
|
|
+ self.board_config.set_adc_delay(2, self.board_config.get('adc_3_delay'))
|
|
|
+ time.sleep(0.1)
|
|
|
+ #Set_ADC_4_Delay.sh 4
|
|
|
+ self.board_config.set_adc_delay(3, self.board_config.get('adc_4_delay'))
|
|
|
+ time.sleep(0.1)
|
|
|
+
|
|
|
+ heb.board.write_pci('{0:08x}'.format(self.board_config.get('orbits_observe')), '0x9020')
|
|
|
+ self.number_of_orbits_spinbox.setValueSilent(self.board_config.get('orbits_observe'))
|
|
|
+
|
|
|
+ heb.board.write_pci('{0:08x}'.format(self.board_config.get('orbits_skip')), '0x9028')
|
|
|
+ self.number_of_skipped_orbits_spinbox.setValueSilent(self.board_config.get('orbits_skip'))
|
|
|
+
|
|
|
+ except heb.board.BoardError as e:
|
|
|
+ logging.error("Setting defaults failed: {}".format(str(e)))
|
|
|
+ disable_wait_cursor()
|
|
|
+ self.do_status_readout()
|
|
|
+ return
|
|
|
+
|
|
|
+ logging.info("Default values set successfully!")
|
|
|
+ self.set_adc_delay_spinboxes_active(True)
|
|
|
+ disable_wait_cursor()
|
|
|
+ self.do_status_readout()
|
|
|
+
|
|
|
+
|
|
|
+ def on_stop_board(self):
|
|
|
+ enable_wait_cursor()
|
|
|
+ if not self._check_for_no_continuous_read():
|
|
|
+ disable_wait_cursor()
|
|
|
+ return
|
|
|
+ try:
|
|
|
+ logging.info("##### Switching Off Board #####")
|
|
|
+ heb.board.write_pci('0x01', '0x9040')
|
|
|
+ heb.board.stop_dma()
|
|
|
+ cmd = ['pci', '--list-dma-engines']
|
|
|
+ logging.info(heb.board.safe_call(cmd))
|
|
|
+ time.sleep(0.5)
|
|
|
+ except heb.board.BoardError as e:
|
|
|
+ logging.error("Sequence failed: {}".format(str(e)))
|
|
|
+ disable_wait_cursor()
|
|
|
+ self.do_status_readout()
|
|
|
+ return
|
|
|
+
|
|
|
+ logging.info("Board switched off successfully!")
|
|
|
+ self.start_board_button.setEnabled(True)
|
|
|
+ self.calibrate_board_button.setEnabled(False)
|
|
|
+ self.sync_board_button.setEnabled(False)
|
|
|
+ self.set_defaults_button.setEnabled(False)
|
|
|
+ self.set_adc_delay_spinboxes_active(False)
|
|
|
+ disable_wait_cursor()
|
|
|
+ self.do_status_readout()
|
|
|
+
|
|
|
+ def on_soft_reset(self):
|
|
|
+ enable_wait_cursor()
|
|
|
+ if not self._check_for_no_continuous_read():
|
|
|
+ disable_wait_cursor()
|
|
|
+ return
|
|
|
+ try:
|
|
|
+ logging.info("Soft-Resetting Board ...")
|
|
|
+ heb.board.write_pci('0x1', '0x9040', hex_mask='0x1')
|
|
|
+ time.sleep(1)
|
|
|
+ heb.board.write_pci('0x0', '0x9040', hex_mask='0x1')
|
|
|
+ except heb.board.BoardError as e:
|
|
|
+ logging.error("Sequence failed: {}".format(str(e)))
|
|
|
+ disable_wait_cursor()
|
|
|
+ self.do_status_readout()
|
|
|
+ return
|
|
|
+
|
|
|
+ logging.info("Soft-Reset successful.")
|
|
|
+ disable_wait_cursor()
|
|
|
+ self.do_status_readout()
|
|
|
+ return
|
|
|
+
|
|
|
+ def disable_acquisition_input(self):
|
|
|
+ self.start_button.setEnabled(False)
|
|
|
+ self.acquire_button.setEnabled(False)
|
|
|
+
|
|
|
+ def enable_acquisition_input(self):
|
|
|
+ self.start_button.setEnabled(True)
|
|
|
+ self.acquire_button.setEnabled(True)
|
|
|
+
|
|
|
+ def set_adc_delay_spinboxes_active(self, value=True):
|
|
|
+ self.th_delay_spinbox.setEnabled(value)
|
|
|
+ self.adc_1_delay_spinbox.setEnabled(value)
|
|
|
+ self.adc_2_delay_spinbox.setEnabled(value)
|
|
|
+ self.adc_3_delay_spinbox.setEnabled(value)
|
|
|
+ self.adc_4_delay_spinbox.setEnabled(value)
|
|
|
+
|
|
|
+ def on_single_read(self):
|
|
|
+ self.continuous_read_checkbox.setEnabled(False)
|
|
|
+ self.disable_acquisition_input()
|
|
|
+ self.read_data_and_safe()
|
|
|
+ self.enable_acquisition_input()
|
|
|
+ self.continuous_read_checkbox.setEnabled(True)
|
|
|
+
|
|
|
+ def _set_continuous_read_active(self):
|
|
|
+ self.start_button.setEnabled(False)
|
|
|
+ self.acquire_button.setEnabled(False)
|
|
|
+ self.scan_button.setEnabled(False)
|
|
|
+ self.continuous_read = True
|
|
|
+
|
|
|
+ def _set_continuous_read_inactive(self):
|
|
|
+ if self.continuous_read:
|
|
|
+ self.continuous_read = False
|
|
|
+ self.timer.stop()
|
|
|
+ self.start_button.setEnabled(True)
|
|
|
+ self.acquire_button.setEnabled(True)
|
|
|
+ self.scan_button.setEnabled(True)
|
|
|
+
|
|
|
+ def on_continuous_read(self):
|
|
|
+ if self.continuous_read_checkbox.isChecked():
|
|
|
+ self._set_continuous_read_active()
|
|
|
+ self.do_continuous_read()
|
|
|
+ else:
|
|
|
+ self._set_continuous_read_inactive()
|
|
|
+
|
|
|
+ def do_continuous_read(self):
|
|
|
+ self.timer = QtCore.QTimer()
|
|
|
+ logging.info("Start continuous read")
|
|
|
+
|
|
|
+ def continuous_read_step():
|
|
|
+ if self.continuous_read:
|
|
|
+ self.read_data()
|
|
|
+ self.timer.start(self.continuous_read_interval_spinbox.value())
|
|
|
+ else:
|
|
|
+ self.timer.stop()
|
|
|
+
|
|
|
+ self.timer.timeout.connect(continuous_read_step)
|
|
|
+ self.timer.start(self.continuous_read_interval_spinbox.value())
|
|
|
+
|
|
|
+ def read_and_update(self, read_func, *args):
|
|
|
+ enable_wait_cursor()
|
|
|
+
|
|
|
+ # Remove all references as soon as possible
|
|
|
+ if self.data:
|
|
|
+ del self.data
|
|
|
+ self.data = None
|
|
|
+
|
|
|
+ header = self.advanced_interface.control_table.get_bit(28)
|
|
|
+ self.data = read_func(*args, force=self.args.force_read, header=header, cache=self.args.cache_data)
|
|
|
+
|
|
|
+ if heb.io.is_data_consistent(self.data):
|
|
|
+ self.data_consistency_led.set_color((0, 190, 0))
|
|
|
+ else:
|
|
|
+ self.data_consistency_led.set_color((255, 0, 0))
|
|
|
+ if read_func is heb.io.read_from_file:
|
|
|
+ self.error_text_area.write(str(args[0]))
|
|
|
+
|
|
|
+ disable_wait_cursor()
|
|
|
+ frm = self.from_spinbox.value()
|
|
|
+ to = self.to_spinbox.value()
|
|
|
+ self.canvas.update_figure(self.opt_widgets, self.data, frm, to)
|
|
|
+
|
|
|
+ def read_and_update_data_from_file(self, filename):
|
|
|
+ self.read_and_update(heb.io.read_from_file, str(filename))
|
|
|
+
|
|
|
+ def read_and_update_data_from_string(self, raw_data):
|
|
|
+ self.read_and_update(heb.io.read_from_string, raw_data)
|
|
|
+
|
|
|
+ def read_data_and_safe(self):
|
|
|
+ now = time.time()
|
|
|
+ filename = '{:0.3f}.out'.format(now)
|
|
|
+
|
|
|
+ try:
|
|
|
+ simulate = self.simulate_checkbox.isChecked()
|
|
|
+ heb.board.acquire_data(filename, simulate=simulate, duration=2.0)
|
|
|
+ #heb.board.run_status('/home/heb/StatusComparison.sh')
|
|
|
+ self.read_and_update_data_from_file(filename)
|
|
|
+ except heb.board.BoardError as e:
|
|
|
+ logging.error("Reading failed: {}".format(str(e)))
|
|
|
+
|
|
|
+ def read_data(self):
|
|
|
+ try:
|
|
|
+ if self.simulate_checkbox.isChecked():
|
|
|
+ heb.board.start_pilot_bunch_emulator()
|
|
|
+
|
|
|
+ heb.board.start_acquisition()
|
|
|
+ heb.board.wait_for_revolutions()
|
|
|
+ heb.board.stop_acquisition()
|
|
|
+ heb.board.enable_transfer()
|
|
|
+ cmd = ['pci', '-r', 'dma0', '-o', '/dev/stdout', '--multipacket']
|
|
|
+ data_raw = heb.board.safe_call(cmd)
|
|
|
+ heb.board.flush_dma()
|
|
|
+ # Sadly, the PCI software also outputs the Information, that it has
|
|
|
+ # written data to the desired file. This is recorded by our
|
|
|
+ # software and needs to be removed, so we split the returned string
|
|
|
+ # at the 'Writting data to file blah, blah" Btw: YES this
|
|
|
+ # 'Writting' is actually correct. No, i don't know why it is
|
|
|
+ # grammatically wrong in the first place :D
|
|
|
+ data_split = data_raw.split('Writting')[0]
|
|
|
+ self.read_and_update_data_from_string(data_split)
|
|
|
+ except heb.board.BoardError as e:
|
|
|
+ logging.error("Reading failed: {}".format(str(e)))
|
|
|
+
|
|
|
+ # return data
|
|
|
+
|
|
|
+ def iterate_spectrograms(self, path):
|
|
|
+ if not os.path.isdir("./"+str(path)):
|
|
|
+ return
|
|
|
+
|
|
|
+ transform = self.data.fft(1, frm=0, to=-1)
|
|
|
+ for i in range(heb.BUNCHES_PER_TURN-1):
|
|
|
+ filename = os.path.join(".", str(path), "%i.hsp" % i)
|
|
|
+ write_header = False
|
|
|
+ if not os.path.isfile(filename):
|
|
|
+ write_header = True
|
|
|
+ f = open(filename, 'ab')
|
|
|
+ if write_header:
|
|
|
+ f.write("#hsp\n") # heb spectrogram magic number
|
|
|
+ #f.write(b'\x40') # 64bit
|
|
|
+ #f.write('64') # 64bit
|
|
|
+ #f.write(sytruct.pack('>I', self.number_of_skipped_orbits_spinbox.value()))
|
|
|
+ f.write("#"+str(self.number_of_skipped_orbits_spinbox.value()))
|
|
|
+ f.write("\n")
|
|
|
+ line = transform[i,:]
|
|
|
+ # f.write(struct.pack('>I', len(line)))
|
|
|
+ #f.write(str(len(line)))
|
|
|
+ f.write('{:0.3f} '.format(time.time()))
|
|
|
+ #data = pArr('d', line)
|
|
|
+ #data.tofile(f)
|
|
|
+ for e in line:
|
|
|
+ f.write("%s "%np.absolute(e))
|
|
|
+ f.write("\n")
|
|
|
+ f.close()
|
|
|
+
|
|
|
+ def stop_acquisition(self):
|
|
|
+ self.timer.stop()
|
|
|
+ self.enable_acquisition_input()
|
|
|
+ self.continuous_read_checkbox.setEnabled(True)
|
|
|
+ self.acquisition_progressbar.reset()
|
|
|
+
|
|
|
+ def on_acquire(self):
|
|
|
+ self.disable_acquisition_input()
|
|
|
+ self.continuous_read_checkbox.setEnabled(False)
|
|
|
+ self.stop_button.setEnabled(True)
|
|
|
+
|
|
|
+ self.timer = QtCore.QTimer()
|
|
|
+ num_acquisitions = self.acquisitions_spinbox.value()
|
|
|
+ self.acquisition_progressbar.setRange(1, num_acquisitions)
|
|
|
+
|
|
|
+ spectrogram_dir = "./spectrograms_{:0.3f}".format(time.time())
|
|
|
+ if self.spectrogram_checkbox.isChecked():
|
|
|
+ os.mkdir(spectrogram_dir)
|
|
|
+
|
|
|
+ # We increase already once because we do a single acquisition before the
|
|
|
+ # timer is started, otherwise we have to wait until the timer fires the
|
|
|
+ # first time.
|
|
|
+ self.current_acquisition = 1
|
|
|
+ self.read_data_and_safe()
|
|
|
+ if self.spectrogram_checkbox.isChecked():
|
|
|
+ self.iterate_spectrograms(spectrogram_dir)
|
|
|
+
|
|
|
+ def on_timeout():
|
|
|
+ if self.current_acquisition < num_acquisitions:
|
|
|
+ self.current_acquisition += 1
|
|
|
+ self.acquisition_progressbar.setValue(self.current_acquisition)
|
|
|
+ self.read_data_and_safe()
|
|
|
+ self.iterate_spectrograms(spectrogram_dir)
|
|
|
+ else:
|
|
|
+ self.stop_acquisition()
|
|
|
+
|
|
|
+ self.timer.timeout.connect(on_timeout)
|
|
|
+ self.timer.start(self.wait_time_spinbox.value() * 1000)
|
|
|
+
|
|
|
+ def on_stop(self):
|
|
|
+ self.stop_acquisition()
|
|
|
+
|
|
|
+ def on_scan(self):
|
|
|
+ c_frm = self.time_scan_coarse_from.value()
|
|
|
+ c_to = self.time_scan_coarse_to.value()
|
|
|
+ if c_frm > c_to:
|
|
|
+ logging.info('Coarse Scan Interval is invalid: (%i > %i)' % (c_frm, c_to))
|
|
|
+ return
|
|
|
+ f_frm = self.time_scan_fine_from.value()
|
|
|
+ f_to = self.time_scan_fine_to.value()
|
|
|
+ if f_frm > f_to:
|
|
|
+ logging.info('Fine Scan Interval is invalid: (%i > %i)' % (f_frm, f_to))
|
|
|
+ return
|
|
|
+
|
|
|
+ self.disable_acquisition_input()
|
|
|
+ self.continuous_read_checkbox.setEnabled(False)
|
|
|
+ self.scan_button.setEnabled(False)
|
|
|
+ self.set_adc_delay_spinboxes_active(False)
|
|
|
+
|
|
|
+ self.canvas.figure.clear()
|
|
|
+
|
|
|
+ th_old = self.board_config.get('th_delay')
|
|
|
+ adc_1_old = self.board_config.get('adc_1_delay')
|
|
|
+ adc_2_old = self.board_config.get('adc_2_delay')
|
|
|
+ adc_3_old = self.board_config.get('adc_3_delay')
|
|
|
+ adc_4_old = self.board_config.get('adc_4_delay')
|
|
|
+
|
|
|
+ minimum = [None, None, None, None]
|
|
|
+ maximum = np.zeros((4, 3))
|
|
|
+ heatmap = np.zeros((4, (f_to - f_frm + 1), (c_to - c_frm + 1)))
|
|
|
+ self.timescan_progressbar.setRange(1, ((f_to - f_frm) + 1) * ((c_to - c_frm) + 1))
|
|
|
+ self.stop_requested = False
|
|
|
+
|
|
|
+ def time_scan_stop():
|
|
|
+ self.stop_requested = True
|
|
|
+ self.board_config.set_delay(th_old)
|
|
|
+ self.board_config.set_chip_delay([0, 1, 2, 3], [adc_1_old, adc_2_old, adc_3_old, adc_4_old])
|
|
|
+ self.enable_acquisition_input()
|
|
|
+ self.continuous_read_checkbox.setEnabled(True)
|
|
|
+ self.scan_button.setEnabled(True)
|
|
|
+ self.scan_stop_button.setEnabled(False)
|
|
|
+ self.set_adc_delay_spinboxes_active(True)
|
|
|
+ self.timescan_progressbar.reset()
|
|
|
+
|
|
|
+ self.scan_stop_button.clicked.connect(time_scan_stop)
|
|
|
+ self.scan_stop_button.setEnabled(True)
|
|
|
+
|
|
|
+ c_step = 0
|
|
|
+ for coarse in range(c_frm, c_to + 1):
|
|
|
+ try:
|
|
|
+ self.board_config.set_delay(coarse)
|
|
|
+ except heb.board.BoardError as e:
|
|
|
+ QtGui.QMessageBox.critical(self, "Board communication", str(e))
|
|
|
+ time_scan_stop()
|
|
|
+ return
|
|
|
+
|
|
|
+ f_step = 0
|
|
|
+ for fine in range(f_frm, f_to + 1):
|
|
|
+ self.board_config.set_chip_delay([0, 1, 2, 3], [fine, fine, fine, fine])
|
|
|
+
|
|
|
+ try:
|
|
|
+ if self.simulate_checkbox.isChecked() is True:
|
|
|
+ heb.board.start_pilot_bunch_emulator()
|
|
|
+
|
|
|
+ heb.board.start_acquisition()
|
|
|
+ heb.board.wait_for_revolutions()
|
|
|
+ heb.board.stop_acquisition()
|
|
|
+ heb.board.enable_transfer()
|
|
|
+ cmd = ['pci', '-r', 'dma0', '-o', '/dev/stdout', '--multipacket']
|
|
|
+ data_raw = heb.board.safe_call(cmd)
|
|
|
+ heb.board.flush_dma()
|
|
|
+ #Sadly, the PCI software also outputs the Information, that it has written data to the desired file.
|
|
|
+ #This is recorded by our software and needs to be removed, so we split the returned string at the
|
|
|
+ # "Writting data to file blah, blah" string.
|
|
|
+ #Btw: YES this 'Writting' is actually correct. Its a typo in the 'pci' driver...
|
|
|
+ data_split = data_raw.split('Writting')[0]
|
|
|
+ data = heb.io.read_from_string(data_split, force=True, cache=False)
|
|
|
+ except heb.board.BoardError as e:
|
|
|
+ QtGui.QMessageBox.critical(self, "Board communication", str(e))
|
|
|
+ time_scan_stop()
|
|
|
+ return
|
|
|
+
|
|
|
+ for adc in range(4):
|
|
|
+ buckets = data.array[:, adc:adc+1].reshape(-1)
|
|
|
+ heatmap[adc, f_step, c_step] = float(buckets.sum())/buckets.shape[0]
|
|
|
+ if heatmap[adc, f_step, c_step] > maximum[adc, 0]:
|
|
|
+ maximum[adc, 0] = heatmap[adc, f_step, c_step]
|
|
|
+ maximum[adc, 1] = coarse
|
|
|
+ maximum[adc, 2] = fine
|
|
|
+ if minimum[adc] is None or minimum[adc] > heatmap[adc, f_step, c_step]:
|
|
|
+ minimum[adc] = heatmap[adc, f_step, c_step]
|
|
|
+
|
|
|
+ self.timescan_progressbar.setValue(((c_step * (f_to - f_frm + 1)) + f_step) + 1)
|
|
|
+ #GUI is blocked in our tight loop. Give it an opportunity to handle events
|
|
|
+ QtGui.QApplication.processEvents()
|
|
|
+ if self.stop_requested is True:
|
|
|
+ # Time Scan Stop is already performed by button press. Nothing else to do but leave
|
|
|
+ return
|
|
|
+ f_step += 1
|
|
|
+ c_step += 1
|
|
|
+
|
|
|
+ self.canvas.figure.clear()
|
|
|
+ self.canvas.figure.suptitle("Timing Analysis")
|
|
|
+
|
|
|
+ if True:
|
|
|
+ now = time.time()
|
|
|
+ filename = 'timescan_{:0.3f}.out'.format(now)
|
|
|
+ f = open(filename, 'wr')
|
|
|
+
|
|
|
+ for adc in range(4):
|
|
|
+ if True:
|
|
|
+ f.write("#ADC_%s\n" % adc)
|
|
|
+ for coarse, curr_cor in enumerate(np.transpose(heatmap[adc])):
|
|
|
+ for fine, value in enumerate(curr_cor):
|
|
|
+ f.write("%i;%i;%f\n" % ((coarse+c_frm), (fine+f_frm), value))
|
|
|
+ f.write('\n')
|
|
|
+
|
|
|
+ axis = self.canvas.figure.add_subplot(2, 2, adc+1)
|
|
|
+ image = axis.imshow(heatmap[adc, :, :], interpolation='nearest', aspect='auto')
|
|
|
+ axis.set_xticks(range((c_to - c_frm) + 1))
|
|
|
+ axis.set_xticklabels(range(c_frm, c_to + 1))
|
|
|
+ axis.set_yticks(range((f_to - f_frm) + 1))
|
|
|
+ axis.set_yticklabels(range(f_frm, f_to + 1))
|
|
|
+ axis.invert_yaxis()
|
|
|
+ if adc > 1:
|
|
|
+ axis.set_xlabel('Coarse Delay')
|
|
|
+ if adc is 0 or adc is 2:
|
|
|
+ axis.set_ylabel('Fine Delay')
|
|
|
+ axis.set_title("ADC_%i, C:%i, F:%i" % (adc+1, maximum[adc, 1], maximum[adc, 2]))
|
|
|
+
|
|
|
+ if True:
|
|
|
+ f.close()
|
|
|
+ f = open(filename+'.gnuplot', 'wr')
|
|
|
+ f.write('set datafile separator ";"\n')
|
|
|
+ f.write('set multiplot layout 2,2\n')
|
|
|
+ f.write('unset key\n')
|
|
|
+ for i in range(4):
|
|
|
+ f.write('set label 1 "ADC_%i" at graph 0.7,0.95 font ",8"\n'%(i+1))
|
|
|
+ f.write('plot "%s" every :::%i::%i using 3 with lines\n'%(filename, i, i))
|
|
|
+ f.write('unset multiplot\n')
|
|
|
+ f.close()
|
|
|
+
|
|
|
+
|
|
|
+ bar_axis = self.canvas.figure.add_axes([0.85, 0.15, 0.05, 0.7])
|
|
|
+ self.canvas.figure.subplots_adjust(right=0.8)
|
|
|
+ self.canvas.figure.colorbar(image, cax=bar_axis)
|
|
|
+ self.canvas.draw()
|
|
|
+ time_scan_stop()
|
|
|
+
|
|
|
+ def on_to_changed(self, value):
|
|
|
+ if value > 0:
|
|
|
+ self.from_spinbox.setMaximum(value - 1)
|
|
|
+
|
|
|
+ self.on_option_change()
|
|
|
+
|
|
|
+ def on_option_change(self):
|
|
|
+ frm = self.from_spinbox.value()
|
|
|
+ to = self.to_spinbox.value()
|
|
|
+
|
|
|
+ enable_wait_cursor()
|
|
|
+ self.canvas.update_figure(self.opt_widgets, self.data, frm, to)
|
|
|
+ disable_wait_cursor()
|
|
|
+
|
|
|
+ def on_adc_delay_changed(self):
|
|
|
+ try:
|
|
|
+ self.board_config.set_delay(self.th_delay_spinbox.value())
|
|
|
+ factors = [self.adc_1_delay_spinbox.value(), self.adc_2_delay_spinbox.value(),
|
|
|
+ self.adc_3_delay_spinbox.value(), self.adc_4_delay_spinbox.value()]
|
|
|
+ self.board_config.set_chip_delay([0, 1, 2, 3], factors)
|
|
|
+ except heb.board.BoardError as e:
|
|
|
+ logging.error("ADC fine delay failed: {}".format(str(e)))
|
|
|
+ self.do_status_readout()
|
|
|
+ return
|
|
|
+
|
|
|
+ def on_number_of_orbits_changed(self):
|
|
|
+ number = self.number_of_orbits_spinbox.value()
|
|
|
+ hex_str = hex(number)
|
|
|
+ try:
|
|
|
+ heb.board.write_pci(hex_str, '0x9020')
|
|
|
+ except heb.board.BoardError as e:
|
|
|
+ logging.error("Setting orbits failed: {}".format(str(e)))
|
|
|
+ self.do_status_readout()
|
|
|
+ return
|
|
|
+
|
|
|
+ def on_number_of_skipped_orbits_changed(self):
|
|
|
+ number = self.number_of_skipped_orbits_spinbox.value()
|
|
|
+ hex_str = hex(number)
|
|
|
+ try:
|
|
|
+ heb.board.write_pci(hex_str, '0x9028')
|
|
|
+ except heb.board.BoardError as e:
|
|
|
+ logging.error("Could not set ob skipped orbits due to an error!")
|
|
|
+ self.do_status_readout()
|
|
|
+ return
|
|
|
+
|
|
|
+ def on_open(self):
|
|
|
+ filename = QtGui.QFileDialog.getOpenFileName(self, 'Open Datafile', '')
|
|
|
+ if filename:
|
|
|
+ self.read_and_update_data_from_file(filename)
|
|
|
+
|
|
|
+ def on_open_config(self):
|
|
|
+ filename = QtGui.QFileDialog.getOpenFileName(self, 'Open Configuration', '')
|
|
|
+ if filename:
|
|
|
+ self.board_config.load_config(filename)
|
|
|
+ self.on_set_defaults()
|
|
|
+
|
|
|
+ def on_save_config(self):
|
|
|
+ filename = QtGui.QFileDialog.getSaveFileName(self, 'Save Configuration', '')
|
|
|
+ if filename:
|
|
|
+ self.board_config.save_config(filename)
|
|
|
+
|
|
|
+ def on_close(self):
|
|
|
+ self.close()
|
|
|
+
|
|
|
+
|
|
|
+def main():
|
|
|
+ parser = argparse.ArgumentParser()
|
|
|
+ parser.add_argument('-i', '--input', metavar='FILE', type=str,
|
|
|
+ help="HEB file")
|
|
|
+ parser.add_argument('-c', '--config', metavar='FILE', type=str,
|
|
|
+ help="Board Configuration File")
|
|
|
+ parser.add_argument('--force-read', action='store_true',
|
|
|
+ help="Ignore cache and force re-computation.")
|
|
|
+ parser.add_argument('--check', action='store_true',
|
|
|
+ help="Check contents of file before proceeding")
|
|
|
+ parser.add_argument('--cache-data', action='store_true',
|
|
|
+ help="Save already processed NumPy arrays")
|
|
|
+ parser.add_argument('-v', '--verbose', action='store_true',
|
|
|
+ help="Increase verbosity.")
|
|
|
+
|
|
|
+ args = parser.parse_args()
|
|
|
+
|
|
|
+ if args.verbose:
|
|
|
+ logging.basicConfig(level=logging.DEBUG)
|
|
|
+ else:
|
|
|
+ logging.basicConfig(level=logging.INFO)
|
|
|
+
|
|
|
+ app = QtGui.QApplication(sys.argv)
|
|
|
+
|
|
|
+ window = ApplicationWindow(args)
|
|
|
+ window.show()
|
|
|
+ try:
|
|
|
+ window.do_status_readout()
|
|
|
+ except heb.board.BoardError as e:
|
|
|
+ QtGui.QMessageBox.critical(window, "Board Communication Error",
|
|
|
+ "Could not communicate with the Board!\nError was: 'pci' " + str(e)
|
|
|
+ + "\nMake sure the HEB Board is connected correctly and run 'Check Status'.")
|
|
|
+ sys.exit(app.exec_())
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == '__main__':
|
|
|
+ main()
|