Exemple #1
0
    def __init__(self, parent=None):
        super(AdrControlWidget, self).__init__(parent)
        self.setupUi(self)
        self.setWindowTitle('ADR Control')
        self.addRowPb.clicked.connect(self.addRowClicked)
        for i in range(0, self.currentTable.columnCount()):
            self.currentTable.horizontalHeader().setResizeMode( i, QHeaderView.ResizeToContents )
        self.deleteRowPb.clicked.connect(self.deleteRowClicked)

        self.restoreSettings()

        magnetRemote = MagnetControlRemote('AdrControl', parent=parent)

        magnetRemote.supplyVoltageReceived.connect(self.supplyVoltageSb.setValue)
        magnetRemote.supplyCurrentReceived.connect(self.supplyCurrentSb.setValue)
        magnetRemote.magnetVoltageReceived.connect(self.magnetVoltageSb.setValue)
        magnetRemote.magnetCurrentReceived.connect(self.magnetCurrentSb.setValue)        
        magnetRemote.magnetCurrentReceived.connect(self.magnetCurrentUpdated)
        #magnetRemote.dIdtReceived.connect(self.updateRampRate)
        self.magnetRemote = magnetRemote

        hkSub = HousekeepingSubscriber(parent=parent)
        hkSub.adrTemperatureReceived.connect(self.adrTemperatureSb.setValue)
        hkSub.start()
        self.hkSub = hkSub

        self.state = State.IDLE
        self.startPb.clicked.connect(self.startPbClicked)
        self.stopPb.clicked.connect(self.stopPbClicked)
        self.skipPb.clicked.connect(self.skip)
        self.loadPb.clicked.connect(self.loadTable)
        self.savePb.clicked.connect(self.saveTable)
    def __init__(self, parent=None):
        super(LockinThermometerWidget, self).__init__(parent)
        self.setupUi(self)
        self.setWindowTitle('Lockin Thermometer')
        self.timer = None
        self.Rthermometer = float('nan')


        '''
        Not sure if DateAxisItem is available anymore
        '''
        axis = pg.AxisItem(orientation='bottom')
        self.plot = pg.PlotWidget(axisItems={'bottom': axis})
        self.plot.setBackground('w')
        self.plot.plotItem.showGrid(x=True, y=True)
        self.plot.addLegend()
        self.verticalLayout.addWidget(self.plot)
        self.curve = pg.PlotCurveItem(name='X', symbol='o', pen='b')
        self.plot.addItem(self.curve)
        self.clearPb.clicked.connect(self.clearData)
        self.clearData()
        self.plotYAxisCombo.currentIndexChanged.connect(self.updatePlot)
        
        self.sr830 = None
        self.runPb.clicked.connect(self.run)
        self.parameterItems = [self.attenuatorGainSb, self.sourceImpedanceSb, self.driveResistanceSb, self.leadResistanceSb, self.preampGainSb, self.sensorVoltageSb]
        self.savePb.clicked.connect(self.saveParameterSet)
        self.loadPb.clicked.connect(self.loadParameterSet)
        self.deletePb.clicked.connect(self.deleteParameterSet)
        self.attenuatorAttenuationSb.valueChanged.connect(self.updateAttenuatorGain)
        self.attenuatorGainSb.valueChanged.connect(self.updateAttenuatorAttenuation)
        self.sensorVoltageIndicator.setUnit('V')
        self.sensorCurrentIndicator.setUnit('A')
        self.sensorPowerIndicator.setUnit('W')

        sr830 = SR830(None)
        sr830.sensitivity.populateEnumComboBox(self.minSensitivityCombo)
        
        self.loadParameterSets()
        self.restoreSettings()
        self.hkSub = HousekeepingSubscriber(parent = self)
        self.hkSub.adrResistanceReceived.connect(self.collectAdrResistance)
        self.hkSub.start()
        self.adrResistanceIndicator.setUnit(u'Ω')
        self.adrResistanceIndicator.setPrecision(5)
        self.publisher = None
        
        combo = self.calibrationCombo
        combo.addItem('RuOx 600')
        combo.addItem('RuOx 2005')
        combo.addItem('RuOx Bus (Shuo)')
        combo.addItem('RuOx Chip (InsideBox)')
        combo.setItemData(0, 'Nominal sensor calibration for RuOx 600 series', Qt.ToolTipRole)
        combo.setItemData(1, 'Calibration for RuOx sensor #2005 series', Qt.ToolTipRole)
        combo.setItemData(2, 'Cross-calibration against RuOx #2005 by Shuo (not so good above ~300mK)', Qt.ToolTipRole)
        combo.setItemData(3, 'Cross-calibration against RuOx #2005 by Yu', Qt.ToolTipRole)
        
        self.selectCalibration()
        combo.currentIndexChanged.connect(self.selectCalibration)
Exemple #3
0
    def __init__(self, parent=None):
        super(TESIVSweepDaqWidget, self).__init__(parent)
        self.setupUi(self)
        self.setWindowTitle('TES IV Sweep (DAQ)')

        self.aoDeviceCombo.currentIndexChanged.connect(
            self.updateDaqChannelsAo)
        self.aiDeviceCombo.currentIndexChanged.connect(
            self.updateDaqChannelsAi)
        self.populateDaqCombos()
        self.msmThread = None
        self.startPb.clicked.connect(self.startPbClicked)
        self.hkSub = HousekeepingSubscriber(self)
        self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue)
        self.hkSub.adrResistanceReceived.connect(
            self.collectThermometerResistance)
        self.hkSub.start()
        self.plotRaw = pg.PlotWidget(title='IV sweeps')
        self.plotLayout.addWidget(self.plotRaw)
        self.curveRaw = pg.PlotCurveItem()
        self.plotRaw.addItem(self.curveRaw)

        self.plotCritical = pg.PlotWidget(title='Critical current')
        self.plotLayout.addWidget(self.plotCritical)

        self.plotCritical.addLegend()
        self.criticalCurve1 = pg.PlotCurveItem(name='+', symbol='o', pen='r')
        self.plotCritical.addItem(self.criticalCurve1)

        self.criticalCurve2 = pg.PlotCurveItem(name='o', symbol='o', pen='b')
        self.plotCritical.addItem(self.criticalCurve2)

        self.plotRaw.setLabel('bottom', 'Vbias', 'V')
        self.plotRaw.setLabel('left', 'I_TES', 'uA')

        self.plotCritical.setLabel('left', 'I_C', 'uA')

        self.clearData()
        self.clearCriticalData()

        self.coilAo = None
        self.clearPb.clicked.connect(self.clearData)
        self.coilSweepCb.toggled.connect(self.toggleCoilSweep)
        self.clearCriticalPb.clicked.connect(self.clearCriticalData)
        self.samplesPerPointSb.valueChanged.connect(
            lambda value: self.discardSamplesSb.setMaximum(value - 1))
        self.coilEnableCb.toggled.connect(self.toggleCoil)
        self.coilVoltageSb.valueChanged.connect(self.updateCoilVoltage)
        #        self.toggleCoil(self.coilEnableCb.isChecked())
        #        self.toggleCoilSweep(self.coilSweepCb.isChecked())
        self.coilDriverCombo.currentIndexChanged.connect(
            self.coilDriverChanged)
        self.Vcoil = np.nan
        self.restoreSettings()
        self.Rthermometers = []
Exemple #4
0
    def __init__(self, qApp):
        self.qApplication = qApp
        self.T = float('nan')
        hkSub = HousekeepingSubscriber()
        #hkSub.adrTemperatureReceived.connect(self._receiveTemp)
        hkSub.thermometerReadingReceived.connect(self._receiveThermometerReading)
        hkSub.start()
        self.hkSub = hkSub

        self.Tremote = PidControlRemote('TemperatureSteps')
        self.clearHistory()
        self.lastUpdate = 0
Exemple #5
0
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        self.setupUi(self)
        self.BoSb.setToSiFactor(SI_uT)
        self.gCoilSb.setToSiFactor(SI_uT / SI_mA)
        self.RcoilSb.setToSiFactor(SI_kOhm)
        self.BcoilSb.setToSiFactor(SI_uT)
        self.RfbSb.setToSiFactor(SI_kOhm)
        self.invMiSb.setToSiFactor(SI_uA / SI_Phi0)
        self.invMfbSb.setToSiFactor(SI_uA / SI_Phi0)
        self.ApSb.setToSiFactor(SI_um**2)
        self.RbiasSb.setToSiFactor(SI_kOhm)
        self.gTesSb.setToSiFactor(SI_uT / SI_mA)
        self.RsSb.setToSiFactor(SI_mOhm)
        self.IbiasSb.setToSiFactor(SI_uA)
        self.ItesSb.setToSiFactor(SI_uA)
        self.RtesSb.setToSiFactor(SI_mOhm)

        self.setWindowTitle('TES Self-Field Cancellation')
        self.SettingsWidgets = [
            self.BoSb, self.gCoilSb, self.RcoilSb, self.RfbSb, self.invMiSb,
            self.invMfbSb, self.ApSb, self.RbiasSb, self.gTesSb, self.IbiasSb,
            self.nameLe, self.aiSamplesSb, self.ItesTargetSb,
            self.disableBiasCb, self.RsSb
        ]
        self.liveWidgets = [
            self.BoSb, self.gTesSb, self.IbiasSb, self.ItesTargetSb,
            self.tesCurrentControlCb, self.disableBiasCb
        ]

        #self.IbiasSb.valueChanged.connect(self.IbiasSb.setMaximum)

        self.restoreSettings()
        self.msmThread = None
        self.hkSub = HousekeepingSubscriber(self)
        self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue)
        self.hkSub.start()
        self.curveVsTime = pg.PlotCurveItem(pen='k')
        self.plotVsTime.addItem(self.curveVsTime)
        self.plotVsTime.plotItem.enableAutoRange(pg.ViewBox.XYAxes, True)
        self.curveVsTemp = pg.PlotCurveItem(pen='k')
        self.plotVsTemp.addItem(self.curveVsTemp)
        self.plotVsTemp.setBackground('w')
        self.plotVsTemp.plotItem.showGrid(x=True, y=True)
        self.plotVsTemp.plotItem.enableAutoRange(pg.ViewBox.XYAxes, True)
        self.clearData()
        self.runPb.clicked.connect(self.run)
Exemple #6
0
    def __init__(self, parent=None):
        super(IVSweepDaqWidget, self).__init__(parent)
        self.setupUi(self)

        self.aoDeviceCombo.currentIndexChanged.connect(
            self.updateDaqChannelsAo)
        self.aiDeviceCombo.currentIndexChanged.connect(
            self.updateDaqChannelsAi)
        self.populateDaqCombos()
        self.restoreSettings()
        self.msmThread = None
        self.startPb.clicked.connect(self.startPbClicked)
        self.hkSub = HousekeepingSubscriber(self)
        self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue)
        self.hkSub.start()
        self.rawPlot.setAxisTitle(QwtPlot.yLeft, 'Vmeas')
        self.rawPlot.setAxisTitle(QwtPlot.xBottom, 'Vdrive')
        self.rawCurve = QwtPlotCurve('')
        self.rawCurve.attach(self.rawPlot)
        self.criticalCurve1 = QwtPlotCurve('+')
        self.criticalCurve1.setSymbol(
            Qwt.QwtSymbol(Qwt.QwtSymbol.Cross, Qt.QBrush(), Qt.QPen(Qt.Qt.red),
                          Qt.QSize(5, 5)))
        self.criticalCurve1.attach(self.criticalPlot)
        self.criticalCurve2 = QwtPlotCurve('-')
        self.criticalCurve2.setSymbol(
            Qwt.QwtSymbol(Qwt.QwtSymbol.Cross, Qt.QBrush(),
                          Qt.QPen(Qt.Qt.blue), Qt.QSize(5, 5)))
        self.criticalCurve2.attach(self.criticalPlot)
        self.criticalPlot.setAxisTitle(QwtPlot.yLeft, 'Vcrit')
        self.clearData()
        self.clearCriticalData()
        self.clearPb.clicked.connect(self.clearData)
        self.coilSweepCb.toggled.connect(self.toggleCoilSweep)
        self.clearCriticalPb.clicked.connect(self.clearCriticalData)
        self.samplesPerPointSb.valueChanged.connect(
            lambda value: self.discardSamplesSb.setMaximum(value - 1))
        self.coilEnableCb.toggled.connect(self.toggleCoil)
        self.coilVoltageSb.valueChanged.connect(self.updateCoilVoltage)
        self.toggleCoil(self.coilEnableCb.isChecked())
        self.coilDriverCombo.currentIndexChanged.connect(
            self.coilDriverChanged)
        self.Vcoil = np.nan
Exemple #7
0
class LockinThermometerWidget(Ui.Ui_Form, QWidget):
    def __init__(self, parent=None):
        super(LockinThermometerWidget, self).__init__(parent)
        self.setupUi(self)
        self.setWindowTitle('Lockin Thermometer')
        self.serverThread = None
        self.timer = None
        self.Rthermometer = float('nan')

        axis = pg.DateAxisItem(orientation='bottom')
        self.plot = pg.PlotWidget(axisItems={'bottom': axis})
        self.plot.setBackground('w')
        self.plot.plotItem.showGrid(x=True, y=True)
        self.plot.addLegend()
        self.verticalLayout.addWidget(self.plot)
        self.curve = pg.PlotCurveItem(name='X', symbol='o', pen='b')
        self.plot.addItem(self.curve)
        self.clearPb.clicked.connect(self.clearData)
        self.clearData()
        self.plotYAxisCombo.currentIndexChanged.connect(self.updatePlot)

        self.sr830 = None
        self.runPb.clicked.connect(self.run)
        self.parameterItems = [
            self.attenuatorGainSb, self.sourceImpedanceSb,
            self.driveResistanceSb, self.leadResistanceSb, self.preampGainSb,
            self.sensorVoltageSb
        ]
        self.savePb.clicked.connect(self.saveParameterSet)
        self.loadPb.clicked.connect(self.loadParameterSet)
        self.deletePb.clicked.connect(self.deleteParameterSet)
        self.attenuatorAttenuationSb.valueChanged.connect(
            self.updateAttenuatorGain)
        self.attenuatorGainSb.valueChanged.connect(
            self.updateAttenuatorAttenuation)
        self.sensorVoltageIndicator.setUnit('V')
        self.sensorCurrentIndicator.setUnit('A')
        self.sensorPowerIndicator.setUnit('W')

        sr830 = SR830(None)
        sr830.sensitivity.populateEnumComboBox(self.minSensitivityCombo)

        self.loadParameterSets()
        self.restoreSettings()
        self.hkSub = HousekeepingSubscriber(parent=self)
        self.hkSub.adrResistanceReceived.connect(self.collectAdrResistance)
        self.hkSub.start()
        self.adrResistanceIndicator.setUnit(u'Ω')
        self.adrResistanceIndicator.setPrecision(5)
        self.publisher = None

        combo = self.calibrationCombo
        for i, calId in enumerate(ThermometerCalIds):
            try:
                cal = getThermometerCalibration(calId)
                info = cal.info
            except Exception as e:
                import warnings
                warnings.warn(
                    'Calibration %s unavailable due to exception %s' %
                    (calId, str(e)))
            combo.addItem(calId)
            combo.setItemData(i, info, Qt.ToolTipRole)

        self.selectCalibration()
        combo.currentIndexChanged.connect(self.selectCalibration)

    def startServerThread(self, port):
        if self.serverThread is not None:
            self.serverThread.stop()
            self.serverThread.wait(1000)
            del self.serverThread
            self.serverThread = None
        self.serverThread = RequestReplyThreadWithBindings(port, parent=self)
        boundWidgets = {
            'adjustExcitation': self.adjustExcitationCb,
            'sensorVoltage': self.sensorVoltageSb,
            'tolerance': self.toleranceSb,
            'autoRanging': self.autoRangingCb
        }
        for name in boundWidgets:
            self.serverThread.bindToWidget(name, boundWidgets[name])
        #logger.info('Starting server thread')
        self.serverThread.start()

    def selectCalibration(self):
        calId = str(self.calibrationCombo.currentText())
        self.calibration = getThermometerCalibration(calId)

    def collectAdrResistance(self, R):
        self.Rthermometer = R
        self.adrResistanceIndicator.setValue(R)

    def updateAttenuatorGain(self, v):
        sb = self.attenuatorGainSb
        block = sb.blockSignals(True)
        sb.setValue(1. / v)
        sb.blockSignals(block)

    def updateAttenuatorAttenuation(self, v):
        sb = self.attenuatorAttenuationSb
        block = sb.blockSignals(True)
        sb.setValue(1. / v)
        sb.blockSignals(block)

    def saveParameterSet(self):
        s = QSettings()
        s.beginGroup('ParameterSets')
        name = self.configCombo.currentText()
        s.beginGroup(name)
        s.setValue('adjustExcitation', self.adjustExcitationCb.isChecked())
        s.setValue('sensorName', self.sensorNameLe.text())
        s.setValue('sr830Visa', self.visaCombo.currentText())
        s.setValue('autoRanging', self.autoRangingCb.isChecked())
        s.setValue('minSensitivity', self.minSensitivityCombo.currentCode())
        for item in self.parameterItems:
            s.setValue(item.objectName(), item.value())
        s.endGroup()
        s.endGroup()

    def loadParameterSet(self):
        s = QSettings()
        name = self.configCombo.currentText()
        s.beginGroup('ParameterSets')
        if not name in s.childGroups():
            dlg = QErrorMessage(self)
            dlg.setWindowTitle('Error')
            dlg.showMessage('No saved parameters available for %s' % name)
            return
        s.beginGroup(name)
        for item in self.parameterItems:
            item.setValue(s.value(item.objectName(), item.value(), type=float))
        self.adjustExcitationCb.setChecked(
            s.value('adjustExcitation', False, type=bool))
        self.sensorNameLe.setText(s.value('sensorName', '', type=QString))
        self.visaCombo.setCurrentIndex(
            self.visaCombo.findText(
                s.value('sr830Visa', 'GPIB0::12', type=QString)))
        self.autoRangingCb.setChecked(s.value('autoRanging', True, type=bool))
        self.minSensitivityCombo.setCurrentCodeSilently(
            s.value('minSensitivity', 0, type=int))
        s.endGroup()
        s.endGroup()

    def loadParameterSets(self):
        s = QSettings()
        s.beginGroup('ParameterSets')
        names = s.childGroups()
        self.configCombo.addItems(names)

    def deleteParameterSet(self):
        i = self.configCombo.currentIndex()
        name = self.configCombo.itemText(i)

        s = QSettings()
        s.beginGroup('ParameterSets')
        s.beginGroup(name)
        s.remove('')
        s.endGroup()
        s.endGroup()

        self.configCombo.removeItem(i)

    def closeEvent(self, event):
        if self.timer:
            self.timer.stop()

        self.saveSettings()
        self.hkSub.stop()
        self.hkSub.wait(1000)

    def restoreSettings(self):
        s = QSettings()
        #visa = s.value('visa', QString(), type=QString)
        #i = self.visaCombo.findText(visa)
        #elf.visaCombo.setCurrentIndex(i)
        self.configCombo.setCurrentIndex(
            self.configCombo.findText(s.value('parameterSet', '',
                                              type=QString)))
        if len(self.configCombo.currentText()):
            self.loadParameterSet()
        #self.sensorNameLe.setText(s.value('sensorName', '', type=QString))

    def saveSettings(self):
        s = QSettings()
        #s.setValue('visa', self.visaCombo.currentText())
        s.setValue('parameterSet', self.configCombo.currentText())
        #s.setValue('sensorName', self.sensorNameLe.text())

    def enableWidgets(self, enable):
        self.visaCombo.setEnabled(enable)
        self.attenuatorGroupBox.setEnabled(enable)
        self.seriesResistanceGroupBox.setEnabled(enable)
        self.preampGroupBox.setEnabled(enable)
        self.sensorNameLe.setEnabled(enable)
        self.loadPb.setEnabled(enable)
        self.savePb.setEnabled(enable)
        self.deletePb.setEnabled(enable)
        self.configCombo.setEnabled(enable)

    def run(self):
        if self.sr830 is not None:
            self.stop()
        else:
            self.start()

    def stop(self):
        self.timer.stop()
        self.timer = None
        self.sr830 = None
        self.runPb.setText('Start')
        self.enableWidgets(True)
        del self.publisher
        self.publisher = None

    def sensorName(self):
        return str(self.sensorNameLe.text())

    def start(self):
        sensorName = self.sensorName()
        self.setWindowTitle('Lock-In Thermometer %s' % sensorName)
        setAppId(sensorName)

        if sensorName == 'BusThermometer':
            icon = QIcon('Icons/LockinThermometer_Bus.ico')
        elif sensorName == 'RuOx2005Thermometer':
            icon = QIcon('Icons/LockinThermometer_BoxOutside.ico')
        elif sensorName == 'BoxThermometer':
            icon = QIcon('Icons/LockinThermometer_BoxInside2.ico')
        else:
            icon = QIcon('Icons/LockinThermometer.ico')

        self.setWindowIcon(icon)

        visa = str(self.visaCombo.currentText())
        self.sr830 = SR830(visa)
        #self.sr830.debug = True

        self.sr830.readAll()
        self.sr830.sineOut.caching = False  # Disable caching on this

        self.publisher = ZmqPublisher('LockinThermometer',
                                      LockInPubSub(sensorName))
        self.startServerThread(LockInRequestReply(sensorName))

        self.runPb.setText('Stop')
        self.timer = QTimer()
        self.timer.setInterval(1000)
        self.timer.timeout.connect(self.snapSignal)
        self.timer.start()
        self.enableWidgets(False)
        self.rangeChangedTime = 0
        self.exChangedTime = 0
        t = time.time()
        timeString = time.strftime('%Y%m%d-%H%M%S', time.localtime(t))
        dateString = time.strftime('%Y%m%d')
        sensorName = str(self.sensorNameLe.text())

        s = QSettings('WiscXrayAstro', application='ADR3RunInfo')
        path = str(s.value('runPath', '', type=str))
        fileName = os.path.join(path, '%s_%s.dat' % (sensorName, dateString))
        if not os.path.isfile(fileName):  # Maybe create new file
            with open(fileName, 'a+') as f:
                f.write('#LockinThermometer.py\n')
                f.write('#Date=%s\n' % timeString)
                f.write('#SensorName=%s\n' % sensorName)
                f.write('#SR830=%s\n' % self.sr830.visaId())
                f.write('#AttenuatorGain=%f\n' % self.attenuatorGainSb.value())
                f.write('#AttenuatorSourceImpedance=%f\n' %
                        self.sourceImpedanceSb.value())
                f.write('#DriveResistance=%f\n' %
                        self.driveResistanceSb.value())
                f.write('#LeadResistance=%f\n' % self.leadResistanceSb.value())
                f.write('#PreampGain=%f\n' % self.preampGainSb.value())
                f.write('#DesiredExcitation=%f\n' %
                        self.sensorVoltageSb.value())
                k = self.sr830.allSettingValues()
                for key, value in k.iteritems():
                    f.write('#SR830/%s=%s\n' % (key, value))
                f.write('#' + '\t'.join([
                    'time', 'VsineOut', 'X', 'Y', 'f', 'Sensitivity', 'RxCalc',
                    'Rtherm'
                ]) + '\n')
        self.fileName = fileName

    def snapSignal(self):
        t = time.time()
        try:
            self.sr830.snapSignal()
        except CommunicationsError as e:
            # TODO Log the error
            self.sr830.clearGarbage()
            return

        VsineOut = self.sr830.sineOut.value
        X = self.sr830.X
        Y = self.sr830.Y
        f = self.sr830.f

        rangeChangeAge = t - self.rangeChangedTime
        exChangeAge = t - self.exChangedTime

        sensitivity = self.sr830.sensitivity.value

        if self.autoRangingCb.isChecked():
            self.sr830.checkStatus()
            minCode = self.minSensitivityCombo.currentCode()
            currentCode = self.sr830.sensitivity.code
            if self.sr830.overload and rangeChangeAge > 10:
                self.sr830.sensitivity.code = currentCode + 1
                self.rangeChangeTime = t
            elif abs(X) > 0.9 * sensitivity and rangeChangeAge > 10:
                self.sr830.sensitivity.code = currentCode + 1
                self.rangeChangedTime = t
            elif abs(X) < 0.3 * sensitivity and rangeChangeAge > 10:
                if currentCode > minCode:
                    self.sr830.sensitivity.code = currentCode - 1
                    self.rangeChangedTime = t
            elif currentCode < minCode:
                self.sr830.sensitivity.code = minCode
                self.rangeChangedTime = t

        G1 = self.attenuatorGainSb.value()
        G2 = self.preampGainSb.value()
        Rsource = self.sourceImpedanceSb.value()
        Rd = self.driveResistanceSb.value()
        Rl = self.leadResistanceSb.value()
        Rs = Rsource + Rd + Rl

        Vx = X / G2

        Vex = VsineOut * G1  # Real excitation
        self.sensorVoltageIndicator.setValue(Vx)

        Rx = Rs / (Vex / abs(Vx) - 1.)
        I = abs(Vx) / Rx
        self.sensorCurrentIndicator.setValue(I)
        P = abs(Vx) * I
        Temp = self.calibration.calculateTemperature(
            [Rx])[0]  # @todo This is really a crutch

        Tbase = self.calibration.correctForReadoutPower(Temp, P)

        if self.publisher is not None:
            if rangeChangeAge > 10 and exChangeAge > 10 and Temp == Temp and Temp > 0 and Temp < 10:
                self.publisher.publishDict(self.sensorName(), {
                    't': t,
                    'R': Rx,
                    'T': Temp,
                    'P': P,
                    'Tbase': Tbase
                })
                #self.publisher.publish('ADR_Sensor_R', Rx)
                #self.publisher.publish('ADR_Temperature', Temp)

        # Log data
        with open(self.fileName, 'a+') as of:
            of.write(
                '%.3f\t%.3f\t%.5E\t%.5E\t%.3f\t%.1E\t%.5E\t%.5E\n' %
                (t, VsineOut, X, Y, f, sensitivity, Rx, self.Rthermometer))

        self.ts.append(t)
        self.xs.append(X)
        self.ys.append(Y)
        self.fs.append(f)
        self.VsineOuts.append(VsineOut)
        self.Rs.append(Rx)
        self.Vxs.append(Vx)
        self.Ps.append(P)
        self.Ts.append(Temp)
        self.Tbases.append(Tbase)

        self.sensorIndicator.setValue(Rx)
        self.temperatureIndicator.setKelvin(Temp)
        self.baseTempIndicator.setKelvin(Tbase)
        self.sensorPowerIndicator.setValue(P)
        self.updateLed.flashOnce()
        self.updatePlot()

        # Not sure where this code came from. Seems questionable (FJ)
        # if len(self.Ts) > 2 :
        #     dTdt = abs((self.Ts[-1] - self.Ts[-2]) / (self.ts[-1] - self.ts[-2]))
        #     if dTdt > 0.1:
        #         self.temperatureIndicator.setKelvin('N/A')
        #         self.stop()

        # Perhaps change excitation
        if exChangeAge < 10 or rangeChangeAge < 10 or not self.adjustExcitationCb.isChecked(
        ):
            return
        VxDesired = self.sensorVoltageSb.value()
        IDesired = VxDesired / Rx
        VexDesired = IDesired * (Rx + Rs)
        change = (VexDesired - Vex) / Vex
        tolerance = 1E-2 * self.toleranceSb.value()
        if abs(change) < tolerance:
            return

        VsineOutDesired = VexDesired / G1  # This is what we would like to see
        # What we actually get may be something different
        Vnew = min(5, max(VsineOutDesired, 0.004))
        if tolerance == 0 and abs(
                Vnew -
                VsineOut) > 0.009:  # If a large step is required, do it slowly
            Vnew = (3. * VsineOut + 1. * Vnew) / 4.

        if abs(Vnew - VsineOut) < 0.002:
            return
        self.exChangedTime = t
        self.sr830.sineOut.value = Vnew

    def clearData(self):
        self.ts = []
        self.xs = []
        self.ys = []
        self.fs = []
        self.Rs = []
        self.Ps = []
        self.VsineOuts = []
        self.Vxs = []
        self.Ts = []
        self.Tbases = []
        self.updatePlot()

    def updatePlot(self):
        yAxis = self.plotYAxisCombo.currentText()
        pl = self.plot
        if yAxis == 'X':
            y = self.xs
            pl.setLabel('left', 'Lock-in X', 'V')
        elif yAxis == 'Y':
            y = self.ys
            pl.setLabel('left', 'Lock-in Y', 'V')
        elif yAxis == 'R':
            y = self.Rs
            pl.setLabel('left', 'R sensor', u'Ω')
        elif yAxis == 'V sine out':
            y = self.VsineOuts
            pl.setLabel('left', 'V sine out', 'V')
        elif yAxis == 'V sensor':
            y = self.Vxs
            pl.setLabel('left', 'V sensor', 'V')
        elif yAxis == 'P sensor':
            y = self.Ps
            pl.setLabel('left', 'P sensor', 'W')
        elif yAxis == 'Sensor temperature':
            y = self.Ts
            pl.setLabel('left', 'T sensor', 'K')
        elif yAxis == 'Base temperature':
            y = self.Tbases
            pl.setLabel('left', 'T base', 'K')

        x = self.ts
        self.curve.setData(x, y)
 def __init__(self, parent = None):
     super(MultitoneLockinWidget, self).__init__(parent)
     self.tableColumns = ['active', 'f', 'A', 'phase', 'bw', 'order', 'X', 'Y', 'R', 'Theta']
     self.setupUi(self)
     self.populateTable()
     self.daqThread = None
     self.liaThread = None
     self.rawWriter = None
     self.hdfFile = None
     self.fMax = 100E6
     
     self.wavePlot.addLegend()
     self.wavePlot.setLabel('left', 'Voltage', units='V')
     self.wavePlot.setLabel('bottom', 'time', units='s')
     self.excitationCurve = pg.PlotDataItem(pen='r', name='Excitation')
     self.wavePlot.addItem(self.excitationCurve)
     self.responseCurve = pg.PlotDataItem(pen='b', name='Response')
     self.wavePlot.addItem(self.responseCurve)
     self.startPb.clicked.connect(self.startMeasurement)
     self.stopPb.clicked.connect(self.stopMeasurement)
     self.settingsWidgets = [self.deviceCombo, self.aoChannelCombo, self.aoRangeCombo,
                             self.aiChannelCombo, self.aiRangeCombo, self.aiTerminalConfigCombo,
                             self.sampleLe, self.commentLe, self.enablePlotCb, 
                             self.sampleRateSb, self.offsetSb, self.rampRateSb, self.inputDecimationCombo,
                             self.saveRawDataCb, self.dcBwSb, self.dcFilterOrderSb,
                             self.saveRawDemodCb, self.subtractDcOffsetCb]
                             
     self.sampleRateSb.valueChanged.connect(self.updateMaximumFrequencies)
     self.deviceCombo.currentIndexChanged.connect(self.updateDevice)
     self.restoreSettings()
     self.hkSub = HousekeepingSubscriber(self)
     self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue)
     self.hkSub.start()        
     self.curve1 = pg.PlotDataItem(symbol='o', symbolSize=7, pen='b', name='')
     self.curve2 = pg.PlotDataItem(symbol='o', symbolSize=7, pen='r', name='')
     self.plot1.addItem(self.curve1)
     self.plot1.setLogMode(x=True)
     self.plot1.setLabel('bottom', 'f', units='Hz')
     self.plot2.addItem(self.curve2)
     self.plot2.setLogMode(x=True)
     self.plot2.setLabel('bottom', 'f', units='Hz')
     self.plot2.setXLink(self.plot1)
     self.plot1.showGrid(x=True, y=True)
     self.plot2.showGrid(x=True, y=True)
     self.plotxy.setLabel('bottom', 'X', units='')
     self.plotxy.setLabel('left', 'Y', units='')
     self.plotxy.setAspectLocked()
     self.curvexy = pg.PlotDataItem(symbol='o', symbolSize=7, pen='b', name='')
     self.plotxy.addItem(self.curvexy)
     self.plotCombo.currentIndexChanged.connect(self.yAxisChanged)
     self.plotCombo.setCurrentIndex(self.plotCombo.currentIndex())
     self.addRowPb.clicked.connect(self.addTableRow)
     self.deleteRowPb.clicked.connect(self.deleteTableRow)
     self.updateMaximumFrequencies()
     self.sampleRateSb.valueChanged.connect(self.updateMaximumFrequencies)
     self.inputDecimationCombo.currentIndexChanged.connect(self.updateMaximumFrequencies)
     self.saveRawDataCb.toggled.connect(self.toggleRawDataCollection)
     self.enablePlotCb.toggled.connect(self.enablePlotToggled)
     self.loadTablePb.clicked.connect(self.loadTable)
     self.saveTablePb.clicked.connect(self.saveTable)
     self.startServerThread()
