示例#1
0
    def __init__(self, parent=None):
        super(TektronixScopeWidget, self).__init__(parent)
        self.setupUi(self)
        self.thread = None
        self.hdfFile = None
        self.columns = {
            'enabled': 0,
            'save': 1,
            'coupling': 2,
            'mode': 3,
            'gain': 4,
            'label': 5
        }
        self.plot.addLegend()
        self.plot.setLabel('left', 'voltage', units='V')
        self.plot.setLabel('bottom', 'time', units='s')
        self.activeChannels = [1, 2]
        pens = 'ybmr'
        self.curves = []
        for ch in self.activeChannels:
            curve = pg.PlotDataItem(pen=pens[ch], name='Channel %d' % ch)
            self.plot.addItem(curve)
            self.curves.append(curve)

        self.connectToInstrument()
        self.publisher = ZmqPublisher('TektronixScope',
                                      port=PubSub.TektronixScope)
示例#2
0
 def __init__(self):
     #aoChannel = daq.AoChannel('USB6002_B/ao0', -10, +10)
     #aiChannel = daq.AiChannel('USB6002_B/ai1', -10, +10)
     aoChannel = daq.AoChannel('Dev2/ao0', -10, +10)
     aiChannel = daq.AiChannel('Dev2/ai0', -10, +10)
     aiChannel.setTerminalConfiguration(daq.AiChannel.TerminalConfiguration.DIFF)
     #aiChannel = None
     self.publisher = ZmqPublisher(origin='FieldCoilBiasDAQ', port=PubSub.FieldCoilBiasDAQ)
     Battery.__init__(self, aoChannel, aiChannel)
示例#3
0
 def __init__(self, magnetSupply, dmm, parent=None):
     QThread.__init__(self, parent)
     self.ms = magnetSupply
     self.dmm = dmm
     self.interval = 0.2 # Update interval
     self.dIdtMax = 1./60. # max rate: 1 A/min = 1./60. A/s
     self.dIdt = 0.
     self.Rmax = 0.6 # Maximum R for quench detection
     self.inductance = 30.0 # 30 Henry
     self.Vmax = 2.8 # Maximum supply voltage
     self.Imax = 8.5 # Maximum current permitted
     self._quenched = False
     self.publisher = ZmqPublisher('MagnetControlThread', PubSub.MagnetControl, self)
示例#4
0
class FieldCoil(Battery):
    def __init__(self):
        #aoChannel = daq.AoChannel('USB6002_B/ao0', -10, +10)
        #aiChannel = daq.AiChannel('USB6002_B/ai1', -10, +10)
        aoChannel = daq.AoChannel('Dev2/ao0', -10, +10)
        aiChannel = daq.AiChannel('Dev2/ai0', -10, +10)
        aiChannel.setTerminalConfiguration(daq.AiChannel.TerminalConfiguration.DIFF)
        #aiChannel = None
        self.publisher = ZmqPublisher(origin='FieldCoilBiasDAQ', port=PubSub.FieldCoilBiasDAQ)
        Battery.__init__(self, aoChannel, aiChannel)

    def _writeData(self, aoTask, V):
        '''Override the superclass atomic write to also do ZMQ publish.'''
        aoTask.writeData([V], autoStart = True)
        self.publisher.publishDict('Coil', {'t':time.time(), 'Vcoil':V})
示例#5
0
    def __init__(self, parent=None):
        super(Avs47SingleChannelWidget, self).__init__(parent)
        self.dmm = None
        self.avs = None
        self.buffer = ''
        self.workerThread = None
        self.setupUi(self)
        self.setWindowTitle('AVS47 Single Channel')
        self.outputFile = None
        self.yaxisCombo.currentIndexChanged.connect(self.switchPlotAxis)
        self.temperatureIndicator.setPrecision(5)

        combo = self.calibrationCombo
        combo.addItem('RuOx 600')
        combo.addItem('RuOx 2005')
        combo.addItem('RuOx Bus (Shuo)')
        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.currentIndexChanged.connect(self.selectCalibration)

        axis = pg.DateAxisItem(orientation='bottom')
        self.plot = pg.PlotWidget(axisItems={'bottom': axis})
        self.plot.addLegend()
        self.verticalLayout.addWidget(self.plot)
        self.curve = pg.PlotCurveItem(name='Actual', symbol='o', pen='g')
        self.plot.addItem(self.curve)
        self.clearData()

        self.runPb.clicked.connect(self.runPbClicked)

        self.widgetsForSettings = [
            self.readoutInternalRadio, self.readoutDmmRadio,
            self.readoutZmqRadio, self.bridgeCombo, self.dmmCombo,
            self.autoRangeCb, self.rangeUpSb, self.rangeDownSb,
            self.yaxisCombo, self.intervalSb, self.calibrationCombo
        ]
        self.restoreSettings()
        self.selectCalibration()
        #self.makeFilters()

        self.publisher = ZmqPublisher('Avs47SingleChannel',
                                      PubSub.AdrTemperature)
