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 __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 __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)
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})
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)
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)
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
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()
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 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
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)
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
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)
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")
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()
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()