class MultitoneLockinWidget(ui.Ui_Form, QWidget):
    __logger = logging.getLogger(__name__ + '.MultitoneLockinWidget')
    
    def __init__(self, parent = None):
        super(MultitoneLockinWidget, self).__init__(parent)
        self.tableColumns = ['active', 'f', 'A', 'phase', 'bw', 'order', 'X', 'Y', 'R', 'Theta']
        self.setupUi(self)
        self.populateTable()
        self.daqThread = None
        self.liaThread = None
        self.rawWriter = None
        self.hdfFile = None
        self.fMax = 100E6
        
        self.wavePlot.addLegend()
        self.wavePlot.setLabel('left', 'Voltage', units='V')
        self.wavePlot.setLabel('bottom', 'time', units='s')
        self.excitationCurve = pg.PlotDataItem(pen='r', name='Excitation')
        self.wavePlot.addItem(self.excitationCurve)
        self.responseCurve = pg.PlotDataItem(pen='b', name='Response')
        self.wavePlot.addItem(self.responseCurve)
        self.startPb.clicked.connect(self.startMeasurement)
        self.stopPb.clicked.connect(self.stopMeasurement)
        self.settingsWidgets = [self.deviceCombo, self.aoChannelCombo, self.aoRangeCombo,
                                self.aiChannelCombo, self.aiRangeCombo, self.aiTerminalConfigCombo,
                                self.sampleLe, self.commentLe, self.enablePlotCb, 
                                self.sampleRateSb, self.offsetSb, self.rampRateSb, self.inputDecimationCombo,
                                self.saveRawDataCb, self.dcBwSb, self.dcFilterOrderSb,
                                self.saveRawDemodCb, self.subtractDcOffsetCb]
                                
        self.sampleRateSb.valueChanged.connect(self.updateMaximumFrequencies)
        self.deviceCombo.currentIndexChanged.connect(self.updateDevice)
        self.restoreSettings()
        self.hkSub = HousekeepingSubscriber(self)
        self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue)
        self.hkSub.start()        
        self.curve1 = pg.PlotDataItem(symbol='o', symbolSize=7, pen='b', name='')
        self.curve2 = pg.PlotDataItem(symbol='o', symbolSize=7, pen='r', name='')
        self.plot1.addItem(self.curve1)
        self.plot1.setLogMode(x=True)
        self.plot1.setLabel('bottom', 'f', units='Hz')
        self.plot2.addItem(self.curve2)
        self.plot2.setLogMode(x=True)
        self.plot2.setLabel('bottom', 'f', units='Hz')
        self.plot2.setXLink(self.plot1)
        self.plot1.showGrid(x=True, y=True)
        self.plot2.showGrid(x=True, y=True)
        self.plotxy.setLabel('bottom', 'X', units='')
        self.plotxy.setLabel('left', 'Y', units='')
        self.plotxy.setAspectLocked()
        self.curvexy = pg.PlotDataItem(symbol='o', symbolSize=7, pen='b', name='')
        self.plotxy.addItem(self.curvexy)
        self.plotCombo.currentIndexChanged.connect(self.yAxisChanged)
        self.plotCombo.setCurrentIndex(self.plotCombo.currentIndex())
        self.addRowPb.clicked.connect(self.addTableRow)
        self.deleteRowPb.clicked.connect(self.deleteTableRow)
        self.updateMaximumFrequencies()
        self.sampleRateSb.valueChanged.connect(self.updateMaximumFrequencies)
        self.inputDecimationCombo.currentIndexChanged.connect(self.updateMaximumFrequencies)
        self.saveRawDataCb.toggled.connect(self.toggleRawDataCollection)
        self.enablePlotCb.toggled.connect(self.enablePlotToggled)
        self.loadTablePb.clicked.connect(self.loadTable)
        self.saveTablePb.clicked.connect(self.saveTable)
        self.startServerThread()

    def startServerThread(self):
        self.serverThread = RequestReplyThreadWithBindings(port=RequestReply.MultitoneLockin, parent=self)
        boundWidgets = {'sample': self.sampleLe, 'comment': self.commentLe,
                        'samplingRate': self.sampleRateSb, 'decimation': self.inputDecimationCombo,
                        'rampRate': self.rampRateSb, 'offset':self.offsetSb,
                        'dcBw': self.dcBwSb, 'dcFilterOrder': self.dcFilterOrderSb,
                        'saveRawData': self.saveRawDataCb, 'saveRawDemod': self.saveRawDemodCb,
                        'aiChannel': self.aiChannelCombo, 'aiRange': self.aiRangeCombo,
                        'aiTerminalConfig': self.aiTerminalConfigCombo,
                        'start': self.startPb, 'stop': self.stopPb, 'subtractOffset': self.subtractDcOffsetCb}
        for name in boundWidgets:
            self.serverThread.bindToWidget(name, boundWidgets[name])
        self.serverThread.bindToFunction('fileName', self.fileName)
        self.serverThread.bindToFunction('loadTable', self.loadTable)
        self.serverThread.start() 
    
    def fileName(self):
        return self._fileName
        
    def loadTable(self):
        s = QSettings()
        import os
        directory = s.value('frequencyTableDirectory', os.path.curdir, type=str)
        fileName = QFileDialog.getOpenFileName(parent=self, caption='Select file for loading table', directory=directory, filter="CSV (*.csv);;HDF5 (*.h5, *.hdf);;Excel (*.xls);;")
        fileName = str(fileName)
        if len(fileName) == 0:
            return
        extension =  os.path.basename(fileName).split('.')[-1].lower()
        directory = os.path.dirname(fileName)
        s.setValue('frequencyTableDirectory', directory)
        import pandas as pd
        df = pd.DataFrame()
        if extension in ['csv']:
            df = pd.read_csv(fileName)
        elif extension in ['xls']:
            df = pd.read_excel(fileName)
        elif extension in ['h5', 'hdf']:
            df = pd.read_hdf(fileName)
        for row in df.itertuples(index=True):
            self.addTableRow(row=row.Index, enabled=row.active, f=row.fRef, A=row.amplitude,
                             phase=row.phase, bw=row.lpBw, order=row.lpOrder)
    
    def saveTable(self):
        s = QSettings()
        import os
        directory = s.value('frequencyTableDirectory', os.path.curdir, type=str)
        fileName = QFileDialog.getSaveFileName(parent=self, caption='Select file for saving table', directory=directory, filter="CSV (*.csv);;HDF5 (*.h5, *.hdf);;Excel (*.xls);;")
        fileName = str(fileName)
        if len(fileName) == 0:
            return
        extension =  os.path.basename(fileName).split('.')[-1].lower()
        directory = os.path.dirname(fileName)
        s.setValue('frequencyTableDirectory', directory)

        import pandas as pd
        active = self.tableColumnValues('active')
        fRefs = self.tableColumnValues('f')
        As = self.tableColumnValues('A')
        phases = self.tableColumnValues('phase')
        bws = self.tableColumnValues('bw')
        orders = self.tableColumnValues('order')
        df = pd.DataFrame({'active':active, 'fRef':fRefs, 'amplitude':As,
                           'phase':phases, 'lpBw':bws, 'lpOrder':orders})
        if extension in ['csv']:
            df.to_csv(fileName)
        elif extension in ['xls']:
            df.to_excel(fileName)
        elif extension in ['h5', 'hdf']:
            df.to_hdf(fileName)
    
    def updateMaximumFrequencies(self):
        fs = self.sampleRateSb.value() * 1E3
        self.fMax = 0.5* fs / int(str(self.inputDecimationCombo.currentText()))
        for row in range(self.table.rowCount()):
            self.tableCellWidget(row, 'f').setMaximum(self.fMax)
    
    def populateTable(self):
        t = self.table
        t.setColumnCount(len(self.tableColumns))
        t.setHorizontalHeaderLabels(self.tableColumns)
        t.resizeRowsToContents()
        t.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents)
        t.horizontalHeader().setStretchLastSection(True)

    def populateDevices(self):
        self.deviceCombo.clear()
        system = daq.System()
        devices = system.findDevices()
        for dev in devices:
            self.deviceCombo.addItem(dev)
        
    def updateDevice(self):
        self.aiChannelCombo.clear()
        self.aoChannelCombo.clear()
        self.aiRangeCombo.clear()
        self.aoRangeCombo.clear()
        
        deviceName = str(self.deviceCombo.currentText())
        if len(deviceName) < 1:
            return
        device = daq.Device(deviceName)

        aiChannels = device.findAiChannels()
        for channel in aiChannels:
            self.aiChannelCombo.addItem(channel)
        
        aoChannels = device.findAoChannels()
        for channel in aoChannels:
            self.aoChannelCombo.addItem(channel)
            
        self.aiRanges = device.voltageRangesAi()
        for r in self.aiRanges:
            self.aiRangeCombo.addItem('%+.2f -> %+.2f V' % (r.min, r.max))

        self.aoRanges = device.voltageRangesAo()            
        for r in self.aoRanges:
            self.aoRangeCombo.addItem('%+.2f -> %+.2f V' % (r.min, r.max))
        
        if len(aiChannels):
            aiChannel = daq.AiChannel('%s/%s' % (deviceName, aiChannels[0]), self.aiRanges[0].min, self.aiRanges[0].max)
            aiTask = daq.AiTask('TestInputSampleRate')
            aiTask.addChannel(aiChannel)
            aiSampleRate = aiTask.maxSampleClockRate()
        else:
            aiSampleRate = 0

        if len(aoChannels):
            aoChannel = daq.AoChannel('%s/%s' % (deviceName, aoChannels[0]), self.aoRanges[0].min, self.aoRanges[0].max)
            aoTask = daq.AoTask('TestOutputSampleRate')
            aoTask.addChannel(aoChannel)
            aoSampleRate = aoTask.maxSampleClockRate()
        else:
            aoSampleRate = 0
            
        rate = min(aiSampleRate, aoSampleRate)
        self.sampleRateSb.setMaximum(int(1E-3*rate))
        #self.updateInfo()

    def terminalConfiguration(self):
        t = str(self.aiTerminalConfigCombo.currentText())
        tc = daq.AiChannel.TerminalConfiguration
        terminalConfigDict = {'RSE': tc.RSE, 'DIFF': tc.DIFF, 'NRSE': tc.NRSE}
        return terminalConfigDict[t]
        
    def restoreSettings(self):
        s = QSettings(OrganizationName, ApplicationName)
        self.populateDevices()
        for w in self.settingsWidgets:
            restoreWidgetFromSettings(s, w)
        geometry = s.value('geometry', QByteArray(), type=QByteArray)
        self.restoreGeometry(geometry)
        state = s.value('splitter1State', QByteArray(), type=QByteArray)
        self.splitter1.restoreState(state)
        state = s.value('splitter2State', QByteArray(), type=QByteArray)
        self.splitter2.restoreState(state)

        rows = s.beginReadArray('frequencyTable')
        for row in range(rows):
            s.setArrayIndex(row)
            enabled = s.value('active', True, type=bool)
            f = s.value('f', 10*(3**row), type=float)
            A = s.value('A', 0.1, type=float)
            phase = s.value('phase', 0, type=float)
            bw = s.value('bw', 5, type=float)
            order = s.value('order', 8, type=int)
            self.addTableRow(row, enabled, f, A, phase, bw, order)
        s.endArray()
        
    def tableCellWidget(self, row, item):
        return self.table.cellWidget(row, self.tableColumns.index(item))
        
    def setTableCellWidget(self, row, item, widget):
        self.table.setCellWidget(row, self.tableColumns.index(item), widget)
        
    def enablePlotToggled(self, checked):
        if self.daqThread is None:
            return
        if checked:
            self.daqThread.dataReady.connect(self.showWaveform)
        else:
            self.daqThread.dataReady.disconnect(self.showWaveform)
        
    def addTableRow(self, row=None, enabled=True, f=10, A=0.1, phase=0.0, bw=5, order=8):
        table = self.table
        if row == None:
            row = table.rowCount()
        if row < 1:
            row = 0
        table.insertRow(row)
        cb = QCheckBox()
        cb.setChecked(enabled)
        self.setTableCellWidget(row, 'active', cb)
        
        frequencySb = QDoubleSpinBox()
        frequencySb.setMinimum(1.0)
        frequencySb.setMaximum(self.fMax)
        frequencySb.setSingleStep(0.01)
        frequencySb.setDecimals(2)
        frequencySb.setValue(f)
        self.setTableCellWidget(row, 'f', frequencySb)
        
        amplitudeSb = AmplitudeSpinBox()
        amplitudeSb.setValue(A)
        amplitudeSb.valueChanged.connect(lambda v: self.amplitudeChanged(row, v))
        self.setTableCellWidget(row, 'A', amplitudeSb)
        
        phaseSb = PhaseSpinBox()
        phaseSb.setValue(phase)
        self.setTableCellWidget(row, 'phase', phaseSb)
        
        bwSb = QDoubleSpinBox()
        bwSb.setMinimum(0.1)
        bwSb.setMaximum(1000)
        bwSb.setValue(bw)
        bwSb.setSuffix(' Hz')
        self.setTableCellWidget(row, 'bw', bwSb)

        orderSb = QSpinBox()
        orderSb.setMinimum(1)
        orderSb.setMaximum(10)
        orderSb.setValue(order)
        self.setTableCellWidget(row, 'order', orderSb)
        
        self.setTableCellWidget(row, 'X', QFloatDisplay())
        self.setTableCellWidget(row, 'Y', QFloatDisplay())
        self.setTableCellWidget(row, 'R', QFloatDisplay())
        self.setTableCellWidget(row, 'Theta', QFloatDisplay())
        
    def amplitudeChanged(self, row, value):
        if self.daqThread is None:
            return
        if not row in self.activeRows:
            return
        i = np.where(row==self.activeRows)[0][0]
        self.daqThread.setAmplitude(i, value)
    
    def deleteTableRow(self):
        table = self.table
        row = table.currentRow()
        table.removeRow(row)
        
    def saveSettings(self):
        s = QSettings(OrganizationName, ApplicationName)
        s.setValue('geometry', self.saveGeometry())
        for w in self.settingsWidgets:
            saveWidgetToSettings(s, w)

        s.setValue('splitter1State', self.splitter1.saveState())
        s.setValue('splitter2State', self.splitter2.saveState())
        
        table = self.table
        nRows = table.rowCount()
        s.beginWriteArray('frequencyTable', nRows)
        w = self.tableCellWidget
        for row in range(nRows):
            s.setArrayIndex(row)
            s.setValue('active', w(row,'active').isChecked())
            s.setValue('f', w(row,'f').value())
            s.setValue('A', w(row,'A').value())
            s.setValue('phase', w(row,'phase').value())
            s.setValue('bw', w(row, 'bw').value())
            s.setValue('rollOff', w(row, 'order').value())
        s.endArray()
        
    def closeEvent(self, event):
        if self.daqThread is not None:
            r = QMessageBox.warning(self, ApplicationName, 'The measurement may be still be running. Do you really want to quit?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
            if r==QMessageBox.No:
                event.ignore()
                return
        self.saveSettings()
        super(MultitoneLockinWidget, self).closeEvent(event)
        
    def tableColumnValues(self, columnId):
        t = self.table
        values = []
        for row in range(t.rowCount()):
            widget = self.tableCellWidget(row, columnId)
            v = widgetValue(widget)
            values.append(v)
        return np.array(values)
                
    def startMeasurement(self):
        self.tProduce = {}
        sampleRate = int(self.sampleRateSb.value()*1E3)
        inputDecimation = int(str(self.inputDecimationCombo.currentText()))

        deviceName = str(self.deviceCombo.currentText())
        aoChannel = str(self.aoChannelCombo.currentText())
        aiChannel = str(self.aiChannelCombo.currentText())
        aiTerminalConfig = self.terminalConfiguration()
        aiRange = self.aiRanges[self.aiRangeCombo.currentIndex()]
        aoRange = self.aoRanges[self.aoRangeCombo.currentIndex()]
        offset = self.offsetSb.value()
        
        active = self.tableColumnValues('active')
        activeRows = np.where(active)[0]
        self.activeRows = activeRows
        fRefs = self.tableColumnValues('f')[activeRows]
        As = self.tableColumnValues('A')[activeRows]
        phases = self.tableColumnValues('phase')[activeRows]*deg2rad
        bws = self.tableColumnValues('bw')[activeRows]
        orders = self.tableColumnValues('order')[activeRows]
        subtractOffset = bool(self.subtractDcOffsetCb.isChecked())
        
        self._fileName = '%s_%s.h5' % (self.sampleLe.text(), time.strftime('%Y%m%d_%H%M%S'))
        hdfFile = hdf.File(self._fileName, mode='w')
        hdfFile.attrs['Program'] = ApplicationName
        hdfFile.attrs['Copyright'] = Copyright
        hdfFile.attrs['Version'] = Version
        hdfFile.attrs['Sample'] = str(self.sampleLe.text())
        hdfFile.attrs['Comment'] = str(self.commentLe.text())
        hdfFile.attrs['StartTimeLocal'] = time.strftime('%Y-%m-%d %H:%M:%S')
        hdfFile.attrs['StartTimeUTC'] =  time.strftime('%Y-%m-%d %H:%M:%SZ', time.gmtime())
        hdfFile.attrs['sampleRate'] = sampleRate
        hdfFile.attrs['inputDecimation'] = inputDecimation
        hdfFile.attrs['offset'] = offset
        hdfFile.attrs['deviceName'] = deviceName
        hdfFile.attrs['aoChannel'] = aoChannel
        hdfFile.attrs['aoRangeMin'] = aoRange.min; hdfFile.attrs['aoRangeMax'] = aoRange.max
        hdfFile.attrs['aiChannel'] = aiChannel
        hdfFile.attrs['aiRangeMin'] = aiRange.min; hdfFile.attrs['aiRangeMax'] = aiRange.max
        hdfFile.attrs['aiTerminalConfig'] = str(self.aiTerminalConfigCombo.currentText())
        hdfFile.attrs['excitationFrequencies'] = fRefs
        hdfFile.attrs['lockinFilterBandwidths'] = bws
        hdfFile.attrs['excitationAmplitudes'] = As
        hdfFile.attrs['excitationPhases'] = phases
        hdfFile.attrs['lockinFilterOrders'] = orders
        hdfFile.attrs['rawCount']  = 0
        hdfFile.attrs['rampRate'] = self.rampRateSb.value()
        hdfFile.attrs['subtractDcOffset'] = subtractOffset
                            
        self.fs = fRefs
        
        variables = [('tGenerated', np.float64), ('tAcquired', np.float64), ('Vdc', np.float64), ('Vrms', np.float64), ('Vmin', np.float64), ('Vmax', np.float64), ('offset', np.float64)]
        self.hdfVector = HdfVectorWriter(hdfFile, variables)

        g = hdfFile.create_group('HK')
        self.hkLogger = HkLogger(g, self.hkSub)
        self.hdfFile = hdfFile
        
        sampleRateDecimated = sampleRate/inputDecimation
        self.__logger.info("Decimated sample rate %f S/s", sampleRateDecimated)
        desiredChunkSize = int(min(0.5*sampleRateDecimated, 2**18)) # Would like pretty big chunks, but update at least twice/second
            
        # Compute phase delays due to the pre-lockin FIR halfband decimator cascade
        dec = DecimatorCascade(inputDecimation, desiredChunkSize)
        phaseDelays = TwoPi*fRefs/sampleRate*(dec.sampleDelay()+1.0)
        self.__logger.info("Phase delays: %s deg", str(phaseDelays*rad2deg))
        phaseDelays = np.mod(phaseDelays, TwoPi)
        
        dcBw = self.dcBwSb.value(); dcFilterOrder = self.dcFilterOrderSb.value()
        lias = LockIns(sampleRateDecimated, fRefs, phases-phaseDelays, bws,
                       orders, desiredChunkSize=desiredChunkSize, dcBw=dcBw,
                       dcFilterOrder=dcFilterOrder, subtractOffset=subtractOffset) # Set up the lock-ins

        self.liaStreamWriters = []
        outputSampleRates = lias.outputSampleRates
        hdfFile.attrs['outputSampleRates']  = outputSampleRates
        saveRawDemod = bool(self.saveRawDemodCb.isChecked())
        hdfFile.attrs['saveRawDemod']  = saveRawDemod

        streams = [('Z',np.complex64)]
        if saveRawDemod:
            streams.append(('Xdec', np.float32))
            streams.append(('Ydec', np.float32))
            
        for i,f in enumerate(fRefs):
            grp = hdfFile.create_group('F_%02d' % i)
            grp.attrs['fRef'] = f
            grp.attrs['fs'] = outputSampleRates[i]
            streamWriter = HdfStreamsWriter(grp, streams, 
                                           scalarFields=[('tGenerated', np.float64),
                                                         ('tAcquired', np.float64),
                                                         ('A', np.float64)],
                                           compression=False, parent=self)
            self.liaStreamWriters.append(streamWriter)


        grp = hdfFile.create_group('DC')
        grp.attrs['sampleRate'] = lias.dcSampleRate
        grp.attrs['lpfBw'] = dcBw
        grp.attrs['lpfOrder'] = dcFilterOrder
        streams = [('DC', np.float)]
        if saveRawDemod:
            streams.append(('DCdec', np.float))
        self.dcStreamWriter = HdfStreamsWriter(grp, streams, 
                                              scalarFields=[('tGenerated', np.float64), 
                                                            ('tAcquired', np.float64)],
                                              compression=False, parent=self)
        
        self.lias = lias
        lias.chunkAnalyzed.connect(self.chunkAnalyzed)
        chunkSize = lias.chunkSize
        self.__logger.info("Lias chunk size: %d", chunkSize)

        self.t = np.linspace(0, chunkSize/sampleRateDecimated, chunkSize)
        self.wavePlot.setXRange(0,self.t[-1])

        daqThread = DaqThread(sampleRate, fRefs, As, phases, chunkSize*inputDecimation, inputDecimation); self.daqThread = daqThread
        daqThread.setRampRate(self.rampRateSb.value())
        daqThread.setOffset(self.offsetSb.value())
        self.rampRateSb.valueChanged.connect(daqThread.setRampRate)
        daqThread.configureDaq(deviceName, aoChannel, aoRange, aiChannel, aiRange, aiTerminalConfig)
        daqThread.chunkProduced.connect(self.chunkProduced)
        daqThread.inputOverload.connect(self.overloadLed.flashOnce)
        daqThread.error.connect(self.reportError)
        daqThread.finished.connect(self.daqThreadFinished)
        self.offsetSb.valueChanged.connect(self.daqThread.setOffset)
        
        if self.enablePlotCb.isChecked():
            daqThread.dataReady.connect(self.showWaveform)
        
        if self.saveRawDataCb.isChecked():
            self.toggleRawDataCollection(True)
        
        self.resultsCollected = 0
        self.chunksProduced = None
        liaThread = QThread(); self.liaThread = liaThread
        lias.moveToThread(liaThread)
        daqThread.dataReady.connect(lias.integrateData)
        lias.resultsAvailable.connect(self.collectLockinResults)
        liaThread.started.connect(lambda: daqThread.start(QThread.HighestPriority))
        liaThread.started.connect(lambda: self.enableWidgets(False))
        liaThread.finished.connect(self.liaThreadFinished)
        liaThread.start() # Start lock-in amplifier(s)
       
        
    def toggleRawDataCollection(self, enabled):
        if self.hdfFile is None: return
        if enabled:
            rawCount = self.hdfFile.attrs['rawCount']
            grp = self.hdfFile.create_group('RAW_%06d' % rawCount) # Need to make a new group
            self.hdfFile.attrs['rawCount'] = rawCount+1
            self.rawWriter = HdfStreamWriter(grp, dtype=np.float32,
                                             scalarFields=[('t', np.float64)],
                                             metaData={}, compression=True,
                                             parent=self)
            self.daqThread.dataReady.connect(self.writeRaw)
        else:
            if self.rawWriter is not None:
                self.rawWriter.deleteLater()
                self.rawWriter = None
                
    def writeRaw(self, data, stats, amplitudes):
        if self.hdfFile is not None:
            self.rawWriter.writeData(data, scalarData=[stats[0]])
        
    def chunkProduced(self, n, t):
        self.tProduce[n] = t
        self.generatingLed.flashOnce() 
        
    def chunkAnalyzed(self, n, t):
        elapsedTime = t - self.tProduce.pop(n)
        self.delayLe.setText('%.3f s' % elapsedTime)
        
    def collectLockinResults(self, tGenerated, tAcquired, Vmin, Vmax, dc, rms, offset, Zs, Xdecs, Ydecs, amplitudes, dcFiltered, DCdec):
        if self.hdfVector is not None:
            self.hdfVector.writeData(tGenerated=tGenerated, tAcquired=tAcquired, Vmin=Vmin, Vmax=Vmax, Vdc=dc, Vrms=rms, offset=offset)
            self.dcStreamWriter.writeData(DC=dcFiltered, DCdec=DCdec, tGenerated=tGenerated, tAcquired=tAcquired)
        self.dcIndicator.setText('%+7.5f V' % dc)
        self.rmsIndicator.setText('%+7.5f Vrms' % rms)
        zs = np.empty((len(Zs),), dtype=np.complex64)
        for i, Z in enumerate(Zs):
            w = self.liaStreamWriters[i]
            if self.hdfFile is not None and w is not None:
                w.writeData(Z=Z, Xdec=Xdecs[i], Ydec=Ydecs[i], tGenerated=tGenerated, tAcquired=tAcquired, A=amplitudes[i])
            z = np.mean(Z)
            zs[i] = z
            row = self.activeRows[i]
            self.tableCellWidget(row, 'X').setValue(np.real(z))
            self.tableCellWidget(row, 'Y').setValue(np.imag(z))
            self.tableCellWidget(row, 'R').setValue(np.abs(z))
            self.tableCellWidget(row, 'Theta').setValue(np.angle(z)*rad2deg)
        self.zs = zs # Store for plot
        self.updatePlot()
                
    def reportError(self, message):
        QMessageBox.critical(self, 'Exception encountered!', message)

    def stopMeasurement(self):
        if self.daqThread is None:
            return
        if self.stopPb.text() == 'Stop':
            self.daqThread.stop()
            self.stopPb.setText('Abort')
            self.measurementFinished()
        else:
            self.daqThread.abort()
            self.daqThread.wait(1000)
            self.measurementFinished()

    def daqThreadFinished(self):
        self.__logger.debug("DAQ thread finished")
        del self.daqThread
        self.daqThread = None
        
    def liaThreadFinished(self):
        self.__logger.debug("Lock-in thread finished")
        self.liaThread.deleteLater()
        self.liaThread = None
        self.lias.deleteLater()
        self.lias = None
        #del self.liaThread; self.liaThread = None
        #del self.lias; self.lias = None
        
    def measurementFinished(self):
        if self.liaThread is not None:
            self.liaThread.quit()
            self.liaThread.wait(2000)
        self.closeFile()
        self.stopPb.setText('Stop')
        self.enableWidgets(True)
        
    def closeFile(self):
        if self.hdfFile is not None:
            del self.hkLogger; self.hkLogger = None
            del self.hdfVector; self.hdfVector = None
            for writer in self.liaStreamWriters:
                del writer
            t = time.time()
            self.hdfFile.attrs['StopTimeLocal'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(t))
            self.hdfFile.attrs['StopTimeUTC'] =  time.strftime('%Y-%m-%d %H:%M:%SZ', time.gmtime(t))
            self.hdfFile.close()
            del self.hdfFile
            self.hdfFile = None
            
    def enableWidgets(self, enable):
        self.driveGroupBox.setEnabled(enable)
        self.inputGroupBox.setEnabled(enable)
        self.startPb.setEnabled(enable)
        self.dcBwSb.setEnabled(enable)
        self.dcFilterOrderSb.setEnabled(enable)
        self.sampleRateSb.setEnabled(enable)
        self.saveRawDemodCb.setEnabled(enable)
        self.stopPb.setEnabled(not enable)
        
        table = self.table
        for item in ['f', 'phase', 'active', 'bw', 'order']:
            col = self.tableColumns.index(item)
            for row in range(table.rowCount()):
                w = table.cellWidget(row, col)
                if isinstance(w, QAbstractSpinBox) or isinstance(w, QLineEdit):
                    w.setReadOnly(not enable)
                    if enable:
                        w.setButtonSymbols(QAbstractSpinBox.UpDownArrows)
                    else:
                        w.setButtonSymbols(QAbstractSpinBox.NoButtons)
                elif isinstance(w, QAbstractButton) or isinstance(w, QComboBox):
                    w.setEnabled(enable)
        
    def showWaveform(self, samples, stats, amplitudes):
        self.responseCurve.setData(self.t, samples)
            
    def yAxisChanged(self):
        yAxis = str(self.plotCombo.currentText())
        if yAxis=='X/Y':
            self.plot1.setLabel('left', 'X', units='')
            self.plot2.setLabel('left', 'Y', units='')
        elif yAxis=='R/Phi':
            self.plot1.setLabel('left', 'R', units='')
            self.plot2.setLabel('left', 'Phi', units='deg')
        elif yAxis=='Vmax/Vmin':
            self.plot1.setLabel('left', 'Vmax', units='V')
            self.plot2.setLabel('left', 'Vmin', units='V')
        elif yAxis=='R/Vdc':
            self.plot1.setLabel('left', 'R', units='')
            self.plot2.setLabel('left', 'Vdc', units='V')
        ylog = (yAxis[0] == 'R') and self.logscaleCb.isChecked()
        self.plot1.setLogMode(x=True, y=ylog)
        self.updatePlot()

    def updatePlot(self):
        yAxis = str(self.plotCombo.currentText())
        i = np.argsort(self.fs)
        f = self.fs[i]; z = self.zs[i]
        X = np.real(z)
        Y = np.imag(z)
        if yAxis=='X/Y':
            y1 = X
            y2 = Y
        elif yAxis=='R/Phi':
            y1 = np.abs(z)
            y2 = rad2deg*np.angle(z)
        else:
            self.__logger.error("Unsupported y-axis: %s" , yAxis)
            return
        self.curve1.setData(f,y1)
        self.curve2.setData(f,y2)
        self.curvexy.setData(X, Y)
Exemple #10
0
    def __init__(self, parent = None):
        super(SineSweepWidget, self).__init__(parent)
        self.setupUi(self)
        self.daqThread = None
        self.liaThread = None
        self.hdfFile = None
        self._fileName = ''
#        self.restartPb.clicked.connect(self.restart)
        
        self.wavePlot.addLegend()
        self.wavePlot.setLabel('left', 'Voltage', units='V')
        self.wavePlot.setLabel('bottom', 'Phase', units='[rad]')
        self.excitationCurve = pg.PlotDataItem(pen='r', name='Excitation')
        self.wavePlot.addItem(self.excitationCurve)
        self.responseCurve = pg.PlotDataItem(pen='b', name='Response')
        self.wavePlot.addItem(self.responseCurve)
        self.wavePlot.setXRange(0,2*np.pi)
        self.startPb.clicked.connect(self.startMeasurement)
        self.stopPb.clicked.connect(self.stopMeasurement)
        self.settingsWidgets = [self.deviceCombo, self.aoChannelCombo, self.aoRangeCombo,
                                self.aiChannelCombo, self.aiRangeCombo, self.aiTerminalConfigCombo,
                                self.aiDriveChannelCombo, self.recordDriveCb, self.sampleLe, self.commentLe,
                                self.enablePlotCb, self.auxAoChannelCombo, self.auxAoRangeCombo,
                                self.auxAoSb, self.auxAoEnableCb, self.sampleRateSb,
                                self.offsetSb, self.amplitudeSb, self.fStopSb, self.fStartSb,
                                self.fStepsSb, self.settlePeriodsSb, self.measurePeriodsSb,
                                self.minSettleTimeSb, self.minMeasureTimeSb]
        self.sampleRateSb.valueChanged.connect(lambda x: self.fStopSb.setMaximum(x*1E3/2))
        self.fStopSb.valueChanged.connect(self.fStartSb.setMaximum)
        self.fStartSb.valueChanged.connect(self.fStopSb.setMinimum)
        self.deviceCombo.currentIndexChanged.connect(self.updateDevice)
        self.restoreSettings()
        #self.updateDevice()
        self.updateInfo()
        for w in [self.sampleRateSb, self.fStopSb, self.fStartSb, self.fStepsSb, self.settlePeriodsSb, self.measurePeriodsSb, self.minSettleTimeSb, self.minMeasureTimeSb]:
            w.valueChanged.connect(self.updateInfo)
        self.hkSub = HousekeepingSubscriber(self)
        self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue)
        self.hkSub.adrResistanceReceived.connect(self.collectAdrResistance)
        self.hkSub.start()        
        self.auxAoSb.valueChanged.connect(self.updateAuxOutputVoltage)
        self.auxAoEnableCb.toggled.connect(self.toggleAuxOut)
        self.auxAoTask = None
        self.curve1 = pg.PlotDataItem(symbol='o', symbolSize=7, pen='b', name='')
        self.curve2 = pg.PlotDataItem(symbol='o', symbolSize=7, pen='r', name='')
        self.plot1.addItem(self.curve1)
        self.plot1.setLogMode(x=True)
        self.plot1.setLabel('bottom', 'f', units='Hz')
        self.plot2.addItem(self.curve2)
        self.plot2.setLogMode(x=True)
        self.plot2.setLabel('bottom', 'f', units='Hz')
        self.plot2.setXLink(self.plot1)
        self.plot1.showGrid(x=True, y=True)
        self.plot2.showGrid(x=True, y=True)
        self.plotxy.setLabel('bottom', 'X', units='')
        self.plotxy.setLabel('left', 'Y', units='')
        self.plotxy.setAspectLocked()
        self.curvexy = pg.PlotDataItem(symbol='o', symbolSize=7, pen='b', name='')
        self.plotxy.addItem(self.curvexy)
        self.plotCombo.currentIndexChanged.connect(self.yAxisChanged)
        self.plotCombo.setCurrentIndex(self.plotCombo.currentIndex())
        
        self.serverThread = RequestReplyThreadWithBindings(port=RequestReply.SineSweepDaq, parent=self)
        boundWidgets = {'sample': self.sampleLe, 'comment': self.commentLe,
                        'samplingRate': self.sampleRateSb, 
                        'auxAo': self.auxAoSb, 'rampRate': self.rampRateSb,
                        'offset':self.offsetSb, 'amplitude':self.amplitudeSb,
                        'settlePeriods': self.settlePeriodsSb, 'minSettleTime': self.minSettleTimeSb,
                        'measurePeriods': self.measurePeriodsSb, 'minMeasureTime': self.minMeasureTimeSb,
                        'totalTime': self.totalTimeSb,
                        'fStart': self.fStartSb, 'fStop': self.fStopSb,
                        'fSteps': self.fStepsSb, 'start': self.startPb, 'stop': self.stopPb}
        for name in boundWidgets:
            self.serverThread.bindToWidget(name, boundWidgets[name])
        self.serverThread.bindToFunction('fileName', self.fileName)
        self.serverThread.start() 
