timingWidget.py 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852
  1. """
  2. This Module Is the Timingsettings subWindow.
  3. """
  4. import logging
  5. from PyQt4 import QtGui, QtCore
  6. import pyqtgraph as pg
  7. import numpy as np
  8. from ..base import kcgwidget as kcgw
  9. from ..base.backend import board
  10. from ..base.backend.board import available_boards
  11. from ..base import backendinterface as bif
  12. from ..base.groupedelements import Elements
  13. from ..base.globals import glob as global_objects
  14. from .. import config
  15. from ..base import storage
  16. tr = kcgw.tr
  17. __widget_id__ = None
  18. __timing_plot_widget_id__ = {}
  19. __timing_plot_widget__ = None
  20. BUNCHES_PER_TURN = config.bunches_per_turn
  21. class timingPlotWidget(kcgw.KCGWidgets):
  22. """
  23. The Timescan result plot subwindow.
  24. """
  25. def __init__(self, unique_id, board_id, parent=None):
  26. """
  27. Initialises the timing plot window
  28. :param unique_id: the id for this window
  29. :param parent: parent object
  30. :return: -
  31. """
  32. super(timingPlotWidget, self).__init__()
  33. self.id = unique_id
  34. self.par = parent
  35. self.board_id = board_id
  36. # ------[ Variable declaration ]------------
  37. self.plot_type = "colour" # The Plot Type for this window (Changeable in this window)
  38. self.xrange = (None, None)
  39. self.yrange = (None, None)
  40. self.x = None # Number of Ticks in the x-direction This is set by the gui programmatically
  41. self.y = None # Number of Ticks in the y-direction This is set by the gui programmatically
  42. self.data = None # Data to plot
  43. self.levels = None # Levels for colors
  44. self.inputsSet = False # If Inputs (Timing) are set
  45. # -------[ Create Plot Elements ]-----------
  46. self.adc1_plot_widget = pg.PlotWidget(title="ADC 1")
  47. self.adc2_plot_widget = pg.PlotWidget(title="ADC 2")
  48. self.adc3_plot_widget = pg.PlotWidget(title="ADC 3")
  49. self.adc4_plot_widget = pg.PlotWidget(title="ADC 4")
  50. # -------[ Create structure ]----------
  51. self.outerLayout = QtGui.QVBoxLayout() # Outermost layout of this window
  52. self.setLayout(self.outerLayout)
  53. self.headerLayout = QtGui.QHBoxLayout() # Layout for the header (including the switch to change plots)
  54. self.outerLayout.addLayout(self.headerLayout)
  55. self.layout = QtGui.QGridLayout() # Main Layout to hold the most elements in this window
  56. # ------[ Create Some Elements ]-----------
  57. self.position_label = self.createLabel("")
  58. self.position_label.setAlignment(QtCore.Qt.AlignCenter)
  59. self.plot_type_switcher = self.createSwitch(connect=self.switch)
  60. self.zero_correction_switcher = self.createSwitch(connect=self.zero_correction)
  61. self.uses_zero_correction = False
  62. self.zero_correction_values = None
  63. # ------[ Add Elements to various layouts ]--------------
  64. self.headerLayout.addWidget(self.position_label)
  65. self.headerLayout.addWidget(self.createLabel("Colour Plot"))
  66. self.headerLayout.addWidget(self.plot_type_switcher)
  67. self.headerLayout.addWidget(self.createLabel("Line Plot"))
  68. t = QtGui.QFrame()
  69. t.setFrameShape(QtGui.QFrame.VLine)
  70. t.setFrameShadow(QtGui.QFrame.Sunken)
  71. self.headerLayout.addWidget(t)
  72. self.headerLayout.addWidget(self.createLabel("RawData"))
  73. self.headerLayout.addWidget(self.zero_correction_switcher)
  74. self.headerLayout.addWidget(self.createLabel("Corrected"))
  75. self.headerLayout.setAlignment(QtCore.Qt.AlignRight)
  76. self.layout.addWidget(self.adc1_plot_widget, 0, 0)
  77. self.layout.addWidget(self.adc2_plot_widget, 0, 1)
  78. self.layout.addWidget(self.adc3_plot_widget, 1, 0)
  79. self.layout.addWidget(self.adc4_plot_widget, 1, 1)
  80. self.outerLayout.addLayout(self.layout)
  81. self.setWindowTitle(tr("Heading", "Timescan Results")+" "+available_boards.get_board_name_from_id(board_id))
  82. # Initially show the colour plot
  83. self.colour_plot()
  84. def switch(self):
  85. """
  86. Switch Plot types
  87. :return: -
  88. """
  89. if self.plot_type == 'colour':
  90. self.remove_colour_plot()
  91. self.line_plot()
  92. self.plot()
  93. else:
  94. self.adc1_plot_widget.plotItem.clearPlots()
  95. self.adc2_plot_widget.plotItem.clearPlots()
  96. self.adc3_plot_widget.plotItem.clearPlots()
  97. self.adc4_plot_widget.plotItem.clearPlots()
  98. self.colour_plot()
  99. self.plot()
  100. def zero_correction(self):
  101. """
  102. Toggle Zero (50Ohm) Correction
  103. """
  104. # Note: when selecting a slice the upper bound has to be included since that's how the timescan works. Python array slicing does not include the upper bound => +1
  105. if self.uses_zero_correction:
  106. self.uses_zero_correction = False
  107. self.data += self.zero_correction_values[:, self.xrange[0]:self.xrange[1]+1, self.yrange[0]:self.yrange[1]+1]
  108. else:
  109. self.uses_zero_correction = True
  110. if self.zero_correction_values is None:
  111. # Adjust for closed measurement of 0 (Measured using a 50 ohm impedance
  112. # This will read the complete file into memory. Could be optimized to read only necessary values
  113. # f = file(config.fifty_ohm_timescan_datafile)
  114. f = file(storage.storage.offset_correction)
  115. c_adc = -1 # -1 because first adc will have #ADC0 and this will increment c_adc
  116. self.zero_correction_values = np.zeros((4, 32, 16)) # we know there are 4 adcs, 16 coarse delays and 32 fine delays
  117. all_check = np.zeros((4, 32, 16), dtype=np.bool)
  118. for line in f:
  119. line = line.strip()
  120. if line == "": continue # skip empty lines
  121. if line.startswith("#"):
  122. c_adc += 1 # c_adc = int(line.split("_")[-1])
  123. continue
  124. coarse, fine, value = line.split(";")
  125. self.zero_correction_values[c_adc, int(fine), int(coarse)] = value
  126. all_check[c_adc, int(fine), int(coarse)] = True
  127. if False in all_check:
  128. logging.warn("Read 50Ohm timescan for offset correction does not include all delay values")
  129. self.data -= self.zero_correction_values[:, self.xrange[0]:self.xrange[1]+1, self.yrange[0]:self.yrange[1]+1]
  130. maxima = np.zeros((4, 3))
  131. for adc, a in enumerate(self.data):
  132. f, c = np.unravel_index(np.argmax(a), a.shape)
  133. maxima[adc] = [a[f, c], c+self.yrange[0], f+self.xrange[0]]
  134. self.plot(maxima=maxima)
  135. def colour_plot(self):
  136. """
  137. Initialize Color Plot
  138. :return: -
  139. """
  140. self.plot_type = "colour"
  141. self.adc1 = pg.ImageItem()
  142. self.adc2 = pg.ImageItem()
  143. self.adc3 = pg.ImageItem()
  144. self.adc4 = pg.ImageItem()
  145. self.adc1.mouseClickEvent = lambda x: self.click("ADC 1", x)
  146. self.adc2.mouseClickEvent = lambda x: self.click("ADC 2", x)
  147. self.adc3.mouseClickEvent = lambda x: self.click("ADC 3", x)
  148. self.adc4.mouseClickEvent = lambda x: self.click("ADC 4", x)
  149. def xtickStrings(values, scale, spacing):
  150. """
  151. Generate the strings for ticks
  152. """
  153. return [str(i + self.xrange[0]) for i in range(int(values[0]), int(values[-1]),int(spacing))]
  154. def ytickStrings(values, scale, spacing):
  155. """
  156. Generate the strings for ticks
  157. """
  158. return [str(int(i) + self.yrange[0]) for i in values]
  159. # ----------[ Configure and add color plots for ADC1 ]----------
  160. for i in range(1, 5):
  161. getattr(self, "adc{}_plot_widget".format(i)).enableAutoRange() #axis=getattr(self, "adc{}_plot_widget".format(i)).getViewBox().YAxis)
  162. getattr(self, "adc{}_plot_widget".format(i)).getPlotItem().setLabel("left", tr("Heading", "Coarse delay"))
  163. getattr(self, "adc{}_plot_widget".format(i)).getPlotItem().setLabel("bottom", tr("Heading", "Fine delay"))
  164. getattr(self, "adc{}_plot_widget".format(i)).plotItem.addItem(getattr(self, "adc"+str(i)))
  165. bax = getattr(self, "adc{}_plot_widget".format(i)).plotItem.getAxis('bottom')
  166. lax = getattr(self, "adc{}_plot_widget".format(i)).plotItem.getAxis('left')
  167. bax.tickSpacing = self.spacing
  168. bax.tickStrings = xtickStrings
  169. lax.setTickSpacing(levels=[(1, 1.5),])
  170. lax.tickStrings = ytickStrings
  171. pos = np.array([0, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 1])
  172. color = np.array([[0, 0, 255, 255], [0, 255, 255, 255], [0, 255, 0, 255], [130, 255, 0, 255], [255, 255, 0, 255], [255, 180, 0, 255], [255, 100, 0, 255], [255, 0, 0, 255]])
  173. cmap = pg.ColorMap(pos, color)
  174. lut = cmap.getLookupTable(0.0, 1.0, 256)
  175. self.adc1.setLookupTable(lut)
  176. self.adc2.setLookupTable(lut)
  177. self.adc3.setLookupTable(lut)
  178. self.adc4.setLookupTable(lut)
  179. def remove_colour_plot(self):
  180. """
  181. Remove the color plot elements
  182. :return: -
  183. """
  184. self.adc1_plot_widget.plotItem.removeItem(self.adc1)
  185. self.adc2_plot_widget.plotItem.removeItem(self.adc2)
  186. self.adc3_plot_widget.plotItem.removeItem(self.adc3)
  187. self.adc4_plot_widget.plotItem.removeItem(self.adc4)
  188. def line_plot_axis_strings(self, values, scale, spacing):
  189. """
  190. Plot to format the strings for the line plot
  191. This is used to override the default tickStrings method of pyqtgraph.AxisItem
  192. :param values: See pyqtgraph.AxisItem.tickStrings
  193. :param scale: See pyqtgraph.AxisItem.tickStrings
  194. :param spacing: See pyqtgraph.AxisItem.tickStrings
  195. :return: See pyqtgraph.AxisItem.tickStrings
  196. """
  197. coarses = [self.yrange[0] + i//(self.x) for i in values]
  198. fines = [self.xrange[0] + i%(self.x) for i in values]
  199. return [str(int(c))+"\n"+str(int(f)) for c, f in zip(coarses, fines)]
  200. def spacing(self, minVal, maxVal, size):
  201. """
  202. Calculate spacing between ticks
  203. This is used to override the default tickSpacing method of pyqtgraph.AxisItem
  204. :param minVal: See pyqtgraph.AxisItem.tickSpacing
  205. :param maxVal: See pyqtgraph.AxisItem.tickSpacing
  206. :param size: See pyqtgraph.AxisItem.tickSpacing
  207. :return: See pyqtgraph.AxisItem.tickSpacing
  208. """
  209. if maxVal - minVal < 10.:
  210. return [(1, 0),]
  211. else:
  212. return [(round((maxVal-minVal)/10.), 0),]
  213. def y_axis_strings(self, values, scale, spacing):
  214. """
  215. Sets the strings for the y axis
  216. :param values:
  217. :param scale:
  218. :param spacing:
  219. :return:
  220. """
  221. if np.mean(values) > 3:
  222. return [str(int(v)) for v in values]
  223. return [str(v) for v in values]
  224. def line_plot(self):
  225. """
  226. Initialise the Line Plot
  227. :return: -
  228. """
  229. self.plot_type = "line"
  230. for i in range(1, 5):
  231. getattr(self, "adc{}_plot_widget".format(i)).enableAutoRange() #axis=getattr(self, "adc{}_plot_widget".format(i)).getViewBox().YAxis)
  232. getattr(self, "adc{}_plot_widget".format(i)).getPlotItem().setLabel("left", tr("Heading", "Intensity"))
  233. getattr(self, "adc{}_plot_widget".format(i)).getPlotItem().setLabel("bottom", tr("Heading", "Coarse over Fine"))
  234. bax = getattr(self, "adc{}_plot_widget".format(i)).plotItem.getAxis('bottom')
  235. lax = getattr(self, "adc{}_plot_widget".format(i)).plotItem.getAxis('left')
  236. bax.setTickSpacing()
  237. bax.tickSpacing = self.spacing
  238. bax.tickStrings = self.line_plot_axis_strings
  239. lax.setTickSpacing()
  240. lax.tickStrings = self.y_axis_strings
  241. def setInputs(self, coarse_input, fine_inputs):
  242. """
  243. Register the Inputfields for timing in the timingsettings window
  244. :param coarse_input: (QSpinBox) Spinbox for coarse timing settings
  245. :param fine_inputs: (list) List of (QSpinBox)es for fine timing settings
  246. :return: -
  247. """
  248. self.c_input = coarse_input
  249. self.adc1_f_input = fine_inputs[0]
  250. self.adc2_f_input = fine_inputs[1]
  251. self.adc3_f_input = fine_inputs[2]
  252. self.adc4_f_input = fine_inputs[3]
  253. self.inputsSet = True
  254. def click(self, adc, event):
  255. """
  256. Handler for the click event in color plots
  257. :param adc: (str) The ADC plot that was clicked on
  258. :param event: (QEvent) the event
  259. :return: -
  260. """
  261. event.accept()
  262. pos = event.pos()
  263. self.position_label.setText(adc + " - " + str(int(pos.x())+self.xrange[0]) + ":"+str(int(pos.y())+self.yrange[0]))
  264. if not self.inputsSet:
  265. return
  266. self.c_input.setValue(int(pos.y())+self.yrange[0])
  267. if adc == "ADC 1":
  268. self.adc1_f_input.setValue(int(pos.x())+self.xrange[0])
  269. elif adc == "ADC 2":
  270. self.adc2_f_input.setValue(int(pos.x())+self.xrange[0])
  271. elif adc == "ADC 3":
  272. self.adc3_f_input.setValue(int(pos.x())+self.xrange[0])
  273. elif adc == "ADC 4":
  274. self.adc4_f_input.setValue(int(pos.x())+self.xrange[0])
  275. def plot(self, data=None, levels=None, ranges=None, newTitle=None, maxima=None):
  276. """
  277. Plot Data
  278. :param data: (numpy 4d array) data to plot
  279. :param levels: (tuple) Min and Max Values for color plot
  280. :param ranges: (list) From to range of coarse (y) and fine (x) delay scan
  281. :param newTitle: (str) The title for the window
  282. :param maxima: (list) Maximum for each adc
  283. :return: -
  284. """
  285. # if self.data is not None and data is not None: # keine ahnung
  286. # return
  287. if self.data is None and data is None:
  288. return
  289. if data is not None:
  290. self.data = data # if called with no data a replot is performed (if data is set previously)
  291. if levels is not None:
  292. self.levels = levels
  293. if ranges is not None:
  294. self.yrange = ranges[0:2]
  295. self.xrange = ranges[2:4]
  296. self.x = self.data.shape[1]
  297. self.y = self.data.shape[2]
  298. if self.plot_type == 'colour':
  299. # self.colour_plot()
  300. self.adc1.setImage(self.data[0])
  301. self.adc2.setImage(self.data[1])
  302. self.adc3.setImage(self.data[2])
  303. self.adc4.setImage(self.data[3])
  304. if self.levels:
  305. self.adc1.setLevels(self.levels)
  306. self.adc2.setLevels(self.levels)
  307. self.adc3.setLevels(self.levels)
  308. self.adc4.setLevels(self.levels)
  309. if newTitle:
  310. self.setWindowTitle(tr("Heading", "Timescan Result:") + " " + newTitle + " " +
  311. available_boards.get_board_name_from_id(self.board_id))
  312. if maxima is not None:
  313. self.adc1_plot_widget.getPlotItem().setTitle(str(tr("Heading", "ADC 1: Maximum C:{c} F:{f}")).format(
  314. c=int(maxima[0, 1]), f=int(maxima[0, 2])))
  315. self.adc2_plot_widget.getPlotItem().setTitle(str(tr("Heading", "ADC 2: Maximum C:{c} F:{f}")).format(
  316. c=int(maxima[1, 1]), f=int(maxima[1, 2])))
  317. self.adc3_plot_widget.getPlotItem().setTitle(str(tr("Heading", "ADC 3: Maximum C:{c} F:{f}")).format(
  318. c=int(maxima[2, 1]), f=int(maxima[2, 2])))
  319. self.adc4_plot_widget.getPlotItem().setTitle(str(tr("Heading", "ADC 4: Maximum C:{c} F:{f}")).format(
  320. c=int(maxima[3, 1]), f=int(maxima[3, 2])))
  321. if self.plot_type == 'line':
  322. # self.line_plot()
  323. def reshape(data):
  324. ''' simply reshape the data'''
  325. return np.reshape(data, data.shape[0]*data.shape[1], order='F')
  326. self.adc1_plot_widget.plotItem.clear()
  327. self.adc2_plot_widget.plotItem.clear()
  328. self.adc3_plot_widget.plotItem.clear()
  329. self.adc4_plot_widget.plotItem.clear()
  330. self.adc1_plot_widget.plotItem.plot(reshape(self.data[0]))
  331. self.adc2_plot_widget.plotItem.plot(reshape(self.data[1]))
  332. self.adc3_plot_widget.plotItem.plot(reshape(self.data[2]))
  333. self.adc4_plot_widget.plotItem.plot(reshape(self.data[3]))
  334. # ---[ Show this window and set focus to it ]------
  335. self.parent().show()
  336. self.show()
  337. self.setFocus()
  338. def closeEvent(self, event):
  339. """
  340. Event handler for closing this window
  341. """
  342. global __timing_plot_widget_id__
  343. __timing_plot_widget_id__ = {}
  344. del self.par.widgets[self.id]
  345. class timingPart(kcgw.KCGWidgets):
  346. """
  347. The actual timing settings window
  348. """
  349. def __init__(self, board_id, parent=None):
  350. """
  351. Initialise the timing settings window
  352. :param unique_id:
  353. :param parent:
  354. :return:
  355. """
  356. super(timingPart, self).__init__()
  357. if __timing_plot_widget__:
  358. self.plotWidget = __timing_plot_widget__
  359. self.parent = parent
  360. self.board_id = board_id
  361. self.layout = QtGui.QGridLayout()
  362. self.outerLayout = QtGui.QVBoxLayout()
  363. self.outerLayout.addLayout(self.layout)
  364. self.setLayout(self.outerLayout)
  365. self.time_scan_enabled = False
  366. # --------[ Create TimeScan part ]----------
  367. # --------[ Create Elements ]-------------
  368. self.coarse_scan_range_label = self.createLabel(tr("Label", "Coarse scan"))
  369. self.fine_scan_range_label = self.createLabel(tr("Label", "Fine scan"))
  370. self.from_label = self.createLabel(tr("Label", "From"))
  371. self.to_label = self.createLabel(tr("Label", "To"))
  372. self.coarse_scan_min_spinbox = self.createSpinbox(0, bif.bk_get_config(board_id, 'th_delay_max'), start_value=0)
  373. self.coarse_scan_max_spinbox = self.createSpinbox(0, bif.bk_get_config(board_id, 'th_delay_max'), start_value=bif.bk_get_config(board_id, 'th_delay_max'))
  374. self.fine_scan_min_spinbox = self.createSpinbox(0, bif.bk_get_config(board_id, 'chip_delay_max'), start_value=0)
  375. self.fine_scan_max_spinbox = self.createSpinbox(0, bif.bk_get_config(board_id, 'chip_delay_max'), start_value=bif.bk_get_config(board_id, 'chip_delay_max'))
  376. self.average_one_bunch_label = self.createLabel(tr("Label", "Average bunch number"))
  377. self.average_all_bunches_checkbox = self.createCheckbox(tr("Button", "Average all bunches"), tr("Tooltip", "Average over signal from all bunches for timescan"),
  378. checked=True,connect=self.showAverageOneBunch)
  379. self.average_one_bunch_spinbox = self.createSpinbox(0, BUNCHES_PER_TURN-1, start_value=0)
  380. self.average_one_bunch_spinbox.setEnabled(False)
  381. self.average_threshold_label = self.createLabel(tr("Label", "Averaging Threshold"))
  382. self.average_threshold_checkbox = self.createCheckbox(tr("Button", "Use Threshold"), tr("Tooltip", "Average over signal from all bunches that are above/below the specified threshold for timescan"),
  383. checked=False,connect=self.considerThreshold)
  384. self.average_threshold_spinbox = self.createSpinbox(0, 2048, start_value=0)
  385. self.average_threshold_spinbox.setEnabled(False)
  386. self.orbits_observe_spinbox = self.createSpinbox(1, 10000000, start_value=100)
  387. self.orbits_skip_spinbox = self.createSpinbox(0, 100, start_value=2)
  388. self.time_scan_progressbar = QtGui.QProgressBar()
  389. self.time_scan_button = self.createButton(tr("Button", "Start time scan"), connect=self.time_scan)
  390. Elements.addButton("start_time_scan_{}".format(board_id), self.time_scan_button)
  391. self.timeScan = QtGui.QWidget()
  392. # --------[ Create and Fill Timescan layouts ]----------------
  393. self.timeScanVLayout = QtGui.QVBoxLayout()
  394. self.timeScanLayout = QtGui.QGridLayout()
  395. self.timeScanVLayout.addWidget(self.time_scan_progressbar)
  396. self.timeScanVLayout.addLayout(self.timeScanLayout)
  397. self.timeScan.setLayout(self.timeScanVLayout)
  398. self.timeScanLayout.addWidget(self.from_label, 0, 1)
  399. self.timeScanLayout.addWidget(self.to_label, 0, 2)
  400. self.timeScanLayout.addWidget(self.coarse_scan_range_label, 1, 0)
  401. self.timeScanLayout.addWidget(self.coarse_scan_min_spinbox, 1, 1)
  402. self.timeScanLayout.addWidget(self.coarse_scan_max_spinbox, 1, 2)
  403. self.timeScanLayout.addWidget(self.fine_scan_range_label, 2, 0)
  404. self.timeScanLayout.addWidget(self.fine_scan_min_spinbox, 2, 1)
  405. self.timeScanLayout.addWidget(self.fine_scan_max_spinbox, 2, 2)
  406. self.timeScanLayout.addWidget(self.createLabel("Orbits to Observe"), 3, 1)
  407. self.timeScanLayout.addWidget(self.orbits_observe_spinbox, 3, 2)
  408. self.timeScanLayout.addWidget(self.createLabel("Orbits to Skip"), 4, 1)
  409. self.timeScanLayout.addWidget(self.orbits_skip_spinbox, 4, 2)
  410. self.timeScanLayout.addWidget(self.average_all_bunches_checkbox, 5, 0)
  411. self.timeScanLayout.addWidget(self.average_one_bunch_label, 5, 1)
  412. self.timeScanLayout.addWidget(self.average_one_bunch_spinbox, 5, 2)
  413. self.timeScanLayout.addWidget(self.average_threshold_checkbox, 6, 0)
  414. self.timeScanLayout.addWidget(self.average_threshold_label, 6, 1)
  415. self.timeScanLayout.addWidget(self.average_threshold_spinbox, 6, 2)
  416. self.timeScanLayout.addWidget(self.time_scan_button, 7, 2)
  417. self.setTabOrder(self.fine_scan_max_spinbox, self.time_scan_button)
  418. # --------[ End Time Scan Part ]-------------
  419. # --------[ Create Labels and corresponding Fields ]---------
  420. def update_delay(which, spinbox):
  421. '''update the delays on the board'''
  422. board.get_board_config(board_id).update(which, getattr(self, spinbox).value())
  423. board.get_board_config(board_id).set_delay(self.coarseInput.value())
  424. self.thdelayLabel = self.createLabel(tr("Label", "T/H Delay"))
  425. self.adc1thdelayLabel = self.createLabel(tr("Label", "2. ADC1 T/H Delay"))
  426. self.coarseLabel = self.createLabel(tr("Label", "Coarse Delay"))
  427. self.coarseInput = self.createSpinbox(0, 15, connect=lambda: update_delay("th_delay", "coarseInput"))
  428. self.adc1CoarseInput = self.createSpinbox(0, 15, connect=lambda: update_delay("adc_1_delay_individual", "adc1CoarseInput"))
  429. self.adc1CoarseInput.setEnabled(False)
  430. self.adc1CoarseInputSwitch = self.createCheckbox(tr("Label", "Enable second ADC1 T/H Delay"), connect=self.toggleAdc1IndividualDelay)
  431. self.fineLabel = self.createLabel(tr("Label", "Fine Delay"))
  432. self.fineAdc1Input = self.createSpinbox(0, 31, connect=self.on_adc_delay_changed)
  433. self.fineAdc2Input = self.createSpinbox(0, 31, connect=self.on_adc_delay_changed)
  434. self.fineAdc3Input = self.createSpinbox(0, 31, connect=self.on_adc_delay_changed)
  435. self.fineAdc4Input = self.createSpinbox(0, 31, connect=self.on_adc_delay_changed)
  436. self.toggleTimeScanCheckbox = self.createCheckbox(tr("Button", "Time Scan"), tr("Tooltip", "Show time scan part"),
  437. connect=self.showTimeScan)
  438. #---------[ End ]---------
  439. Elements.addItem(["timing_{}".format(self.board_id), "no_board_{}".format(self.board_id)], [
  440. self.coarseInput,
  441. self.fineAdc1Input,
  442. self.fineAdc2Input,
  443. self.fineAdc3Input,
  444. self.fineAdc4Input,
  445. self.time_scan_button,
  446. self.adc1CoarseInputSwitch
  447. ])
  448. Elements.addItem('acquire_{}'.format(self.board_id), self.time_scan_button)
  449. def setValueSilent(value, spinbox):
  450. '''set values silent to not trigger signals'''
  451. spinbox.blockSignals(True)
  452. spinbox.setValue(value)
  453. spinbox.blockSignals(False)
  454. # --------[ Set observers ]------------
  455. def obs(who, what):
  456. '''observe something'''
  457. board.get_board_config(board_id).observe(
  458. who,
  459. lambda value=None: setValueSilent(value=value, spinbox=who),
  460. what
  461. )
  462. obs(self.fineAdc1Input, 'chip_1_delay')
  463. obs(self.fineAdc2Input, 'chip_2_delay')
  464. obs(self.fineAdc3Input, 'chip_3_delay')
  465. obs(self.fineAdc4Input, 'chip_4_delay')
  466. obs(self.coarseInput, 'th_delay')
  467. obs(self.adc1CoarseInput, 'adc_1_delay_individual')
  468. # -------[ Create outputs ]---------------
  469. self.totalLabel = self.createLabel(tr("Label", "Total Delay"))
  470. self.totalAdc1Box = self.createInput("", read_only=True)
  471. self.totalAdc2Box = self.createInput("", read_only=True)
  472. self.totalAdc3Box = self.createInput("", read_only=True)
  473. self.totalAdc4Box = self.createInput("", read_only=True)
  474. def observe_function(x, box):
  475. '''method to pass as callable to to the configuration as observer'''
  476. box.setText('%i + %i' % (board.get_board_config(board_id).get('th_delay')*
  477. board.get_board_config(board_id).get('th_delay_factor'),
  478. x*board.get_board_config(board_id).get('chip_delay_factor')))
  479. def observe_function_total(x, box, adc):
  480. '''method to pass as callable to to the configuration as observer'''
  481. box.setText('%i + %i' % (x *
  482. board.get_board_config(board_id).get('th_delay_factor'),
  483. board.get_board_config(board_id).get('chip_'+adc+'_delay') *
  484. board.get_board_config(board_id).get('chip_delay_factor')))
  485. # def adc1_observe_function_total(x, box):
  486. # adc1_individual = board.get_board_config(board_id).get("adc_1_delay_individual")
  487. # if adc1_individual == -1:
  488. # adc1_individual = 0
  489. # box.setText('%i + %i' % ((x+adc1_individual)*
  490. # board.get_board_config(board_id).get('th_delay_factor'),
  491. # board.get_board_config(board_id).get('chip_1_delay') *
  492. # board.get_board_config(board_id).get('chip_delay_factor')))
  493. board.get_board_config(board_id).observe(self.totalAdc1Box,
  494. lambda x: observe_function(x, self.totalAdc1Box), 'chip_1_delay')
  495. board.get_board_config(board_id).observe(self.totalAdc1Box,
  496. lambda x: observe_function_total(x, self.totalAdc1Box, '1'), 'th_delay')
  497. # board.get_board_config(board_id).observe(self.totalAdc1Box, lambda x: adc1_observe_function_total(x, self.totalAdc1Box), 'th_delay')
  498. # board.get_board_config(board_id).observe(self.totalAdc1Box, lambda x: adc1_observe_function_total(x, self.totalAdc1Box), 'adc_1_delay_individual')
  499. board.get_board_config(board_id).observe(self.totalAdc2Box,
  500. lambda x: observe_function(x, self.totalAdc2Box), 'chip_2_delay')
  501. board.get_board_config(board_id).observe(self.totalAdc2Box,
  502. lambda x: observe_function_total(x, self.totalAdc2Box, '2'), 'th_delay')
  503. board.get_board_config(board_id).observe(self.totalAdc3Box,
  504. lambda x: observe_function(x, self.totalAdc3Box), 'chip_3_delay')
  505. board.get_board_config(board_id).observe(self.totalAdc3Box,
  506. lambda x: observe_function_total(x, self.totalAdc3Box, '3'), 'th_delay')
  507. board.get_board_config(board_id).observe(self.totalAdc4Box,
  508. lambda x: observe_function(x, self.totalAdc4Box), 'chip_4_delay')
  509. board.get_board_config(board_id).observe(self.totalAdc4Box,
  510. lambda x: observe_function_total(x, self.totalAdc4Box, '4'), 'th_delay')
  511. #--------[ Fill Grid ]----------------
  512. self.layout.addWidget(self.thdelayLabel, 0, 1)
  513. self.layout.addWidget(self.coarseLabel, 1, 0)
  514. self.layout.addWidget(self.coarseInput, 1, 1)
  515. self.layout.addWidget(self.adc1thdelayLabel, 0, 2)
  516. self.layout.addWidget(self.adc1CoarseInput, 1, 2)
  517. self.layout.addWidget(self.adc1CoarseInputSwitch, 1, 3, 1, 2)
  518. self.layout.addItem(QtGui.QSpacerItem(10, 15), 2, 1)
  519. # Leave some rows free for additional things (empty rows will not be shown)
  520. for i in range(4):
  521. self.layout.addWidget(self.createLabel("ADC "+str(i+1)), 5, i+1)
  522. self.layout.addWidget(self.fineLabel, 6, 0)
  523. self.layout.addWidget(self.fineAdc1Input, 6, 1)
  524. self.layout.addWidget(self.fineAdc2Input, 6, 2)
  525. self.layout.addWidget(self.fineAdc3Input, 6, 3)
  526. self.layout.addWidget(self.fineAdc4Input, 6, 4)
  527. self.layout.addItem(QtGui.QSpacerItem(10, 15), 7, 1)
  528. line = QtGui.QFrame()
  529. line.setFrameStyle(QtGui.QFrame.HLine)
  530. self.layout.addWidget(line, 8, 0, 1, 5)
  531. self.layout.addItem(QtGui.QSpacerItem(10, 15), 9, 1)
  532. self.layout.addWidget(self.totalLabel, 10, 0)
  533. self.layout.addWidget(self.totalAdc1Box, 10, 1)
  534. self.layout.addWidget(self.totalAdc2Box, 10, 2)
  535. self.layout.addWidget(self.totalAdc3Box, 10, 3)
  536. self.layout.addWidget(self.totalAdc4Box, 10, 4)
  537. self.layout.addItem(QtGui.QSpacerItem(10, 15), 11, 1)
  538. self.layout.addWidget(self.toggleTimeScanCheckbox, 12, 0)
  539. # -------[ Add TimeScan ]---------
  540. self.outerLayout.addWidget(self.timeScan)
  541. self.timeScan.hide()
  542. self.outerLayout.addStretch(1)
  543. # -------[ End ]-----------
  544. # ------[ Exclude Total Delay Boxes from Tab Order ]--------
  545. self.totalAdc1Box.setFocusPolicy(QtCore.Qt.ClickFocus)
  546. self.totalAdc2Box.setFocusPolicy(QtCore.Qt.ClickFocus)
  547. self.totalAdc3Box.setFocusPolicy(QtCore.Qt.ClickFocus)
  548. self.totalAdc4Box.setFocusPolicy(QtCore.Qt.ClickFocus)
  549. self.setTabOrder(self.fineAdc4Input, self.toggleTimeScanCheckbox)
  550. self.setValues()
  551. self.setWindowTitle(tr("Heading", "Timing"))
  552. def toggleAdc1IndividualDelay(self):
  553. """
  554. Toggle to use an individual delay for adc1 or not
  555. :return: -
  556. """
  557. self.adc1CoarseInput.setEnabled(self.adc1CoarseInputSwitch.checkState())
  558. if not self.adc1CoarseInput.isEnabled():
  559. board.get_board_config(self.board_id).update('adc_1_delay_individual', -1) # Be careful this does no silent update
  560. def showAverageOneBunch(self):
  561. """
  562. Toggle to use an individual bunch for the averaging in the time delay scan or to use all bunches
  563. :return: -
  564. """
  565. self.average_one_bunch_spinbox.setEnabled(not self.average_all_bunches_checkbox.checkState())
  566. self.average_threshold_spinbox.setEnabled(self.average_all_bunches_checkbox.checkState() and self.average_threshold_checkbox.checkState())
  567. self.average_threshold_checkbox.setEnabled(self.average_all_bunches_checkbox.checkState())
  568. self.average_threshold_checkbox.setChecked(False)
  569. def considerThreshold(self):
  570. """
  571. Toggle to consider the Threshold for the averaging in the time delay scan
  572. :return: -
  573. """
  574. self.average_threshold_spinbox.setEnabled(self.average_threshold_checkbox.checkState() and self.average_all_bunches_checkbox.checkState())
  575. def showTimeScan(self):
  576. """
  577. Show the time scan part of this window
  578. :return: -
  579. """
  580. if self.timeScan.isHidden():
  581. # self.outerLayout.addWidget(self.timeScan)
  582. self.timeScan.show()
  583. else:
  584. self.timeScan.hide()
  585. # self.outerLayout.removeWidget(self.timeScan)
  586. self.parent.adjustSizeForTimeScan()
  587. def time_scan(self):
  588. """
  589. Toggle timescan and create timingPlotWidget if it doesn't exist
  590. :return: -
  591. """
  592. global __timing_plot_widget_id__
  593. global __timing_plot_widget__
  594. if not (self.board_id in __timing_plot_widget_id__):
  595. nid = kcgw.idg.genid()
  596. __timing_plot_widget_id__[self.board_id] = nid
  597. self.plotWidget = timingPlotWidget(nid, self.board_id, global_objects.get_global('area'))
  598. __timing_plot_widget__ = self.plotWidget
  599. global_objects.get_global('area').newWidget(self.plotWidget, tr("Heading", "Timing result"), nid, widget_type=5)
  600. self.plotWidget.parent().hide()
  601. self.plotWidget.setInputs(self.coarseInput, [
  602. self.fineAdc1Input,
  603. self.fineAdc2Input,
  604. self.fineAdc3Input,
  605. self.fineAdc4Input
  606. ])
  607. bif.bk_time_scan(
  608. self.board_id,
  609. self.coarse_scan_min_spinbox.value(),
  610. self.coarse_scan_max_spinbox.value(),
  611. self.fine_scan_min_spinbox.value(),
  612. self.fine_scan_max_spinbox.value(),
  613. self.time_scan_progressbar,
  614. self.plotWidget.plot,
  615. orbits_observe = self.orbits_observe_spinbox.value(),
  616. orbits_skip = self.orbits_skip_spinbox.value(),
  617. bucket_to_use = self.average_one_bunch_spinbox.value() if self.average_one_bunch_spinbox.isEnabled() else None,
  618. threshold_counts = self.average_threshold_spinbox.value() if self.average_threshold_spinbox.isEnabled() else None
  619. )
  620. def setValueSilent(self, element, value):
  621. """
  622. Set Values to inputs without notifying observers
  623. :param element: the input
  624. :param value: the value
  625. :return: -
  626. """
  627. element.blockSignals(True)
  628. element.setValue(value)
  629. element.blockSignals(False)
  630. def setValues(self):
  631. """
  632. Set Values to inputs (initially)
  633. :return: -
  634. """
  635. self.setValueSilent(self.fineAdc1Input, board.get_board_config(self.board_id).get('chip_1_delay'))
  636. self.setValueSilent(self.fineAdc2Input, board.get_board_config(self.board_id).get('chip_2_delay'))
  637. self.setValueSilent(self.fineAdc3Input, board.get_board_config(self.board_id).get('chip_3_delay'))
  638. self.setValueSilent(self.fineAdc4Input, board.get_board_config(self.board_id).get('chip_4_delay'))
  639. self.setValueSilent(self.coarseInput, board.get_board_config(self.board_id).get('th_delay'))
  640. if board.get_board_config(self.board_id).get('adc_1_delay_individual') != -1: # This may be nonsense Also: it is only possible to set adc1 higher than th_delay
  641. self.totalAdc1Box.setText('%i + %i' % ((board.get_board_config(self.board_id).get('th_delay')+board.get_board_config(self.board_id).get('adc_1_delay_individual'))
  642. * board.get_board_config(self.board_id).get('th_delay_factor'),
  643. board.get_board_config(self.board_id).get('chip_1_delay')*board.get_board_config(self.board_id).get('chip_delay_factor')))
  644. else:
  645. self.totalAdc1Box.setText('%i + %i' % (board.get_board_config(self.board_id).get('th_delay')* board.get_board_config(self.board_id).get('th_delay_factor'),
  646. board.get_board_config(self.board_id).get('chip_1_delay')*board.get_board_config(self.board_id).get('chip_delay_factor')))
  647. self.totalAdc2Box.setText('%i + %i' % (board.get_board_config(self.board_id).get('th_delay')* board.get_board_config(self.board_id).get('th_delay_factor'),
  648. board.get_board_config(self.board_id).get('chip_2_delay')*board.get_board_config(self.board_id).get('chip_delay_factor')))
  649. self.totalAdc3Box.setText('%i + %i' % (board.get_board_config(self.board_id).get('th_delay')* board.get_board_config(self.board_id).get('th_delay_factor'),
  650. board.get_board_config(self.board_id).get('chip_3_delay')*board.get_board_config(self.board_id).get('chip_delay_factor')))
  651. self.totalAdc4Box.setText('%i + %i' % (board.get_board_config(self.board_id).get('th_delay')* board.get_board_config(self.board_id).get('th_delay_factor'),
  652. board.get_board_config(self.board_id).get('chip_4_delay')*board.get_board_config(self.board_id).get('chip_delay_factor')))
  653. def closeEvent(self, event):
  654. """
  655. Event handler when this window is closed
  656. """
  657. Elements.emptyGroup('timing_{}'.format(self.board_id))
  658. board.get_board_config(self.board_id).unobserve(self.fineAdc1Input, 'chip_1_delay')
  659. board.get_board_config(self.board_id).unobserve(self.fineAdc2Input, 'chip_2_delay')
  660. board.get_board_config(self.board_id).unobserve(self.fineAdc3Input, 'chip_3_delay')
  661. board.get_board_config(self.board_id).unobserve(self.fineAdc4Input, 'chip_4_delay')
  662. board.get_board_config(self.board_id).unobserve(self.coarseInput, 'th_delay')
  663. board.get_board_config(self.board_id).unobserve(self.totalAdc1Box, 'chip_1_delay')
  664. board.get_board_config(self.board_id).unobserve(self.totalAdc2Box, 'chip_2_delay')
  665. board.get_board_config(self.board_id).unobserve(self.totalAdc3Box, 'chip_3_delay')
  666. board.get_board_config(self.board_id).unobserve(self.totalAdc4Box, 'chip_4_delay')
  667. board.get_board_config(self.board_id).unobserve(self.totalAdc1Box, 'th_delay')
  668. board.get_board_config(self.board_id).unobserve(self.totalAdc2Box, 'th_delay')
  669. board.get_board_config(self.board_id).unobserve(self.totalAdc3Box, 'th_delay')
  670. board.get_board_config(self.board_id).unobserve(self.totalAdc4Box, 'th_delay')
  671. board.get_board_config(self.board_id).unobserve(self.adc1CoarseInput, 'adc_1_delay_individual')
  672. Elements.removeItem('no_board_{}'.format(self.board_id),
  673. [
  674. self.fineAdc1Input,
  675. self.fineAdc2Input,
  676. self.fineAdc3Input,
  677. self.fineAdc4Input,
  678. self.coarseInput,
  679. self.time_scan_button,
  680. self.adc1CoarseInputSwitch
  681. ])
  682. Elements.removeItem("start_time_scan_{}".format(self.board_id), self.time_scan_button)
  683. Elements.removeItem(['acquire_{}'.format(self.board_id), "start_time_scan_{}".format(self.board_id)], self.time_scan_button)
  684. super(timingPart, self).closeEvent(event)
  685. def on_adc_delay_changed(self):
  686. """
  687. Handler that gets called when an adc delay gets changed
  688. """
  689. try:
  690. board.get_board_config(self.board_id).set_delay(self.coarseInput.value())
  691. factors = [self.fineAdc1Input.value(), self.fineAdc2Input.value(),
  692. self.fineAdc3Input.value(), self.fineAdc4Input.value()]
  693. board.get_board_config(self.board_id).set_chip_delay([0, 1, 2, 3], factors)
  694. except board.BoardError as e:
  695. logging.error("ADC fine delay failed: {}".format(str(e)))
  696. bif.bk_status_readout(self.board_id)
  697. return
  698. class timingWidget(kcgw.KCGWidgets):
  699. """
  700. This is the container that holds the tab widget which contains the timing widgets for each board
  701. """
  702. def __init__(self, unique_id, parent=None):
  703. super(timingWidget, self).__init__()
  704. self.id = unique_id
  705. self.par = parent
  706. self.setWindowTitle("Timing")
  707. self.layout = QtGui.QHBoxLayout()
  708. self.setLayout(self.layout)
  709. self.widgets = {i: timingPart(i, self) for i in available_boards} # has to set parent with self because
  710. # otherwise the window does not get resized correctly upon enabling timescan
  711. if available_boards.multi_board:
  712. self.tabWidget = QtGui.QTabWidget()
  713. self.layout.addWidget(self.tabWidget)
  714. for id, widget in self.widgets.iteritems():
  715. self.tabWidget.addTab(widget, available_boards.get_board_name_from_id(id))
  716. else:
  717. self.single_board_widget = self.widgets.values()[0]
  718. self.layout.addWidget(self.single_board_widget)
  719. def adjustSizeForTimeScan(self):
  720. """
  721. Adjust the size of the widget to accomodate the time_scan part
  722. :return:
  723. """
  724. # self.parentWindow = self.parent().parent().parent().parent() # one up is stacked widget, second up is
  725. # tab widget, third up is timingWidget fourh up is KCGWSubWindow (the actual window)
  726. QtCore.QCoreApplication.processEvents()
  727. if self.parent().windowState() & QtCore.Qt.WindowMaximized:
  728. self.parent().setWindowState(QtCore.Qt.WindowMaximized)
  729. else:
  730. # self.parent().resize(self.minimumSizeHint().width() * 1.2, self.minimumSizeHint().height()*1.1)
  731. self.parent().adjustSize()
  732. def closeEvent(self, event):
  733. global __widget_id__
  734. __widget_id__ = None
  735. for widget in self.widgets.values():
  736. widget.closeEvent(event)
  737. del self.par.widgets[self.id]
  738. super(timingWidget, self).closeEvent(event)
  739. def addTimingWidget():
  740. """
  741. Add this widget to the gui.
  742. This function will actually open the subwindow.
  743. :return: -
  744. """
  745. global __widget_id__
  746. if __widget_id__:
  747. global_objects.get_global('area').widgets[__widget_id__].setFocus()
  748. else:
  749. nid = kcgw.idg.genid()
  750. __widget_id__ = nid
  751. w = timingWidget(nid, global_objects.get_global('area'))
  752. global_objects.get_global('area').newWidget(w, tr("Heading", "Timing"), nid, widget_type=4, minSize=True) #TODO: proper type
  753. kcgw.register_widget(QtGui.QIcon(config.install_path + config.timingIcon), tr("Heading", "Timing"), addTimingWidget, "Ctrl+T")