示例#6
0
 def __init__(self, parent = None):
     super(DaqStreamingWidget, self).__init__(parent)
     self.setupUi(self)
     self.thread = None
     self.hdfFile = None
     self.columns = {'enabled':0, 'save': 1, 'coupling':2, 'mode':3, 'gain':4, 'squid':5, 'reset':6, 'label':7}
     self.plot.addLegend()
     self.plot.setLabel('left', 'voltage', units='V')
     self.plot.setLabel('bottom', 'time', units='s')
     self.curves = []
     self.runPb.clicked.connect(self.runPbClicked)
     self.bFilter = []
     self.aFilter = []
     self.populateUi()
     self.publisher = ZmqPublisher('LegacyDaqStreaming', port=PubSub.LegacyDaqStreaming)
     self.writeDataPb.toggled.connect(self.writeDataToggled)
示例#7
0
 def __init__(self, powerSupply, parent=None):
     QThread.__init__(self, parent)
     self.buffer = ''
     self.ps = powerSupply
     #print "Power supply", self.ps
     self.dIdtMax = 1000. * mAperMin  # max rate: 1 A/min
     self.dIdtTarget = 0.
     self.Rsense = 0.02  # 20mOhm sense resistor
     self.Rmax = 1.0  # Maximum R for quench detection
     self.inductance = 30.0  # 30 Henry
     self.VSupplyMax = self.OutputVoltageLimit + self.SupplyDeltaMax  # Maximum supply voltage
     self.ISupplyLimit = 8.8  # 8.9 Maximum current permitted for supply
     self.IOutputMax = 8.5  # 8.5 # Maximum current for magnet
     self._forcedShutdown = False
     try:
         self.publisher = ZmqPublisher('MagnetControlThread',
                                       PubSub.MagnetControl, self)
     except Exception, e:
         self.warn('Unable to start ZMQ publisher: %s' % e)
         self.publisher = None
示例#8
0
    def __init__(self, parent=None):
        super(mainWindow, self).__init__(parent)
        self.setupUi(self)
        #set plot
        self.yAxisCombo.currentIndexChanged.connect(self.plotLabel)
        axis = pg.DateAxisItem(orientation='bottom')
        self.plot = pg.PlotWidget(axisItems={'bottom': axis})
        self.verticalLayout.addWidget(self.plot)
        self.plot.addLegend()
        self.plot.setLabel('bottom', 'time')

        #connect
        self.clearPb.clicked.connect(self.clearData)
        self.StartAllPb.clicked.connect(self.startAll)
        self.StopAllPb.clicked.connect(self.stopAll)
        self.addRowPb.clicked.connect(self.addRow)
        self.deleteRowPb.clicked.connect(self.deleteRow)

        #set table
        self.rowCount = 0
        self.tableWidget.setColumnCount(6)
        header = u'Enable,DMM VISA,Thermometer,Current (µA),Voltage,Temperature'
        self.tableWidget.setHorizontalHeaderLabels(header.split(','))
        self.tableWidget.setColumnWidth(1, 150)
        self.tableWidget.setColumnWidth(2, 150)

        self.objectList = []
        self.errorDisplayArray = [None for x in range(self.rowCount)]
        for i in range(self.errorDisplayArray.__len__()):
            self.errorDisplayTE.append(self.errorDisplayArray[i])

        self.publisher = ZmqPublisher('DiodeThermometer',
                                      PubSub.DiodeThermometers)
        self.rowSelected = -1
        self.settingsWidgets = [self.yAxisCombo]
        self.tableWidget.connect(self.tableWidget.verticalHeader(),
                                 SIGNAL("sectionClicked(int)"), self.selectRow)

        self.restoreSetting()
示例#9
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)
示例#10
0
    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