Exemple #11
0
class SineSweepWidget(ui.Ui_Form, QWidget):
    EXIT_CODE_REBOOT = -123    
    def __init__(self, parent = None):
        super(SineSweepWidget, self).__init__(parent)
        self.setupUi(self)
        self.daqThread = None
        self.liaThread = None
        self.hdfFile = None
        self._fileName = ''
#        self.restartPb.clicked.connect(self.restart)
        
        self.wavePlot.addLegend()
        self.wavePlot.setLabel('left', 'Voltage', units='V')
        self.wavePlot.setLabel('bottom', 'Phase', units='[rad]')
        self.excitationCurve = pg.PlotDataItem(pen='r', name='Excitation')
        self.wavePlot.addItem(self.excitationCurve)
        self.responseCurve = pg.PlotDataItem(pen='b', name='Response')
        self.wavePlot.addItem(self.responseCurve)
        self.wavePlot.setXRange(0,2*np.pi)
        self.startPb.clicked.connect(self.startMeasurement)
        self.stopPb.clicked.connect(self.stopMeasurement)
        self.settingsWidgets = [self.deviceCombo, self.aoChannelCombo, self.aoRangeCombo,
                                self.aiChannelCombo, self.aiRangeCombo, self.aiTerminalConfigCombo,
                                self.aiDriveChannelCombo, self.recordDriveCb, self.sampleLe, self.commentLe,
                                self.enablePlotCb, self.auxAoChannelCombo, self.auxAoRangeCombo,
                                self.auxAoSb, self.auxAoEnableCb, self.sampleRateSb,
                                self.offsetSb, self.amplitudeSb, self.fStopSb, self.fStartSb,
                                self.fStepsSb, self.settlePeriodsSb, self.measurePeriodsSb,
                                self.minSettleTimeSb, self.minMeasureTimeSb]
        self.sampleRateSb.valueChanged.connect(lambda x: self.fStopSb.setMaximum(x*1E3/2))
        self.fStopSb.valueChanged.connect(self.fStartSb.setMaximum)
        self.fStartSb.valueChanged.connect(self.fStopSb.setMinimum)
        self.deviceCombo.currentIndexChanged.connect(self.updateDevice)
        self.restoreSettings()
        #self.updateDevice()
        self.updateInfo()
        for w in [self.sampleRateSb, self.fStopSb, self.fStartSb, self.fStepsSb, self.settlePeriodsSb, self.measurePeriodsSb, self.minSettleTimeSb, self.minMeasureTimeSb]:
            w.valueChanged.connect(self.updateInfo)
        self.hkSub = HousekeepingSubscriber(self)
        self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue)
        self.hkSub.adrResistanceReceived.connect(self.collectAdrResistance)
        self.hkSub.start()        
        self.auxAoSb.valueChanged.connect(self.updateAuxOutputVoltage)
        self.auxAoEnableCb.toggled.connect(self.toggleAuxOut)
        self.auxAoTask = None
        self.curve1 = pg.PlotDataItem(symbol='o', symbolSize=7, pen='b', name='')
        self.curve2 = pg.PlotDataItem(symbol='o', symbolSize=7, pen='r', name='')
        self.plot1.addItem(self.curve1)
        self.plot1.setLogMode(x=True)
        self.plot1.setLabel('bottom', 'f', units='Hz')
        self.plot2.addItem(self.curve2)
        self.plot2.setLogMode(x=True)
        self.plot2.setLabel('bottom', 'f', units='Hz')
        self.plot2.setXLink(self.plot1)
        self.plot1.showGrid(x=True, y=True)
        self.plot2.showGrid(x=True, y=True)
        self.plotxy.setLabel('bottom', 'X', units='')
        self.plotxy.setLabel('left', 'Y', units='')
        self.plotxy.setAspectLocked()
        self.curvexy = pg.PlotDataItem(symbol='o', symbolSize=7, pen='b', name='')
        self.plotxy.addItem(self.curvexy)
        self.plotCombo.currentIndexChanged.connect(self.yAxisChanged)
        self.plotCombo.setCurrentIndex(self.plotCombo.currentIndex())
        
        self.serverThread = RequestReplyThreadWithBindings(port=RequestReply.SineSweepDaq, parent=self)
        boundWidgets = {'sample': self.sampleLe, 'comment': self.commentLe,
                        'samplingRate': self.sampleRateSb, 
                        'auxAo': self.auxAoSb, 'rampRate': self.rampRateSb,
                        'offset':self.offsetSb, 'amplitude':self.amplitudeSb,
                        'settlePeriods': self.settlePeriodsSb, 'minSettleTime': self.minSettleTimeSb,
                        'measurePeriods': self.measurePeriodsSb, 'minMeasureTime': self.minMeasureTimeSb,
                        'totalTime': self.totalTimeSb,
                        'fStart': self.fStartSb, 'fStop': self.fStopSb,
                        'fSteps': self.fStepsSb, 'start': self.startPb, 'stop': self.stopPb}
        for name in boundWidgets:
            self.serverThread.bindToWidget(name, boundWidgets[name])
        self.serverThread.bindToFunction('fileName', self.fileName)
        self.serverThread.start() 
                
    def toggleAuxOut(self, enabled):
        if enabled:
            deviceName = str(self.deviceCombo.currentText())
            aoChannel = str(self.auxAoChannelCombo.currentText())
            aoRange = self.aoRanges[self.auxAoRangeCombo.currentIndex()]
            aoChannel = daq.AoChannel('%s/%s' % (deviceName, aoChannel), aoRange.min, aoRange.max)
            aoTask = daq.AoTask('AO auxilliary')
            aoTask.addChannel(aoChannel)
            self.auxAoTask = aoTask
            self.updateAuxOutputVoltage()
        else:
            if self.auxAoTask is not None:
                self.auxAoTask.stop()
                del self.auxAoTask
                self.auxAoTask = None

    def updateAuxOutputVoltage(self):
        if self.auxAoTask is None:
            return
        try:
            self.auxAoTask.writeData([self.auxAoSb.value()], autoStart=True)
        except Exception:
            exceptionString = traceback.format_exc()
            self.reportError(exceptionString)
            
    def collectAdrResistance(self, R):
        if self.hdfFile is None:
            return
        
        timeStamp = time.time()
        self.dsTimeStamps.resize((self.dsTimeStamps.shape[0]+1,))
        self.dsTimeStamps[-1] = timeStamp
        
        self.dsAdrResistance.resize((self.dsAdrResistance.shape[0]+1,))
        self.dsAdrResistance[-1] = R

    def populateDevices(self):
        self.deviceCombo.clear()
        system = daq.System()
        devices = system.findDevices()
        for dev in devices:
            self.deviceCombo.addItem(dev)
        
    def updateDevice(self):
        print("Updating device")
        self.aiChannelCombo.clear()
        self.aiDriveChannelCombo.clear()
        self.aoChannelCombo.clear()
        self.aiRangeCombo.clear()
        self.aoRangeCombo.clear()
        self.auxAoRangeCombo.clear()
        self.auxAoChannelCombo.clear()
        
        deviceName = str(self.deviceCombo.currentText())
        if len(deviceName) < 1:
            return
        device = daq.Device(deviceName)

        aiChannels = device.findAiChannels()
        for channel in aiChannels:
            self.aiChannelCombo.addItem(channel)
            self.aiDriveChannelCombo.addItem(channel)
        
        aoChannels = device.findAoChannels()
        for channel in aoChannels:
            self.aoChannelCombo.addItem(channel)
            self.auxAoChannelCombo.addItem(channel)
            
        self.aiRanges = device.voltageRangesAi()
        for r in self.aiRanges:
            self.aiRangeCombo.addItem('%+.2f -> %+.2f V' % (r.min, r.max))

        self.aoRanges = device.voltageRangesAo()            
        for r in self.aoRanges:
            self.aoRangeCombo.addItem('%+.2f -> %+.2f V' % (r.min, r.max))
            self.auxAoRangeCombo.addItem('%+.2f -> %+.2f V' % (r.min, r.max))
        
        if len(aiChannels):
            aiChannel = daq.AiChannel('%s/%s' % (deviceName, aiChannels[0]), self.aiRanges[0].min, self.aiRanges[0].max)
            aiTask = daq.AiTask('TestInputSampleRate_SineSweepDaq')
            aiTask.addChannel(aiChannel)
            aiSampleRate = aiTask.maxSampleClockRate()
            del aiTask
        else:
            aiSampleRate = 0

        if len(aoChannels):
            aoChannel = daq.AoChannel('%s/%s' % (deviceName, aoChannels[0]), self.aoRanges[0].min, self.aoRanges[0].max)
            aoTask = daq.AoTask('TestOutputSampleRate_SineSweepDaq')
            aoTask.addChannel(aoChannel)
            aoSampleRate = aoTask.maxSampleClockRate()
            del aoTask                
        else:
            aoSampleRate = 0
        
        rate = min(aiSampleRate, aoSampleRate)

        self.sampleRateSb.setMaximum(int(1E-3*rate))
        if rate < 1:
            return
        self.updateInfo()

    def terminalConfiguration(self):
        t = str(self.aiTerminalConfigCombo.currentText())
        tc = daq.AiChannel.TerminalConfiguration
        terminalConfigDict = {'RSE': tc.RSE, 'DIFF': tc.DIFF, 'NRSE': tc.NRSE}
        return terminalConfigDict[t]
            
    def updateInfo(self):
        nSettle = self.settlePeriodsSb.value(); nMeasure = self.measurePeriodsSb.value();
        tSettleMin = self.minSettleTimeSb.value(); tMeasureMin = self.minMeasureTimeSb.value()
        fStart = self.fStartSb.value(); fStop = self.fStopSb.value(); n=self.fStepsSb.value()
        print("fstart, fstop, n", fStart, fStop, n)
        fs = np.logspace(np.log10(fStart), np.log10(fStop), n)
        nMeasure = np.maximum(np.ceil(tMeasureMin*fs), nMeasure) # Compute the number of (integer) measurement periods for each frequency
        
        fSample = self.sampleRateSb.value()*1E3 # Now figure out which frequencies we can actually exactly synthesize
        k = np.ceil(nMeasure*fSample/fs)
        fs = nMeasure*fSample/k

        if False:        
            fBad = 15.  # Now eliminate uneven harmonics of 15 Hz
            fBadMax = 500 # But only below 500 Hz
            iBad = (np.abs(np.mod(fs, 2*fBad)-fBad) < 2) & (fs < fBadMax)
        else:
            iBad = np.zeros_like(fs, dtype=np.bool)
        self.pointsSkippedSb.setValue(np.count_nonzero(iBad))
        fs = fs[~iBad]
        self.nMeasure = nMeasure[~iBad]
        self.fGoals = fs
        periods = 1./fs
        self.nSettle = np.maximum(np.ceil(tSettleMin/periods), nSettle) # Compute the number of (integer) settle periods for each frequency
        tTotal = np.sum((self.nSettle+self.nMeasure) * periods) # TODO probably should add time for DAQmx setup overhead?
        self.totalTimeSb.setValue(tTotal)
        if fStart > 0 and fStop > 0:
            self.plot1.setXRange(np.log10(fStart), np.log10(fStop))
    def restoreSettings(self):
        s = QSettings(OrganizationName, ApplicationName)
        self.populateDevices()
        for w in self.settingsWidgets:
            restoreWidgetFromSettings(s, w)
        
    def saveSettings(self):
        s = QSettings(OrganizationName, ApplicationName)
        for w in self.settingsWidgets:
            saveWidgetToSettings(s, w)
        
    def closeEvent(self, event):
        if self.daqThread is not None:
            event.ignore()
            return
        self.saveSettings()
        super(SineSweepWidget, self).closeEvent(event)
        
    def restart(self):
        qApp.exit( self.EXIT_CODE_REBOOT )
                
    def startMeasurement(self):
        sampleRate = self.sampleRateSb.value()*1E3
        deviceName = str(self.deviceCombo.currentText())
        aoChannel = str(self.aoChannelCombo.currentText())
        aiChannel = str(self.aiChannelCombo.currentText())
        aiTerminalConfig = self.terminalConfiguration()
        aiRange = self.aiRanges[self.aiRangeCombo.currentIndex()]
        aoRange = self.aoRanges[self.aoRangeCombo.currentIndex()]
        offset = self.offsetSb.value()
        amplitude = self.amplitudeSb.value()
        fStart = self.fStartSb.value()
        fStop = self.fStopSb.value()
        fSteps = self.fStepsSb.value()
        settlePeriods = self.settlePeriodsSb.value(); measurePeriods = self.measurePeriodsSb.value()
        minSettleTime = self.minSettleTimeSb.value(); minMeasureTime = self.minMeasureTimeSb.value()

        s = QSettings('WiscXrayAstro', application='ADR3RunInfo')
        path = str(s.value('runPath', '', type=str))
        fileName = path+'/TF/%s_%s.h5' % (self.sampleLe.text(), time.strftime('%Y%m%d_%H%M%S'))
        self._fileName = fileName
        hdfFile = hdf.File(fileName, mode='w')
        hdfFile.attrs['Program'] = ApplicationName
        hdfFile.attrs['Version'] = Version
        hdfFile.attrs['Sample'] = str(self.sampleLe.text())
        hdfFile.attrs['Comment'] = str(self.commentLe.text())
        hdfFile.attrs['StartTimeLocal'] = time.strftime('%Y-%m-%d %H:%M:%S')
        hdfFile.attrs['StartTimeUTC'] =  time.strftime('%Y-%m-%d %H:%M:%SZ', time.gmtime())
        hdfFile.attrs['sampleRate'] = sampleRate
        hdfFile.attrs['offset'] = offset
        hdfFile.attrs['amplitude'] = amplitude
        hdfFile.attrs['fStart'] = fStart
        hdfFile.attrs['fStop'] = fStop
        hdfFile.attrs['fSteps'] = fSteps
        hdfFile.attrs['settlePeriods'] = settlePeriods
        hdfFile.attrs['measurePeriods'] = measurePeriods
        hdfFile.attrs['minSettleTime'] = minSettleTime
        hdfFile.attrs['minMeasureTime'] = minMeasureTime
        hdfFile.attrs['deviceName'] = deviceName
        hdfFile.attrs['aoChannel'] = aoChannel
        hdfFile.attrs['aoRangeMin'] = aoRange.min; hdfFile.attrs['aoRangeMax'] = aoRange.max
        hdfFile.attrs['aiChannel'] = aiChannel
        hdfFile.attrs['aiRangeMin'] = aiRange.min; hdfFile.attrs['aiRangeMax'] = aiRange.max
        hdfFile.attrs['aiTerminalConfig'] = str(self.aiTerminalConfigCombo.currentText())
        
        if self.auxAoTask is not None:
            hdfFile.attrs['auxAoChannel'] = str(self.auxAoTask.channels[0])
            auxAoRange = self.aoRanges[self.auxAoRangeCombo.currentIndex()]
            hdfFile.attrs['auxAoRangeMin'] = auxAoRange.min; hdfFile.attrs['auxAoRangeMax'] = auxAoRange.max 
            hdfFile.attrs['auxAoValue'] = self.auxAoSb.value()

        hkGroup = hdfFile.require_group('HK')
        self.hkLogger = HkLogger(hkGroup, self.hkSub) # Should remove stuff below soon - only kept for backwards compatibility
        self.dsTimeStamps = hdfFile.create_dataset('AdrResistance_TimeStamps', (0,), maxshape=(None,), chunks=(500,), dtype=np.float64)
        self.dsTimeStamps.attrs['units'] = 's'
        self.dsAdrResistance = hdfFile.create_dataset('AdrResistance', (0,), maxshape=(None,), chunks=(500,), dtype=np.float64)
        self.dsAdrResistance.attrs['units'] = 'Ohms'
        self.hdfFile = hdfFile

        lia = LockIn(); self.lia = lia
        liaThread = QThread(parent=self); self.liaThread = liaThread
        lia.moveToThread(liaThread)
        lia.integrationComplete.connect(self.collectData)

