FrequencyExtractWidget.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. from PyQt4 import QtCore, QtGui
  2. from ..base import kcgwidget as kcgw
  3. from ..base.globals import glob as global_objects
  4. from ..base.backend import board
  5. from ..base.backend.board import available_boards
  6. from .. import config
  7. import pyqtgraph as pg
  8. from math import ceil
  9. import numpy as np
  10. from .PlotWidget import SubPlotWidget
  11. __widget_id__ = None
  12. class FrequencyExtractWidget(kcgw.KCGWidgets):
  13. def __init__(self, unique_id, parent):
  14. super(FrequencyExtractWidget, self).__init__()
  15. self.id = unique_id
  16. self.par = parent
  17. #ToDo: Maybe find valid data from the get-go?
  18. self.data = None
  19. self.board_config = board.get_board_config(available_boards[0])
  20. self.board_config.observe(self, self.dataSetChanged, 'lastDataSet')
  21. self.numADCs = self.board_config.get('adc_number')
  22. #Use a VBox. Top half should be the SubPlotWidget, bottom
  23. #half should be the controls
  24. self.layout = QtGui.QVBoxLayout()
  25. self.plotBox = QtGui.QVBoxLayout()
  26. self.plot = SubPlotWidget()
  27. self.region = pg.LinearRegionItem(values=[10,20])
  28. self.plot.plotItem.addItem(self.region)
  29. self.region.sigRegionChangeFinished.connect(self.doPlot)
  30. self.line = pg.InfiniteLine(angle=0, label='Y={value:0.2f}',
  31. labelOpts={'movable': True})
  32. self.plot.plotItem.addItem(self.line)
  33. self.line.hide()
  34. self.plotBox.addWidget(self.plot)
  35. #Controls for the plot
  36. self.controlsBox = QtGui.QHBoxLayout()
  37. self.adcSelect = QtGui.QComboBox(self)
  38. for i in range(self.numADCs):
  39. self.adcSelect.addItem("ADC {}".format(i+1))
  40. self.adcSelect.currentIndexChanged.connect(self.doPlot)
  41. self.bucketSelect = QtGui.QComboBox(self)
  42. self.bucketSelect.addItem("Mean")
  43. for i in range(184):
  44. self.bucketSelect.addItem("Bucket {}".format(i+1))
  45. self.bucketSelect.currentIndexChanged.connect(self.doPlot)
  46. self.fromBox = self.createSpinbox(0, 100000000, interval=100, connect=self.doPlot)
  47. self.toBox = self.createSpinbox(0, 100000000, start_value=1000, interval=100, connect=self.doPlot)
  48. self.controlsBox.addWidget(self.adcSelect)
  49. self.controlsBox.addWidget(self.bucketSelect)
  50. self.controlsBox.addStretch()
  51. self.controlsBox.addWidget(self.createLabel(text="From:"))
  52. self.controlsBox.addWidget(self.fromBox)
  53. self.controlsBox.addWidget(self.createLabel(text="To:"))
  54. self.controlsBox.addWidget(self.toBox)
  55. self.frequencyTools = QtGui.QHBoxLayout()
  56. self.freqText = QtGui.QLineEdit(self)
  57. self.isFreqValid = False
  58. self.frequencyTools.addWidget(self.createLabel(text="Frequency:"))
  59. self.frequencyTools.addWidget(self.freqText)
  60. self.layout.addLayout(self.plotBox)
  61. self.layout.addLayout(self.controlsBox)
  62. self.layout.addLayout(self.frequencyTools)
  63. self.setLayout(self.layout)
  64. self.setWindowTitle("Frequency Extract")
  65. def dataSetChanged(self, data=None):
  66. self.data = data
  67. self.doPlot()
  68. def doPlot(self):
  69. if not self.data:
  70. return
  71. #PlotWidget's "plot" function expects the fft_mode to have two entries
  72. #which we don't need here. So we need to shift our index by +2 in order
  73. #to accomodate for the two missing options in the bucket_select ComboBox
  74. fft_mode = self.bucketSelect.currentIndex() + 2
  75. adc = self.adcSelect.currentIndex()
  76. self.fftData = self.data.fft(adc=self.adcSelect.currentIndex(),
  77. frm=self.fromBox.value(),
  78. to=self.toBox.value(),
  79. drop_first_bin=True,
  80. nobunching=False)
  81. self.plot.plot(
  82. self.fftData,
  83. autorange=False,
  84. xvalueborders=[self.data.fftFreqDist(), self.data.fftMaxFreq()],
  85. fft_mode=fft_mode,
  86. log=False)
  87. self.plot.plotItem.setLabel('left', 'Spectral Intensity')
  88. #I am cheating a little bit and getting the readily processed data
  89. #from the plotItems, so I don't have to process, convert, format, etc.
  90. #the fftData again. We already did that in the SubPlotWidget.plot
  91. #function, so no need to do it again. Just steal the data from the
  92. #plotItems!
  93. self.fftYValues = self.plot.plotItemPlot[0].yData
  94. self.fftXValues = self.plot.plotItemPlot[0].xData
  95. region = self.region.getRegion()
  96. #Clip to valid data ranges
  97. rLeft = ceil(region[0]) if region[0] >=0 else 0
  98. rRight = ceil(region[1]) if region[1] < len(self.fftYValues) else len(self.fftYValues)
  99. #Make sure the region is within the boundaries of our data array
  100. if rLeft >= len(self.fftYValues) or rRight < 0:
  101. self.freqText.setText("Region boundaries out of data range")
  102. self.line.hide()
  103. self.isFreqValid = False
  104. self.freqText.setText("NaN")
  105. return
  106. #If the region is not really a region, ignore it
  107. if rLeft == rRight:
  108. self.freqText.setText("Region too small")
  109. self.line.hide()
  110. self.isFreqValid = False
  111. self.freqText.setText("NaN")
  112. return
  113. chunk = self.fftYValues[rLeft:rRight]
  114. maxVal = np.max(chunk)
  115. maxIndexInChunk = np.where(chunk == maxVal)
  116. self.line.setValue(maxVal)
  117. self.line.show()
  118. #np.where returns a tuple of indices (because it supports
  119. #multidimensional arrays). But since we know that our array is only
  120. #1-Dimensional, we can simply pick only the first dimension
  121. maxIndexInChunk = maxIndexInChunk[0]
  122. indexInData = rLeft + maxIndexInChunk
  123. #We are basically just repeating what the plot item also did to
  124. #determine the X-Axis labeling, and use it calculate the X-Value
  125. #at the index we have identified
  126. xAxisScale = (self.data.fftMaxFreq() - self.data.fftFreqDist()) / float(self.fftYValues.shape[0])
  127. self.freqText.setText(str(self.fftXValues[indexInData][0]*xAxisScale))
  128. self.isFreqValid = True
  129. def closeEvent(self, event):
  130. global __widget_id__
  131. __widget_id__ = None
  132. del self.par.widgets[self.id]
  133. self.board_config.unobserve(self, 'lastDataSet')
  134. def addFrequencyExtractWidget():
  135. global __widget_id__
  136. if __widget_id__:
  137. global_objects.get_global('area').widgets[__widget_id__].setFocus()
  138. else:
  139. nid = kcgw.idg.genid()
  140. __widget_id__ = nid
  141. w = FrequencyExtractWidget(nid, global_objects.get_global('area'))
  142. global_objects.get_global('area').newWidget(w, "Frequency Extract", nid, widget_type=4)
  143. kcgw.register_widget(QtGui.QIcon(config.icon_path("sproject.svg")), "Frequency Extract", addFrequencyExtractWidget, "Ctrl+e")