示例#11
0
class TektronixScopeWidget(Ui.Ui_Form, QWidget):
    def __init__(self, parent=None):
        super(TektronixScopeWidget, self).__init__(parent)
        self.setupUi(self)
        self.thread = None
        self.hdfFile = None
        self.columns = {
            'enabled': 0,
            'save': 1,
            'coupling': 2,
            'mode': 3,
            'gain': 4,
            'label': 5
        }
        self.plot.addLegend()
        self.plot.setLabel('left', 'voltage', units='V')
        self.plot.setLabel('bottom', 'time', units='s')
        self.activeChannels = [1, 2]
        pens = 'ybmr'
        self.curves = []
        for ch in self.activeChannels:
            curve = pg.PlotDataItem(pen=pens[ch], name='Channel %d' % ch)
            self.plot.addItem(curve)
            self.curves.append(curve)

        self.connectToInstrument()
        self.publisher = ZmqPublisher('TektronixScope',
                                      port=PubSub.TektronixScope)

    def removeAllCurves(self):
        for curve in self.curves:
            self.plot.removeItem(curve)
            del curve
        self.curves = []
        self.plot.plotItem.legend.items = []

    def connectToInstrument(self):
        address = "wisptek2.physics.wisc.edu"
        print "Connecting to instrument:", address
        scope = TektronixTds3000(address)
        print scope.identify()
        for source in scope.TriggerSources:
            self.triggerSourceCombo.addItem(source)

        self.triggerSourceCombo.currentIndexChanged.connect(self.update)
        self.scope = scope
        self.thread = ScopeThread(self.scope, self)
        self.thread.setActiveChannels(self.activeChannels)
        self.thread.dataReady.connect(self.collectData)
        self.thread.terminated.connect(self.restartThread)
        self.thread.start()

    def collectData(self, channel, t, y):
        dt = t[1] - t[0]
        timeStamp = time.time()
        dataSet = {'t': timeStamp, 'dt': dt}
        print "Sample rate:", 1. / dt

        self.publisher.publish(channel, dataSet, arrays={'CH%d' % channel: y})

        for i, ch in enumerate(self.activeChannels):
            if ch == channel:
                self.curves[i].setData(t, y)

    def populateUi(self):
        s = QSettings(OrganizationName, ApplicationName)

    def saveSettings(self):
        s = QSettings(OrganizationName, ApplicationName)

    def restartThread(self):
        '''This is a hack to get the thread restarted if it stops due to communications error'''
        print "Restarting"
        self.thread.wait(1000)
        del self.thread
        self.thread = None
        self.connectToInstrument()

    def endThread(self):
        self.thread.stop()
        self.thread.wait(2000)
        self.thread = None

    def closeEvent(self, e):
        if self.thread is not None:
            self.endThread()
        self.saveSettings()
        super(TektronixScopeWidget, self).closeEvent(e)
示例#12
0
 def __init__(self, Rbias, biasChannel, fieldChannel=None, pflChannel = None):
     self.publisher = ZmqPublisher(origin='TesBiasDAQ', port=PubSub.TesBiasDAQ)
     self.Rbias = Rbias
     self.biasChannel = biasChannel
     self.pflChannel = pflChannel
     self.Vbias = 0