#        if self.recordDriveCb.isChecked():
#            aiDriveChannel = str(self.aiDriveChannelCombo.currentText())
#            hdfFile.attrs['aiDriveChannel'] = aiDriveChannel
#            thread.enableDriveRecording(aiDriveChannel)
        
        daqThread = DaqThread(deviceName, aoChannel, aoRange, aiChannel, aiRange, aiTerminalConfig, parent=self); self.daqThread = daqThread
        daqThread.setSampleRate(sampleRate)
        daqThread.setParameters(self.fGoals, self.nSettle, self.nMeasure)
        daqThread.setExcitation(amplitude, offset)
        self.sweep = Sweep(nPoints = len(self.fGoals), parent=self)
        if self.enablePlotCb.isChecked():
            daqThread.waveformAvailable.connect(self.showWaveform)
        daqThread.waveformAvailable.connect(lia.integrateData)
        daqThread.waveformComplete.connect(lia.completeIntegration)
        daqThread.error.connect(self.reportError)
        self.enableWidgets(False)
        daqThread.finished.connect(self.daqThreadFinished)
        liaThread.finished.connect(self.liaThreadFinished)

        nCollected = gc.collect()
        print('GC collected:', nCollected)
        gc.disable()
        
        liaThread.started.connect(lambda: daqThread.start(QThread.HighestPriority)) # Start producer after consumer
        liaThread.start()

    def fileName(self):
        return self._fileName

    def reportError(self, message):
        tString = time.strftime('%Y%m%d_%H%M%S')
        QMessageBox.critical(self, 'Exception encountered at %s!' % (tString), message) # WORK

    def stopMeasurement(self):
        if self.daqThread is None:
            return
        if self.stopPb.text() == 'Stop':
            self.daqThread.stop()
            self.stopPb.setText('Abort')
            self.measurementFinished()
        else:
            self.daqThread.abort()
            self.daqThread.wait(1000)
            self.measurementFinished()

    def daqThreadFinished(self):
        print("DAQ thread finished")
        del self.daqThread
        self.daqThread = None
        
    def liaThreadFinished(self):
        print("LIA thread finished")
        del self.liaThread; self.liaThread = None
        del self.lia; self.lia = None
        
    def measurementFinished(self):
        del self.hkLogger; self.hkLogger = None
        if self.liaThread is not None:
            self.liaThread.quit()
        grp = self.hdfFile.create_group('Sweep')
        self.sweep.toHdf(grp)
        self.closeFile()
        self.stopPb.setText('Stop')
        self.enableWidgets(True)
        gc.enable()
        
    def closeFile(self):
        if self.hdfFile is not None:
            t = time.time()
            self.hdfFile.attrs['StopTimeLocal'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(t))
            self.hdfFile.attrs['StopTimeUTC'] =  time.strftime('%Y-%m-%d %H:%M:%SZ', time.gmtime(t))
            self.hdfFile.close()
            del self.hdfFile
            self.hdfFile = None
            
    def enableWidgets(self, enable):
        self.driveGroupBox.setEnabled(enable)
        self.inputGroupBox.setEnabled(enable)
        self.startPb.setEnabled(enable)
        self.stopPb.setEnabled(not enable)
        
    def showWaveform(self, phases, y):
        self.responseCurve.setData(phases, y)

    def collectData(self, *args):
        #try:
        self.sweep.collectData(*args)
        f = args[1]
        self.currentFrequencySb.setValue(f)
        self.updatePlot()
        if self.sweep.isComplete():
            self.measurementFinished()
            
        #except Exception as e:
        #    print("Exception:",e)
            
    def yAxisChanged(self):
        yAxis = str(self.plotCombo.currentText())
        if yAxis=='X/Y':
            self.plot1.setLabel('left', 'X', units='')
            self.plot2.setLabel('left', 'Y', units='')
        elif yAxis=='R/Phi':
            self.plot1.setLabel('left', 'R', units='')
            self.plot2.setLabel('left', 'Phi', units='deg')
        elif yAxis=='Vmax/Vmin':
            self.plot1.setLabel('left', 'Vmax', units='V')
            self.plot2.setLabel('left', 'Vmin', units='V')
        elif yAxis=='R/Vdc':
            self.plot1.setLabel('left', 'R', units='')
            self.plot2.setLabel('left', 'Vdc', units='V')
        ylog = yAxis[0] == 'R'
        self.plot1.setLogMode(x=True, y=ylog)
        self.updatePlot()

    def updatePlot(self):
        yAxis = str(self.plotCombo.currentText())
        s = self.sweep
        x = s.f
        if yAxis=='X/Y':
            y1 = s.X
            y2 = s.Y
        elif yAxis=='R/Phi':
            y1 = s.R
            y2 = rad2deg*s.Theta
        elif yAxis=='Vmax/Vmin':
            y1 = s.Vmax
            y2 = s.Vmin
        elif yAxis=='R/Vdc':
            y1 = s.R
            y2 = s.dc
        else:
            warnings.warn("Unsupported y-axis: %s" % yAxis)
            return
        self.curve1.setData(x,y1)
        self.curve2.setData(x,y2)
        X = s.X; Y = s.Y
        self.curvexy.setData(X, Y)
Exemple #12
0
    def __init__(self, parent=None):
        super(IvCurveWidget, self).__init__(parent)
        self.setupUi(self)
        self.restartPb.clicked.connect(self.restart)
        self.thread = None
        self.hdfFile = None
        self.squid = None
        self._fileName = ''
        self.plot.addLegend()
        self.plot.setLabel('left', 'SQUID response', units='V')
        self.plot.setLabel('bottom', 'bias voltage', units='V')
        self.curves = []
        self.startPb.clicked.connect(self.startMeasurement)
        self.stopPb.clicked.connect(self.stopMeasurement)

        #        self.aoRangeCombo.currentIndexChanged.connect(self.updateAoRange)
        #        self.auxAoChannelCombo.currentIndexChanged.connect(self.updateAuxAoRange)

        self.osr = OpenSquidRemote(port=7894)
        squids = self.osr.findSquids()
        self.tesCombo.addItem('None')
        self.tesCombo.addItems(squids)

        self.settingsWidgets = [
            self.deviceCombo, self.aoChannelCombo, self.aoRangeCombo,
            self.aiChannelCombo, self.aiRangeCombo, self.aiTerminalConfigCombo,
            self.aiDriveChannelCombo, self.recordDriveCb, self.maxDriveSb,
            self.slewRateSb, self.zeroHoldTimeSb, self.peakHoldTimeSb,
            self.betweenHoldTimeSb, self.decimateCombo, self.sampleRateSb,
            self.sampleLe, self.commentLe, self.enablePlotCb,
            self.auxAoChannelCombo, self.auxAoRangeCombo, self.auxAoSb,
            self.auxAoEnableCb, self.polarityCombo, self.tesCombo,
            self.pflResetCb, self.driveOffsetSb
        ]
        self.deviceCombo.currentIndexChanged.connect(self.updateDevice)
        for w in [
                self.maxDriveSb, self.slewRateSb, self.zeroHoldTimeSb,
                self.peakHoldTimeSb, self.betweenHoldTimeSb, self.sampleRateSb
        ]:
            w.valueChanged.connect(self.updateInfo)
        self.decimateCombo.currentIndexChanged.connect(self.updateInfo)
        self.polarityCombo.currentIndexChanged.connect(self.updateInfo)
        self.auxAoSb.valueChanged.connect(self.updateAuxOutputVoltage)
        self.auxAoEnableCb.toggled.connect(self.toggleAuxOut)
        self.auxAoRamper = None
        self.restoreSettings()
        self.hkSub = HousekeepingSubscriber(self)
        self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue)
        self.hkSub.adrResistanceReceived.connect(self.collectAdrResistance)
        self.hkSub.start()

        self.serverThread = RequestReplyThreadWithBindings(
            port=RequestReply.IvCurveDaq, parent=self)
        boundWidgets = {
            'sampleName': self.sampleLe,
            'auxAoEnable': self.auxAoEnableCb,
            'auxVoltage': self.auxAoSb,
            'maxDrive': self.maxDriveSb,
            'slewRate': self.slewRateSb,
            'start': self.startPb,
            'stop': self.stopPb,
            'totalTime': self.totalTimeSb,
            'sweepCount': self.sweepCountSb,
            'comment': self.commentLe,
            'driveOffset': self.driveOffsetSb
        }
        for name in boundWidgets:
            self.serverThread.bindToWidget(name, boundWidgets[name])
        self.serverThread.bindToFunction('fileName', self.fileName)
        self.serverThread.bindToFunction('restart', self.requestRestart)
        self.serverThread.start()

        pens = 'rgbc'
        for i in range(4):
            curve = pg.PlotDataItem(pen=pens[i], name='Curve %d' % i)
            self.plot.addItem(curve)
            self.curves.append(curve)

        self.restartRequested = False
        timer = QTimer(self)
        timer.timeout.connect(self.checkRestartRequested)
        timer.setInterval(1000)
        timer.start()
        self.timer = timer
        self.logger.info('IvCurveDaq started.')
class TemperatureControlMainWindow(Ui.Ui_MainWindow, QMainWindow):
    def __init__(self, parent = None):
        super(TemperatureControlMainWindow, self).__init__(parent)
        self.setupUi(self)
        self.setpointSb.setDecimals(7)
        self.setpointSb.setSingleStep(1E-7)
        self.rampRateSb.setDecimals(2)
        self.rampRateSb.setSingleStep(1E-2)
        self.serverThread = None
        self.selectedSensorName = ''
        self.clearData()
        self.pid = None
        self.outputFile = None
        self.magnetControlRemote = None
        self.rampEnableCb.toggled.connect(self.fixupRampRate)
        self.updatePlotsCb.toggled.connect(self.showPlots)
        
        self.widgetsForSettings = [self.thermometerCombo, 
                                   self.setpointSb, self.KSb, self.TiSb, self.TdSb,
                                   self.TtSb, self.TfSb, self.betaSb, self.gammaSb,
                                   self.controlMinSb, self.controlMaxSb, self.rampRateSb,
                                   self.rampTargetSb, self.rampEnableCb, self.updatePlotsCb,
                                   self.useBaseTemperatureCb]
        
        axis = pg.DateAxisItem(orientation='bottom')
        self.pvPlot = pg.PlotWidget(axisItems={'bottom': axis})
        self.mainVerticalLayout.addWidget(self.pvPlot)

        self.pvPlot.addLegend()
        self.curvePv = pg.PlotCurveItem(name='Actual', symbol='o', pen='g')
        self.curveSetpoint = pg.PlotCurveItem(name='Setpoint', symbol='o', pen='r')
        self.pvPlot.addItem(self.curvePv)
        self.pvPlot.addItem(self.curveSetpoint)


        axis = pg.DateAxisItem(orientation='bottom')
        self.pidPlot = pg.PlotWidget(axisItems={'bottom': axis})
        self.mainVerticalLayout.addWidget(self.pidPlot)
        self.pidPlot.addLegend()
        self.curveP = pg.PlotCurveItem(name='P', symbol='o', pen='r')
        self.curveI = pg.PlotCurveItem(name='I', symbol='o', pen='g')
        self.curveD = pg.PlotCurveItem(name='D', symbol='o', pen='b')
        self.curveControl = pg.PlotCurveItem(name='control', symbol='o', pen='w')
        self.pidPlot.addItem(self.curveP)
        self.pidPlot.addItem(self.curveI)
        self.pidPlot.addItem(self.curveD)
        self.pidPlot.addItem(self.curveControl)

        self.KSb.setMinimum(-1E6)
        self.KSb.setMaximum(1E6)

        self.hkSub = HousekeepingSubscriber(self)
        self.hkSub.thermometerListUpdated.connect(self.updateThermometerList)
        self.hkSub.thermometerReadingReceived.connect(self.receiveTemperature)
        self.hkSub.start()

        self.startPb.clicked.connect(self.startPid)
        self.stopPb.clicked.connect(self.stopPid)

        self.timer = QTimer(self)   # A 250 ms timer to implement ramping
        self.timer.timeout.connect(self.updateSetpoint)
        self.tOld = time.time()
        self.timer.start(250)

        self.thermometerCombo.currentIndexChanged.connect(self.updateThermometerSelection)

        self.restoreSettings()
        # Give the hkSub some time to update thermometer list before we update thermometer selection
        QTimer.singleShot(2000, self.restoreThermometerSelection) 
        
    def restoreThermometerSelection(self):
        s = QSettings()
        restoreWidgetFromSettings(s, self.thermometerCombo)

    def updateThermometerSelection(self):
        t = str(self.thermometerCombo.currentText())
        self.selectedSensorName = t
        
    def updateThermometerList(self, thermometerList):
        selected = self.thermometerCombo.currentText()
        self.thermometerCombo.clear()
        self.thermometerCombo.addItems(thermometerList)
        i = self.thermometerCombo.findText(selected)
        self.thermometerCombo.setCurrentIndex(i)
        
    def startServerThread(self):
        self.serverThread = RequestReplyThreadWithBindings(port=RequestReply.AdrPidControl, parent=self)
        boundWidgets = {'stop': self.stopPb,'start': self.startPb,'rampRate':self.rampRateSb, 'rampTarget':self.rampTargetSb, 'rampEnable':self.rampEnableCb, 'setpoint':self.setpointSb}
        for name in boundWidgets:
            self.serverThread.bindToWidget(name, boundWidgets[name])
        logger.info('Starting server thread')
        self.serverThread.start()
        
    def showPlots(self, enable):
        self.pvPlot.setVisible(enable)
        self.pidPlot.setVisible(enable)
        self.adjustSize()
    
    def fixupRampRate(self, doIt):
        if doIt:
            Tset = self.setpointSb.value()
            Ttarget = self.rampTargetSb.value()
            absRate = abs(self.rampRateSb.value())
            if Tset < Ttarget:
                self.rampRateSb.setValue(+absRate)
            else:
                self.rampRateSb.setValue(-absRate)
        
    def updateSetpoint(self):
        t = time.time()
        if self.rampEnableCb.isChecked():
            Tset = self.setpointSb.value()
            Ttarget = self.rampTargetSb.value()
            rate = self.rampRateSb.value() * SIUnits.mK/SIUnits.minute
            deltaT = rate * (t-self.tOld)
            if rate > 0:
                Tset = min(Ttarget, Tset+deltaT)
            elif rate < 0:
                Tset = max(Ttarget, Tset+deltaT)
            self.setpointSb.setValue(Tset)
            if Tset == Ttarget:
                self.rampEnableCb.setChecked(False)
        self.tOld = t                

    def startPid(self):
        self.enableControls(False)
        pid = Pid(parent=self)
        pid.setControlMaximum(self.controlMaxSb.value()*mAperMin)
        pid.setControlMinimum(self.controlMinSb.value()*mAperMin)
        
        self.setpointSb.setValue(self.pv)

        self.KSb.valueChanged.connect(self.updatePidParameters)
        self.TiSb.valueChanged.connect(self.updatePidParameters)
        self.TdSb.valueChanged.connect(self.updatePidParameters)
        self.TfSb.valueChanged.connect(self.updatePidParameters)
        self.TtSb.valueChanged.connect(self.updatePidParameters)
        self.betaSb.valueChanged.connect(self.updatePidParameters)
        self.gammaSb.valueChanged.connect(self.updatePidParameters)
        self.controlMinSb.valueChanged.connect(lambda x: pid.setControlMinimum(x*mAperMin))
        self.controlMaxSb.valueChanged.connect(lambda x: pid.setControlMaximum(x*mAperMin))
        self.magnetControlRemote = MagnetControlRemote('AdrTemperatureControlPid', parent=self)
        if not self.magnetControlRemote.checkConnection():
            self.appendErrorMessage('Cannot connect to magnet control')
            self.stopPid(abort=True)
            return
        self.magnetControlRemote.error.connect(self.appendErrorMessage)
        self.magnetControlRemote.disableDriftCorrection()
        self.pid = pid # This effectively enables the loop
        self.updatePidParameters()
        self.startServerThread()

    def endServerThread(self):
        if self.serverThread is not None:
            logger.info('Stopping server thread')
            self.serverThread.stop()
            self.serverThread.wait(1000)
            del self.serverThread
            self.serverThread = None

    def stopPid(self, abort=False):
        self.endServerThread()
        if self.pid:
            del self.pid
            self.pid = None
        if self.magnetControlRemote:
            if not abort:
                self.requestRampRate(0.0)
                self.magnetControlRemote.enableDriftCorrection()
            self.magnetControlRemote.stop()
            self.magnetControlRemote.wait(2)
            self.magnetControlRemote.deleteLater()
            self.magnetControlRemote = None
        
        if self.outputFile is not None:
            self.outputFile.close()
        self.enableControls(True)

    def updatePidParameters(self):
        if self.pid is None: return
        #print "Updating PID parameters"
        K = self.KSb.value()*mAperMin
        Ti = self.TiSb.value()
        Td = self.TdSb.value()
        Tt = self.TtSb.value()
        Tf = self.TfSb.value()

        beta = self.betaSb.value()
        gamma = self.gammaSb.value()

        self.pid.updateParameters(K, Ti, Td, Tt, Tf, beta, gamma)

        if self.outputFile is not None:
            self.outputFile.close()
            self.outputFile = None

        s = QSettings('WiscXrayAstro', application='ADR3RunInfo')
        path = str(s.value('runPath', '', type=str))

        timeString = time.strftime('%Y%m%d-%H%M%S')
        fileName = path+'/AdrPID_%s.dat' % timeString
        self.outputFile = open(fileName, 'a+')
        self.outputFile.write('#AdrTemperatureControlPid.py\n')
        self.outputFile.write('#Date=%s\n' % timeString)
        self.outputFile.write('#K=%.8g\n' % K)
        self.outputFile.write('#ti=%.6g\n' % Ti)
        self.outputFile.write('#td=%.6g\n' % Td)
        self.outputFile.write('#tt=%.6g\n' % Tt)
        self.outputFile.write('#tf=%.6g\n' % Tf)
        self.outputFile.write('#beta=%.6g\n' % beta)
        self.outputFile.write('#gamma=%.6g\n' % gamma)
        self.outputFile.write('#'+'\t'.join(['time', 'Ts', 'T', 'u', 'p', 'i', 'd' ])+'\n')

    def requestRampRate(self, rate):
        '''Request new ramp rate [units: A/s]'''
        #print "Requesting new ramp rate:", rate, 'A/s'
        ok = self.magnetControlRemote.changeRampRate(rate)
        if not ok: 
            self.appendErrorMessage('Unable to change ramp rate. Aborting.')
            self.stopPid(abort=True)

    def appendErrorMessage(self, message):
        timeString = time.strftime('%Y%m%d-%H%M%S')
        self.errorTextEdit.append('%s: %s' % (timeString, str(message)))        
        logger.error('%s: %s' % (timeString, str(message)))
        
    def updateLoop(self, sp, pv):
        if self.pid is None: return
        if self.pv == 0: return
        (u, p, i, d) = self.pid.updateLoop(sp, pv)
        self.requestRampRate(u)

        t = time.time()
        string = "%.3f\t%.6g\t%.6g\t%.6g\t%.6g\t%.6g\t%.6g\n" % (t, sp, pv, u, p, i, d)
        self.outputFile.write(string)

        unit = mAperMin
        total = p+i+d
        self.controlIndicator.setValue(u/unit)
        self.pIndicator.setValue(p/unit)
        self.iIndicator.setValue(i/unit)
        self.dIndicator.setValue(d/unit)
        self.totalIndicator.setValue(total/unit)
        
        self.ts_pid.append(t)
        self.ps.append(p/unit)
        self.Is.append(i/unit)
        self.ds.append(d/unit)
        self.controls.append(u/unit)
        
        historyLength = 10000
        if len(self.ts_pid) > 1.1*historyLength:
            self.ts_pid = self.ts_pid[-historyLength:]
            self.ps = self.ps[-historyLength:]
            self.Is = self.Is[-historyLength:]
            self.ds = self.ds[-historyLength:]
            self.controls = self.controls[-historyLength:]
            
        if self.updatePlotsCb.isChecked():
            self.curveP.setData(self.ts_pid, self.ps)
            self.curveI.setData(self.ts_pid, self.Is)
            self.curveD.setData(self.ts_pid, self.ds)
            self.curveControl.setData(self.ts_pid, self.controls)

    def enableControls(self, enable):
        self.thermometerCombo.setEnabled(enable)
        self.startPb.setEnabled(enable)
        self.stopPb.setEnabled(not enable)

    def restoreSettings(self):
        settings = QSettings()
        for widget in self.widgetsForSettings:
            restoreWidgetFromSettings(settings, widget)

    def saveSettings(self):
        settings = QSettings()
        for widget in self.widgetsForSettings:
            saveWidgetToSettings(settings, widget)

    def clearData(self):
        self.ts_pid = []
        self.ps = []
        self.Is = []
        self.ds = []
        self.controls = []

        self.ts_pv = []
        self.pvs = []

        #self.ts_setpoint = []
        self.setpoints = []

    def receiveTemperature(self, sensorName, t, R, T, P, Tbase):
        if sensorName != self.selectedSensorName:
            return
        
        if self.useBaseTemperatureCb.isChecked():
            pv = Tbase
        else:
            pv = T
            
        sp = self.setpointSb.value()
        self.pv = pv
        self.updateLoop(sp, pv)

        t = time.time()
        self.pvIndicator.setValue(pv)
        self.ts_pv.append(t)
        self.pvs.append(pv)
        self.setpoints.append(sp)
        
        historyLength = 10000
        if len(self.ts_pv) > 1.1*historyLength:
            self.ts_pv = self.ts_pv[-historyLength:]
            self.pvs = self.pvs[-historyLength:]
            self.setpoints = self.setpoints[-historyLength:]

        if self.updatePlotsCb.isChecked():
            self.curvePv.setData(self.ts_pv, self.pvs)
            self.curveSetpoint.setData(self.ts_pv, self.setpoints)

    def closeEvent(self, e):
        if self.pid is not None:
            self.stopPid()
        self.hkSub.stop()
        if self.serverThread is not None:
            self.serverThread.stop()
            self.serverThread.wait(1000)
        if self.magnetControlRemote is not None:
            self.magnetControlRemote.stop()
            self.magnetControlRemote.wait(1000)
        self.hkSub.wait(1000)
        self.saveSettings()
Exemple #14
0
class IVSweepDaqWidget(IVSweepsDaqUi.Ui_Form, QWidget):
    def __init__(self, parent=None):
        super(IVSweepDaqWidget, self).__init__(parent)
        self.setupUi(self)

        self.aoDeviceCombo.currentIndexChanged.connect(
            self.updateDaqChannelsAo)
        self.aiDeviceCombo.currentIndexChanged.connect(
            self.updateDaqChannelsAi)
        self.populateDaqCombos()
        self.restoreSettings()
        self.msmThread = None
        self.startPb.clicked.connect(self.startPbClicked)
        self.hkSub = HousekeepingSubscriber(self)
        self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue)
        self.hkSub.start()
        self.rawPlot.setAxisTitle(QwtPlot.yLeft, 'Vmeas')
        self.rawPlot.setAxisTitle(QwtPlot.xBottom, 'Vdrive')
        self.rawCurve = QwtPlotCurve('')
        self.rawCurve.attach(self.rawPlot)
        self.criticalCurve1 = QwtPlotCurve('+')
        self.criticalCurve1.setSymbol(
            Qwt.QwtSymbol(Qwt.QwtSymbol.Cross, Qt.QBrush(), Qt.QPen(Qt.Qt.red),
                          Qt.QSize(5, 5)))
        self.criticalCurve1.attach(self.criticalPlot)
        self.criticalCurve2 = QwtPlotCurve('-')
        self.criticalCurve2.setSymbol(
            Qwt.QwtSymbol(Qwt.QwtSymbol.Cross, Qt.QBrush(),
                          Qt.QPen(Qt.Qt.blue), Qt.QSize(5, 5)))
        self.criticalCurve2.attach(self.criticalPlot)
        self.criticalPlot.setAxisTitle(QwtPlot.yLeft, 'Vcrit')
        self.clearData()
        self.clearCriticalData()
        self.clearPb.clicked.connect(self.clearData)
        self.coilSweepCb.toggled.connect(self.toggleCoilSweep)
        self.clearCriticalPb.clicked.connect(self.clearCriticalData)
        self.samplesPerPointSb.valueChanged.connect(
            lambda value: self.discardSamplesSb.setMaximum(value - 1))
        self.coilEnableCb.toggled.connect(self.toggleCoil)
        self.coilVoltageSb.valueChanged.connect(self.updateCoilVoltage)
        self.toggleCoil(self.coilEnableCb.isChecked())
        self.coilDriverCombo.currentIndexChanged.connect(
            self.coilDriverChanged)
        self.Vcoil = np.nan

    def coilDriverChanged(self):
        text = self.coilDriverCombo.currentText()
        self.coilVisaLabel.setText('%s VISA' % text)
        self.coilVisaCombo.clear()
        if text == 'SR830':
            suffix = ' V'
            maxDrive = 10
            self.coilVisaCombo.addItem('GPIB0::12')
            self.auxOutChannelSb.setEnabled(True)

        elif text == 'Keithley 6430':
            suffix = ' mA'
            maxDrive = 50
            self.coilVisaCombo.addItem('GPIB0::24')
            self.auxOutChannelSb.setEnabled(False)

        controls = [
            self.coilVoltageSb, self.coilSweepMinSb, self.coilSweepMaxSb
        ]
        for control in controls:
            control.setSuffix(suffix)
            control.setMinimum(-maxDrive)
            control.setMaximum(+maxDrive)

    def toggleCoil(self, enabled):
        self.coilDriverCombo.setEnabled(not enabled)
        if enabled:
            driver = self.coilDriverCombo.currentText()
            if driver == 'SR830':
                self.sr830 = SR830(str(self.coilVisaCombo.currentText()))
                self.coilAo = VoltageSourceSR830(self.sr830,
                                                 self.auxOutChannelSb.value())
            elif driver == 'Keithley 6430':
                self.coilAo = CurrentSourceKeithley(str(
                    self.coilVisaCombo.currentText()),
                                                    currentRange=10E-3)
            self.Vcoil = self.coilAo.dcDrive()
            self.coilVoltageSb.setValue(self.Vcoil)
            self.auxOutChannelSb.setEnabled(False)
            self.coilVisaCombo.setEnabled(False)
        else:
            self.coilAo = None
            self.auxOutChannelSb.setEnabled(True)
            self.coilVisaCombo.setEnabled(True)

    def updateCoilVoltage(self, V):
        if self.coilAo is not None:
            self.coilAo.setDcDrive(V)
            self.Vcoil = self.coilAo.dcDrive()

    def updateDaqChannelsAi(self):
        dev = str(self.aiDeviceCombo.currentText())
        self.aiChannelCombo.clear()
        if len(dev):
            device = daq.Device(dev)
            aiChannels = device.findAiChannels()
            for channel in aiChannels:
                self.aiChannelCombo.addItem(channel)

    def updateDaqChannelsAo(self):
        dev = str(self.aoDeviceCombo.currentText())
        self.aoChannelCombo.clear()
        if len(dev):
            device = daq.Device(dev)
            aoChannels = device.findAoChannels()
            for channel in aoChannels:
                self.aoChannelCombo.addItem(channel)

    def populateDaqCombos(self):
        system = daq.System()
        devices = system.findDevices()
        for devName in devices:
            dev = daq.Device(devName)
            if len(dev.findAiChannels()):
                self.aiDeviceCombo.addItem(devName)
            if len(dev.findAoChannels()):
                self.aoDeviceCombo.addItem(devName)

    def clearData(self):
        self.Vdrive = []
        self.Vmeas = []
        self.rawCurve.setData(self.Vdrive, self.Vmeas)
        self.rawPlot.replot()

    def clearCriticalData(self):
        self.Vcrit1 = []
        self.Vcrit2 = []
        self.Tcrit1 = []
        self.Tcrit2 = []
        self.VcoilCrit1 = []
        self.VcoilCrit2 = []
        self.updateCriticalPlot()

    def updateRawData(self, t, Vsource, Vmeas, Vo, T):
        string = "%.3f\t%.6f\t%.6f\t%.6f\t%.6f\t%.3f\n" % (t, Vsource, Vmeas,
                                                           Vo, T, self.Vcoil)
        self.outputFile.write(string)
        self.Vdrive.append(Vsource)
        self.Vmeas.append(Vmeas - Vo)
        maxLength = 10000
        if len(self.Vdrive) > int(
                maxLength * 1.1):  # Automatically expire old data
            self.Vdrive = self.Vdrive[-maxLength:]
            self.Vmeas = self.Vmeas[-maxLength:]
        self.rawCurve.setData(self.Vdrive, self.Vmeas)
        self.rawPlot.replot()

    def toggleCoilSweep(self, checked):
        if checked:
            self.coilVoltages = np.linspace(self.coilSweepMinSb.value(),
                                            self.coilSweepMaxSb.value(),
                                            self.coilSweepStepsSb.value())
            self.coilVoltages = np.append(self.coilVoltages,
                                          self.coilVoltages[::-1])
            self.currentCoilStep = 0
            self.stepCoil()
        else:
            self.coilVoltages = []
            self.currentCoilStep = 0

    def stepCoil(self):
        if self.coilAo is not None:
            self.coilVoltageSb.setValue(
                self.coilVoltages[self.currentCoilStep])
        self.currentCoilStep += 1
        if self.currentCoilStep >= len(self.coilVoltages):  # Start over
            self.currentCoilStep = 0

    def collectSweep(self, T, Vc):
        if Vc >= 0:
            self.Tcrit1.append(T)
            self.Vcrit1.append(Vc)
            self.VcoilCrit1.append(self.Vcoil)
        else:
            self.Tcrit2.append(T)
            self.Vcrit2.append(-Vc)
            self.VcoilCrit2.append(self.Vcoil)
        self.updateCriticalPlot()

        if self.coilSweepCb.isChecked():  # Move on to next coil voltage
            if self.bipolarCb.isChecked():
                if not len(self.Tcrit2) == len(self.Tcrit1):
                    print "Still need to do negative"
                    return
            self.stepCoil()

    def updateCriticalPlot(self):
        xAxis = self.plotAxisCombo.currentText()
        if xAxis == 'T':
            x1 = self.Tcrit1
            x2 = self.Tcrit2
            self.criticalPlot.setAxisTitle(QwtPlot.xBottom, 'T')
        elif xAxis == 'Coil V':
            x1 = self.VcoilCrit1
            x2 = self.VcoilCrit2
            self.criticalPlot.setAxisTitle(QwtPlot.xBottom, 'Coil V')
        self.criticalCurve1.setData(x1, self.Vcrit1)
        self.criticalCurve2.setData(x2, self.Vcrit2)
        self.criticalPlot.replot()

    def startPbClicked(self):
        timeString = time.strftime('%Y%m%d-%H%M%S')
        fileName = self.sampleLineEdit.text() + '_%s_IV.dat' % timeString
        self.outputFile = open(fileName, 'a+')
        self.outputFile.write('#Program=IVSweepsDaq.py\n')
        self.outputFile.write('#Date=%s\n' % timeString)
        self.outputFile.write('#Sample=%s\n' % self.sampleLineEdit.text())
        self.outputFile.write('#Source=%s\n' % self.sourceCombo.currentText())
        self.outputFile.write('#Pre-amp gain=%.5g\n' %
                              self.preampGainSb.value())
        self.outputFile.write('#Drive impedance=%.6g\n' %
                              self.dcDriveImpedanceSb.value())
        self.outputFile.write('#Inter-sweep delay=%d\n' %
                              self.interSweepDelaySb.value())
        self.outputFile.write('#Samples per point=%d\n' %
                              self.samplesPerPointSb.value())
        self.outputFile.write('#Discard samples=%d\n' %
                              self.discardSamplesSb.value())
        self.outputFile.write('#Threshold=%.5g\n' %
                              self.thresholdVoltageSb.value())
        self.outputFile.write('#Bipolar=%d\n' %
                              int(self.bipolarCb.isChecked()))
        if self.coilAo is not None:
            self.outputFile.write('#Coil enabled=1\n')
            self.outputFile.write('#Coil driver=%s\n' % self.coilAo.name())
            self.outputFile.write('#Coil drive=%.3g\n' % self.coilAo.dcDrive())
        else:
            self.outputFile.write('#Coil enabled=0\n')
        if self.coilSweepCb.isChecked():
            self.outputFile.write('#Coil sweep=1\n')
            self.outputFile.write('#Coil min=%.3f\n' %
                                  self.coilSweepMinSb.value())
            self.outputFile.write('#Coil max=%.3f\n' %
                                  self.coilSweepMaxSb.value())
            self.outputFile.write('#Coil steps=%d\n' %
                                  self.coilSweepStepsSb.value())
        else:
            self.outputFile.write('#Coil sweep=0\n')

        self.ao = VoltageSourceDaq(str(self.aoDeviceCombo.currentText()),
                                   str(self.aoChannelCombo.currentText()))
        self.ai = VoltmeterDaq(str(self.aiDeviceCombo.currentText()),
                               str(self.aiChannelCombo.currentText()),
                               -10,
                               10,
                               samples=self.samplesPerPointSb.value(),
                               drop=self.discardSamplesSb.value())
        self.msmThread = IVSweepMeasurement(self.ao, self.ai, self)
        self.msmThread.setFileName(fileName)
        self.msmThread.setThreshold(self.thresholdVoltageSb.value())
        self.msmThread.readingAvailable.connect(self.updateRawData)
        self.msmThread.setMinimumVoltage(self.startVSb.value())
        self.msmThread.setMaximumVoltage(self.stopVSb.value())
        self.msmThread.setSteps(self.stepsSb.value())
        self.msmThread.setInterSweepDelay(self.interSweepDelaySb.value())
        self.msmThread.sweepComplete.connect(self.collectSweep)
        self.msmThread.enableBipolar(self.bipolarCb.isChecked())
        self.hkSub.adrTemperatureReceived.connect(
            self.msmThread.updateTemperature)
        self.msmThread.finished.connect(self.finished)
        self.stopPb.clicked.connect(self.msmThread.stop)
        self.thresholdVoltageSb.valueChanged.connect(
            self.msmThread.setThreshold)
        self.outputFile.write(
            '#' + '\t'.join(['time', 'Vdrive', 'Vmeas', 'Vo', 'T', 'Vcoil']) +
            '\n')
        self.enableWidgets(False)
        self.msmThread.start()
        print "Thread started"

    def finished(self):
        self.ai.clear()
        self.ao.clear()
        self.outputFile.close()
        self.enableWidgets(True)
        self.msmThread.deleteLater()