示例#13
0
class Tes(object):
    class States:
        Idle = 0
        RampAboveBias = 1
        RampBias = 2
        WaitForTlow = 3
        HoldLow = 4
        WaitForThigh = 5
        HoldHigh = 6
        WaitForTmid = 7
        HoldTmid = 8
        
    def __init__(self, Rbias, biasChannel, fieldChannel=None, pflChannel = None):
        self.publisher = ZmqPublisher(origin='TesBiasDAQ', port=PubSub.TesBiasDAQ)
        self.Rbias = Rbias
        self.biasChannel = biasChannel
        self.pflChannel = pflChannel
        self.Vbias = 0
        
    def _makeAoTask(self):
        aoTask = daq.AoTask('biasTask')
        aoTask.addChannel(self.biasChannel)
        return aoTask

    def _makeAiTask(self):
        aiTask = daq.AiTask('pflTask')
        aiTask.addChannel(self.pflChannel)
        return aiTask
      
    def setBias(self, Ibias):
        aoTask = self._makeAoTask()
        Vbias = Ibias * self.Rbias
        #logger.info('Applying Vbias=%.5f V' % Vbias)
        self._writeData(aoTask, Vbias)
        self.Vbias = Vbias
        aoTask.stop()
        aoTask.clear()

    def _writeData(self, aoTask, V):
        '''Actually update the voltage and publish to ZMQ.'''
        aoTask.writeData([V], autoStart = True) # This is the only place where aoTask.writeData should appear
        self.publisher.publishDict('Coil', {'t':time.time(), 'Vbias':V})
        
    def rampBias(self, Ibias, Vstep=0.001):
        aoTask = self._makeAoTask()
        Vbias = Ibias * self.Rbias
        step = Vstep * np.sign(Vbias-self.Vbias)
        Vs = np.arange(self.Vbias, Vbias+step, step)
        for V in Vs:
            self._writeData(aoTask, V)
        self._writeData(aoTask, Vbias)
        self.Vbias = Vbias
        aoTask.stop()
        aoTask.clear()
        
    def logRampBias(self, Ibias, nSteps, V0=0):
        assert Ibias > 0
        assert self.Vbias > 0
        aoTask = self._makeAoTask()
        Vbias = Ibias * self.Rbias
        Vs = V0+np.logspace(np.log10(self.Vbias-V0), np.log10(Vbias-V0), nSteps)
        for V in Vs:
            self._writeData(aoTask, V)
        self._writeData(aoTask, Vbias)
        self.Vbias = Vbias
        aoTask.stop()
        aoTask.clear()
        
    def measureIvsT(self, adr, Ibias, Tmid, deltaT, fileName=''):
        assert deltaT > 0
        VbiasTarget = Ibias * self.Rbias
        VbiasTargetAbove = 3.5
        aoTask = self._makeAoTask()
        aiTask = self._makeAiTask()
        state = Tes.States.Idle
        ts = []
        Vsquids = []
        Vbiases = []
        Tadrs = []
        step = 0.02
        nHold = 40
        
        while True:
            time.sleep(0.1)
            QApplication.processEvents()
            Vsquid = np.mean(aiTask.readData(500)[0])
            t = time.time()
            ts.append(time.time())
            Vsquids.append(Vsquid)
            Vbiases.append(self.Vbias)
            T=adr.T
            Tadrs.append(T)
            #print('State=', state, self.Vbias, Vsquid)
            if len(fileName):
                with open(fileName, 'a') as f:
                    f.write('%.3f\t%d\t%.5f\t%.6f\t%.6f\n' % (t, state, self.Vbias, T, Vsquid))
            n = len(Vsquids)
            if state == Tes.States.Idle:
                if n > nHold:
                    state = Tes.States.RampAboveBias
            elif state == Tes.States.RampAboveBias:
                s = step * np.sign(VbiasTargetAbove - self.Vbias)
                Vbias = self.Vbias + s
                if s < 0:
                    Vbias = max(VbiasTargetAbove, Vbias)
                else:
                    Vbias = min(VbiasTargetAbove, Vbias)
                self._writeData(aoTask, Vbias)
                self.Vbias = Vbias
                if abs(self.Vbias-VbiasTargetAbove) < 1E-5:
                    state = Tes.States.RampBias
                    nTransition = n
            elif state == Tes.States.RampBias:
                s = step * np.sign(VbiasTarget - self.Vbias)
                Vbias = self.Vbias + s
                if s < 0:
                    Vbias = max(VbiasTarget, Vbias)
                else:
                    Vbias = min(VbiasTarget, Vbias)
                self._writeData(aoTask, Vbias)
                self.Vbias = Vbias
                if abs(self.Vbias-VbiasTarget) < 1E-5:
                    adr.rampTo(Tmid-deltaT)
                    state = Tes.States.WaitForTlow
                    nTransition = n
            elif state == Tes.States.WaitForTlow:
                if adr.T <= Tmid - deltaT:
                    state = Tes.States.HoldLow
                    nTransition = n
            elif state == Tes.States.HoldLow:
                if n > nTransition + nHold:
                    state = Tes.States.WaitForThigh
                    adr.rampTo(Tmid + deltaT)
                    nTransition = n
            elif state == Tes.States.WaitForThigh:
                if adr.T >= Tmid + deltaT:
                    state = Tes.States.HoldHigh
                    nTransition = n
            elif state == Tes.States.HoldHigh:
                if n > nTransition + nHold:
                    state = Tes.States.WaitForTmid
                    adr.rampTo(Tmid)
                    nTransition = n
            elif state == Tes.States.WaitForTmid:
                if abs(adr.T - Tmid) < 20E-6:
                    state = Tes.States.HoldTmid
                    nTransition = n
            elif state == Tes.States.HoldTmid:
                if n > nTransition+nHold:
                    break
                
        return np.asarray(ts), np.asarray(Tadrs), np.asarray(Vbiases), np.asarray(Vsquids)