#        self.updateStatus('Completed')

    def enableWidgets(self, enable=True):
        self.sampleLineEdit.setEnabled(enable)
        self.startPb.setEnabled(enable)
        self.dcDriveImpedanceSb.setEnabled(enable)
        self.aiChannelCombo.setEnabled(enable)
        self.aiDeviceCombo.setEnabled(enable)
        self.aoChannelCombo.setEnabled(enable)
        self.aoDeviceCombo.setEnabled(enable)
        self.startVSb.setEnabled(enable)
        self.stopVSb.setEnabled(enable)
        self.stepsSb.setEnabled(enable)
        self.interSweepDelaySb.setEnabled(enable)
        self.bipolarCb.setEnabled(enable)

        self.stopPb.setEnabled(not enable)

    def closeEvent(self, e):
        if self.msmThread:
            self.msmThread.stop()
        if self.hkSub:
            self.hkSub.stop()
        self.saveSettings()
        super(IVSweepDaqWidget, self).closeEvent(e)

    def saveSettings(self):
        s = QSettings()
        s.setValue('sampleId', self.sampleLineEdit.text())
        s.setValue('dcDriveImpedance', self.dcDriveImpedanceSb.value())
        s.setValue('bipolar', self.bipolarCb.isChecked())
        s.setValue('startV', self.startVSb.value())
        s.setValue('stopV', self.stopVSb.value())
        s.setValue('steps', self.stepsSb.value())
        s.setValue('interSweepDelay', self.interSweepDelaySb.value())
        s.setValue('thresholdVoltage', self.thresholdVoltageSb.value())
        s.setValue('samplesPerPoint', self.samplesPerPointSb.value())
        s.setValue('discardSamples', self.discardSamplesSb.value())

        s.setValue('preampGain', self.preampGainSb.value())
        s.setValue('geometry', self.saveGeometry())
        s.setValue('AI_Device', self.aiDeviceCombo.currentText())
        s.setValue('AO_Device', self.aoDeviceCombo.currentText())
        s.setValue('AI_Channel', self.aiChannelCombo.currentText())
        s.setValue('AO_Channel', self.aoChannelCombo.currentText())
        s.setValue('SourceType', self.sourceCombo.currentText())

        s.setValue('coilVisa', self.coilVisaCombo.currentText())
        s.setValue('CoilAuxOut', self.auxOutChannelSb.value())
        s.setValue('CoilVoltage', self.coilVoltageSb.value())

    def restoreSettings(self):
        s = QSettings()
        self.sampleLineEdit.setText(s.value('sampleId', '', type=QString))
        self.dcDriveImpedanceSb.setValue(
            s.value('dcDriveImpedance', 10E3, type=float))
        self.bipolarCb.setChecked(s.value('bipolar', False, type=bool))
        self.startVSb.setValue(s.value('startV', 0, type=float))
        self.stopVSb.setValue(s.value('stopV', 3, type=float))
        self.stepsSb.setValue(s.value('steps', 10, type=int))
        self.preampGainSb.setValue(s.value('preampGain', 1., type=float))
        self.interSweepDelaySb.setValue(s.value('interSweepDelay', 0,
                                                type=int))
        self.thresholdVoltageSb.setValue(
            s.value('thresholdVoltage', 0.010, type=float))
        self.samplesPerPointSb.setValue(s.value('samplesPerPoint', 1,
                                                type=int))
        self.discardSamplesSb.setValue(s.value('discardSamples', 0, type=int))
        self.aiDeviceCombo.setCurrentIndex(
            self.aiDeviceCombo.findText(s.value('AI_Device', '', type=str)))
        self.aoDeviceCombo.setCurrentIndex(
            self.aoDeviceCombo.findText(s.value('AO_Device', '', type=str)))
        self.aiChannelCombo.setCurrentIndex(
            self.aiChannelCombo.findText(s.value('AI_Channel', '', type=str)))
        self.aoChannelCombo.setCurrentIndex(
            self.aoChannelCombo.findText(s.value('AO_Channel', '', type=str)))
        self.sourceCombo.setCurrentIndex(
            self.sourceCombo.findText(s.value('SourceType', '', type=str)))
        self.coilVisaCombo.setCurrentIndex(
            self.coilVisaCombo.findText(s.value('coilVisa', '', type=str)))
        self.auxOutChannelSb.setValue(s.value('CoilAuxOut', 1, type=int))
        self.coilVoltageSb.setValue(s.value('CoilVoltage', 0.0, type=float))
        geometry = s.value('geometry', QByteArray(), type=QByteArray)
        self.restoreGeometry(geometry)
Exemple #15
0
class TESIVSweepDaqWidget(TES_IVSweepsDaqUi.Ui_Form, QWidget):
    def __init__(self, parent=None):
        super(TESIVSweepDaqWidget, self).__init__(parent)
        self.setupUi(self)
        self.setWindowTitle('TES IV Sweep (DAQ)')

        self.aoDeviceCombo.currentIndexChanged.connect(
            self.updateDaqChannelsAo)
        self.aiDeviceCombo.currentIndexChanged.connect(
            self.updateDaqChannelsAi)
        self.populateDaqCombos()
        self.msmThread = None
        self.startPb.clicked.connect(self.startPbClicked)
        self.hkSub = HousekeepingSubscriber(self)
        self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue)
        self.hkSub.adrResistanceReceived.connect(
            self.collectThermometerResistance)
        self.hkSub.start()
        self.plotRaw = pg.PlotWidget(title='IV sweeps')
        self.plotLayout.addWidget(self.plotRaw)
        self.curveRaw = pg.PlotCurveItem()
        self.plotRaw.addItem(self.curveRaw)

        self.plotCritical = pg.PlotWidget(title='Critical current')
        self.plotLayout.addWidget(self.plotCritical)

        self.plotCritical.addLegend()
        self.criticalCurve1 = pg.PlotCurveItem(name='+', symbol='o', pen='r')
        self.plotCritical.addItem(self.criticalCurve1)

        self.criticalCurve2 = pg.PlotCurveItem(name='o', symbol='o', pen='b')
        self.plotCritical.addItem(self.criticalCurve2)

        self.plotRaw.setLabel('bottom', 'Vbias', 'V')
        self.plotRaw.setLabel('left', 'I_TES', 'uA')

        self.plotCritical.setLabel('left', 'I_C', 'uA')

        self.clearData()
        self.clearCriticalData()

        self.coilAo = None
        self.clearPb.clicked.connect(self.clearData)
        self.coilSweepCb.toggled.connect(self.toggleCoilSweep)
        self.clearCriticalPb.clicked.connect(self.clearCriticalData)
        self.samplesPerPointSb.valueChanged.connect(
            lambda value: self.discardSamplesSb.setMaximum(value - 1))
        self.coilEnableCb.toggled.connect(self.toggleCoil)
        self.coilVoltageSb.valueChanged.connect(self.updateCoilVoltage)
        #        self.toggleCoil(self.coilEnableCb.isChecked())
        #        self.toggleCoilSweep(self.coilSweepCb.isChecked())
        self.coilDriverCombo.currentIndexChanged.connect(
            self.coilDriverChanged)
        self.Vcoil = np.nan
        self.restoreSettings()
        self.Rthermometers = []

    def coilDriverChanged(self):
        text = self.coilDriverCombo.currentText()
        self.coilVisaLabel.setText('%s VISA' % text)
        self.coilVisaCombo.clear()
        if text == 'SR830':
            suffix = ' V'
            maxDrive = 10
            self.coilDriveLabel.setText('Voltage')
            self.coilVisaCombo.addItem('GPIB0::12')
            self.auxOutChannelSb.setEnabled(True)

        elif text == 'Keithley 6430':
            suffix = ' mA'
            maxDrive = 10
            self.coilDriveLabel.setText('Current')
            self.coilVisaCombo.addItem('GPIB0::24')
            self.auxOutChannelSb.setEnabled(False)

        controls = [
            self.coilVoltageSb, self.coilSweepMinSb, self.coilSweepMaxSb
        ]
        for control in controls:
            control.setSuffix(suffix)
            control.setMinimum(-maxDrive)
            control.setMaximum(+maxDrive)

    def toggleCoil(self, enabled):
        self.coilDriverCombo.setEnabled(not enabled)
        if enabled:
            drive = self.coilDriverCombo.currentText()
            if drive == 'SR830':
                self.sr830 = SR830(str(self.coilVisaCombo.currentText()))
                self.coilAo = VoltageSourceSR830(self.sr830,
                                                 self.auxOutChannelSb.value())
            elif drive == 'Keithley 6430':
                self.coilAo = CurrentSourceKeithley(str(
                    self.coilVisaCombo.currentText()),
                                                    currentRange=10.0E-3,
                                                    complianceVoltage=20.0)
            self.Vcoil = self.coilAo.dcDrive()
            self.coilVoltageSb.setValue(self.Vcoil)
            self.auxOutChannelSb.setEnabled(False)
            self.coilVisaCombo.setEnabled(False)
        else:
            self.coilAo = None
            self.auxOutChannelSb.setEnabled(True)
            self.coilVisaCombo.setEnabled(True)

    def updateCoilVoltage(self, V):
        if self.coilAo is not None:
            self.coilAo.setDcDrive(V)
            self.Vcoil = self.coilAo.dcDrive()

    def updateDaqChannelsAi(self):
        dev = str(self.aiDeviceCombo.currentText())
        self.aiChannelCombo.clear()
        if len(dev):
            device = daq.Device(dev)
            aiChannels = device.findAiChannels()
            for channel in aiChannels:
                self.aiChannelCombo.addItem(channel)

    def updateDaqChannelsAo(self):
        dev = str(self.aoDeviceCombo.currentText())
        self.aoChannelCombo.clear()
        if len(dev):
            device = daq.Device(dev)
            aoChannels = device.findAoChannels()
            for channel in aoChannels:
                self.aoChannelCombo.addItem(channel)

    def populateDaqCombos(self):
        system = daq.System()
        devices = system.findDevices()
        for devName in devices:
            dev = daq.Device(devName)
            if len(dev.findAiChannels()):
                self.aiDeviceCombo.addItem(devName)
            if len(dev.findAoChannels()):
                self.aoDeviceCombo.addItem(devName)

    def clearData(self):
        self.Vdrive = []
        self.Vmeas = []
        self.curveRaw.setData(self.Vdrive, self.Vmeas)

    def clearCriticalData(self):
        self.VcritDrive1 = []
        self.VcritDrive2 = []
        self.VcritMeas1 = []
        self.VcritMeas2 = []
        self.Tcrit1 = []
        self.Tcrit2 = []
        self.VcoilCrit1 = []
        self.VcoilCrit2 = []
        self.updateCriticalPlot()

    def updateRawData(self, t, Vdrive, Vmeas, Vo, T):
        #string = "%.3f\t%.6f\t%.6f\t%.6f\t%.6f\t%.3f\n" % (t, Vdrive, Vmeas, Vo, T, self.Vcoil)
        #self.outputFile.write(string)
        self.Vdrive.append(Vdrive)
        self.Vmeas.append(Vmeas)
        maxLength = 10000
        if len(self.Vdrive) > int(
                maxLength * 1.1):  # Automatically expire old data
            self.Vdrive = self.Vdrive[-maxLength:]
            self.Vmeas = self.Vmeas[-maxLength:]
        self.curveRaw.setData(self.Vdrive, self.Vmeas, connect='finite')

    def toggleCoilSweep(self, checked):
        if checked:
            print "Making coil voltages"
            self.coilVoltages = np.linspace(self.coilSweepMinSb.value(),
                                            self.coilSweepMaxSb.value(),
                                            self.coilSweepStepsSb.value())
            if self.coilSweepReverseCb.isChecked():
                self.coilVoltages = np.append(self.coilVoltages,
                                              self.coilVoltages[::-1])
            self.currentCoilStep = 0
            self.stepCoil()
        else:
            self.coilVoltages = []
            self.currentCoilStep = 0

    def stepCoil(self):
        if self.coilAo is not None:
            self.coilVoltageSb.setValue(
                self.coilVoltages[self.currentCoilStep])
        self.currentCoilStep += 1
        if self.currentCoilStep >= len(self.coilVoltages):  # Start over
            self.currentCoilStep = 0

    def collectThermometerResistance(self, R):
        self.Rthermometers.append(R)

    def collectSweep(self, T, Vo, VcDrive, VcMeas, tStart, tEnd, Vdrives,
                     Vmeas, t):
        with hdf.File(self.hdfFileName, mode='a') as f:
            grp = f.create_group('Sweeps/%d' % self.sweepNumber)
            self.sweepNumber += 1
            grp.create_dataset('Vdrive', data=Vdrives, compression='gzip')
            grp.create_dataset('Vmeasured', data=Vmeas, compression='gzip')
            grp.create_dataset('t', data=t, compression='gzip')
            grp.attrs['tStart'] = tStart
            grp.attrs['tEnd'] = tEnd
            grp.attrs['Rthermometer'] = np.mean(self.Rthermometers)
            grp.attrs['Vcoil'] = self.Vcoil
            grp.attrs['T'] = T
            grp.attrs['VcritDrive'] = VcDrive
            grp.attrs['VcritMeas'] = VcMeas
            grp.attrs['Vo'] = Vo
        self.Rthermometers = []

        self.updateRawData(0, np.nan, np.nan, np.nan, np.nan)

        #if np.max(VcDrive) >= 0:
        if np.max(Vdrives) > 0:
            self.Tcrit1.append(T)
            self.VcritDrive1.append(VcDrive)
            self.VcritMeas1.append(VcMeas)
            self.VcoilCrit1.append(self.Vcoil)
        else:
            self.Tcrit2.append(T)
            self.VcritDrive2.append(-VcDrive)
            self.VcritMeas2.append(-VcMeas)
            self.VcoilCrit2.append(self.Vcoil)
        self.updateCriticalPlot()

        if self.coilSweepCb.isChecked():  # Move on to next coil voltage
            if self.bipolarCb.isChecked():
                if not len(self.Tcrit2) == len(self.Tcrit1):
                    print "len(Tcrit2), len(Tcrit1)", len(self.Tcrit2), len(
                        self.Tcrit1)
                    print "Still need to do negative"
                    return
            self.stepCoil()

    def updateCriticalPlot(self):
        xAxis = self.plotXaxisCombo.currentText()
        if xAxis == 'T':
            x1 = self.Tcrit1
            x2 = self.Tcrit2
            self.plotCritical.setLabel('bottom', 'T', 'K')
        elif xAxis == 'Coil V':
            x1 = self.VcoilCrit1
            x2 = self.VcoilCrit2
            self.plotCritical.setLabel('bottom', 'Coil', 'V/A')

        yAxis = self.plotYaxisCombo.currentText()
        if yAxis == 'V_c':
            y1 = self.VcritDrive1
            y2 = self.VcritDrive2
        elif yAxis == 'I_c':
            y1 = self.VcritMeas1
            y2 = self.VcritMeas2
        self.criticalCurve1.setData(x1, y1)
        self.criticalCurve2.setData(x2, y2)
        #self.criticalPlot.replot()
        # -1/3.256 +1/2.823

    def writeHeader(self, item, value, hdfFile):
        #self.outputFile.write('#%s=%s\n' % (item, value))
        if isinstance(value, QString):
            value = str(value)
        hdfFile.attrs[item] = value

    def startPbClicked(self):
        timeString = time.strftime('%Y%m%d-%H%M%S')
        fileName = self.sampleLineEdit.text() + '_%s_IV.dat' % timeString
        self.sweepNumber = 0
        #        self.outputFile = open(fileName, 'a+')
        self.hdfFileName = str(self.sampleLineEdit.text() +
                               '_%s_IV.h5' % timeString)
        with hdf.File(self.hdfFileName, mode='w') as f:
            self.writeHeader('Program', 'TES_IVSweepsDaq.py', f)
            self.writeHeader('Date', timeString, f)
            self.writeHeader('Sample', self.sampleLineEdit.text(), f)
            self.writeHeader('Source', self.sourceCombo.currentText(), f)
            self.writeHeader('Pre-amp gain', self.preampGainSb.value(), f)
            self.writeHeader('Drive impedance',
                             self.dcDriveImpedanceSb.value(), f)
            self.writeHeader('Inter-sweep delay',
                             self.interSweepDelaySb.value(), f)
            self.writeHeader('Samples per point',
                             self.samplesPerPointSb.value(), f)
            self.writeHeader('Discard samples', self.discardSamplesSb.value(),
                             f)
            #self.writeHeader('Threshold', self.thresholdVoltageSb.value(), f)
            self.writeHeader('Bipolar', int(self.bipolarCb.isChecked()), f)
            self.writeHeader('ReverseSweep',
                             int(self.reverseSweepCb.isChecked()), f)
            if self.coilAo is not None:
                self.writeHeader('Coil enabled', 1, f)
                self.writeHeader('Coil driver', self.coilAo.name(), f)
                self.writeHeader('Coil drive', self.coilAo.dcDrive(), f)
            else:
                self.writeHeader('Coil enabled', 0, f)
            if self.coilSweepCb.isChecked():
                self.writeHeader('Coil sweep', 1, f)
                self.writeHeader('Coil min', self.coilSweepMinSb.value(), f)
                self.writeHeader('Coil max', self.coilSweepMaxSb.value(), f)
                self.writeHeader('Coil steps', self.coilSweepStepsSb.value(),
                                 f)
            else:
                self.writeHeader('Coil sweep', 0, f)

        self.ao = VoltageSourceDaq(str(self.aoDeviceCombo.currentText()),
                                   str(self.aoChannelCombo.currentText()))
        self.ai = VoltmeterDaq(str(self.aiDeviceCombo.currentText()),
                               str(self.aiChannelCombo.currentText()),
                               -10,
                               10,
                               samples=self.samplesPerPointSb.value(),
                               drop=self.discardSamplesSb.value())
        self.msmThread = IVSweepMeasurement(self.ao, self.ai, self)
        self.msmThread.setFileName(fileName)
        self.msmThread.readingAvailable.connect(self.updateRawData)
        self.msmThread.setMinimumVoltage(self.startVSb.value())
        self.msmThread.setMaximumVoltage(self.stopVSb.value())
        self.msmThread.setSteps(self.stepsSb.value())
        self.msmThread.setInterSweepDelay(self.interSweepDelaySb.value())
        self.msmThread.sweepComplete.connect(self.collectSweep)
        self.msmThread.enableBipolar(self.bipolarCb.isChecked())
        self.msmThread.enableReverseSweep(self.reverseSweepCb.isChecked())
        self.msmThread.setAdaptiveLower(1E-2 * self.adaptiveLowerSb.value())
        self.msmThread.setAdaptiveUpper(1E-2 * self.adaptiveUpperSb.value())
        self.msmThread.enableAdaptiveSweep(
            self.adaptiveSweepingGroupBox.isChecked())
        self.hkSub.adrTemperatureReceived.connect(
            self.msmThread.updateTemperature)
        self.msmThread.finished.connect(self.finished)
        self.stopPb.clicked.connect(self.msmThread.stop)
        self.reverseSweepCb.toggled.connect(self.msmThread.enableReverseSweep)
        self.adaptiveLowerSb.valueChanged.connect(
            lambda x: self.msmThread.setAdaptiveLower(1E-2 * x))
        self.adaptiveUpperSb.valueChanged.connect(
            lambda x: self.msmThread.setAdaptiveUpper(1E-2 * x))
        self.adaptiveSweepingGroupBox.toggled.connect(
            self.msmThread.enableAdaptiveSweep)

        #self.thresholdVoltageSb.valueChanged.connect(self.msmThread.setThreshold)
        #self.outputFile.write('#'+'\t'.join(['time', 'Vdrive', 'Vmeas', 'Vo', 'T', 'Vcoil' ])+'\n')
        self.enableWidgets(False)
        self.Rthermometers = []

        self.msmThread.start()
        print "Thread started"

    def finished(self):
        self.ai.clear()
        self.ao.clear()
        #self.outputFile.close()
        with hdf.File(self.hdfFileName, mode='a') as f:
            grp = f.create_group('Results/Positive')
            grp.create_dataset('Tcrit', data=self.Tcrit1)
            grp.create_dataset('VcritDrive', data=self.VcritDrive1)
            grp.create_dataset('Vcritmeas', data=self.VcritMeas1)
            grp.create_dataset('Vcoil', data=self.VcoilCrit1)
            grp = f.create_group('Results/Negative')
            grp.create_dataset('Tcrit', data=self.Tcrit2)
            grp.create_dataset('VcritDrive', data=self.VcritDrive2)
            grp.create_dataset('Vcritmeas', data=self.VcritMeas2)
            grp.create_dataset('Vcoil', data=self.VcoilCrit2)

        self.enableWidgets(True)
        self.msmThread.deleteLater()


#        self.updateStatus('Completed')

    def enableWidgets(self, enable=True):
        self.sampleLineEdit.setEnabled(enable)
        self.startPb.setEnabled(enable)
        self.dcDriveImpedanceSb.setEnabled(enable)
        self.aiChannelCombo.setEnabled(enable)
        self.aiDeviceCombo.setEnabled(enable)
        self.aoChannelCombo.setEnabled(enable)
        self.aoDeviceCombo.setEnabled(enable)
        self.startVSb.setEnabled(enable)
        self.stopVSb.setEnabled(enable)
        self.stepsSb.setEnabled(enable)
        self.interSweepDelaySb.setEnabled(enable)
        self.bipolarCb.setEnabled(enable)

        self.stopPb.setEnabled(not enable)

    def closeEvent(self, e):
        if self.msmThread:
            self.msmThread.stop()
        if self.hkSub:
            self.hkSub.stop()
        self.saveSettings()
        super(TESIVSweepDaqWidget, self).closeEvent(e)

    def saveSettings(self):
        #widgets = [self.sampleLineEdit, self.dcDriveImpedanceSb, self.bipolarCb, self.startVSb]
        s = QSettings()
        print "Saving settings"
        s.setValue('sampleId', self.sampleLineEdit.text())
        s.setValue('dcDriveImpedance', self.dcDriveImpedanceSb.value())
        s.setValue('bipolar', self.bipolarCb.isChecked())
        s.setValue('reverse', self.reverseSweepCb.isChecked())
        s.setValue('startV', self.startVSb.value())
        s.setValue('stopV', self.stopVSb.value())
        s.setValue('steps', self.stepsSb.value())
        s.setValue('interSweepDelay', self.interSweepDelaySb.value())
        s.setValue('samplesPerPoint', self.samplesPerPointSb.value())
        s.setValue('discardSamples', self.discardSamplesSb.value())

        s.setValue('preampGain', self.preampGainSb.value())
        s.setValue('geometry', self.saveGeometry())
        s.setValue('AI_Device', self.aiDeviceCombo.currentText())
        s.setValue('AO_Device', self.aoDeviceCombo.currentText())
        s.setValue('AI_Channel', self.aiChannelCombo.currentText())
        s.setValue('AO_Channel', self.aoChannelCombo.currentText())
        s.setValue('SourceType', self.sourceCombo.currentText())
        s.setValue('CoilDriver', self.coilDriverCombo.currentText())
        s.setValue('coilVisa', self.coilVisaCombo.currentText())
        s.setValue('coilEnable', self.coilEnableCb.isChecked())
        s.setValue('CoilAuxOut', self.auxOutChannelSb.value())
        s.setValue('CoilVoltage', self.coilVoltageSb.value())
        s.setValue('CoilSweepEnable', self.coilSweepCb.isChecked())
        s.setValue('CoilSweepSteps', self.coilSweepStepsSb.value())
        s.setValue('CoilSweepMin', self.coilSweepMinSb.value())
        s.setValue('CoilSweepMax', self.coilSweepMaxSb.value())
        s.setValue('adaptiveLower', self.adaptiveLowerSb.value())
        s.setValue('adaptiveUpper', self.adaptiveUpperSb.value())
        s.setValue('adaptiveSweep', self.adaptiveSweepingGroupBox.isChecked())

    def restoreSettings(self):
        s = QSettings()
        print "Restoring settings"

        self.sampleLineEdit.setText(s.value('sampleId', '', type=QString))
        self.dcDriveImpedanceSb.setValue(
            s.value('dcDriveImpedance', 10E3, type=float))
        self.bipolarCb.setChecked(s.value('bipolar', False, type=bool))
        self.reverseSweepCb.setChecked(s.value('reverse', False, type=bool))
        self.startVSb.setValue(s.value('startV', 0, type=float))
        self.stopVSb.setValue(s.value('stopV', 3, type=float))
        self.stepsSb.setValue(s.value('steps', 10, type=int))
        self.preampGainSb.setValue(s.value('preampGain', 1., type=float))
        self.interSweepDelaySb.setValue(
            s.value('interSweepDelay', 0.5, type=float))
        self.samplesPerPointSb.setValue(s.value('samplesPerPoint', 1,
                                                type=int))
        self.discardSamplesSb.setValue(s.value('discardSamples', 0, type=int))
        self.aiDeviceCombo.setCurrentIndex(
            self.aiDeviceCombo.findText(s.value('AI_Device', '', type=str)))
        self.aoDeviceCombo.setCurrentIndex(
            self.aoDeviceCombo.findText(s.value('AO_Device', '', type=str)))
        self.aiChannelCombo.setCurrentIndex(
            self.aiChannelCombo.findText(s.value('AI_Channel', '', type=str)))
        self.aoChannelCombo.setCurrentIndex(
            self.aoChannelCombo.findText(s.value('AO_Channel', '', type=str)))
        self.sourceCombo.setCurrentIndex(
            self.sourceCombo.findText(s.value('SourceType', '', type=str)))
        self.coilDriverCombo.setCurrentIndex(
            self.coilDriverCombo.findText(s.value('CoilDriver', '', type=str)))
        self.coilVisaCombo.setCurrentIndex(
            self.coilVisaCombo.findText(s.value('coilVisa', '', type=str)))
        self.coilEnableCb.setChecked(s.value('coilEnable', False, type=bool))
        self.auxOutChannelSb.setValue(s.value('CoilAuxOut', 1, type=int))
        self.coilVoltageSb.setValue(s.value('CoilVoltage', 0.0, type=float))
        self.coilSweepMinSb.setValue(s.value('CoilSweepMin', -1, type=float))
        self.coilSweepMaxSb.setValue(s.value('CoilSweepMax', 1, type=float))
        self.coilSweepStepsSb.setValue(
            s.value('CoilSweepSteps', 21, type=float))
        self.coilSweepCb.setChecked(s.value('CoilSweepEnable', True,
                                            type=bool))
        self.adaptiveLowerSb.setValue(s.value('adaptiveLower', 75, type=int))
        self.adaptiveUpperSb.setValue(s.value('adaptiveUpper', 130, type=int))
        self.adaptiveSweepingGroupBox.setChecked(
            s.value('adaptiveSweep', True, type=bool))
        geometry = s.value('geometry', QByteArray(), type=QByteArray)
        self.restoreGeometry(geometry)
Exemple #16
0
    def __init__(self, parent=None):
        super(LockinThermometerWidget, self).__init__(parent)
        self.setupUi(self)
        self.setWindowTitle('Lockin Thermometer')
        self.serverThread = None
        self.timer = None
        self.Rthermometer = float('nan')

        axis = pg.DateAxisItem(orientation='bottom')
        self.plot = pg.PlotWidget(axisItems={'bottom': axis})
        self.plot.setBackground('w')
        self.plot.plotItem.showGrid(x=True, y=True)
        self.plot.addLegend()
        self.verticalLayout.addWidget(self.plot)
        self.curve = pg.PlotCurveItem(name='X', symbol='o', pen='b')
        self.plot.addItem(self.curve)
        self.clearPb.clicked.connect(self.clearData)
        self.clearData()
        self.plotYAxisCombo.currentIndexChanged.connect(self.updatePlot)

        self.sr830 = None
        self.runPb.clicked.connect(self.run)
        self.parameterItems = [
            self.attenuatorGainSb, self.sourceImpedanceSb,
            self.driveResistanceSb, self.leadResistanceSb, self.preampGainSb,
            self.sensorVoltageSb
        ]
        self.savePb.clicked.connect(self.saveParameterSet)
        self.loadPb.clicked.connect(self.loadParameterSet)
        self.deletePb.clicked.connect(self.deleteParameterSet)
        self.attenuatorAttenuationSb.valueChanged.connect(
            self.updateAttenuatorGain)
        self.attenuatorGainSb.valueChanged.connect(
            self.updateAttenuatorAttenuation)
        self.sensorVoltageIndicator.setUnit('V')
        self.sensorCurrentIndicator.setUnit('A')
        self.sensorPowerIndicator.setUnit('W')

        sr830 = SR830(None)
        sr830.sensitivity.populateEnumComboBox(self.minSensitivityCombo)

        self.loadParameterSets()
        self.restoreSettings()
        self.hkSub = HousekeepingSubscriber(parent=self)
        self.hkSub.adrResistanceReceived.connect(self.collectAdrResistance)
        self.hkSub.start()
        self.adrResistanceIndicator.setUnit(u'Ω')
        self.adrResistanceIndicator.setPrecision(5)
        self.publisher = None

        combo = self.calibrationCombo
        for i, calId in enumerate(ThermometerCalIds):
            try:
                cal = getThermometerCalibration(calId)
                info = cal.info
            except Exception as e:
                import warnings
                warnings.warn(
                    'Calibration %s unavailable due to exception %s' %
                    (calId, str(e)))
            combo.addItem(calId)
            combo.setItemData(i, info, Qt.ToolTipRole)

        self.selectCalibration()
        combo.currentIndexChanged.connect(self.selectCalibration)
Exemple #17
0
    def magnetReadingReceived(self, t, Vmagnet, ImagnetCoarse, ImagnetFine):
        #print('Magnet')
        self.magnetWriter.writeData(t=t, Vmagnet=Vmagnet, ImagnetCoarse=ImagnetCoarse, ImagnetFine=ImagnetFine)

    def tesBiasReceived(self, t, Vbias):
        #print('TES bias')
        self.tesBiasWriter.writeData(t=t, Vbias=Vbias)

    def fieldCoilBiasReceived(self, t, Vcoil):
        #print('Field coil bias')
        self.fieldCoilBiasWriter.writeData(t=t, Vcoil=Vcoil)
        
if __name__ == '__main__':
    from PyQt4.QtGui import QApplication
    app = QApplication([])
    import h5py as hdf
    
    from Zmq.Subscribers import HousekeepingSubscriber
    hkSub = HousekeepingSubscriber()
    hdfRoot = hdf.File('HKTest_2.h5', 'w')
    logger = HkLogger(hdfRoot, hkSub)
    hkSub.start()
    import time
    for i in range(50):
        time.sleep(1)
        app.processEvents()

    hkSub.stop()        
    hdfRoot.close()
 
Exemple #18
0
class Widget(gui.QWidget, ui.Ui_Form):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        self.setupUi(self)
        self.BoSb.setToSiFactor(SI_uT)
        self.gCoilSb.setToSiFactor(SI_uT / SI_mA)
        self.RcoilSb.setToSiFactor(SI_kOhm)
        self.BcoilSb.setToSiFactor(SI_uT)
        self.RfbSb.setToSiFactor(SI_kOhm)
        self.invMiSb.setToSiFactor(SI_uA / SI_Phi0)
        self.invMfbSb.setToSiFactor(SI_uA / SI_Phi0)
        self.ApSb.setToSiFactor(SI_um**2)
        self.RbiasSb.setToSiFactor(SI_kOhm)
        self.gTesSb.setToSiFactor(SI_uT / SI_mA)
        self.RsSb.setToSiFactor(SI_mOhm)
        self.IbiasSb.setToSiFactor(SI_uA)
        self.ItesSb.setToSiFactor(SI_uA)
        self.RtesSb.setToSiFactor(SI_mOhm)

        self.setWindowTitle('TES Self-Field Cancellation')
        self.SettingsWidgets = [
            self.BoSb, self.gCoilSb, self.RcoilSb, self.RfbSb, self.invMiSb,
            self.invMfbSb, self.ApSb, self.RbiasSb, self.gTesSb, self.IbiasSb,
            self.nameLe, self.aiSamplesSb, self.ItesTargetSb,
            self.disableBiasCb, self.RsSb
        ]
        self.liveWidgets = [
            self.BoSb, self.gTesSb, self.IbiasSb, self.ItesTargetSb,
            self.tesCurrentControlCb, self.disableBiasCb
        ]

        #self.IbiasSb.valueChanged.connect(self.IbiasSb.setMaximum)

        self.restoreSettings()
        self.msmThread = None
        self.hkSub = HousekeepingSubscriber(self)
        self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue)
        self.hkSub.start()
        self.curveVsTime = pg.PlotCurveItem(pen='k')
        self.plotVsTime.addItem(self.curveVsTime)
        self.plotVsTime.plotItem.enableAutoRange(pg.ViewBox.XYAxes, True)
        self.curveVsTemp = pg.PlotCurveItem(pen='k')
        self.plotVsTemp.addItem(self.curveVsTemp)
        self.plotVsTemp.setBackground('w')
        self.plotVsTemp.plotItem.showGrid(x=True, y=True)
        self.plotVsTemp.plotItem.enableAutoRange(pg.ViewBox.XYAxes, True)
        self.clearData()
        self.runPb.clicked.connect(self.run)

    def run(self):
        #        if self.TES1Cb.isChecked():
        #            self.invMiSb.setValue(5.7866)
        #            self.invMfbSb.setValue(6.03355)
        #            self.RbiasSb.setValue(1.6976)
        #            self.RsSb.setValue(0.250)
        #            whichTES = 'TES1'
        #
        #        if self.TES2Cb.isChecked():
        #            self.invMiSb.setValue(5.673681)
        #            self.invMfbSb.setValue(5.96605)
        #            self.RbiasSb.setValue(1.6017)
        #            self.RsSb.setValue(0.256)
        #            whichTES = 'TES2'

        if self.msmThread is not None:
            self.msmThread.stop()
            self.msmThread.wait()
            self.file.close()
            self.msmThread.deleteLater()
            self.msmThread = None
        else:
            Mi = 1. / self.invMiSb.valueSi()
            Mfb = 1. / self.invMfbSb.valueSi()
            print Mi, Mfb, Mi / Mfb, Mfb / Mi
            Rfb = self.RfbSb.valueSi()
            gCoil = self.gCoilSb.valueSi()
            self.gCoil = gCoil
            Rcoil = self.RcoilSb.valueSi()
            self.Rcoil = Rcoil
            Ap = self.ApSb.valueSi()
            Rbias = self.RbiasSb.valueSi()
            print "Rbias:", Rbias
            Rs = self.RsSb.valueSi()
            gTes = self.gTesSb.valueSi()
            Bo = self.BoSb.valueSi()
            Ibias = self.IbiasSb.valueSi()
            print "Ibias:", Ibias
            nSamples = self.aiSamplesSb.value()

            fileName = 'D:/Users/Runs/G4C/RT/%s_%s.dat' % (
                self.nameLe.text(), time.strftime('%Y%m%d_%H%M%S'))
            self.file = open(fileName, 'w')

            def writeParameter(name, value):
                self.file.write('#%s=%.6e\n' % (name, value))

            writeParameter('Mi', Mi)
            writeParameter('Mfb', Mfb)
            writeParameter('Rfb', Rfb)
            writeParameter('gCoil', gCoil)
            writeParameter('Rcoil', Rcoil)
            writeParameter('Ap', Ap)
            writeParameter('Rbias', Rbias)
            writeParameter('Rs', Rs)
            writeParameter('gTes', gTes)
            writeParameter('Bo', Bo)
            writeParameter('gTes', gTes)
            writeParameter('Ibias', Ibias)
            self.file.write('#%s\n' % '\t'.join([
                't', 'Tadr', 'Vbias', 'Vcoil', 'Vo', 'Vsquid', 'Ites', 'Rtes'
            ]))

            msmThread = MsmThread(parent=self)
            msmThread.setFixedParameters(Mi=Mi,
                                         Mfb=Mfb,
                                         Rfb=Rfb,
                                         gCoil=gCoil,
                                         Rcoil=Rcoil,
                                         Ap=Ap,
                                         Rbias=Rbias,
                                         Rs=Rs)
            msmThread.setgTes(gTes)
            msmThread.setBo(Bo)
            msmThread.setIbias(Ibias)
            msmThread.setNumberOfSamples(nSamples)
            msmThread.finished.connect(self.threadFinished)
            msmThread.measurementAvailable.connect(self.collectData)
            msmThread.setDisableBias(self.disableBiasCb.isChecked())
            self.disableBiasCb.toggled.connect(msmThread.setDisableBias)
            self.gTesSb.valueChangedSi.connect(msmThread.setgTes)
            self.BoSb.valueChangedSi.connect(msmThread.setBo)
            self.IbiasSb.valueChangedSi.connect(msmThread.setIbias)
            self.ItesTargetSb.valueChangedSi.connect(msmThread.setItes)
            self.msmThread = msmThread
            msmThread.start()
            msmThread.started.connect(self.threadStarted)

    def clearData(self):
        self.ts = []
        self.T = []
        self.Vbias = []
        self.Vcoil = []
        self.Vo = []
        self.Vsquid = []
        self.Ites = []
        self.Rtes = []
        self.updatePlots()

    def collectData(self, t, Vbias, Vcoil, Vo, Vsquid, Ites, Rtes):
        Tadr = self.temperatureSb.value()
        self.ts.append(t)
        self.T.append(Tadr)
        self.Vbias.append(Vbias)
        self.Vcoil.append(Vcoil)
        self.Vo.append(Vo)
        self.Vsquid.append(Vsquid)
        self.Ites.append(Ites)
        self.Rtes.append(Rtes)
        self.file.write('%.3f\t%.6e\t%.6f\t%.6f\t%.6f\t%.6f\t%.6e\t%.6e\n' %
                        (t, Tadr, Vbias, Vcoil, Vo, Vsquid, Ites, Rtes))

        l = len(self.ts)
        if l % 20 != 0:
            return

        i = min(l, 100)
        if i > 10:
            Dt = t - self.ts[-i]
            rate = float(i) / Dt
            self.loopRateSb.setValue(rate)
        self.lockLostLed.setValue(abs(Vsquid) > 10)

        self.ts = pruneData(self.ts)
        self.T = pruneData(self.T)
        self.Vbias = pruneData(self.Vbias)
        self.Vcoil = pruneData(self.Vcoil)
        self.Vo = pruneData(self.Vo)
        self.Vsquid = pruneData(self.Vsquid)
        self.Ites = pruneData(self.Ites)
        self.Rtes = pruneData(self.Rtes)

        self.VbiasSb.setValue(Vbias)
        self.VcoilSb.setValue(Vcoil)
        self.VoSb.setValue(Vo)
        self.VsquidSb.setValue(Vsquid)
        self.ItesSb.setValueSi(Ites)
        self.RtesSb.setValueSi(Rtes)
        Bcoil = self.gCoil * Vcoil / self.Rcoil
        self.BcoilSb.setValueSi(Bcoil)
        self.updatePlots()

    def updatePlots(self):
        yAxis = self.yAxisCombo.currentText()
        if yAxis == 'Vbias':
            y = self.Vbias
        elif yAxis == 'Vcoil':
            y = self.Vcoil
        elif yAxis == 'Vo':
            y = self.Vo
        elif yAxis == 'Vsquid':
            y = self.Vsquid
        elif yAxis == 'Ites':
            y = self.Ites
        elif yAxis == 'Rtes':
            y = self.Rtes

        if yAxis == 'Ites':
            unit = 'A'
        elif yAxis == 'Rtes':
            unit = 'Ohm'
        else:
            unit = 'V'
        yax = self.plotVsTime.getAxis('left')
        yax.setLabel(yAxis, unit)
        yax = self.plotVsTemp.getAxis('left')
        yax.setLabel(yAxis, unit)
        self.curveVsTime.setData(x=self.ts, y=y)
        self.curveVsTemp.setData(x=self.T, y=y)

    def threadStarted(self):
        self.enableControls(False)

    def threadFinished(self):
        self.enableControls(True)

    def enableControls(self, enable):
        for w in self.SettingsWidgets:
            if not w in self.liveWidgets:
                w.setEnabled(enable)
        self.runPb.setText('Run' if enable else 'Stop')

    def closeEvent(self, event):
        self.saveSettings()

    def restoreSettings(self):
        s = QSettings()
        for w in self.SettingsWidgets:
            ut.restoreWidgetFromSettings(s, w)

    def saveSettings(self):
        s = QSettings()
        for w in self.SettingsWidgets:
            ut.saveWidgetToSettings(s, w)
    def __init__(self, parent = None):
        super(TemperatureControlMainWindow, self).__init__(parent)
        self.setupUi(self)
        self.setpointSb.setDecimals(7)
        self.setpointSb.setSingleStep(1E-7)
        self.rampRateSb.setDecimals(2)
        self.rampRateSb.setSingleStep(1E-2)
        self.serverThread = None
        self.selectedSensorName = ''
        self.clearData()
        self.pid = None
        self.outputFile = None
        self.magnetControlRemote = None
        self.rampEnableCb.toggled.connect(self.fixupRampRate)
        self.updatePlotsCb.toggled.connect(self.showPlots)
        
        self.widgetsForSettings = [self.thermometerCombo, 
                                   self.setpointSb, self.KSb, self.TiSb, self.TdSb,
                                   self.TtSb, self.TfSb, self.betaSb, self.gammaSb,
                                   self.controlMinSb, self.controlMaxSb, self.rampRateSb,
                                   self.rampTargetSb, self.rampEnableCb, self.updatePlotsCb,
                                   self.useBaseTemperatureCb]
        
        axis = pg.DateAxisItem(orientation='bottom')
        self.pvPlot = pg.PlotWidget(axisItems={'bottom': axis})
        self.mainVerticalLayout.addWidget(self.pvPlot)

        self.pvPlot.addLegend()
        self.curvePv = pg.PlotCurveItem(name='Actual', symbol='o', pen='g')
        self.curveSetpoint = pg.PlotCurveItem(name='Setpoint', symbol='o', pen='r')
        self.pvPlot.addItem(self.curvePv)
        self.pvPlot.addItem(self.curveSetpoint)


        axis = pg.DateAxisItem(orientation='bottom')
        self.pidPlot = pg.PlotWidget(axisItems={'bottom': axis})
        self.mainVerticalLayout.addWidget(self.pidPlot)
        self.pidPlot.addLegend()
        self.curveP = pg.PlotCurveItem(name='P', symbol='o', pen='r')
        self.curveI = pg.PlotCurveItem(name='I', symbol='o', pen='g')
        self.curveD = pg.PlotCurveItem(name='D', symbol='o', pen='b')
        self.curveControl = pg.PlotCurveItem(name='control', symbol='o', pen='w')
        self.pidPlot.addItem(self.curveP)
        self.pidPlot.addItem(self.curveI)
        self.pidPlot.addItem(self.curveD)
        self.pidPlot.addItem(self.curveControl)

        self.KSb.setMinimum(-1E6)
        self.KSb.setMaximum(1E6)

        self.hkSub = HousekeepingSubscriber(self)
        self.hkSub.thermometerListUpdated.connect(self.updateThermometerList)
        self.hkSub.thermometerReadingReceived.connect(self.receiveTemperature)
        self.hkSub.start()

        self.startPb.clicked.connect(self.startPid)
        self.stopPb.clicked.connect(self.stopPid)

        self.timer = QTimer(self)   # A 250 ms timer to implement ramping
        self.timer.timeout.connect(self.updateSetpoint)
        self.tOld = time.time()
        self.timer.start(250)

        self.thermometerCombo.currentIndexChanged.connect(self.updateThermometerSelection)

        self.restoreSettings()
        # Give the hkSub some time to update thermometer list before we update thermometer selection
        QTimer.singleShot(2000, self.restoreThermometerSelection) 