示例#14
0
class MagnetControlThread(QThread):
    '''Magnet control thread'''
    DIODE_I0 = 4.2575E-11 # A
    DIODE_A  = 37.699     # 1/V (=q_q/(k_B*T))
    quenchDetected = pyqtSignal()
    supplyVoltageUpdated = pyqtSignal(float)
    programmedSupplyVoltageUpdated = pyqtSignal(float)
    supplyCurrentUpdated = pyqtSignal(float)
    magnetVoltageUpdated = pyqtSignal(float)
    diodeVoltageUpdated = pyqtSignal(float)
    resistiveVoltageUpdated = pyqtSignal(float)
    resistanceUpdated = pyqtSignal(float)
    rampRateUpdated = pyqtSignal(float)
    measurementAvailable = pyqtSignal(float, float, float, float)

    def __init__(self, magnetSupply, dmm, parent=None):
        QThread.__init__(self, parent)
        self.ms = magnetSupply
        self.dmm = dmm
        self.interval = 0.2 # Update interval
        self.dIdtMax = 1./60. # max rate: 1 A/min = 1./60. A/s
        self.dIdt = 0.
        self.Rmax = 0.6 # Maximum R for quench detection
        self.inductance = 30.0 # 30 Henry
        self.Vmax = 2.8 # Maximum supply voltage
        self.Imax = 8.5 # Maximum current permitted
        self._quenched = False
        self.publisher = ZmqPublisher('MagnetControlThread', PubSub.MagnetControl, self)

    @property
    def quenched(self):
        return self._quenched

    @property
    def maximumCurrent(self):
        return self._Imax

    @maximumCurrent.setter
    def maximumCurrent(self, Imax):
        self._Imax = Imax

    @property
    def inductance(self):
        return self._L

    @inductance.setter
    def inductance(self, L):
        if L > 0:
            self._L = L
        else:
            raise Exception('Inductance must be positive.')

    def setRampRate(self, dIdt):
        '''Specify desired ramp rate in A/s.'''
        if self.quenched:
            self.dIdt = -0.5*self.dIdtMax # Fast ramp down
        else:
            self.dIdt = max(-self.dIdtMax, min(self.dIdtMax, dIdt))
        self.rampRateUpdated.emit(self.dIdt)

    def log(self, t):
        s = QSettings('WiscXrayAstro', application='ADR3RunInfo')
        path = str(s.value('runPath', '', type=str))
        fileName = os.path.join(path,'MagnetControl_%s.dat' % time.strftime('%Y%m%d'))
        if not os.path.isfile(fileName):
            with open(fileName, 'a') as f:
                header = '#tUnix\tVsupply\tIsupply\tVmagnet\tVsupplyProg\n'
                f.write(header)
        text = '%.3f\t%.3f\t%.3f\t%.5g\t%.5f\n' % (t, self.supplyVoltage, self.supplyCurrent, self.magnetVoltage, self.programmedSupplyVoltage)
        with open(fileName, 'a') as f:
            f.write(text)

    def diodeVoltageDrop(self, current):
        '''Calculate approximate the diode voltage drop for a given current.
        '''
        if current < 0:
            raise Exception('Current must be positive')
        Vdiode = log(current/self.DIODE_I0+1)/self.DIODE_A
        return Vdiode

    @property
    def interval(self):
        return self._interval

    @interval.setter
    def interval(self, seconds):
        self._interval = float(seconds)

    def stop(self):
        self.stopRequested = True
        logger.debug("MagnetControlThread stop requested.")

    def triggerQuench(self):
        self._quenched = True
        logger.warning("Quench detected!")
        self.quenchDetected.emit()
        self.setRampRate(0) # This will set the ramp rate to ramp down

    def measureSupplyCurrent(self):
        I = self.ms.supplyCurrent()
        self.supplyCurrent = I
        self.supplyCurrentUpdated.emit(I)
        return I

    def measureSupplyVoltage(self):
        (Vprogrammed, Vmeasured) = self.ms.supplyVoltages()
        self.supplyVoltage = Vmeasured
        self.supplyVoltageUpdated.emit(Vmeasured)
        self.programmedSupplyVoltage = Vprogrammed
        self.programmedSupplyVoltageUpdated.emit(Vprogrammed)
        return Vprogrammed

    def measureMagnetVoltage(self):
        logger.info("Measuring magnet voltage")
        V = self.dmm.voltageDc()
        logger.info("Vmagnet=%fV" % V)
        self.magnetVoltage = V
        self.magnetVoltageUpdated.emit(V)
        return V

    def setSuppyVoltage(self, Vnew):
            if Vnew >= self.Vmax:
                Vnew = self.Vmax
                self.supplyLimit = True
            elif Vnew <= 0:
                Vnew = 0
                self.supplyLimit = True
            else:
                self.supplyLimit = False
            self.programmedSupplyVoltage = self.ms.setSupplyVoltage(Vnew)
            self.programmedSupplyVoltageUpdated.emit(self.programmedSupplyVoltage)

    def sleepPrecise(self,tOld):
            tSleep = int(1E3*(self.interval-time.time()+tOld-0.010))
            if tSleep>0.010:
                self.msleep(tSleep)
            while(time.time()-tOld < self.interval):
                pass
            
    def query(self, item):
        return self.publisher.query(item)

    def run(self):
        self.stopRequested = False
        logger.info("Thread starting")
        currentHistory = History(maxAge=3)
        dIdtThreshold = 100E-3 # 100mA/s
        mismatchCount = 0
        mismatch = False
        try:
            while not self.stopRequested:
                # First, take all measurements
                t = time.time()
                Isupply = self.measureSupplyCurrent()
                Vsupply = self.measureSupplyVoltage()
                Vmagnet = self.measureMagnetVoltage()
                self.measurementAvailable.emit(t, Isupply, Vsupply, Vmagnet)

                self.publisher.publish('Isupply', Isupply)
                self.publisher.publish('Vsupply', Vsupply)
                self.publisher.publish('Vmagnet', Vmagnet)
                self.publisher.publish('Imagnet', Isupply)
                self.publisher.publish('dIdt', Vmagnet/self.inductance)

                # Log all measurements
                self.log(t)

                # Check that Vmagnet matches L * dI/dt
                currentHistory.append(t, Isupply)
                dIdt = currentHistory.dydt()

                if abs(dIdt) > dIdtThreshold or abs(Vmagnet) > self.inductance*dIdtThreshold:
                    match = (dIdt /  (Vmagnet/self.inductance))-1.
                    if abs(match) > 0.5:
                        logger.warn("Mismatch between dIdt (%.4f A/s) and magnet voltage (%.5f V)." % (dIdt, Vmagnet))
                        mismatchCount += 1
                    else:
                        mismatchCount = 0
                else:
                    mismatchCount = 0

                # Check for quench
                if Isupply < 0:
                    Isupply = 0
                Vdiode = self.diodeVoltageDrop(Isupply)
                self.diodeVoltageUpdated.emit(Vdiode)
                V_R = Vsupply - Vmagnet - Vdiode
                self.resistiveVoltageUpdated.emit(V_R)
                if Isupply > 0.1:
                    R = V_R / Isupply
                else:
                    R = float('nan')
                self.resistanceUpdated.emit(R)
                if Isupply > 1:
                    if R > self.Rmax:
                        self.triggerQuench()

                #Compute new parameters
                VmagnetGoal = self.inductance*self.dIdt

                if Isupply >= self.Imax and self.dIdt > 0:
                    VmagnetGoal = 0

                errorTerm = (VmagnetGoal-Vmagnet)
                logger.info("Programmed supply voltage %f" % self.programmedSupplyVoltage)
                if mismatchCount > 5:
                    logger.warn('Mismatch between dIdt and magnet voltage has persisted!')
                    #logger.warn("Mismatch between dIdt and magnet voltage has persisted, ramping down supply!")
                    #mismatch = True

                if not mismatch:
                    Vnew = Vsupply + errorTerm
                else:
                    Vnew = Vsupply - 0.1
                    if Vnew < 0:
                        break
                logger.info("Vnew=%f V"% Vnew)
                self.setSuppyVoltage(Vnew)
                self.sleepPrecise(t)

        except Exception:
            logger.warn("Exception:", exc_info=True)
        logger.info("MagnetControl ending")