Exemple #20
0
class IvCurveWidget(ui.Ui_Form, QWidget):
    EXIT_CODE_REBOOT = -1234
    logger = logging.getLogger('PulseCollectorMainWindow')

    def __init__(self, parent=None):
        super(IvCurveWidget, self).__init__(parent)
        self.setupUi(self)
        self.restartPb.clicked.connect(self.restart)
        self.thread = None
        self.hdfFile = None
        self.squid = None
        self._fileName = ''
        self.plot.addLegend()
        self.plot.setLabel('left', 'SQUID response', units='V')
        self.plot.setLabel('bottom', 'bias voltage', units='V')
        self.curves = []
        self.startPb.clicked.connect(self.startMeasurement)
        self.stopPb.clicked.connect(self.stopMeasurement)

        #        self.aoRangeCombo.currentIndexChanged.connect(self.updateAoRange)
        #        self.auxAoChannelCombo.currentIndexChanged.connect(self.updateAuxAoRange)

        self.osr = OpenSquidRemote(port=7894)
        squids = self.osr.findSquids()
        self.tesCombo.addItem('None')
        self.tesCombo.addItems(squids)

        self.settingsWidgets = [
            self.deviceCombo, self.aoChannelCombo, self.aoRangeCombo,
            self.aiChannelCombo, self.aiRangeCombo, self.aiTerminalConfigCombo,
            self.aiDriveChannelCombo, self.recordDriveCb, self.maxDriveSb,
            self.slewRateSb, self.zeroHoldTimeSb, self.peakHoldTimeSb,
            self.betweenHoldTimeSb, self.decimateCombo, self.sampleRateSb,
            self.sampleLe, self.commentLe, self.enablePlotCb,
            self.auxAoChannelCombo, self.auxAoRangeCombo, self.auxAoSb,
            self.auxAoEnableCb, self.polarityCombo, self.tesCombo,
            self.pflResetCb, self.driveOffsetSb
        ]
        self.deviceCombo.currentIndexChanged.connect(self.updateDevice)
        for w in [
                self.maxDriveSb, self.slewRateSb, self.zeroHoldTimeSb,
                self.peakHoldTimeSb, self.betweenHoldTimeSb, self.sampleRateSb
        ]:
            w.valueChanged.connect(self.updateInfo)
        self.decimateCombo.currentIndexChanged.connect(self.updateInfo)
        self.polarityCombo.currentIndexChanged.connect(self.updateInfo)
        self.auxAoSb.valueChanged.connect(self.updateAuxOutputVoltage)
        self.auxAoEnableCb.toggled.connect(self.toggleAuxOut)
        self.auxAoRamper = None
        self.restoreSettings()
        self.hkSub = HousekeepingSubscriber(self)
        self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue)
        self.hkSub.adrResistanceReceived.connect(self.collectAdrResistance)
        self.hkSub.start()

        self.serverThread = RequestReplyThreadWithBindings(
            port=RequestReply.IvCurveDaq, parent=self)
        boundWidgets = {
            'sampleName': self.sampleLe,
            'auxAoEnable': self.auxAoEnableCb,
            'auxVoltage': self.auxAoSb,
            'maxDrive': self.maxDriveSb,
            'slewRate': self.slewRateSb,
            'start': self.startPb,
            'stop': self.stopPb,
            'totalTime': self.totalTimeSb,
            'sweepCount': self.sweepCountSb,
            'comment': self.commentLe,
            'driveOffset': self.driveOffsetSb
        }
        for name in boundWidgets:
            self.serverThread.bindToWidget(name, boundWidgets[name])
        self.serverThread.bindToFunction('fileName', self.fileName)
        self.serverThread.bindToFunction('restart', self.requestRestart)
        self.serverThread.start()

        pens = 'rgbc'
        for i in range(4):
            curve = pg.PlotDataItem(pen=pens[i], name='Curve %d' % i)
            self.plot.addItem(curve)
            self.curves.append(curve)

        self.restartRequested = False
        timer = QTimer(self)
        timer.timeout.connect(self.checkRestartRequested)
        timer.setInterval(1000)
        timer.start()
        self.timer = timer
        self.logger.info('IvCurveDaq started.')

    def checkRestartRequested(self):
        if self.restartRequested:
            self.restart()

    def requestRestart(self):
        print('Restarting soon!')
        self.restartRequested = True
        return True

    def restart(self):
        if self.thread is not None:
            result = QMessageBox.question(
                self, "Restart program",
                "The measurement thread is still running. Do you really want to restart the program?",
                QMessageBox.Yes, QMessageBox.No)
            if result != QMessageBox.Yes:
                return
            self.thread.stop()
            self.thread.wait(3000)
        self.close()
        self.logger.info('IvCurveDaq restarting.')
        qApp.exit(self.EXIT_CODE_REBOOT)

    def fileName(self):
        return self._fileName

    def toggleAuxOut(self, enabled):
        if enabled:
            deviceName = str(self.deviceCombo.currentText())
            aoChannel = str(self.auxAoChannelCombo.currentText())
            aoRange = self.aoRanges[self.auxAoRangeCombo.currentIndex()]
            self.auxAoRamper = AuxAoRamper(deviceName, aoChannel, aoRange)
            self.auxAoRamper.setTo(self.auxAoSb.value())
        else:
            del self.auxAoRamper
            self.auxAoRamper = None

    def threadRunning(self):
        if self.thread is not None:
            return self.thread.isRunning()
        else:
            return False

    def updateAuxOutputVoltage(self):
        V = self.auxAoSb.value()
        if self.threadRunning():
            self.thread.updateAoVoltage(V)
        elif self.auxAoRamper is not None:
            try:
                self.auxAoRamper.rampTo(V)
            except Exception:
                exceptionString = traceback.format_exc()
                self.reportError(exceptionString)

#    def updateAuxAoRange(self):
#        #auxAoChannel = str(self.auxAoChannelCombo.currentText())
#        r = self.aoRanges[self.auxAoRangeCombo.currentIndex()]
#        #self.auxAoSb.setMaximum(r.max)
#        #self.auxAoSb.setMinimum(r.min)
#        if self.auxAoEnableCb.isChecked(): # If output is already on, we have to kill the old task and make a new one
#            self.toggleAuxOut(False)
#            self.toggleAuxOut(True)

    def collectAdrResistance(self, R):
        if self.hdfFile is None:
            return

        timeStamp = time.time()
        self.dsTimeStamps.resize((self.dsTimeStamps.shape[0] + 1, ))
        self.dsTimeStamps[-1] = timeStamp

        self.dsAdrResistance.resize((self.dsAdrResistance.shape[0] + 1, ))
        self.dsAdrResistance[-1] = R

    def populateDevices(self):
        self.deviceCombo.clear()
        system = daq.System()
        devices = system.findDevices()
        for dev in devices:
            self.deviceCombo.addItem(dev)


#    def updateAoRange(self):
#        aoRange = self.aoRanges[self.aoRangeCombo.currentIndex()]
#        self.maxDriveSb.setMaximum(aoRange.max)

    def updateDevice(self):
        self.aiChannelCombo.clear()
        self.aiDriveChannelCombo.clear()
        self.aoChannelCombo.clear()
        self.aiRangeCombo.clear()
        self.aoRangeCombo.clear()
        self.auxAoRangeCombo.clear()
        self.auxAoChannelCombo.clear()

        deviceName = str(self.deviceCombo.currentText())
        if len(deviceName) < 1:
            return
        device = daq.Device(deviceName)

        aiChannels = device.findAiChannels()
        for channel in aiChannels:
            self.aiChannelCombo.addItem(channel)
            self.aiDriveChannelCombo.addItem(channel)

        aoChannels = device.findAoChannels()
        for channel in aoChannels:
            self.aoChannelCombo.addItem(channel)
            self.auxAoChannelCombo.addItem(channel)

        self.aiRanges = device.voltageRangesAi()
        for r in self.aiRanges:
            self.aiRangeCombo.addItem('%+.2f -> %+.2f V' % (r.min, r.max))

        self.aoRanges = device.voltageRangesAo()
        for r in self.aoRanges:
            self.aoRangeCombo.addItem('%+.2f -> %+.2f V' % (r.min, r.max))
            self.auxAoRangeCombo.addItem('%+.2f -> %+.2f V' % (r.min, r.max))

        if len(aiChannels):
            aiChannel = daq.AiChannel('%s/%s' % (deviceName, aiChannels[0]),
                                      self.aiRanges[0].min,
                                      self.aiRanges[0].max)
            aiTask = daq.AiTask('TestInputSampleRate')
            aiTask.addChannel(aiChannel)
            aiSampleRate = aiTask.maxSampleClockRate()
        else:
            aiSampleRate = 0

        if len(aoChannels):
            aoChannel = daq.AoChannel('%s/%s' % (deviceName, aoChannels[0]),
                                      self.aoRanges[0].min,
                                      self.aoRanges[0].max)
            aoTask = daq.AoTask('TestOutputSampleRate')
            aoTask.addChannel(aoChannel)
            aoSampleRate = aoTask.maxSampleClockRate()
        else:
            aoSampleRate = 0

        rate = min(aiSampleRate, aoSampleRate)
        self.sampleRateSb.setMaximum(int(1E-3 * rate))
        self.updateInfo()

    def terminalConfiguration(self):
        t = str(self.aiTerminalConfigCombo.currentText())
        tc = daq.AiChannel.TerminalConfiguration
        terminalConfigDict = {'RSE': tc.RSE, 'DIFF': tc.DIFF, 'NRSE': tc.NRSE}
        return terminalConfigDict[t]

    def updateInfo(self):
        tz = self.zeroHoldTimeSb.value()
        tr = self.maxDriveSb.value() / self.slewRateSb.value()
        tp = self.peakHoldTimeSb.value()
        tb = self.betweenHoldTimeSb.value()
        f = self.sampleRateSb.value() * 1E3
        polarity = self.polarityCombo.currentText()
        if polarity == 'bipolar':
            ttotal = 2 * tz + 2 * (2 * tr + tp) + tb
        elif polarity == 'IvsB':
            ttotal = 2 * tz + 4 * tr + tb
        else:
            ttotal = 2 * tz + 1 * (2 * tr + tp)
        self.ttotal = ttotal
        self.totalTimeSb.setValue(ttotal)
        samplesPerSweep = int(
            np.ceil(ttotal * f / float(self.decimateCombo.currentText())))
        self.samplesPerSweepSb.setValue(samplesPerSweep)

    def restoreSettings(self):
        s = QSettings(OrganizationName, ApplicationName)
        self.populateDevices()
        for w in self.settingsWidgets:
            restoreWidgetFromSettings(s, w)

    def saveSettings(self):
        s = QSettings(OrganizationName, ApplicationName)
        for w in self.settingsWidgets:
            saveWidgetToSettings(s, w)

    def closeEvent(self, event):
        if self.thread is not None:
            event.ignore()
            return
        self.saveSettings()
        super(IvCurveWidget, self).closeEvent(event)

    def removeAllCurves(self):
        for curve in self.curves:
            curve.setData([], [])
        self.nSweeps = 0
        self.sweepCountSb.setValue(self.nSweeps)

    def startMeasurement(self):
        self.removeAllCurves()

        f = self.sampleRateSb.value() * 1E3
        Voffset = self.driveOffsetSb.value()
        Vmax = self.maxDriveSb.value()

        polarity = self.polarityCombo.currentText()

        if polarity == 'IvsB':
            whold1 = -Vmax * np.ones(int(self.zeroHoldTimeSb.value() * f))
            wrs = np.linspace(-Vmax, +Vmax,
                              int(2 * Vmax / self.slewRateSb.value() * f))
            whold2 = Vmax * np.ones(
                (int(self.betweenHoldTimeSb.value() * f), ), dtype=float)
            wave = np.hstack([whold1, wrs, whold2, wrs[::-1], whold1])
        else:
            wz = np.zeros((int(self.zeroHoldTimeSb.value() * f), ),
                          dtype=float)  # Zero
            wr = np.linspace(0, Vmax,
                             int(Vmax / self.slewRateSb.value() * f))  # Ramp
            wp = np.ones((int(self.peakHoldTimeSb.value() * f), ),
                         dtype=float) * Vmax  # Hold at peak
            wb = np.zeros((int(self.betweenHoldTimeSb.value() * f), ),
                          dtype=float)  # Hold between

            if polarity == 'bipolar':
                wave = np.hstack(
                    [wz, wr, wp, wr[::-1], wb, -wr, -wp, -wr[::-1],
                     wz])  # Up down and back
            elif polarity == 'positive only':
                wave = np.hstack([wz, wr, wp, wr[::-1], wz])  # Positive only
            elif polarity == 'negative only':
                wave = np.hstack([wz, -wr, wp, -wr[::-1], wz])  # Positive only
            else:
                raise Exception('Unsupported polarity choice')

        Voffset = self.driveOffsetSb.value()
        wave += Voffset

        squidId = str(self.tesCombo.currentText())
        if squidId != 'None':
            squid = Pfl102Remote(self.osr, squidId)
        else:
            squid = None
        self.squid = squid

        self.decimation = int(self.decimateCombo.currentText())
        self.x = iterativeDecimate(wave, self.decimation)

        deviceName = str(self.deviceCombo.currentText())
        aoChannel = str(self.aoChannelCombo.currentText())
        aiChannel = str(self.aiChannelCombo.currentText())
        aiTerminalConfig = self.terminalConfiguration()
        aiRange = self.aiRanges[self.aiRangeCombo.currentIndex()]
        aoRange = self.aoRanges[self.aoRangeCombo.currentIndex()]

        s = QSettings('WiscXrayAstro', application='ADR3RunInfo')
        path = str(s.value('runPath', '', type=str))
        fileName = path + '/IV/%s_%s.h5' % (self.sampleLe.text(),
                                            time.strftime('%Y%m%d_%H%M%S'))
        self._fileName = fileName
        hdfFile = hdf.File(fileName, mode='w')
        hdfFile.attrs['Program'] = ApplicationName
        hdfFile.attrs['Version'] = Version
        hdfFile.attrs[
            'Decimation'] = 'Iterative decimate using customized scipy code with filtfilt.'
        hdfFile.attrs['Sample'] = str(self.sampleLe.text())
        hdfFile.attrs['Comment'] = str(self.commentLe.text())
        t = time.time()
        hdfFile.attrs['StartTime'] = t
        hdfFile.attrs['StartTimeLocal'] = time.strftime(
            '%Y-%m-%d %H:%M:%S', time.localtime(t))
        hdfFile.attrs['StartTimeUTC'] = time.strftime('%Y-%m-%d %H:%M:%SZ',
                                                      time.gmtime(t))
        hdfFile.attrs['Voffset'] = Voffset
        hdfFile.attrs['Vmax'] = Vmax
        hdfFile.attrs['sampleRate'] = f
        hdfFile.attrs['decimation'] = self.decimation
        hdfFile.attrs['deviceName'] = deviceName
        hdfFile.attrs['aoChannel'] = aoChannel
        hdfFile.attrs['aoRangeMin'] = aoRange.min
        hdfFile.attrs['aoRangeMax'] = aoRange.max
        hdfFile.attrs['aiChannel'] = aiChannel
        hdfFile.attrs['aiRangeMin'] = aiRange.min
        hdfFile.attrs['aiRangeMax'] = aiRange.max
        hdfFile.attrs['aiTerminalConfig'] = str(
            self.aiTerminalConfigCombo.currentText())
        hdfFile.attrs['zeroHoldTime'] = self.zeroHoldTimeSb.value()
        hdfFile.attrs['peakHoldTime'] = self.peakHoldTimeSb.value()
        hdfFile.attrs['betweenHoldTime'] = self.betweenHoldTimeSb.value()
        hdfFile.attrs['slewRate'] = self.slewRateSb.value()
        hdfFile.attrs['polarity'] = str(polarity)
        hdfFile.attrs['resetPflEverySweep'] = bool(self.pflResetCb.isChecked())

        if squid is not None:
            hdfFile.attrs['pflReport'] = str(squid.report())
            hdfFile.attrs['pflRfb'] = squid.feedbackR()
            hdfFile.attrs['pflCfb'] = squid.feedbackC()

        if self.auxAoRamper is not None:
            hdfFile.attrs['auxAoChannel'] = str(self.auxAoRamper.channel)
            auxAoRange = self.aoRanges[self.auxAoRangeCombo.currentIndex()]
            hdfFile.attrs['auxAoRangeMin'] = auxAoRange.min
            hdfFile.attrs['auxAoRangeMax'] = auxAoRange.max
            hdfFile.attrs['auxAoValue'] = self.auxAoSb.value()

        ds = hdfFile.create_dataset('excitationWave',
                                    data=wave,
                                    compression='lzf',
                                    shuffle=True,
                                    fletcher32=True)
        ds.attrs['units'] = 'V'
        ds = hdfFile.create_dataset('excitationWave_decimated',
                                    data=self.x,
                                    compression='lzf',
                                    shuffle=True,
                                    fletcher32=True)
        ds.attrs['units'] = 'V'

        g = hdfFile.create_group('HK')
        self.hkLogger = HkLogger(
            g, self.hkSub
        )  # Should remove stuff below soon - only kept for backwards compatibility
        self.dsTimeStamps = hdfFile.create_dataset('AdrResistance_TimeStamps',
                                                   (0, ),
                                                   maxshape=(None, ),
                                                   chunks=(500, ),
                                                   dtype=np.float64)
        self.dsTimeStamps.attrs['units'] = 's'
        self.dsAdrResistance = hdfFile.create_dataset('AdrResistance', (0, ),
                                                      maxshape=(None, ),
                                                      chunks=(500, ),
                                                      dtype=np.float64)
        self.dsAdrResistance.attrs['units'] = 'Ohms'
        self.hdfFile = hdfFile

        thread = DaqThread(deviceName,
                           aoChannel,
                           aoRange,
                           aiChannel,
                           aiRange,
                           aiTerminalConfig,
                           parent=self)
        thread.setWave(wave)
        thread.setSampleRate(f)
        thread.dataReady.connect(self.collectData)
        thread.setAuxAoRamper(self.auxAoRamper)
        thread.updateAoVoltage(self.auxAoSb.value())

        if self.recordDriveCb.isChecked():
            aiDriveChannel = str(self.aiDriveChannelCombo.currentText())
            hdfFile.attrs['aiDriveChannel'] = aiDriveChannel
            thread.enableDriveRecording(aiDriveChannel)
            thread.driveDataReady.connect(self.collectDriveData)

        if self.pflResetCb.isChecked():
            thread.setSquid(squid)
        thread.error.connect(self.reportError)
        self.enableWidgets(False)
        self.thread = thread
        self.t0 = None

        #nCollected = gc.collect()
        #print('Garbarge collection before measurement:', nCollected)
        #gc.disable()

        thread.start()
        thread.finished.connect(self.threadFinished)
        #print guppyH.heap()

    def reportError(self, message):
        message = str(message)
        self.logger.error('Exception encountered: %s', message)
        from Utility.Gmail import sendMessage
        try:
            sendMessage(['*****@*****.**'], 'IvCurveDaq error',
                        message)
        except Exception as e:
            self.logger.error('Unable to send email: %s', str(e))
        QMessageBox.critical(self, 'Exception encountered!', message)

    def stopMeasurement(self):
        if self.thread is None:
            return
        if self.stopPb.text() == 'Stop':
            self.thread.stop()
            self.stopPb.setText('Abort')
        else:
            self.thread.abort()

    def threadFinished(self):
        print('Finished, cleaning up.')
        del self.hkLogger
        self.hkLogger = None
        del self.thread
        self.thread = None
        self.closeFile()
        del self.squid
        self.squid = None
        del self.dsAdrResistance
        self.dsAdrResistance = None
        del self.dsTimeStamps
        self.dsTimeStamps = None
        del self.x
        self.x = None
        self.stopPb.setText('Stop')
        self.enableWidgets(True)
        print('Done cleaning.')
        nCollected = gc.collect()
        print('Garbarge collection after measurement:', nCollected)
        #gc.enable()

    def closeFile(self):
        if self.hdfFile is not None:
            del self.hkLogger
            self.hkLogger = None
            t = time.time()
            self.hdfFile.attrs['StopTime'] = t
            self.hdfFile.attrs['StopTimeLocal'] = time.strftime(
                '%Y-%m-%d %H:%M:%S', time.localtime(t))
            self.hdfFile.attrs['StopTimeUTC'] = time.strftime(
                '%Y-%m-%d %H:%M:%SZ', time.gmtime(t))
            self.hdfFile.close()
            del self.hdfFile
            self.hdfFile = None

    def enableWidgets(self, enable):
        self.driveGroupBox.setEnabled(enable)
        self.inputGroupBox.setEnabled(enable)
        self.auxAoChannelCombo.setEnabled(enable)
        self.auxAoRangeCombo.setEnabled(enable)
        self.auxAoEnableCb.setEnabled(enable)
        self.startPb.setEnabled(enable)
        self.stopPb.setEnabled(not enable)

    def collectDriveData(self, timeStamp, dt, data):
        ds = self.hdfFile.create_dataset('DriveSignalRaw',
                                         data=data,
                                         compression='lzf',
                                         shuffle=True,
                                         fletcher32=True)
        ds.attrs['units'] = 'V'
        data = iterativeDecimate(data, self.decimation)
        ds = self.hdfFile.create_dataset('DriveSignalDecimated',
                                         data=data,
                                         compression='lzf',
                                         shuffle=True,
                                         fletcher32=True)
        ds.attrs['units'] = 'V'

    def collectData(self, timeStamp, auxAoVoltage, dt, data):
        Tadr = self.temperatureSb.value()
        if self.t0 is None:
            self.t0 = timeStamp
        data = iterativeDecimate(data, self.decimation)

        currentSweep = self.nSweeps
        self.nSweeps += 1
        self.sweepCountSb.setValue(self.nSweeps)
        try:
            grp = self.hdfFile.create_group('Sweep_%06d' % currentSweep)
            grp.attrs['Time'] = timeStamp
            grp.attrs['TimeLocal'] = time.strftime('%Y-%m-%d %H:%M:%S',
                                                   time.localtime(timeStamp))
            grp.attrs['TimeUTC'] = time.strftime('%Y-%m-%d %H:%M:%SZ',
                                                 time.gmtime(timeStamp))
            grp.attrs['Tadr'] = Tadr
            grp.attrs['auxAoValue'] = auxAoVoltage
            ds = grp.create_dataset('Vsquid',
                                    data=data,
                                    compression='lzf',
                                    shuffle=True,
                                    fletcher32=True)
            ds.attrs['units'] = 'V'
        except Exception:
            exceptionString = traceback.format_exc()
            self.reportError(exceptionString)
            self.stopMeasurement()

        if self.enablePlotCb.isChecked():
            self.curves[currentSweep % len(self.curves)].setData(self.x, data)
        else:
            for curve in self.curves:
                curve.setData([], [])