示例#15
0
    def start(self):
        sensorName = self.sensorName()
        self.setWindowTitle('Lock-In Thermometer %s' % 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.lia = SR830(visa)
        self.setupAutomaton()

        Sockets = {
            'BusThermometer': PubSub.LockinThermometerAdr,
            'RuOx2005Thermometer': PubSub.LockinThermometerRuOx2005,
            'BoxThermometer': PubSub.LockinThermometerBox
        }
        socket = Sockets[sensorName]
        self.publisher = ZmqPublisher('LockinThermometer', socket)

        self.runPb.setText('Stop')
        self.enableWidgets(False)

        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.lia.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.lia.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
        self.PDA.start()
示例#16
0
class Avs47SingleChannelWidget(Ui.Ui_Form, QWidget):
    def __init__(self, parent=None):
        super(Avs47SingleChannelWidget, self).__init__(parent)
        self.dmm = None
        self.avs = None
        self.buffer = ''
        self.workerThread = None
        self.setupUi(self)
        self.setWindowTitle('AVS47 Single Channel')
        self.outputFile = None
        self.yaxisCombo.currentIndexChanged.connect(self.switchPlotAxis)
        self.temperatureIndicator.setPrecision(5)

        combo = self.calibrationCombo
        combo.addItem('RuOx 600')
        combo.addItem('RuOx 2005')
        combo.addItem('RuOx Bus (Shuo)')
        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.currentIndexChanged.connect(self.selectCalibration)

        axis = pg.DateAxisItem(orientation='bottom')
        self.plot = pg.PlotWidget(axisItems={'bottom': axis})
        self.plot.addLegend()
        self.verticalLayout.addWidget(self.plot)
        self.curve = pg.PlotCurveItem(name='Actual', symbol='o', pen='g')
        self.plot.addItem(self.curve)
        self.clearData()

        self.runPb.clicked.connect(self.runPbClicked)

        self.widgetsForSettings = [
            self.readoutInternalRadio, self.readoutDmmRadio,
            self.readoutZmqRadio, self.bridgeCombo, self.dmmCombo,
            self.autoRangeCb, self.rangeUpSb, self.rangeDownSb,
            self.yaxisCombo, self.intervalSb, self.calibrationCombo
        ]
        self.restoreSettings()
        self.selectCalibration()
        #self.makeFilters()

        self.publisher = ZmqPublisher('Avs47SingleChannel',
                                      PubSub.AdrTemperature)

        #self.serverThread = RequestReplyThreadWithBindings(port=RequestReply.AdrPidControl, parent=self)
        #boundWidgets = {'rampRate':self.rampRateSb, 'rampTarget':self.rampTargetSb, 'rampEnable':self.rampEnableCb, 'setpoint':self.setpointSb}
        #for name in boundWidgets:
        #self.serverThread.bindToWidget(name, boundWidgets[name])
        #self.serverThread.start()

    def makeFilters(self):
        fs = 10
        fc = 0.5
        order = 3
        self.lpfR = IIRFilter.lowpass(order=order, fc=fc, fs=fs)
        self.lpfT = IIRFilter.lowpass(order=order, fc=fc, fs=fs)
        self.lpfR.initializeFilterFlatHistory(self.Rs[-1])
        self.lpfT.initializeFilterFlatHistory(self.Ts[-1])
        Rfilt = self.lpfR.filterCausal(R)
        Tfilt = self.lpfT.filterCausal(T)

    def selectCalibration(self):
        cal = self.calibrationCombo.currentText()
        if cal == 'RuOx 600':
            self.calibration = RuOx600()
        elif cal == 'RuOx 2005':
            self.calibration = RuOx2005()
        elif cal == 'RuOx Bus (Shuo)':
            self.calibration = RuOxBus()
        else:
            warnings.warn('Unknown calibration selected:', cal)

    def switchPlotAxis(self):
        yaxis = self.yaxisCombo.currentText()
        if yaxis == 'R':
            self.plot.getAxis('left').setLabel('R', 'Ohm')
        elif yaxis == 'T':
            self.plot.getAxis('left').setLabel('T', 'K')
        self.updatePlot()

    def runPbClicked(self):
        if self.workerThread is not None:
            self.workerThread.stop()
            return
        self.avs = Avs47(self.bridgeCombo.visaResource())
        self.avs.range.bindToEnumComboBox(self.rangeCombo)
        self.avs.muxChannel.bindToSpinBox(self.channelSb)
        #self.channelSb.setValue(self.avs.muxChannel.value)
        self.avs.excitation.bindToEnumComboBox(self.excitationCombo)
        self.avs.range.caching = True
        self.avs.excitation.caching = True
        self.avs.muxChannel.caching = True

        thread = WorkerThread()
        thread.setBridge(self.avs)
        if self.readoutDmmRadio.isChecked():
            self.dmm = Agilent34401A(self.dmmCombo.visaResource())
            thread.setDmm(self.dmm)

        connectAndUpdate(self.rangeUpSb, thread.setAutoRangeUp)
        connectAndUpdate(self.rangeDownSb, thread.setAutoRangeDown)
        connectAndUpdate(self.autoRangeCb, thread.enableAutoRange)

        thread.finished.connect(self.threadFinished)
        thread.readingAvailable.connect(self.collectReading)
        self.intervalSb.valueChanged.connect(thread.setInterval)
        self.workerThread = thread
        self.t0 = time.time()
        thread.start()
        self.enableControls(False)
        self.runPb.setText('Stop')

    def threadFinished(self):
        self.workerThread.deleteLater()
        self.workerThread = None
        self.flushBuffer()
        self.runPb.setText('Start')
        self.enableControls(True)

    def appendErrorMessage(self, message):
        self.errorTextEdit.append(message)

    def enableControls(self, enable):
        self.readoutGroupBox.setEnabled(enable)
        self.bridgeCombo.setEnabled(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 = []
        self.Rs = []
        self.Ts = []
        self.updatePlot()

    def collectReading(self, R, t):

        channel = self.avs.muxChannel.value
        excitation = self.avs.excitation.code
        Range = self.avs.range.code

        V = self.avs.excitation.value

        try:
            P = V**2 / R
        except ZeroDivisionError:
            P = np.nan

        try:
            T = self.calibration.calculateTemperature(
                [R])[0]  # @todo This is really a crutch
        except Exception as e:
            self.appendErrorMessage(e)
            T = 0

        self.ts.append(t)
        self.Rs.append(R)
        self.Ts.append(T)

        string = "%.3f\t%d\t%d\t%d\t%.6g\n" % (t, channel, excitation, Range,
                                               R)
        self.buffer += string
        if len(self.buffer) > 2048:
            self.flushBuffer()

        Sensors = {0: 'FAA', 1: 'GGG'}
        if self.publisher is not None:
            if Sensors.has_key(channel):
                sensorName = Sensors[channel]
                self.publisher.publishDict(sensorName, {
                    't': t,
                    'R': R,
                    'T': T,
                    'P': P
                })

        maxLength = 20000
        self.ts = pruneData(self.ts, maxLength)
        self.Rs = pruneData(self.Rs, maxLength)
        self.Ts = pruneData(self.Ts, maxLength)

        self.resistanceIndicator.setValue(R)
        self.temperatureIndicator.setKelvin(T)
        self.updatePlot()

    def flushBuffer(self):
        t = time.time()
        timeString = time.strftime('%Y%m%d', time.localtime(t))
        s = QSettings('WiscXrayAstro', application='ADR3RunInfo')
        path = str(s.value('runPath', '', type=str))
        fileName = os.path.join(path, 'AVSBridge_%s.dat' % timeString)

        if not os.path.isfile(fileName):  # Maybe create new file
            x = '#Avs47SingleChannel.py\n'
            x += '#Date=%s\n' % timeString
            x += '#AVS-47=%s\n' % self.avs.visaId()
            if self.dmm is not None:
                x += '#Readout=DMM\n'
                x += '#DMM=%s\n' % self.dmm.visaId()
            else:
                x += '#Readout=AVS-47\n'
            x += '#' + '\t'.join(
                ['time', 'channel', 'excitation', 'range', 'R']) + '\n'
            self.buffer = x + self.buffer

        try:
            with open(fileName, 'a') as f:
                f.write(self.buffer)
            self.buffer = ''
        except Exception as e:
            warnings.warn('Unable to write buffer to log file: %s' % e)

    def updatePlot(self):
        yaxis = self.yaxisCombo.currentText()
        if yaxis == 'R':
            self.curve.setData(self.ts, self.Rs)
        elif yaxis == 'T':
            self.curve.setData(self.ts, self.Ts)

    def closeEvent(self, e):
        if self.workerThread is not None:
            self.workerThread.stop()
            self.workerThread.wait(2000)
        self.saveSettings()