def __init__(self, parent=None): super(AdrControlWidget, self).__init__(parent) self.setupUi(self) self.setWindowTitle('ADR Control') self.addRowPb.clicked.connect(self.addRowClicked) for i in range(0, self.currentTable.columnCount()): self.currentTable.horizontalHeader().setResizeMode( i, QHeaderView.ResizeToContents ) self.deleteRowPb.clicked.connect(self.deleteRowClicked) self.restoreSettings() magnetRemote = MagnetControlRemote('AdrControl', parent=parent) magnetRemote.supplyVoltageReceived.connect(self.supplyVoltageSb.setValue) magnetRemote.supplyCurrentReceived.connect(self.supplyCurrentSb.setValue) magnetRemote.magnetVoltageReceived.connect(self.magnetVoltageSb.setValue) magnetRemote.magnetCurrentReceived.connect(self.magnetCurrentSb.setValue) magnetRemote.magnetCurrentReceived.connect(self.magnetCurrentUpdated) #magnetRemote.dIdtReceived.connect(self.updateRampRate) self.magnetRemote = magnetRemote hkSub = HousekeepingSubscriber(parent=parent) hkSub.adrTemperatureReceived.connect(self.adrTemperatureSb.setValue) hkSub.start() self.hkSub = hkSub self.state = State.IDLE self.startPb.clicked.connect(self.startPbClicked) self.stopPb.clicked.connect(self.stopPbClicked) self.skipPb.clicked.connect(self.skip) self.loadPb.clicked.connect(self.loadTable) self.savePb.clicked.connect(self.saveTable)
def __init__(self, parent=None): super(LockinThermometerWidget, self).__init__(parent) self.setupUi(self) self.setWindowTitle('Lockin Thermometer') self.timer = None self.Rthermometer = float('nan') ''' Not sure if DateAxisItem is available anymore ''' axis = pg.AxisItem(orientation='bottom') self.plot = pg.PlotWidget(axisItems={'bottom': axis}) self.plot.setBackground('w') self.plot.plotItem.showGrid(x=True, y=True) self.plot.addLegend() self.verticalLayout.addWidget(self.plot) self.curve = pg.PlotCurveItem(name='X', symbol='o', pen='b') self.plot.addItem(self.curve) self.clearPb.clicked.connect(self.clearData) self.clearData() self.plotYAxisCombo.currentIndexChanged.connect(self.updatePlot) self.sr830 = None self.runPb.clicked.connect(self.run) self.parameterItems = [self.attenuatorGainSb, self.sourceImpedanceSb, self.driveResistanceSb, self.leadResistanceSb, self.preampGainSb, self.sensorVoltageSb] self.savePb.clicked.connect(self.saveParameterSet) self.loadPb.clicked.connect(self.loadParameterSet) self.deletePb.clicked.connect(self.deleteParameterSet) self.attenuatorAttenuationSb.valueChanged.connect(self.updateAttenuatorGain) self.attenuatorGainSb.valueChanged.connect(self.updateAttenuatorAttenuation) self.sensorVoltageIndicator.setUnit('V') self.sensorCurrentIndicator.setUnit('A') self.sensorPowerIndicator.setUnit('W') sr830 = SR830(None) sr830.sensitivity.populateEnumComboBox(self.minSensitivityCombo) self.loadParameterSets() self.restoreSettings() self.hkSub = HousekeepingSubscriber(parent = self) self.hkSub.adrResistanceReceived.connect(self.collectAdrResistance) self.hkSub.start() self.adrResistanceIndicator.setUnit(u'Ω') self.adrResistanceIndicator.setPrecision(5) self.publisher = None combo = self.calibrationCombo combo.addItem('RuOx 600') combo.addItem('RuOx 2005') combo.addItem('RuOx Bus (Shuo)') combo.addItem('RuOx Chip (InsideBox)') combo.setItemData(0, 'Nominal sensor calibration for RuOx 600 series', Qt.ToolTipRole) combo.setItemData(1, 'Calibration for RuOx sensor #2005 series', Qt.ToolTipRole) combo.setItemData(2, 'Cross-calibration against RuOx #2005 by Shuo (not so good above ~300mK)', Qt.ToolTipRole) combo.setItemData(3, 'Cross-calibration against RuOx #2005 by Yu', Qt.ToolTipRole) self.selectCalibration() combo.currentIndexChanged.connect(self.selectCalibration)
def __init__(self, parent=None): super(TESIVSweepDaqWidget, self).__init__(parent) self.setupUi(self) self.setWindowTitle('TES IV Sweep (DAQ)') self.aoDeviceCombo.currentIndexChanged.connect( self.updateDaqChannelsAo) self.aiDeviceCombo.currentIndexChanged.connect( self.updateDaqChannelsAi) self.populateDaqCombos() self.msmThread = None self.startPb.clicked.connect(self.startPbClicked) self.hkSub = HousekeepingSubscriber(self) self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue) self.hkSub.adrResistanceReceived.connect( self.collectThermometerResistance) self.hkSub.start() self.plotRaw = pg.PlotWidget(title='IV sweeps') self.plotLayout.addWidget(self.plotRaw) self.curveRaw = pg.PlotCurveItem() self.plotRaw.addItem(self.curveRaw) self.plotCritical = pg.PlotWidget(title='Critical current') self.plotLayout.addWidget(self.plotCritical) self.plotCritical.addLegend() self.criticalCurve1 = pg.PlotCurveItem(name='+', symbol='o', pen='r') self.plotCritical.addItem(self.criticalCurve1) self.criticalCurve2 = pg.PlotCurveItem(name='o', symbol='o', pen='b') self.plotCritical.addItem(self.criticalCurve2) self.plotRaw.setLabel('bottom', 'Vbias', 'V') self.plotRaw.setLabel('left', 'I_TES', 'uA') self.plotCritical.setLabel('left', 'I_C', 'uA') self.clearData() self.clearCriticalData() self.coilAo = None self.clearPb.clicked.connect(self.clearData) self.coilSweepCb.toggled.connect(self.toggleCoilSweep) self.clearCriticalPb.clicked.connect(self.clearCriticalData) self.samplesPerPointSb.valueChanged.connect( lambda value: self.discardSamplesSb.setMaximum(value - 1)) self.coilEnableCb.toggled.connect(self.toggleCoil) self.coilVoltageSb.valueChanged.connect(self.updateCoilVoltage) # self.toggleCoil(self.coilEnableCb.isChecked()) # self.toggleCoilSweep(self.coilSweepCb.isChecked()) self.coilDriverCombo.currentIndexChanged.connect( self.coilDriverChanged) self.Vcoil = np.nan self.restoreSettings() self.Rthermometers = []
def __init__(self, qApp): self.qApplication = qApp self.T = float('nan') hkSub = HousekeepingSubscriber() #hkSub.adrTemperatureReceived.connect(self._receiveTemp) hkSub.thermometerReadingReceived.connect(self._receiveThermometerReading) hkSub.start() self.hkSub = hkSub self.Tremote = PidControlRemote('TemperatureSteps') self.clearHistory() self.lastUpdate = 0
def __init__(self, parent=None): super(Widget, self).__init__(parent) self.setupUi(self) self.BoSb.setToSiFactor(SI_uT) self.gCoilSb.setToSiFactor(SI_uT / SI_mA) self.RcoilSb.setToSiFactor(SI_kOhm) self.BcoilSb.setToSiFactor(SI_uT) self.RfbSb.setToSiFactor(SI_kOhm) self.invMiSb.setToSiFactor(SI_uA / SI_Phi0) self.invMfbSb.setToSiFactor(SI_uA / SI_Phi0) self.ApSb.setToSiFactor(SI_um**2) self.RbiasSb.setToSiFactor(SI_kOhm) self.gTesSb.setToSiFactor(SI_uT / SI_mA) self.RsSb.setToSiFactor(SI_mOhm) self.IbiasSb.setToSiFactor(SI_uA) self.ItesSb.setToSiFactor(SI_uA) self.RtesSb.setToSiFactor(SI_mOhm) self.setWindowTitle('TES Self-Field Cancellation') self.SettingsWidgets = [ self.BoSb, self.gCoilSb, self.RcoilSb, self.RfbSb, self.invMiSb, self.invMfbSb, self.ApSb, self.RbiasSb, self.gTesSb, self.IbiasSb, self.nameLe, self.aiSamplesSb, self.ItesTargetSb, self.disableBiasCb, self.RsSb ] self.liveWidgets = [ self.BoSb, self.gTesSb, self.IbiasSb, self.ItesTargetSb, self.tesCurrentControlCb, self.disableBiasCb ] #self.IbiasSb.valueChanged.connect(self.IbiasSb.setMaximum) self.restoreSettings() self.msmThread = None self.hkSub = HousekeepingSubscriber(self) self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue) self.hkSub.start() self.curveVsTime = pg.PlotCurveItem(pen='k') self.plotVsTime.addItem(self.curveVsTime) self.plotVsTime.plotItem.enableAutoRange(pg.ViewBox.XYAxes, True) self.curveVsTemp = pg.PlotCurveItem(pen='k') self.plotVsTemp.addItem(self.curveVsTemp) self.plotVsTemp.setBackground('w') self.plotVsTemp.plotItem.showGrid(x=True, y=True) self.plotVsTemp.plotItem.enableAutoRange(pg.ViewBox.XYAxes, True) self.clearData() self.runPb.clicked.connect(self.run)
def __init__(self, parent=None): super(IVSweepDaqWidget, self).__init__(parent) self.setupUi(self) self.aoDeviceCombo.currentIndexChanged.connect( self.updateDaqChannelsAo) self.aiDeviceCombo.currentIndexChanged.connect( self.updateDaqChannelsAi) self.populateDaqCombos() self.restoreSettings() self.msmThread = None self.startPb.clicked.connect(self.startPbClicked) self.hkSub = HousekeepingSubscriber(self) self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue) self.hkSub.start() self.rawPlot.setAxisTitle(QwtPlot.yLeft, 'Vmeas') self.rawPlot.setAxisTitle(QwtPlot.xBottom, 'Vdrive') self.rawCurve = QwtPlotCurve('') self.rawCurve.attach(self.rawPlot) self.criticalCurve1 = QwtPlotCurve('+') self.criticalCurve1.setSymbol( Qwt.QwtSymbol(Qwt.QwtSymbol.Cross, Qt.QBrush(), Qt.QPen(Qt.Qt.red), Qt.QSize(5, 5))) self.criticalCurve1.attach(self.criticalPlot) self.criticalCurve2 = QwtPlotCurve('-') self.criticalCurve2.setSymbol( Qwt.QwtSymbol(Qwt.QwtSymbol.Cross, Qt.QBrush(), Qt.QPen(Qt.Qt.blue), Qt.QSize(5, 5))) self.criticalCurve2.attach(self.criticalPlot) self.criticalPlot.setAxisTitle(QwtPlot.yLeft, 'Vcrit') self.clearData() self.clearCriticalData() self.clearPb.clicked.connect(self.clearData) self.coilSweepCb.toggled.connect(self.toggleCoilSweep) self.clearCriticalPb.clicked.connect(self.clearCriticalData) self.samplesPerPointSb.valueChanged.connect( lambda value: self.discardSamplesSb.setMaximum(value - 1)) self.coilEnableCb.toggled.connect(self.toggleCoil) self.coilVoltageSb.valueChanged.connect(self.updateCoilVoltage) self.toggleCoil(self.coilEnableCb.isChecked()) self.coilDriverCombo.currentIndexChanged.connect( self.coilDriverChanged) self.Vcoil = np.nan
class LockinThermometerWidget(Ui.Ui_Form, QWidget): def __init__(self, parent=None): super(LockinThermometerWidget, self).__init__(parent) self.setupUi(self) self.setWindowTitle('Lockin Thermometer') self.serverThread = None self.timer = None self.Rthermometer = float('nan') axis = pg.DateAxisItem(orientation='bottom') self.plot = pg.PlotWidget(axisItems={'bottom': axis}) self.plot.setBackground('w') self.plot.plotItem.showGrid(x=True, y=True) self.plot.addLegend() self.verticalLayout.addWidget(self.plot) self.curve = pg.PlotCurveItem(name='X', symbol='o', pen='b') self.plot.addItem(self.curve) self.clearPb.clicked.connect(self.clearData) self.clearData() self.plotYAxisCombo.currentIndexChanged.connect(self.updatePlot) self.sr830 = None self.runPb.clicked.connect(self.run) self.parameterItems = [ self.attenuatorGainSb, self.sourceImpedanceSb, self.driveResistanceSb, self.leadResistanceSb, self.preampGainSb, self.sensorVoltageSb ] self.savePb.clicked.connect(self.saveParameterSet) self.loadPb.clicked.connect(self.loadParameterSet) self.deletePb.clicked.connect(self.deleteParameterSet) self.attenuatorAttenuationSb.valueChanged.connect( self.updateAttenuatorGain) self.attenuatorGainSb.valueChanged.connect( self.updateAttenuatorAttenuation) self.sensorVoltageIndicator.setUnit('V') self.sensorCurrentIndicator.setUnit('A') self.sensorPowerIndicator.setUnit('W') sr830 = SR830(None) sr830.sensitivity.populateEnumComboBox(self.minSensitivityCombo) self.loadParameterSets() self.restoreSettings() self.hkSub = HousekeepingSubscriber(parent=self) self.hkSub.adrResistanceReceived.connect(self.collectAdrResistance) self.hkSub.start() self.adrResistanceIndicator.setUnit(u'Ω') self.adrResistanceIndicator.setPrecision(5) self.publisher = None combo = self.calibrationCombo for i, calId in enumerate(ThermometerCalIds): try: cal = getThermometerCalibration(calId) info = cal.info except Exception as e: import warnings warnings.warn( 'Calibration %s unavailable due to exception %s' % (calId, str(e))) combo.addItem(calId) combo.setItemData(i, info, Qt.ToolTipRole) self.selectCalibration() combo.currentIndexChanged.connect(self.selectCalibration) def startServerThread(self, port): if self.serverThread is not None: self.serverThread.stop() self.serverThread.wait(1000) del self.serverThread self.serverThread = None self.serverThread = RequestReplyThreadWithBindings(port, parent=self) boundWidgets = { 'adjustExcitation': self.adjustExcitationCb, 'sensorVoltage': self.sensorVoltageSb, 'tolerance': self.toleranceSb, 'autoRanging': self.autoRangingCb } for name in boundWidgets: self.serverThread.bindToWidget(name, boundWidgets[name]) #logger.info('Starting server thread') self.serverThread.start() def selectCalibration(self): calId = str(self.calibrationCombo.currentText()) self.calibration = getThermometerCalibration(calId) def collectAdrResistance(self, R): self.Rthermometer = R self.adrResistanceIndicator.setValue(R) def updateAttenuatorGain(self, v): sb = self.attenuatorGainSb block = sb.blockSignals(True) sb.setValue(1. / v) sb.blockSignals(block) def updateAttenuatorAttenuation(self, v): sb = self.attenuatorAttenuationSb block = sb.blockSignals(True) sb.setValue(1. / v) sb.blockSignals(block) def saveParameterSet(self): s = QSettings() s.beginGroup('ParameterSets') name = self.configCombo.currentText() s.beginGroup(name) s.setValue('adjustExcitation', self.adjustExcitationCb.isChecked()) s.setValue('sensorName', self.sensorNameLe.text()) s.setValue('sr830Visa', self.visaCombo.currentText()) s.setValue('autoRanging', self.autoRangingCb.isChecked()) s.setValue('minSensitivity', self.minSensitivityCombo.currentCode()) for item in self.parameterItems: s.setValue(item.objectName(), item.value()) s.endGroup() s.endGroup() def loadParameterSet(self): s = QSettings() name = self.configCombo.currentText() s.beginGroup('ParameterSets') if not name in s.childGroups(): dlg = QErrorMessage(self) dlg.setWindowTitle('Error') dlg.showMessage('No saved parameters available for %s' % name) return s.beginGroup(name) for item in self.parameterItems: item.setValue(s.value(item.objectName(), item.value(), type=float)) self.adjustExcitationCb.setChecked( s.value('adjustExcitation', False, type=bool)) self.sensorNameLe.setText(s.value('sensorName', '', type=QString)) self.visaCombo.setCurrentIndex( self.visaCombo.findText( s.value('sr830Visa', 'GPIB0::12', type=QString))) self.autoRangingCb.setChecked(s.value('autoRanging', True, type=bool)) self.minSensitivityCombo.setCurrentCodeSilently( s.value('minSensitivity', 0, type=int)) s.endGroup() s.endGroup() def loadParameterSets(self): s = QSettings() s.beginGroup('ParameterSets') names = s.childGroups() self.configCombo.addItems(names) def deleteParameterSet(self): i = self.configCombo.currentIndex() name = self.configCombo.itemText(i) s = QSettings() s.beginGroup('ParameterSets') s.beginGroup(name) s.remove('') s.endGroup() s.endGroup() self.configCombo.removeItem(i) def closeEvent(self, event): if self.timer: self.timer.stop() self.saveSettings() self.hkSub.stop() self.hkSub.wait(1000) def restoreSettings(self): s = QSettings() #visa = s.value('visa', QString(), type=QString) #i = self.visaCombo.findText(visa) #elf.visaCombo.setCurrentIndex(i) self.configCombo.setCurrentIndex( self.configCombo.findText(s.value('parameterSet', '', type=QString))) if len(self.configCombo.currentText()): self.loadParameterSet() #self.sensorNameLe.setText(s.value('sensorName', '', type=QString)) def saveSettings(self): s = QSettings() #s.setValue('visa', self.visaCombo.currentText()) s.setValue('parameterSet', self.configCombo.currentText()) #s.setValue('sensorName', self.sensorNameLe.text()) def enableWidgets(self, enable): self.visaCombo.setEnabled(enable) self.attenuatorGroupBox.setEnabled(enable) self.seriesResistanceGroupBox.setEnabled(enable) self.preampGroupBox.setEnabled(enable) self.sensorNameLe.setEnabled(enable) self.loadPb.setEnabled(enable) self.savePb.setEnabled(enable) self.deletePb.setEnabled(enable) self.configCombo.setEnabled(enable) def run(self): if self.sr830 is not None: self.stop() else: self.start() def stop(self): self.timer.stop() self.timer = None self.sr830 = None self.runPb.setText('Start') self.enableWidgets(True) del self.publisher self.publisher = None def sensorName(self): return str(self.sensorNameLe.text()) def start(self): sensorName = self.sensorName() self.setWindowTitle('Lock-In Thermometer %s' % sensorName) setAppId(sensorName) if sensorName == 'BusThermometer': icon = QIcon('Icons/LockinThermometer_Bus.ico') elif sensorName == 'RuOx2005Thermometer': icon = QIcon('Icons/LockinThermometer_BoxOutside.ico') elif sensorName == 'BoxThermometer': icon = QIcon('Icons/LockinThermometer_BoxInside2.ico') else: icon = QIcon('Icons/LockinThermometer.ico') self.setWindowIcon(icon) visa = str(self.visaCombo.currentText()) self.sr830 = SR830(visa) #self.sr830.debug = True self.sr830.readAll() self.sr830.sineOut.caching = False # Disable caching on this self.publisher = ZmqPublisher('LockinThermometer', LockInPubSub(sensorName)) self.startServerThread(LockInRequestReply(sensorName)) self.runPb.setText('Stop') self.timer = QTimer() self.timer.setInterval(1000) self.timer.timeout.connect(self.snapSignal) self.timer.start() self.enableWidgets(False) self.rangeChangedTime = 0 self.exChangedTime = 0 t = time.time() timeString = time.strftime('%Y%m%d-%H%M%S', time.localtime(t)) dateString = time.strftime('%Y%m%d') sensorName = str(self.sensorNameLe.text()) s = QSettings('WiscXrayAstro', application='ADR3RunInfo') path = str(s.value('runPath', '', type=str)) fileName = os.path.join(path, '%s_%s.dat' % (sensorName, dateString)) if not os.path.isfile(fileName): # Maybe create new file with open(fileName, 'a+') as f: f.write('#LockinThermometer.py\n') f.write('#Date=%s\n' % timeString) f.write('#SensorName=%s\n' % sensorName) f.write('#SR830=%s\n' % self.sr830.visaId()) f.write('#AttenuatorGain=%f\n' % self.attenuatorGainSb.value()) f.write('#AttenuatorSourceImpedance=%f\n' % self.sourceImpedanceSb.value()) f.write('#DriveResistance=%f\n' % self.driveResistanceSb.value()) f.write('#LeadResistance=%f\n' % self.leadResistanceSb.value()) f.write('#PreampGain=%f\n' % self.preampGainSb.value()) f.write('#DesiredExcitation=%f\n' % self.sensorVoltageSb.value()) k = self.sr830.allSettingValues() for key, value in k.iteritems(): f.write('#SR830/%s=%s\n' % (key, value)) f.write('#' + '\t'.join([ 'time', 'VsineOut', 'X', 'Y', 'f', 'Sensitivity', 'RxCalc', 'Rtherm' ]) + '\n') self.fileName = fileName def snapSignal(self): t = time.time() try: self.sr830.snapSignal() except CommunicationsError as e: # TODO Log the error self.sr830.clearGarbage() return VsineOut = self.sr830.sineOut.value X = self.sr830.X Y = self.sr830.Y f = self.sr830.f rangeChangeAge = t - self.rangeChangedTime exChangeAge = t - self.exChangedTime sensitivity = self.sr830.sensitivity.value if self.autoRangingCb.isChecked(): self.sr830.checkStatus() minCode = self.minSensitivityCombo.currentCode() currentCode = self.sr830.sensitivity.code if self.sr830.overload and rangeChangeAge > 10: self.sr830.sensitivity.code = currentCode + 1 self.rangeChangeTime = t elif abs(X) > 0.9 * sensitivity and rangeChangeAge > 10: self.sr830.sensitivity.code = currentCode + 1 self.rangeChangedTime = t elif abs(X) < 0.3 * sensitivity and rangeChangeAge > 10: if currentCode > minCode: self.sr830.sensitivity.code = currentCode - 1 self.rangeChangedTime = t elif currentCode < minCode: self.sr830.sensitivity.code = minCode self.rangeChangedTime = t G1 = self.attenuatorGainSb.value() G2 = self.preampGainSb.value() Rsource = self.sourceImpedanceSb.value() Rd = self.driveResistanceSb.value() Rl = self.leadResistanceSb.value() Rs = Rsource + Rd + Rl Vx = X / G2 Vex = VsineOut * G1 # Real excitation self.sensorVoltageIndicator.setValue(Vx) Rx = Rs / (Vex / abs(Vx) - 1.) I = abs(Vx) / Rx self.sensorCurrentIndicator.setValue(I) P = abs(Vx) * I Temp = self.calibration.calculateTemperature( [Rx])[0] # @todo This is really a crutch Tbase = self.calibration.correctForReadoutPower(Temp, P) if self.publisher is not None: if rangeChangeAge > 10 and exChangeAge > 10 and Temp == Temp and Temp > 0 and Temp < 10: self.publisher.publishDict(self.sensorName(), { 't': t, 'R': Rx, 'T': Temp, 'P': P, 'Tbase': Tbase }) #self.publisher.publish('ADR_Sensor_R', Rx) #self.publisher.publish('ADR_Temperature', Temp) # Log data with open(self.fileName, 'a+') as of: of.write( '%.3f\t%.3f\t%.5E\t%.5E\t%.3f\t%.1E\t%.5E\t%.5E\n' % (t, VsineOut, X, Y, f, sensitivity, Rx, self.Rthermometer)) self.ts.append(t) self.xs.append(X) self.ys.append(Y) self.fs.append(f) self.VsineOuts.append(VsineOut) self.Rs.append(Rx) self.Vxs.append(Vx) self.Ps.append(P) self.Ts.append(Temp) self.Tbases.append(Tbase) self.sensorIndicator.setValue(Rx) self.temperatureIndicator.setKelvin(Temp) self.baseTempIndicator.setKelvin(Tbase) self.sensorPowerIndicator.setValue(P) self.updateLed.flashOnce() self.updatePlot() # Not sure where this code came from. Seems questionable (FJ) # if len(self.Ts) > 2 : # dTdt = abs((self.Ts[-1] - self.Ts[-2]) / (self.ts[-1] - self.ts[-2])) # if dTdt > 0.1: # self.temperatureIndicator.setKelvin('N/A') # self.stop() # Perhaps change excitation if exChangeAge < 10 or rangeChangeAge < 10 or not self.adjustExcitationCb.isChecked( ): return VxDesired = self.sensorVoltageSb.value() IDesired = VxDesired / Rx VexDesired = IDesired * (Rx + Rs) change = (VexDesired - Vex) / Vex tolerance = 1E-2 * self.toleranceSb.value() if abs(change) < tolerance: return VsineOutDesired = VexDesired / G1 # This is what we would like to see # What we actually get may be something different Vnew = min(5, max(VsineOutDesired, 0.004)) if tolerance == 0 and abs( Vnew - VsineOut) > 0.009: # If a large step is required, do it slowly Vnew = (3. * VsineOut + 1. * Vnew) / 4. if abs(Vnew - VsineOut) < 0.002: return self.exChangedTime = t self.sr830.sineOut.value = Vnew def clearData(self): self.ts = [] self.xs = [] self.ys = [] self.fs = [] self.Rs = [] self.Ps = [] self.VsineOuts = [] self.Vxs = [] self.Ts = [] self.Tbases = [] self.updatePlot() def updatePlot(self): yAxis = self.plotYAxisCombo.currentText() pl = self.plot if yAxis == 'X': y = self.xs pl.setLabel('left', 'Lock-in X', 'V') elif yAxis == 'Y': y = self.ys pl.setLabel('left', 'Lock-in Y', 'V') elif yAxis == 'R': y = self.Rs pl.setLabel('left', 'R sensor', u'Ω') elif yAxis == 'V sine out': y = self.VsineOuts pl.setLabel('left', 'V sine out', 'V') elif yAxis == 'V sensor': y = self.Vxs pl.setLabel('left', 'V sensor', 'V') elif yAxis == 'P sensor': y = self.Ps pl.setLabel('left', 'P sensor', 'W') elif yAxis == 'Sensor temperature': y = self.Ts pl.setLabel('left', 'T sensor', 'K') elif yAxis == 'Base temperature': y = self.Tbases pl.setLabel('left', 'T base', 'K') x = self.ts self.curve.setData(x, y)
def __init__(self, parent = None): super(MultitoneLockinWidget, self).__init__(parent) self.tableColumns = ['active', 'f', 'A', 'phase', 'bw', 'order', 'X', 'Y', 'R', 'Theta'] self.setupUi(self) self.populateTable() self.daqThread = None self.liaThread = None self.rawWriter = None self.hdfFile = None self.fMax = 100E6 self.wavePlot.addLegend() self.wavePlot.setLabel('left', 'Voltage', units='V') self.wavePlot.setLabel('bottom', 'time', units='s') self.excitationCurve = pg.PlotDataItem(pen='r', name='Excitation') self.wavePlot.addItem(self.excitationCurve) self.responseCurve = pg.PlotDataItem(pen='b', name='Response') self.wavePlot.addItem(self.responseCurve) self.startPb.clicked.connect(self.startMeasurement) self.stopPb.clicked.connect(self.stopMeasurement) self.settingsWidgets = [self.deviceCombo, self.aoChannelCombo, self.aoRangeCombo, self.aiChannelCombo, self.aiRangeCombo, self.aiTerminalConfigCombo, self.sampleLe, self.commentLe, self.enablePlotCb, self.sampleRateSb, self.offsetSb, self.rampRateSb, self.inputDecimationCombo, self.saveRawDataCb, self.dcBwSb, self.dcFilterOrderSb, self.saveRawDemodCb, self.subtractDcOffsetCb] self.sampleRateSb.valueChanged.connect(self.updateMaximumFrequencies) self.deviceCombo.currentIndexChanged.connect(self.updateDevice) self.restoreSettings() self.hkSub = HousekeepingSubscriber(self) self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue) self.hkSub.start() self.curve1 = pg.PlotDataItem(symbol='o', symbolSize=7, pen='b', name='') self.curve2 = pg.PlotDataItem(symbol='o', symbolSize=7, pen='r', name='') self.plot1.addItem(self.curve1) self.plot1.setLogMode(x=True) self.plot1.setLabel('bottom', 'f', units='Hz') self.plot2.addItem(self.curve2) self.plot2.setLogMode(x=True) self.plot2.setLabel('bottom', 'f', units='Hz') self.plot2.setXLink(self.plot1) self.plot1.showGrid(x=True, y=True) self.plot2.showGrid(x=True, y=True) self.plotxy.setLabel('bottom', 'X', units='') self.plotxy.setLabel('left', 'Y', units='') self.plotxy.setAspectLocked() self.curvexy = pg.PlotDataItem(symbol='o', symbolSize=7, pen='b', name='') self.plotxy.addItem(self.curvexy) self.plotCombo.currentIndexChanged.connect(self.yAxisChanged) self.plotCombo.setCurrentIndex(self.plotCombo.currentIndex()) self.addRowPb.clicked.connect(self.addTableRow) self.deleteRowPb.clicked.connect(self.deleteTableRow) self.updateMaximumFrequencies() self.sampleRateSb.valueChanged.connect(self.updateMaximumFrequencies) self.inputDecimationCombo.currentIndexChanged.connect(self.updateMaximumFrequencies) self.saveRawDataCb.toggled.connect(self.toggleRawDataCollection) self.enablePlotCb.toggled.connect(self.enablePlotToggled) self.loadTablePb.clicked.connect(self.loadTable) self.saveTablePb.clicked.connect(self.saveTable) self.startServerThread()
class MultitoneLockinWidget(ui.Ui_Form, QWidget): __logger = logging.getLogger(__name__ + '.MultitoneLockinWidget') def __init__(self, parent = None): super(MultitoneLockinWidget, self).__init__(parent) self.tableColumns = ['active', 'f', 'A', 'phase', 'bw', 'order', 'X', 'Y', 'R', 'Theta'] self.setupUi(self) self.populateTable() self.daqThread = None self.liaThread = None self.rawWriter = None self.hdfFile = None self.fMax = 100E6 self.wavePlot.addLegend() self.wavePlot.setLabel('left', 'Voltage', units='V') self.wavePlot.setLabel('bottom', 'time', units='s') self.excitationCurve = pg.PlotDataItem(pen='r', name='Excitation') self.wavePlot.addItem(self.excitationCurve) self.responseCurve = pg.PlotDataItem(pen='b', name='Response') self.wavePlot.addItem(self.responseCurve) self.startPb.clicked.connect(self.startMeasurement) self.stopPb.clicked.connect(self.stopMeasurement) self.settingsWidgets = [self.deviceCombo, self.aoChannelCombo, self.aoRangeCombo, self.aiChannelCombo, self.aiRangeCombo, self.aiTerminalConfigCombo, self.sampleLe, self.commentLe, self.enablePlotCb, self.sampleRateSb, self.offsetSb, self.rampRateSb, self.inputDecimationCombo, self.saveRawDataCb, self.dcBwSb, self.dcFilterOrderSb, self.saveRawDemodCb, self.subtractDcOffsetCb] self.sampleRateSb.valueChanged.connect(self.updateMaximumFrequencies) self.deviceCombo.currentIndexChanged.connect(self.updateDevice) self.restoreSettings() self.hkSub = HousekeepingSubscriber(self) self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue) self.hkSub.start() self.curve1 = pg.PlotDataItem(symbol='o', symbolSize=7, pen='b', name='') self.curve2 = pg.PlotDataItem(symbol='o', symbolSize=7, pen='r', name='') self.plot1.addItem(self.curve1) self.plot1.setLogMode(x=True) self.plot1.setLabel('bottom', 'f', units='Hz') self.plot2.addItem(self.curve2) self.plot2.setLogMode(x=True) self.plot2.setLabel('bottom', 'f', units='Hz') self.plot2.setXLink(self.plot1) self.plot1.showGrid(x=True, y=True) self.plot2.showGrid(x=True, y=True) self.plotxy.setLabel('bottom', 'X', units='') self.plotxy.setLabel('left', 'Y', units='') self.plotxy.setAspectLocked() self.curvexy = pg.PlotDataItem(symbol='o', symbolSize=7, pen='b', name='') self.plotxy.addItem(self.curvexy) self.plotCombo.currentIndexChanged.connect(self.yAxisChanged) self.plotCombo.setCurrentIndex(self.plotCombo.currentIndex()) self.addRowPb.clicked.connect(self.addTableRow) self.deleteRowPb.clicked.connect(self.deleteTableRow) self.updateMaximumFrequencies() self.sampleRateSb.valueChanged.connect(self.updateMaximumFrequencies) self.inputDecimationCombo.currentIndexChanged.connect(self.updateMaximumFrequencies) self.saveRawDataCb.toggled.connect(self.toggleRawDataCollection) self.enablePlotCb.toggled.connect(self.enablePlotToggled) self.loadTablePb.clicked.connect(self.loadTable) self.saveTablePb.clicked.connect(self.saveTable) self.startServerThread() def startServerThread(self): self.serverThread = RequestReplyThreadWithBindings(port=RequestReply.MultitoneLockin, parent=self) boundWidgets = {'sample': self.sampleLe, 'comment': self.commentLe, 'samplingRate': self.sampleRateSb, 'decimation': self.inputDecimationCombo, 'rampRate': self.rampRateSb, 'offset':self.offsetSb, 'dcBw': self.dcBwSb, 'dcFilterOrder': self.dcFilterOrderSb, 'saveRawData': self.saveRawDataCb, 'saveRawDemod': self.saveRawDemodCb, 'aiChannel': self.aiChannelCombo, 'aiRange': self.aiRangeCombo, 'aiTerminalConfig': self.aiTerminalConfigCombo, 'start': self.startPb, 'stop': self.stopPb, 'subtractOffset': self.subtractDcOffsetCb} for name in boundWidgets: self.serverThread.bindToWidget(name, boundWidgets[name]) self.serverThread.bindToFunction('fileName', self.fileName) self.serverThread.bindToFunction('loadTable', self.loadTable) self.serverThread.start() def fileName(self): return self._fileName def loadTable(self): s = QSettings() import os directory = s.value('frequencyTableDirectory', os.path.curdir, type=str) fileName = QFileDialog.getOpenFileName(parent=self, caption='Select file for loading table', directory=directory, filter="CSV (*.csv);;HDF5 (*.h5, *.hdf);;Excel (*.xls);;") fileName = str(fileName) if len(fileName) == 0: return extension = os.path.basename(fileName).split('.')[-1].lower() directory = os.path.dirname(fileName) s.setValue('frequencyTableDirectory', directory) import pandas as pd df = pd.DataFrame() if extension in ['csv']: df = pd.read_csv(fileName) elif extension in ['xls']: df = pd.read_excel(fileName) elif extension in ['h5', 'hdf']: df = pd.read_hdf(fileName) for row in df.itertuples(index=True): self.addTableRow(row=row.Index, enabled=row.active, f=row.fRef, A=row.amplitude, phase=row.phase, bw=row.lpBw, order=row.lpOrder) def saveTable(self): s = QSettings() import os directory = s.value('frequencyTableDirectory', os.path.curdir, type=str) fileName = QFileDialog.getSaveFileName(parent=self, caption='Select file for saving table', directory=directory, filter="CSV (*.csv);;HDF5 (*.h5, *.hdf);;Excel (*.xls);;") fileName = str(fileName) if len(fileName) == 0: return extension = os.path.basename(fileName).split('.')[-1].lower() directory = os.path.dirname(fileName) s.setValue('frequencyTableDirectory', directory) import pandas as pd active = self.tableColumnValues('active') fRefs = self.tableColumnValues('f') As = self.tableColumnValues('A') phases = self.tableColumnValues('phase') bws = self.tableColumnValues('bw') orders = self.tableColumnValues('order') df = pd.DataFrame({'active':active, 'fRef':fRefs, 'amplitude':As, 'phase':phases, 'lpBw':bws, 'lpOrder':orders}) if extension in ['csv']: df.to_csv(fileName) elif extension in ['xls']: df.to_excel(fileName) elif extension in ['h5', 'hdf']: df.to_hdf(fileName) def updateMaximumFrequencies(self): fs = self.sampleRateSb.value() * 1E3 self.fMax = 0.5* fs / int(str(self.inputDecimationCombo.currentText())) for row in range(self.table.rowCount()): self.tableCellWidget(row, 'f').setMaximum(self.fMax) def populateTable(self): t = self.table t.setColumnCount(len(self.tableColumns)) t.setHorizontalHeaderLabels(self.tableColumns) t.resizeRowsToContents() t.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents) t.horizontalHeader().setStretchLastSection(True) def populateDevices(self): self.deviceCombo.clear() system = daq.System() devices = system.findDevices() for dev in devices: self.deviceCombo.addItem(dev) def updateDevice(self): self.aiChannelCombo.clear() self.aoChannelCombo.clear() self.aiRangeCombo.clear() self.aoRangeCombo.clear() deviceName = str(self.deviceCombo.currentText()) if len(deviceName) < 1: return device = daq.Device(deviceName) aiChannels = device.findAiChannels() for channel in aiChannels: self.aiChannelCombo.addItem(channel) aoChannels = device.findAoChannels() for channel in aoChannels: self.aoChannelCombo.addItem(channel) self.aiRanges = device.voltageRangesAi() for r in self.aiRanges: self.aiRangeCombo.addItem('%+.2f -> %+.2f V' % (r.min, r.max)) self.aoRanges = device.voltageRangesAo() for r in self.aoRanges: self.aoRangeCombo.addItem('%+.2f -> %+.2f V' % (r.min, r.max)) if len(aiChannels): aiChannel = daq.AiChannel('%s/%s' % (deviceName, aiChannels[0]), self.aiRanges[0].min, self.aiRanges[0].max) aiTask = daq.AiTask('TestInputSampleRate') aiTask.addChannel(aiChannel) aiSampleRate = aiTask.maxSampleClockRate() else: aiSampleRate = 0 if len(aoChannels): aoChannel = daq.AoChannel('%s/%s' % (deviceName, aoChannels[0]), self.aoRanges[0].min, self.aoRanges[0].max) aoTask = daq.AoTask('TestOutputSampleRate') aoTask.addChannel(aoChannel) aoSampleRate = aoTask.maxSampleClockRate() else: aoSampleRate = 0 rate = min(aiSampleRate, aoSampleRate) self.sampleRateSb.setMaximum(int(1E-3*rate)) #self.updateInfo() def terminalConfiguration(self): t = str(self.aiTerminalConfigCombo.currentText()) tc = daq.AiChannel.TerminalConfiguration terminalConfigDict = {'RSE': tc.RSE, 'DIFF': tc.DIFF, 'NRSE': tc.NRSE} return terminalConfigDict[t] def restoreSettings(self): s = QSettings(OrganizationName, ApplicationName) self.populateDevices() for w in self.settingsWidgets: restoreWidgetFromSettings(s, w) geometry = s.value('geometry', QByteArray(), type=QByteArray) self.restoreGeometry(geometry) state = s.value('splitter1State', QByteArray(), type=QByteArray) self.splitter1.restoreState(state) state = s.value('splitter2State', QByteArray(), type=QByteArray) self.splitter2.restoreState(state) rows = s.beginReadArray('frequencyTable') for row in range(rows): s.setArrayIndex(row) enabled = s.value('active', True, type=bool) f = s.value('f', 10*(3**row), type=float) A = s.value('A', 0.1, type=float) phase = s.value('phase', 0, type=float) bw = s.value('bw', 5, type=float) order = s.value('order', 8, type=int) self.addTableRow(row, enabled, f, A, phase, bw, order) s.endArray() def tableCellWidget(self, row, item): return self.table.cellWidget(row, self.tableColumns.index(item)) def setTableCellWidget(self, row, item, widget): self.table.setCellWidget(row, self.tableColumns.index(item), widget) def enablePlotToggled(self, checked): if self.daqThread is None: return if checked: self.daqThread.dataReady.connect(self.showWaveform) else: self.daqThread.dataReady.disconnect(self.showWaveform) def addTableRow(self, row=None, enabled=True, f=10, A=0.1, phase=0.0, bw=5, order=8): table = self.table if row == None: row = table.rowCount() if row < 1: row = 0 table.insertRow(row) cb = QCheckBox() cb.setChecked(enabled) self.setTableCellWidget(row, 'active', cb) frequencySb = QDoubleSpinBox() frequencySb.setMinimum(1.0) frequencySb.setMaximum(self.fMax) frequencySb.setSingleStep(0.01) frequencySb.setDecimals(2) frequencySb.setValue(f) self.setTableCellWidget(row, 'f', frequencySb) amplitudeSb = AmplitudeSpinBox() amplitudeSb.setValue(A) amplitudeSb.valueChanged.connect(lambda v: self.amplitudeChanged(row, v)) self.setTableCellWidget(row, 'A', amplitudeSb) phaseSb = PhaseSpinBox() phaseSb.setValue(phase) self.setTableCellWidget(row, 'phase', phaseSb) bwSb = QDoubleSpinBox() bwSb.setMinimum(0.1) bwSb.setMaximum(1000) bwSb.setValue(bw) bwSb.setSuffix(' Hz') self.setTableCellWidget(row, 'bw', bwSb) orderSb = QSpinBox() orderSb.setMinimum(1) orderSb.setMaximum(10) orderSb.setValue(order) self.setTableCellWidget(row, 'order', orderSb) self.setTableCellWidget(row, 'X', QFloatDisplay()) self.setTableCellWidget(row, 'Y', QFloatDisplay()) self.setTableCellWidget(row, 'R', QFloatDisplay()) self.setTableCellWidget(row, 'Theta', QFloatDisplay()) def amplitudeChanged(self, row, value): if self.daqThread is None: return if not row in self.activeRows: return i = np.where(row==self.activeRows)[0][0] self.daqThread.setAmplitude(i, value) def deleteTableRow(self): table = self.table row = table.currentRow() table.removeRow(row) def saveSettings(self): s = QSettings(OrganizationName, ApplicationName) s.setValue('geometry', self.saveGeometry()) for w in self.settingsWidgets: saveWidgetToSettings(s, w) s.setValue('splitter1State', self.splitter1.saveState()) s.setValue('splitter2State', self.splitter2.saveState()) table = self.table nRows = table.rowCount() s.beginWriteArray('frequencyTable', nRows) w = self.tableCellWidget for row in range(nRows): s.setArrayIndex(row) s.setValue('active', w(row,'active').isChecked()) s.setValue('f', w(row,'f').value()) s.setValue('A', w(row,'A').value()) s.setValue('phase', w(row,'phase').value()) s.setValue('bw', w(row, 'bw').value()) s.setValue('rollOff', w(row, 'order').value()) s.endArray() def closeEvent(self, event): if self.daqThread is not None: r = QMessageBox.warning(self, ApplicationName, 'The measurement may be still be running. Do you really want to quit?', QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if r==QMessageBox.No: event.ignore() return self.saveSettings() super(MultitoneLockinWidget, self).closeEvent(event) def tableColumnValues(self, columnId): t = self.table values = [] for row in range(t.rowCount()): widget = self.tableCellWidget(row, columnId) v = widgetValue(widget) values.append(v) return np.array(values) def startMeasurement(self): self.tProduce = {} sampleRate = int(self.sampleRateSb.value()*1E3) inputDecimation = int(str(self.inputDecimationCombo.currentText())) deviceName = str(self.deviceCombo.currentText()) aoChannel = str(self.aoChannelCombo.currentText()) aiChannel = str(self.aiChannelCombo.currentText()) aiTerminalConfig = self.terminalConfiguration() aiRange = self.aiRanges[self.aiRangeCombo.currentIndex()] aoRange = self.aoRanges[self.aoRangeCombo.currentIndex()] offset = self.offsetSb.value() active = self.tableColumnValues('active') activeRows = np.where(active)[0] self.activeRows = activeRows fRefs = self.tableColumnValues('f')[activeRows] As = self.tableColumnValues('A')[activeRows] phases = self.tableColumnValues('phase')[activeRows]*deg2rad bws = self.tableColumnValues('bw')[activeRows] orders = self.tableColumnValues('order')[activeRows] subtractOffset = bool(self.subtractDcOffsetCb.isChecked()) self._fileName = '%s_%s.h5' % (self.sampleLe.text(), time.strftime('%Y%m%d_%H%M%S')) hdfFile = hdf.File(self._fileName, mode='w') hdfFile.attrs['Program'] = ApplicationName hdfFile.attrs['Copyright'] = Copyright hdfFile.attrs['Version'] = Version hdfFile.attrs['Sample'] = str(self.sampleLe.text()) hdfFile.attrs['Comment'] = str(self.commentLe.text()) hdfFile.attrs['StartTimeLocal'] = time.strftime('%Y-%m-%d %H:%M:%S') hdfFile.attrs['StartTimeUTC'] = time.strftime('%Y-%m-%d %H:%M:%SZ', time.gmtime()) hdfFile.attrs['sampleRate'] = sampleRate hdfFile.attrs['inputDecimation'] = inputDecimation hdfFile.attrs['offset'] = offset hdfFile.attrs['deviceName'] = deviceName hdfFile.attrs['aoChannel'] = aoChannel hdfFile.attrs['aoRangeMin'] = aoRange.min; hdfFile.attrs['aoRangeMax'] = aoRange.max hdfFile.attrs['aiChannel'] = aiChannel hdfFile.attrs['aiRangeMin'] = aiRange.min; hdfFile.attrs['aiRangeMax'] = aiRange.max hdfFile.attrs['aiTerminalConfig'] = str(self.aiTerminalConfigCombo.currentText()) hdfFile.attrs['excitationFrequencies'] = fRefs hdfFile.attrs['lockinFilterBandwidths'] = bws hdfFile.attrs['excitationAmplitudes'] = As hdfFile.attrs['excitationPhases'] = phases hdfFile.attrs['lockinFilterOrders'] = orders hdfFile.attrs['rawCount'] = 0 hdfFile.attrs['rampRate'] = self.rampRateSb.value() hdfFile.attrs['subtractDcOffset'] = subtractOffset self.fs = fRefs variables = [('tGenerated', np.float64), ('tAcquired', np.float64), ('Vdc', np.float64), ('Vrms', np.float64), ('Vmin', np.float64), ('Vmax', np.float64), ('offset', np.float64)] self.hdfVector = HdfVectorWriter(hdfFile, variables) g = hdfFile.create_group('HK') self.hkLogger = HkLogger(g, self.hkSub) self.hdfFile = hdfFile sampleRateDecimated = sampleRate/inputDecimation self.__logger.info("Decimated sample rate %f S/s", sampleRateDecimated) desiredChunkSize = int(min(0.5*sampleRateDecimated, 2**18)) # Would like pretty big chunks, but update at least twice/second # Compute phase delays due to the pre-lockin FIR halfband decimator cascade dec = DecimatorCascade(inputDecimation, desiredChunkSize) phaseDelays = TwoPi*fRefs/sampleRate*(dec.sampleDelay()+1.0) self.__logger.info("Phase delays: %s deg", str(phaseDelays*rad2deg)) phaseDelays = np.mod(phaseDelays, TwoPi) dcBw = self.dcBwSb.value(); dcFilterOrder = self.dcFilterOrderSb.value() lias = LockIns(sampleRateDecimated, fRefs, phases-phaseDelays, bws, orders, desiredChunkSize=desiredChunkSize, dcBw=dcBw, dcFilterOrder=dcFilterOrder, subtractOffset=subtractOffset) # Set up the lock-ins self.liaStreamWriters = [] outputSampleRates = lias.outputSampleRates hdfFile.attrs['outputSampleRates'] = outputSampleRates saveRawDemod = bool(self.saveRawDemodCb.isChecked()) hdfFile.attrs['saveRawDemod'] = saveRawDemod streams = [('Z',np.complex64)] if saveRawDemod: streams.append(('Xdec', np.float32)) streams.append(('Ydec', np.float32)) for i,f in enumerate(fRefs): grp = hdfFile.create_group('F_%02d' % i) grp.attrs['fRef'] = f grp.attrs['fs'] = outputSampleRates[i] streamWriter = HdfStreamsWriter(grp, streams, scalarFields=[('tGenerated', np.float64), ('tAcquired', np.float64), ('A', np.float64)], compression=False, parent=self) self.liaStreamWriters.append(streamWriter) grp = hdfFile.create_group('DC') grp.attrs['sampleRate'] = lias.dcSampleRate grp.attrs['lpfBw'] = dcBw grp.attrs['lpfOrder'] = dcFilterOrder streams = [('DC', np.float)] if saveRawDemod: streams.append(('DCdec', np.float)) self.dcStreamWriter = HdfStreamsWriter(grp, streams, scalarFields=[('tGenerated', np.float64), ('tAcquired', np.float64)], compression=False, parent=self) self.lias = lias lias.chunkAnalyzed.connect(self.chunkAnalyzed) chunkSize = lias.chunkSize self.__logger.info("Lias chunk size: %d", chunkSize) self.t = np.linspace(0, chunkSize/sampleRateDecimated, chunkSize) self.wavePlot.setXRange(0,self.t[-1]) daqThread = DaqThread(sampleRate, fRefs, As, phases, chunkSize*inputDecimation, inputDecimation); self.daqThread = daqThread daqThread.setRampRate(self.rampRateSb.value()) daqThread.setOffset(self.offsetSb.value()) self.rampRateSb.valueChanged.connect(daqThread.setRampRate) daqThread.configureDaq(deviceName, aoChannel, aoRange, aiChannel, aiRange, aiTerminalConfig) daqThread.chunkProduced.connect(self.chunkProduced) daqThread.inputOverload.connect(self.overloadLed.flashOnce) daqThread.error.connect(self.reportError) daqThread.finished.connect(self.daqThreadFinished) self.offsetSb.valueChanged.connect(self.daqThread.setOffset) if self.enablePlotCb.isChecked(): daqThread.dataReady.connect(self.showWaveform) if self.saveRawDataCb.isChecked(): self.toggleRawDataCollection(True) self.resultsCollected = 0 self.chunksProduced = None liaThread = QThread(); self.liaThread = liaThread lias.moveToThread(liaThread) daqThread.dataReady.connect(lias.integrateData) lias.resultsAvailable.connect(self.collectLockinResults) liaThread.started.connect(lambda: daqThread.start(QThread.HighestPriority)) liaThread.started.connect(lambda: self.enableWidgets(False)) liaThread.finished.connect(self.liaThreadFinished) liaThread.start() # Start lock-in amplifier(s) def toggleRawDataCollection(self, enabled): if self.hdfFile is None: return if enabled: rawCount = self.hdfFile.attrs['rawCount'] grp = self.hdfFile.create_group('RAW_%06d' % rawCount) # Need to make a new group self.hdfFile.attrs['rawCount'] = rawCount+1 self.rawWriter = HdfStreamWriter(grp, dtype=np.float32, scalarFields=[('t', np.float64)], metaData={}, compression=True, parent=self) self.daqThread.dataReady.connect(self.writeRaw) else: if self.rawWriter is not None: self.rawWriter.deleteLater() self.rawWriter = None def writeRaw(self, data, stats, amplitudes): if self.hdfFile is not None: self.rawWriter.writeData(data, scalarData=[stats[0]]) def chunkProduced(self, n, t): self.tProduce[n] = t self.generatingLed.flashOnce() def chunkAnalyzed(self, n, t): elapsedTime = t - self.tProduce.pop(n) self.delayLe.setText('%.3f s' % elapsedTime) def collectLockinResults(self, tGenerated, tAcquired, Vmin, Vmax, dc, rms, offset, Zs, Xdecs, Ydecs, amplitudes, dcFiltered, DCdec): if self.hdfVector is not None: self.hdfVector.writeData(tGenerated=tGenerated, tAcquired=tAcquired, Vmin=Vmin, Vmax=Vmax, Vdc=dc, Vrms=rms, offset=offset) self.dcStreamWriter.writeData(DC=dcFiltered, DCdec=DCdec, tGenerated=tGenerated, tAcquired=tAcquired) self.dcIndicator.setText('%+7.5f V' % dc) self.rmsIndicator.setText('%+7.5f Vrms' % rms) zs = np.empty((len(Zs),), dtype=np.complex64) for i, Z in enumerate(Zs): w = self.liaStreamWriters[i] if self.hdfFile is not None and w is not None: w.writeData(Z=Z, Xdec=Xdecs[i], Ydec=Ydecs[i], tGenerated=tGenerated, tAcquired=tAcquired, A=amplitudes[i]) z = np.mean(Z) zs[i] = z row = self.activeRows[i] self.tableCellWidget(row, 'X').setValue(np.real(z)) self.tableCellWidget(row, 'Y').setValue(np.imag(z)) self.tableCellWidget(row, 'R').setValue(np.abs(z)) self.tableCellWidget(row, 'Theta').setValue(np.angle(z)*rad2deg) self.zs = zs # Store for plot self.updatePlot() def reportError(self, message): QMessageBox.critical(self, 'Exception encountered!', message) def stopMeasurement(self): if self.daqThread is None: return if self.stopPb.text() == 'Stop': self.daqThread.stop() self.stopPb.setText('Abort') self.measurementFinished() else: self.daqThread.abort() self.daqThread.wait(1000) self.measurementFinished() def daqThreadFinished(self): self.__logger.debug("DAQ thread finished") del self.daqThread self.daqThread = None def liaThreadFinished(self): self.__logger.debug("Lock-in thread finished") self.liaThread.deleteLater() self.liaThread = None self.lias.deleteLater() self.lias = None #del self.liaThread; self.liaThread = None #del self.lias; self.lias = None def measurementFinished(self): if self.liaThread is not None: self.liaThread.quit() self.liaThread.wait(2000) self.closeFile() self.stopPb.setText('Stop') self.enableWidgets(True) def closeFile(self): if self.hdfFile is not None: del self.hkLogger; self.hkLogger = None del self.hdfVector; self.hdfVector = None for writer in self.liaStreamWriters: del writer t = time.time() self.hdfFile.attrs['StopTimeLocal'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(t)) self.hdfFile.attrs['StopTimeUTC'] = time.strftime('%Y-%m-%d %H:%M:%SZ', time.gmtime(t)) self.hdfFile.close() del self.hdfFile self.hdfFile = None def enableWidgets(self, enable): self.driveGroupBox.setEnabled(enable) self.inputGroupBox.setEnabled(enable) self.startPb.setEnabled(enable) self.dcBwSb.setEnabled(enable) self.dcFilterOrderSb.setEnabled(enable) self.sampleRateSb.setEnabled(enable) self.saveRawDemodCb.setEnabled(enable) self.stopPb.setEnabled(not enable) table = self.table for item in ['f', 'phase', 'active', 'bw', 'order']: col = self.tableColumns.index(item) for row in range(table.rowCount()): w = table.cellWidget(row, col) if isinstance(w, QAbstractSpinBox) or isinstance(w, QLineEdit): w.setReadOnly(not enable) if enable: w.setButtonSymbols(QAbstractSpinBox.UpDownArrows) else: w.setButtonSymbols(QAbstractSpinBox.NoButtons) elif isinstance(w, QAbstractButton) or isinstance(w, QComboBox): w.setEnabled(enable) def showWaveform(self, samples, stats, amplitudes): self.responseCurve.setData(self.t, samples) def yAxisChanged(self): yAxis = str(self.plotCombo.currentText()) if yAxis=='X/Y': self.plot1.setLabel('left', 'X', units='') self.plot2.setLabel('left', 'Y', units='') elif yAxis=='R/Phi': self.plot1.setLabel('left', 'R', units='') self.plot2.setLabel('left', 'Phi', units='deg') elif yAxis=='Vmax/Vmin': self.plot1.setLabel('left', 'Vmax', units='V') self.plot2.setLabel('left', 'Vmin', units='V') elif yAxis=='R/Vdc': self.plot1.setLabel('left', 'R', units='') self.plot2.setLabel('left', 'Vdc', units='V') ylog = (yAxis[0] == 'R') and self.logscaleCb.isChecked() self.plot1.setLogMode(x=True, y=ylog) self.updatePlot() def updatePlot(self): yAxis = str(self.plotCombo.currentText()) i = np.argsort(self.fs) f = self.fs[i]; z = self.zs[i] X = np.real(z) Y = np.imag(z) if yAxis=='X/Y': y1 = X y2 = Y elif yAxis=='R/Phi': y1 = np.abs(z) y2 = rad2deg*np.angle(z) else: self.__logger.error("Unsupported y-axis: %s" , yAxis) return self.curve1.setData(f,y1) self.curve2.setData(f,y2) self.curvexy.setData(X, Y)
def __init__(self, parent = None): super(SineSweepWidget, self).__init__(parent) self.setupUi(self) self.daqThread = None self.liaThread = None self.hdfFile = None self._fileName = '' # self.restartPb.clicked.connect(self.restart) self.wavePlot.addLegend() self.wavePlot.setLabel('left', 'Voltage', units='V') self.wavePlot.setLabel('bottom', 'Phase', units='[rad]') self.excitationCurve = pg.PlotDataItem(pen='r', name='Excitation') self.wavePlot.addItem(self.excitationCurve) self.responseCurve = pg.PlotDataItem(pen='b', name='Response') self.wavePlot.addItem(self.responseCurve) self.wavePlot.setXRange(0,2*np.pi) self.startPb.clicked.connect(self.startMeasurement) self.stopPb.clicked.connect(self.stopMeasurement) self.settingsWidgets = [self.deviceCombo, self.aoChannelCombo, self.aoRangeCombo, self.aiChannelCombo, self.aiRangeCombo, self.aiTerminalConfigCombo, self.aiDriveChannelCombo, self.recordDriveCb, self.sampleLe, self.commentLe, self.enablePlotCb, self.auxAoChannelCombo, self.auxAoRangeCombo, self.auxAoSb, self.auxAoEnableCb, self.sampleRateSb, self.offsetSb, self.amplitudeSb, self.fStopSb, self.fStartSb, self.fStepsSb, self.settlePeriodsSb, self.measurePeriodsSb, self.minSettleTimeSb, self.minMeasureTimeSb] self.sampleRateSb.valueChanged.connect(lambda x: self.fStopSb.setMaximum(x*1E3/2)) self.fStopSb.valueChanged.connect(self.fStartSb.setMaximum) self.fStartSb.valueChanged.connect(self.fStopSb.setMinimum) self.deviceCombo.currentIndexChanged.connect(self.updateDevice) self.restoreSettings() #self.updateDevice() self.updateInfo() for w in [self.sampleRateSb, self.fStopSb, self.fStartSb, self.fStepsSb, self.settlePeriodsSb, self.measurePeriodsSb, self.minSettleTimeSb, self.minMeasureTimeSb]: w.valueChanged.connect(self.updateInfo) self.hkSub = HousekeepingSubscriber(self) self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue) self.hkSub.adrResistanceReceived.connect(self.collectAdrResistance) self.hkSub.start() self.auxAoSb.valueChanged.connect(self.updateAuxOutputVoltage) self.auxAoEnableCb.toggled.connect(self.toggleAuxOut) self.auxAoTask = None self.curve1 = pg.PlotDataItem(symbol='o', symbolSize=7, pen='b', name='') self.curve2 = pg.PlotDataItem(symbol='o', symbolSize=7, pen='r', name='') self.plot1.addItem(self.curve1) self.plot1.setLogMode(x=True) self.plot1.setLabel('bottom', 'f', units='Hz') self.plot2.addItem(self.curve2) self.plot2.setLogMode(x=True) self.plot2.setLabel('bottom', 'f', units='Hz') self.plot2.setXLink(self.plot1) self.plot1.showGrid(x=True, y=True) self.plot2.showGrid(x=True, y=True) self.plotxy.setLabel('bottom', 'X', units='') self.plotxy.setLabel('left', 'Y', units='') self.plotxy.setAspectLocked() self.curvexy = pg.PlotDataItem(symbol='o', symbolSize=7, pen='b', name='') self.plotxy.addItem(self.curvexy) self.plotCombo.currentIndexChanged.connect(self.yAxisChanged) self.plotCombo.setCurrentIndex(self.plotCombo.currentIndex()) self.serverThread = RequestReplyThreadWithBindings(port=RequestReply.SineSweepDaq, parent=self) boundWidgets = {'sample': self.sampleLe, 'comment': self.commentLe, 'samplingRate': self.sampleRateSb, 'auxAo': self.auxAoSb, 'rampRate': self.rampRateSb, 'offset':self.offsetSb, 'amplitude':self.amplitudeSb, 'settlePeriods': self.settlePeriodsSb, 'minSettleTime': self.minSettleTimeSb, 'measurePeriods': self.measurePeriodsSb, 'minMeasureTime': self.minMeasureTimeSb, 'totalTime': self.totalTimeSb, 'fStart': self.fStartSb, 'fStop': self.fStopSb, 'fSteps': self.fStepsSb, 'start': self.startPb, 'stop': self.stopPb} for name in boundWidgets: self.serverThread.bindToWidget(name, boundWidgets[name]) self.serverThread.bindToFunction('fileName', self.fileName) self.serverThread.start()
class SineSweepWidget(ui.Ui_Form, QWidget): EXIT_CODE_REBOOT = -123 def __init__(self, parent = None): super(SineSweepWidget, self).__init__(parent) self.setupUi(self) self.daqThread = None self.liaThread = None self.hdfFile = None self._fileName = '' # self.restartPb.clicked.connect(self.restart) self.wavePlot.addLegend() self.wavePlot.setLabel('left', 'Voltage', units='V') self.wavePlot.setLabel('bottom', 'Phase', units='[rad]') self.excitationCurve = pg.PlotDataItem(pen='r', name='Excitation') self.wavePlot.addItem(self.excitationCurve) self.responseCurve = pg.PlotDataItem(pen='b', name='Response') self.wavePlot.addItem(self.responseCurve) self.wavePlot.setXRange(0,2*np.pi) self.startPb.clicked.connect(self.startMeasurement) self.stopPb.clicked.connect(self.stopMeasurement) self.settingsWidgets = [self.deviceCombo, self.aoChannelCombo, self.aoRangeCombo, self.aiChannelCombo, self.aiRangeCombo, self.aiTerminalConfigCombo, self.aiDriveChannelCombo, self.recordDriveCb, self.sampleLe, self.commentLe, self.enablePlotCb, self.auxAoChannelCombo, self.auxAoRangeCombo, self.auxAoSb, self.auxAoEnableCb, self.sampleRateSb, self.offsetSb, self.amplitudeSb, self.fStopSb, self.fStartSb, self.fStepsSb, self.settlePeriodsSb, self.measurePeriodsSb, self.minSettleTimeSb, self.minMeasureTimeSb] self.sampleRateSb.valueChanged.connect(lambda x: self.fStopSb.setMaximum(x*1E3/2)) self.fStopSb.valueChanged.connect(self.fStartSb.setMaximum) self.fStartSb.valueChanged.connect(self.fStopSb.setMinimum) self.deviceCombo.currentIndexChanged.connect(self.updateDevice) self.restoreSettings() #self.updateDevice() self.updateInfo() for w in [self.sampleRateSb, self.fStopSb, self.fStartSb, self.fStepsSb, self.settlePeriodsSb, self.measurePeriodsSb, self.minSettleTimeSb, self.minMeasureTimeSb]: w.valueChanged.connect(self.updateInfo) self.hkSub = HousekeepingSubscriber(self) self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue) self.hkSub.adrResistanceReceived.connect(self.collectAdrResistance) self.hkSub.start() self.auxAoSb.valueChanged.connect(self.updateAuxOutputVoltage) self.auxAoEnableCb.toggled.connect(self.toggleAuxOut) self.auxAoTask = None self.curve1 = pg.PlotDataItem(symbol='o', symbolSize=7, pen='b', name='') self.curve2 = pg.PlotDataItem(symbol='o', symbolSize=7, pen='r', name='') self.plot1.addItem(self.curve1) self.plot1.setLogMode(x=True) self.plot1.setLabel('bottom', 'f', units='Hz') self.plot2.addItem(self.curve2) self.plot2.setLogMode(x=True) self.plot2.setLabel('bottom', 'f', units='Hz') self.plot2.setXLink(self.plot1) self.plot1.showGrid(x=True, y=True) self.plot2.showGrid(x=True, y=True) self.plotxy.setLabel('bottom', 'X', units='') self.plotxy.setLabel('left', 'Y', units='') self.plotxy.setAspectLocked() self.curvexy = pg.PlotDataItem(symbol='o', symbolSize=7, pen='b', name='') self.plotxy.addItem(self.curvexy) self.plotCombo.currentIndexChanged.connect(self.yAxisChanged) self.plotCombo.setCurrentIndex(self.plotCombo.currentIndex()) self.serverThread = RequestReplyThreadWithBindings(port=RequestReply.SineSweepDaq, parent=self) boundWidgets = {'sample': self.sampleLe, 'comment': self.commentLe, 'samplingRate': self.sampleRateSb, 'auxAo': self.auxAoSb, 'rampRate': self.rampRateSb, 'offset':self.offsetSb, 'amplitude':self.amplitudeSb, 'settlePeriods': self.settlePeriodsSb, 'minSettleTime': self.minSettleTimeSb, 'measurePeriods': self.measurePeriodsSb, 'minMeasureTime': self.minMeasureTimeSb, 'totalTime': self.totalTimeSb, 'fStart': self.fStartSb, 'fStop': self.fStopSb, 'fSteps': self.fStepsSb, 'start': self.startPb, 'stop': self.stopPb} for name in boundWidgets: self.serverThread.bindToWidget(name, boundWidgets[name]) self.serverThread.bindToFunction('fileName', self.fileName) self.serverThread.start() def toggleAuxOut(self, enabled): if enabled: deviceName = str(self.deviceCombo.currentText()) aoChannel = str(self.auxAoChannelCombo.currentText()) aoRange = self.aoRanges[self.auxAoRangeCombo.currentIndex()] aoChannel = daq.AoChannel('%s/%s' % (deviceName, aoChannel), aoRange.min, aoRange.max) aoTask = daq.AoTask('AO auxilliary') aoTask.addChannel(aoChannel) self.auxAoTask = aoTask self.updateAuxOutputVoltage() else: if self.auxAoTask is not None: self.auxAoTask.stop() del self.auxAoTask self.auxAoTask = None def updateAuxOutputVoltage(self): if self.auxAoTask is None: return try: self.auxAoTask.writeData([self.auxAoSb.value()], autoStart=True) except Exception: exceptionString = traceback.format_exc() self.reportError(exceptionString) def collectAdrResistance(self, R): if self.hdfFile is None: return timeStamp = time.time() self.dsTimeStamps.resize((self.dsTimeStamps.shape[0]+1,)) self.dsTimeStamps[-1] = timeStamp self.dsAdrResistance.resize((self.dsAdrResistance.shape[0]+1,)) self.dsAdrResistance[-1] = R def populateDevices(self): self.deviceCombo.clear() system = daq.System() devices = system.findDevices() for dev in devices: self.deviceCombo.addItem(dev) def updateDevice(self): print("Updating device") self.aiChannelCombo.clear() self.aiDriveChannelCombo.clear() self.aoChannelCombo.clear() self.aiRangeCombo.clear() self.aoRangeCombo.clear() self.auxAoRangeCombo.clear() self.auxAoChannelCombo.clear() deviceName = str(self.deviceCombo.currentText()) if len(deviceName) < 1: return device = daq.Device(deviceName) aiChannels = device.findAiChannels() for channel in aiChannels: self.aiChannelCombo.addItem(channel) self.aiDriveChannelCombo.addItem(channel) aoChannels = device.findAoChannels() for channel in aoChannels: self.aoChannelCombo.addItem(channel) self.auxAoChannelCombo.addItem(channel) self.aiRanges = device.voltageRangesAi() for r in self.aiRanges: self.aiRangeCombo.addItem('%+.2f -> %+.2f V' % (r.min, r.max)) self.aoRanges = device.voltageRangesAo() for r in self.aoRanges: self.aoRangeCombo.addItem('%+.2f -> %+.2f V' % (r.min, r.max)) self.auxAoRangeCombo.addItem('%+.2f -> %+.2f V' % (r.min, r.max)) if len(aiChannels): aiChannel = daq.AiChannel('%s/%s' % (deviceName, aiChannels[0]), self.aiRanges[0].min, self.aiRanges[0].max) aiTask = daq.AiTask('TestInputSampleRate_SineSweepDaq') aiTask.addChannel(aiChannel) aiSampleRate = aiTask.maxSampleClockRate() del aiTask else: aiSampleRate = 0 if len(aoChannels): aoChannel = daq.AoChannel('%s/%s' % (deviceName, aoChannels[0]), self.aoRanges[0].min, self.aoRanges[0].max) aoTask = daq.AoTask('TestOutputSampleRate_SineSweepDaq') aoTask.addChannel(aoChannel) aoSampleRate = aoTask.maxSampleClockRate() del aoTask else: aoSampleRate = 0 rate = min(aiSampleRate, aoSampleRate) self.sampleRateSb.setMaximum(int(1E-3*rate)) if rate < 1: return self.updateInfo() def terminalConfiguration(self): t = str(self.aiTerminalConfigCombo.currentText()) tc = daq.AiChannel.TerminalConfiguration terminalConfigDict = {'RSE': tc.RSE, 'DIFF': tc.DIFF, 'NRSE': tc.NRSE} return terminalConfigDict[t] def updateInfo(self): nSettle = self.settlePeriodsSb.value(); nMeasure = self.measurePeriodsSb.value(); tSettleMin = self.minSettleTimeSb.value(); tMeasureMin = self.minMeasureTimeSb.value() fStart = self.fStartSb.value(); fStop = self.fStopSb.value(); n=self.fStepsSb.value() print("fstart, fstop, n", fStart, fStop, n) fs = np.logspace(np.log10(fStart), np.log10(fStop), n) nMeasure = np.maximum(np.ceil(tMeasureMin*fs), nMeasure) # Compute the number of (integer) measurement periods for each frequency fSample = self.sampleRateSb.value()*1E3 # Now figure out which frequencies we can actually exactly synthesize k = np.ceil(nMeasure*fSample/fs) fs = nMeasure*fSample/k if False: fBad = 15. # Now eliminate uneven harmonics of 15 Hz fBadMax = 500 # But only below 500 Hz iBad = (np.abs(np.mod(fs, 2*fBad)-fBad) < 2) & (fs < fBadMax) else: iBad = np.zeros_like(fs, dtype=np.bool) self.pointsSkippedSb.setValue(np.count_nonzero(iBad)) fs = fs[~iBad] self.nMeasure = nMeasure[~iBad] self.fGoals = fs periods = 1./fs self.nSettle = np.maximum(np.ceil(tSettleMin/periods), nSettle) # Compute the number of (integer) settle periods for each frequency tTotal = np.sum((self.nSettle+self.nMeasure) * periods) # TODO probably should add time for DAQmx setup overhead? self.totalTimeSb.setValue(tTotal) if fStart > 0 and fStop > 0: self.plot1.setXRange(np.log10(fStart), np.log10(fStop)) def restoreSettings(self): s = QSettings(OrganizationName, ApplicationName) self.populateDevices() for w in self.settingsWidgets: restoreWidgetFromSettings(s, w) def saveSettings(self): s = QSettings(OrganizationName, ApplicationName) for w in self.settingsWidgets: saveWidgetToSettings(s, w) def closeEvent(self, event): if self.daqThread is not None: event.ignore() return self.saveSettings() super(SineSweepWidget, self).closeEvent(event) def restart(self): qApp.exit( self.EXIT_CODE_REBOOT ) def startMeasurement(self): sampleRate = self.sampleRateSb.value()*1E3 deviceName = str(self.deviceCombo.currentText()) aoChannel = str(self.aoChannelCombo.currentText()) aiChannel = str(self.aiChannelCombo.currentText()) aiTerminalConfig = self.terminalConfiguration() aiRange = self.aiRanges[self.aiRangeCombo.currentIndex()] aoRange = self.aoRanges[self.aoRangeCombo.currentIndex()] offset = self.offsetSb.value() amplitude = self.amplitudeSb.value() fStart = self.fStartSb.value() fStop = self.fStopSb.value() fSteps = self.fStepsSb.value() settlePeriods = self.settlePeriodsSb.value(); measurePeriods = self.measurePeriodsSb.value() minSettleTime = self.minSettleTimeSb.value(); minMeasureTime = self.minMeasureTimeSb.value() s = QSettings('WiscXrayAstro', application='ADR3RunInfo') path = str(s.value('runPath', '', type=str)) fileName = path+'/TF/%s_%s.h5' % (self.sampleLe.text(), time.strftime('%Y%m%d_%H%M%S')) self._fileName = fileName hdfFile = hdf.File(fileName, mode='w') hdfFile.attrs['Program'] = ApplicationName hdfFile.attrs['Version'] = Version hdfFile.attrs['Sample'] = str(self.sampleLe.text()) hdfFile.attrs['Comment'] = str(self.commentLe.text()) hdfFile.attrs['StartTimeLocal'] = time.strftime('%Y-%m-%d %H:%M:%S') hdfFile.attrs['StartTimeUTC'] = time.strftime('%Y-%m-%d %H:%M:%SZ', time.gmtime()) hdfFile.attrs['sampleRate'] = sampleRate hdfFile.attrs['offset'] = offset hdfFile.attrs['amplitude'] = amplitude hdfFile.attrs['fStart'] = fStart hdfFile.attrs['fStop'] = fStop hdfFile.attrs['fSteps'] = fSteps hdfFile.attrs['settlePeriods'] = settlePeriods hdfFile.attrs['measurePeriods'] = measurePeriods hdfFile.attrs['minSettleTime'] = minSettleTime hdfFile.attrs['minMeasureTime'] = minMeasureTime hdfFile.attrs['deviceName'] = deviceName hdfFile.attrs['aoChannel'] = aoChannel hdfFile.attrs['aoRangeMin'] = aoRange.min; hdfFile.attrs['aoRangeMax'] = aoRange.max hdfFile.attrs['aiChannel'] = aiChannel hdfFile.attrs['aiRangeMin'] = aiRange.min; hdfFile.attrs['aiRangeMax'] = aiRange.max hdfFile.attrs['aiTerminalConfig'] = str(self.aiTerminalConfigCombo.currentText()) if self.auxAoTask is not None: hdfFile.attrs['auxAoChannel'] = str(self.auxAoTask.channels[0]) auxAoRange = self.aoRanges[self.auxAoRangeCombo.currentIndex()] hdfFile.attrs['auxAoRangeMin'] = auxAoRange.min; hdfFile.attrs['auxAoRangeMax'] = auxAoRange.max hdfFile.attrs['auxAoValue'] = self.auxAoSb.value() hkGroup = hdfFile.require_group('HK') self.hkLogger = HkLogger(hkGroup, self.hkSub) # Should remove stuff below soon - only kept for backwards compatibility self.dsTimeStamps = hdfFile.create_dataset('AdrResistance_TimeStamps', (0,), maxshape=(None,), chunks=(500,), dtype=np.float64) self.dsTimeStamps.attrs['units'] = 's' self.dsAdrResistance = hdfFile.create_dataset('AdrResistance', (0,), maxshape=(None,), chunks=(500,), dtype=np.float64) self.dsAdrResistance.attrs['units'] = 'Ohms' self.hdfFile = hdfFile lia = LockIn(); self.lia = lia liaThread = QThread(parent=self); self.liaThread = liaThread lia.moveToThread(liaThread) lia.integrationComplete.connect(self.collectData) # if self.recordDriveCb.isChecked(): # aiDriveChannel = str(self.aiDriveChannelCombo.currentText()) # hdfFile.attrs['aiDriveChannel'] = aiDriveChannel # thread.enableDriveRecording(aiDriveChannel) daqThread = DaqThread(deviceName, aoChannel, aoRange, aiChannel, aiRange, aiTerminalConfig, parent=self); self.daqThread = daqThread daqThread.setSampleRate(sampleRate) daqThread.setParameters(self.fGoals, self.nSettle, self.nMeasure) daqThread.setExcitation(amplitude, offset) self.sweep = Sweep(nPoints = len(self.fGoals), parent=self) if self.enablePlotCb.isChecked(): daqThread.waveformAvailable.connect(self.showWaveform) daqThread.waveformAvailable.connect(lia.integrateData) daqThread.waveformComplete.connect(lia.completeIntegration) daqThread.error.connect(self.reportError) self.enableWidgets(False) daqThread.finished.connect(self.daqThreadFinished) liaThread.finished.connect(self.liaThreadFinished) nCollected = gc.collect() print('GC collected:', nCollected) gc.disable() liaThread.started.connect(lambda: daqThread.start(QThread.HighestPriority)) # Start producer after consumer liaThread.start() def fileName(self): return self._fileName def reportError(self, message): tString = time.strftime('%Y%m%d_%H%M%S') QMessageBox.critical(self, 'Exception encountered at %s!' % (tString), message) # WORK def stopMeasurement(self): if self.daqThread is None: return if self.stopPb.text() == 'Stop': self.daqThread.stop() self.stopPb.setText('Abort') self.measurementFinished() else: self.daqThread.abort() self.daqThread.wait(1000) self.measurementFinished() def daqThreadFinished(self): print("DAQ thread finished") del self.daqThread self.daqThread = None def liaThreadFinished(self): print("LIA thread finished") del self.liaThread; self.liaThread = None del self.lia; self.lia = None def measurementFinished(self): del self.hkLogger; self.hkLogger = None if self.liaThread is not None: self.liaThread.quit() grp = self.hdfFile.create_group('Sweep') self.sweep.toHdf(grp) self.closeFile() self.stopPb.setText('Stop') self.enableWidgets(True) gc.enable() def closeFile(self): if self.hdfFile is not None: t = time.time() self.hdfFile.attrs['StopTimeLocal'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(t)) self.hdfFile.attrs['StopTimeUTC'] = time.strftime('%Y-%m-%d %H:%M:%SZ', time.gmtime(t)) self.hdfFile.close() del self.hdfFile self.hdfFile = None def enableWidgets(self, enable): self.driveGroupBox.setEnabled(enable) self.inputGroupBox.setEnabled(enable) self.startPb.setEnabled(enable) self.stopPb.setEnabled(not enable) def showWaveform(self, phases, y): self.responseCurve.setData(phases, y) def collectData(self, *args): #try: self.sweep.collectData(*args) f = args[1] self.currentFrequencySb.setValue(f) self.updatePlot() if self.sweep.isComplete(): self.measurementFinished() #except Exception as e: # print("Exception:",e) def yAxisChanged(self): yAxis = str(self.plotCombo.currentText()) if yAxis=='X/Y': self.plot1.setLabel('left', 'X', units='') self.plot2.setLabel('left', 'Y', units='') elif yAxis=='R/Phi': self.plot1.setLabel('left', 'R', units='') self.plot2.setLabel('left', 'Phi', units='deg') elif yAxis=='Vmax/Vmin': self.plot1.setLabel('left', 'Vmax', units='V') self.plot2.setLabel('left', 'Vmin', units='V') elif yAxis=='R/Vdc': self.plot1.setLabel('left', 'R', units='') self.plot2.setLabel('left', 'Vdc', units='V') ylog = yAxis[0] == 'R' self.plot1.setLogMode(x=True, y=ylog) self.updatePlot() def updatePlot(self): yAxis = str(self.plotCombo.currentText()) s = self.sweep x = s.f if yAxis=='X/Y': y1 = s.X y2 = s.Y elif yAxis=='R/Phi': y1 = s.R y2 = rad2deg*s.Theta elif yAxis=='Vmax/Vmin': y1 = s.Vmax y2 = s.Vmin elif yAxis=='R/Vdc': y1 = s.R y2 = s.dc else: warnings.warn("Unsupported y-axis: %s" % yAxis) return self.curve1.setData(x,y1) self.curve2.setData(x,y2) X = s.X; Y = s.Y self.curvexy.setData(X, Y)
def __init__(self, parent=None): super(IvCurveWidget, self).__init__(parent) self.setupUi(self) self.restartPb.clicked.connect(self.restart) self.thread = None self.hdfFile = None self.squid = None self._fileName = '' self.plot.addLegend() self.plot.setLabel('left', 'SQUID response', units='V') self.plot.setLabel('bottom', 'bias voltage', units='V') self.curves = [] self.startPb.clicked.connect(self.startMeasurement) self.stopPb.clicked.connect(self.stopMeasurement) # self.aoRangeCombo.currentIndexChanged.connect(self.updateAoRange) # self.auxAoChannelCombo.currentIndexChanged.connect(self.updateAuxAoRange) self.osr = OpenSquidRemote(port=7894) squids = self.osr.findSquids() self.tesCombo.addItem('None') self.tesCombo.addItems(squids) self.settingsWidgets = [ self.deviceCombo, self.aoChannelCombo, self.aoRangeCombo, self.aiChannelCombo, self.aiRangeCombo, self.aiTerminalConfigCombo, self.aiDriveChannelCombo, self.recordDriveCb, self.maxDriveSb, self.slewRateSb, self.zeroHoldTimeSb, self.peakHoldTimeSb, self.betweenHoldTimeSb, self.decimateCombo, self.sampleRateSb, self.sampleLe, self.commentLe, self.enablePlotCb, self.auxAoChannelCombo, self.auxAoRangeCombo, self.auxAoSb, self.auxAoEnableCb, self.polarityCombo, self.tesCombo, self.pflResetCb, self.driveOffsetSb ] self.deviceCombo.currentIndexChanged.connect(self.updateDevice) for w in [ self.maxDriveSb, self.slewRateSb, self.zeroHoldTimeSb, self.peakHoldTimeSb, self.betweenHoldTimeSb, self.sampleRateSb ]: w.valueChanged.connect(self.updateInfo) self.decimateCombo.currentIndexChanged.connect(self.updateInfo) self.polarityCombo.currentIndexChanged.connect(self.updateInfo) self.auxAoSb.valueChanged.connect(self.updateAuxOutputVoltage) self.auxAoEnableCb.toggled.connect(self.toggleAuxOut) self.auxAoRamper = None self.restoreSettings() self.hkSub = HousekeepingSubscriber(self) self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue) self.hkSub.adrResistanceReceived.connect(self.collectAdrResistance) self.hkSub.start() self.serverThread = RequestReplyThreadWithBindings( port=RequestReply.IvCurveDaq, parent=self) boundWidgets = { 'sampleName': self.sampleLe, 'auxAoEnable': self.auxAoEnableCb, 'auxVoltage': self.auxAoSb, 'maxDrive': self.maxDriveSb, 'slewRate': self.slewRateSb, 'start': self.startPb, 'stop': self.stopPb, 'totalTime': self.totalTimeSb, 'sweepCount': self.sweepCountSb, 'comment': self.commentLe, 'driveOffset': self.driveOffsetSb } for name in boundWidgets: self.serverThread.bindToWidget(name, boundWidgets[name]) self.serverThread.bindToFunction('fileName', self.fileName) self.serverThread.bindToFunction('restart', self.requestRestart) self.serverThread.start() pens = 'rgbc' for i in range(4): curve = pg.PlotDataItem(pen=pens[i], name='Curve %d' % i) self.plot.addItem(curve) self.curves.append(curve) self.restartRequested = False timer = QTimer(self) timer.timeout.connect(self.checkRestartRequested) timer.setInterval(1000) timer.start() self.timer = timer self.logger.info('IvCurveDaq started.')
class TemperatureControlMainWindow(Ui.Ui_MainWindow, QMainWindow): def __init__(self, parent = None): super(TemperatureControlMainWindow, self).__init__(parent) self.setupUi(self) self.setpointSb.setDecimals(7) self.setpointSb.setSingleStep(1E-7) self.rampRateSb.setDecimals(2) self.rampRateSb.setSingleStep(1E-2) self.serverThread = None self.selectedSensorName = '' self.clearData() self.pid = None self.outputFile = None self.magnetControlRemote = None self.rampEnableCb.toggled.connect(self.fixupRampRate) self.updatePlotsCb.toggled.connect(self.showPlots) self.widgetsForSettings = [self.thermometerCombo, self.setpointSb, self.KSb, self.TiSb, self.TdSb, self.TtSb, self.TfSb, self.betaSb, self.gammaSb, self.controlMinSb, self.controlMaxSb, self.rampRateSb, self.rampTargetSb, self.rampEnableCb, self.updatePlotsCb, self.useBaseTemperatureCb] axis = pg.DateAxisItem(orientation='bottom') self.pvPlot = pg.PlotWidget(axisItems={'bottom': axis}) self.mainVerticalLayout.addWidget(self.pvPlot) self.pvPlot.addLegend() self.curvePv = pg.PlotCurveItem(name='Actual', symbol='o', pen='g') self.curveSetpoint = pg.PlotCurveItem(name='Setpoint', symbol='o', pen='r') self.pvPlot.addItem(self.curvePv) self.pvPlot.addItem(self.curveSetpoint) axis = pg.DateAxisItem(orientation='bottom') self.pidPlot = pg.PlotWidget(axisItems={'bottom': axis}) self.mainVerticalLayout.addWidget(self.pidPlot) self.pidPlot.addLegend() self.curveP = pg.PlotCurveItem(name='P', symbol='o', pen='r') self.curveI = pg.PlotCurveItem(name='I', symbol='o', pen='g') self.curveD = pg.PlotCurveItem(name='D', symbol='o', pen='b') self.curveControl = pg.PlotCurveItem(name='control', symbol='o', pen='w') self.pidPlot.addItem(self.curveP) self.pidPlot.addItem(self.curveI) self.pidPlot.addItem(self.curveD) self.pidPlot.addItem(self.curveControl) self.KSb.setMinimum(-1E6) self.KSb.setMaximum(1E6) self.hkSub = HousekeepingSubscriber(self) self.hkSub.thermometerListUpdated.connect(self.updateThermometerList) self.hkSub.thermometerReadingReceived.connect(self.receiveTemperature) self.hkSub.start() self.startPb.clicked.connect(self.startPid) self.stopPb.clicked.connect(self.stopPid) self.timer = QTimer(self) # A 250 ms timer to implement ramping self.timer.timeout.connect(self.updateSetpoint) self.tOld = time.time() self.timer.start(250) self.thermometerCombo.currentIndexChanged.connect(self.updateThermometerSelection) self.restoreSettings() # Give the hkSub some time to update thermometer list before we update thermometer selection QTimer.singleShot(2000, self.restoreThermometerSelection) def restoreThermometerSelection(self): s = QSettings() restoreWidgetFromSettings(s, self.thermometerCombo) def updateThermometerSelection(self): t = str(self.thermometerCombo.currentText()) self.selectedSensorName = t def updateThermometerList(self, thermometerList): selected = self.thermometerCombo.currentText() self.thermometerCombo.clear() self.thermometerCombo.addItems(thermometerList) i = self.thermometerCombo.findText(selected) self.thermometerCombo.setCurrentIndex(i) def startServerThread(self): self.serverThread = RequestReplyThreadWithBindings(port=RequestReply.AdrPidControl, parent=self) boundWidgets = {'stop': self.stopPb,'start': self.startPb,'rampRate':self.rampRateSb, 'rampTarget':self.rampTargetSb, 'rampEnable':self.rampEnableCb, 'setpoint':self.setpointSb} for name in boundWidgets: self.serverThread.bindToWidget(name, boundWidgets[name]) logger.info('Starting server thread') self.serverThread.start() def showPlots(self, enable): self.pvPlot.setVisible(enable) self.pidPlot.setVisible(enable) self.adjustSize() def fixupRampRate(self, doIt): if doIt: Tset = self.setpointSb.value() Ttarget = self.rampTargetSb.value() absRate = abs(self.rampRateSb.value()) if Tset < Ttarget: self.rampRateSb.setValue(+absRate) else: self.rampRateSb.setValue(-absRate) def updateSetpoint(self): t = time.time() if self.rampEnableCb.isChecked(): Tset = self.setpointSb.value() Ttarget = self.rampTargetSb.value() rate = self.rampRateSb.value() * SIUnits.mK/SIUnits.minute deltaT = rate * (t-self.tOld) if rate > 0: Tset = min(Ttarget, Tset+deltaT) elif rate < 0: Tset = max(Ttarget, Tset+deltaT) self.setpointSb.setValue(Tset) if Tset == Ttarget: self.rampEnableCb.setChecked(False) self.tOld = t def startPid(self): self.enableControls(False) pid = Pid(parent=self) pid.setControlMaximum(self.controlMaxSb.value()*mAperMin) pid.setControlMinimum(self.controlMinSb.value()*mAperMin) self.setpointSb.setValue(self.pv) self.KSb.valueChanged.connect(self.updatePidParameters) self.TiSb.valueChanged.connect(self.updatePidParameters) self.TdSb.valueChanged.connect(self.updatePidParameters) self.TfSb.valueChanged.connect(self.updatePidParameters) self.TtSb.valueChanged.connect(self.updatePidParameters) self.betaSb.valueChanged.connect(self.updatePidParameters) self.gammaSb.valueChanged.connect(self.updatePidParameters) self.controlMinSb.valueChanged.connect(lambda x: pid.setControlMinimum(x*mAperMin)) self.controlMaxSb.valueChanged.connect(lambda x: pid.setControlMaximum(x*mAperMin)) self.magnetControlRemote = MagnetControlRemote('AdrTemperatureControlPid', parent=self) if not self.magnetControlRemote.checkConnection(): self.appendErrorMessage('Cannot connect to magnet control') self.stopPid(abort=True) return self.magnetControlRemote.error.connect(self.appendErrorMessage) self.magnetControlRemote.disableDriftCorrection() self.pid = pid # This effectively enables the loop self.updatePidParameters() self.startServerThread() def endServerThread(self): if self.serverThread is not None: logger.info('Stopping server thread') self.serverThread.stop() self.serverThread.wait(1000) del self.serverThread self.serverThread = None def stopPid(self, abort=False): self.endServerThread() if self.pid: del self.pid self.pid = None if self.magnetControlRemote: if not abort: self.requestRampRate(0.0) self.magnetControlRemote.enableDriftCorrection() self.magnetControlRemote.stop() self.magnetControlRemote.wait(2) self.magnetControlRemote.deleteLater() self.magnetControlRemote = None if self.outputFile is not None: self.outputFile.close() self.enableControls(True) def updatePidParameters(self): if self.pid is None: return #print "Updating PID parameters" K = self.KSb.value()*mAperMin Ti = self.TiSb.value() Td = self.TdSb.value() Tt = self.TtSb.value() Tf = self.TfSb.value() beta = self.betaSb.value() gamma = self.gammaSb.value() self.pid.updateParameters(K, Ti, Td, Tt, Tf, beta, gamma) if self.outputFile is not None: self.outputFile.close() self.outputFile = None s = QSettings('WiscXrayAstro', application='ADR3RunInfo') path = str(s.value('runPath', '', type=str)) timeString = time.strftime('%Y%m%d-%H%M%S') fileName = path+'/AdrPID_%s.dat' % timeString self.outputFile = open(fileName, 'a+') self.outputFile.write('#AdrTemperatureControlPid.py\n') self.outputFile.write('#Date=%s\n' % timeString) self.outputFile.write('#K=%.8g\n' % K) self.outputFile.write('#ti=%.6g\n' % Ti) self.outputFile.write('#td=%.6g\n' % Td) self.outputFile.write('#tt=%.6g\n' % Tt) self.outputFile.write('#tf=%.6g\n' % Tf) self.outputFile.write('#beta=%.6g\n' % beta) self.outputFile.write('#gamma=%.6g\n' % gamma) self.outputFile.write('#'+'\t'.join(['time', 'Ts', 'T', 'u', 'p', 'i', 'd' ])+'\n') def requestRampRate(self, rate): '''Request new ramp rate [units: A/s]''' #print "Requesting new ramp rate:", rate, 'A/s' ok = self.magnetControlRemote.changeRampRate(rate) if not ok: self.appendErrorMessage('Unable to change ramp rate. Aborting.') self.stopPid(abort=True) def appendErrorMessage(self, message): timeString = time.strftime('%Y%m%d-%H%M%S') self.errorTextEdit.append('%s: %s' % (timeString, str(message))) logger.error('%s: %s' % (timeString, str(message))) def updateLoop(self, sp, pv): if self.pid is None: return if self.pv == 0: return (u, p, i, d) = self.pid.updateLoop(sp, pv) self.requestRampRate(u) t = time.time() string = "%.3f\t%.6g\t%.6g\t%.6g\t%.6g\t%.6g\t%.6g\n" % (t, sp, pv, u, p, i, d) self.outputFile.write(string) unit = mAperMin total = p+i+d self.controlIndicator.setValue(u/unit) self.pIndicator.setValue(p/unit) self.iIndicator.setValue(i/unit) self.dIndicator.setValue(d/unit) self.totalIndicator.setValue(total/unit) self.ts_pid.append(t) self.ps.append(p/unit) self.Is.append(i/unit) self.ds.append(d/unit) self.controls.append(u/unit) historyLength = 10000 if len(self.ts_pid) > 1.1*historyLength: self.ts_pid = self.ts_pid[-historyLength:] self.ps = self.ps[-historyLength:] self.Is = self.Is[-historyLength:] self.ds = self.ds[-historyLength:] self.controls = self.controls[-historyLength:] if self.updatePlotsCb.isChecked(): self.curveP.setData(self.ts_pid, self.ps) self.curveI.setData(self.ts_pid, self.Is) self.curveD.setData(self.ts_pid, self.ds) self.curveControl.setData(self.ts_pid, self.controls) def enableControls(self, enable): self.thermometerCombo.setEnabled(enable) self.startPb.setEnabled(enable) self.stopPb.setEnabled(not enable) def restoreSettings(self): settings = QSettings() for widget in self.widgetsForSettings: restoreWidgetFromSettings(settings, widget) def saveSettings(self): settings = QSettings() for widget in self.widgetsForSettings: saveWidgetToSettings(settings, widget) def clearData(self): self.ts_pid = [] self.ps = [] self.Is = [] self.ds = [] self.controls = [] self.ts_pv = [] self.pvs = [] #self.ts_setpoint = [] self.setpoints = [] def receiveTemperature(self, sensorName, t, R, T, P, Tbase): if sensorName != self.selectedSensorName: return if self.useBaseTemperatureCb.isChecked(): pv = Tbase else: pv = T sp = self.setpointSb.value() self.pv = pv self.updateLoop(sp, pv) t = time.time() self.pvIndicator.setValue(pv) self.ts_pv.append(t) self.pvs.append(pv) self.setpoints.append(sp) historyLength = 10000 if len(self.ts_pv) > 1.1*historyLength: self.ts_pv = self.ts_pv[-historyLength:] self.pvs = self.pvs[-historyLength:] self.setpoints = self.setpoints[-historyLength:] if self.updatePlotsCb.isChecked(): self.curvePv.setData(self.ts_pv, self.pvs) self.curveSetpoint.setData(self.ts_pv, self.setpoints) def closeEvent(self, e): if self.pid is not None: self.stopPid() self.hkSub.stop() if self.serverThread is not None: self.serverThread.stop() self.serverThread.wait(1000) if self.magnetControlRemote is not None: self.magnetControlRemote.stop() self.magnetControlRemote.wait(1000) self.hkSub.wait(1000) self.saveSettings()
class IVSweepDaqWidget(IVSweepsDaqUi.Ui_Form, QWidget): def __init__(self, parent=None): super(IVSweepDaqWidget, self).__init__(parent) self.setupUi(self) self.aoDeviceCombo.currentIndexChanged.connect( self.updateDaqChannelsAo) self.aiDeviceCombo.currentIndexChanged.connect( self.updateDaqChannelsAi) self.populateDaqCombos() self.restoreSettings() self.msmThread = None self.startPb.clicked.connect(self.startPbClicked) self.hkSub = HousekeepingSubscriber(self) self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue) self.hkSub.start() self.rawPlot.setAxisTitle(QwtPlot.yLeft, 'Vmeas') self.rawPlot.setAxisTitle(QwtPlot.xBottom, 'Vdrive') self.rawCurve = QwtPlotCurve('') self.rawCurve.attach(self.rawPlot) self.criticalCurve1 = QwtPlotCurve('+') self.criticalCurve1.setSymbol( Qwt.QwtSymbol(Qwt.QwtSymbol.Cross, Qt.QBrush(), Qt.QPen(Qt.Qt.red), Qt.QSize(5, 5))) self.criticalCurve1.attach(self.criticalPlot) self.criticalCurve2 = QwtPlotCurve('-') self.criticalCurve2.setSymbol( Qwt.QwtSymbol(Qwt.QwtSymbol.Cross, Qt.QBrush(), Qt.QPen(Qt.Qt.blue), Qt.QSize(5, 5))) self.criticalCurve2.attach(self.criticalPlot) self.criticalPlot.setAxisTitle(QwtPlot.yLeft, 'Vcrit') self.clearData() self.clearCriticalData() self.clearPb.clicked.connect(self.clearData) self.coilSweepCb.toggled.connect(self.toggleCoilSweep) self.clearCriticalPb.clicked.connect(self.clearCriticalData) self.samplesPerPointSb.valueChanged.connect( lambda value: self.discardSamplesSb.setMaximum(value - 1)) self.coilEnableCb.toggled.connect(self.toggleCoil) self.coilVoltageSb.valueChanged.connect(self.updateCoilVoltage) self.toggleCoil(self.coilEnableCb.isChecked()) self.coilDriverCombo.currentIndexChanged.connect( self.coilDriverChanged) self.Vcoil = np.nan def coilDriverChanged(self): text = self.coilDriverCombo.currentText() self.coilVisaLabel.setText('%s VISA' % text) self.coilVisaCombo.clear() if text == 'SR830': suffix = ' V' maxDrive = 10 self.coilVisaCombo.addItem('GPIB0::12') self.auxOutChannelSb.setEnabled(True) elif text == 'Keithley 6430': suffix = ' mA' maxDrive = 50 self.coilVisaCombo.addItem('GPIB0::24') self.auxOutChannelSb.setEnabled(False) controls = [ self.coilVoltageSb, self.coilSweepMinSb, self.coilSweepMaxSb ] for control in controls: control.setSuffix(suffix) control.setMinimum(-maxDrive) control.setMaximum(+maxDrive) def toggleCoil(self, enabled): self.coilDriverCombo.setEnabled(not enabled) if enabled: driver = self.coilDriverCombo.currentText() if driver == 'SR830': self.sr830 = SR830(str(self.coilVisaCombo.currentText())) self.coilAo = VoltageSourceSR830(self.sr830, self.auxOutChannelSb.value()) elif driver == 'Keithley 6430': self.coilAo = CurrentSourceKeithley(str( self.coilVisaCombo.currentText()), currentRange=10E-3) self.Vcoil = self.coilAo.dcDrive() self.coilVoltageSb.setValue(self.Vcoil) self.auxOutChannelSb.setEnabled(False) self.coilVisaCombo.setEnabled(False) else: self.coilAo = None self.auxOutChannelSb.setEnabled(True) self.coilVisaCombo.setEnabled(True) def updateCoilVoltage(self, V): if self.coilAo is not None: self.coilAo.setDcDrive(V) self.Vcoil = self.coilAo.dcDrive() def updateDaqChannelsAi(self): dev = str(self.aiDeviceCombo.currentText()) self.aiChannelCombo.clear() if len(dev): device = daq.Device(dev) aiChannels = device.findAiChannels() for channel in aiChannels: self.aiChannelCombo.addItem(channel) def updateDaqChannelsAo(self): dev = str(self.aoDeviceCombo.currentText()) self.aoChannelCombo.clear() if len(dev): device = daq.Device(dev) aoChannels = device.findAoChannels() for channel in aoChannels: self.aoChannelCombo.addItem(channel) def populateDaqCombos(self): system = daq.System() devices = system.findDevices() for devName in devices: dev = daq.Device(devName) if len(dev.findAiChannels()): self.aiDeviceCombo.addItem(devName) if len(dev.findAoChannels()): self.aoDeviceCombo.addItem(devName) def clearData(self): self.Vdrive = [] self.Vmeas = [] self.rawCurve.setData(self.Vdrive, self.Vmeas) self.rawPlot.replot() def clearCriticalData(self): self.Vcrit1 = [] self.Vcrit2 = [] self.Tcrit1 = [] self.Tcrit2 = [] self.VcoilCrit1 = [] self.VcoilCrit2 = [] self.updateCriticalPlot() def updateRawData(self, t, Vsource, Vmeas, Vo, T): string = "%.3f\t%.6f\t%.6f\t%.6f\t%.6f\t%.3f\n" % (t, Vsource, Vmeas, Vo, T, self.Vcoil) self.outputFile.write(string) self.Vdrive.append(Vsource) self.Vmeas.append(Vmeas - Vo) maxLength = 10000 if len(self.Vdrive) > int( maxLength * 1.1): # Automatically expire old data self.Vdrive = self.Vdrive[-maxLength:] self.Vmeas = self.Vmeas[-maxLength:] self.rawCurve.setData(self.Vdrive, self.Vmeas) self.rawPlot.replot() def toggleCoilSweep(self, checked): if checked: self.coilVoltages = np.linspace(self.coilSweepMinSb.value(), self.coilSweepMaxSb.value(), self.coilSweepStepsSb.value()) self.coilVoltages = np.append(self.coilVoltages, self.coilVoltages[::-1]) self.currentCoilStep = 0 self.stepCoil() else: self.coilVoltages = [] self.currentCoilStep = 0 def stepCoil(self): if self.coilAo is not None: self.coilVoltageSb.setValue( self.coilVoltages[self.currentCoilStep]) self.currentCoilStep += 1 if self.currentCoilStep >= len(self.coilVoltages): # Start over self.currentCoilStep = 0 def collectSweep(self, T, Vc): if Vc >= 0: self.Tcrit1.append(T) self.Vcrit1.append(Vc) self.VcoilCrit1.append(self.Vcoil) else: self.Tcrit2.append(T) self.Vcrit2.append(-Vc) self.VcoilCrit2.append(self.Vcoil) self.updateCriticalPlot() if self.coilSweepCb.isChecked(): # Move on to next coil voltage if self.bipolarCb.isChecked(): if not len(self.Tcrit2) == len(self.Tcrit1): print "Still need to do negative" return self.stepCoil() def updateCriticalPlot(self): xAxis = self.plotAxisCombo.currentText() if xAxis == 'T': x1 = self.Tcrit1 x2 = self.Tcrit2 self.criticalPlot.setAxisTitle(QwtPlot.xBottom, 'T') elif xAxis == 'Coil V': x1 = self.VcoilCrit1 x2 = self.VcoilCrit2 self.criticalPlot.setAxisTitle(QwtPlot.xBottom, 'Coil V') self.criticalCurve1.setData(x1, self.Vcrit1) self.criticalCurve2.setData(x2, self.Vcrit2) self.criticalPlot.replot() def startPbClicked(self): timeString = time.strftime('%Y%m%d-%H%M%S') fileName = self.sampleLineEdit.text() + '_%s_IV.dat' % timeString self.outputFile = open(fileName, 'a+') self.outputFile.write('#Program=IVSweepsDaq.py\n') self.outputFile.write('#Date=%s\n' % timeString) self.outputFile.write('#Sample=%s\n' % self.sampleLineEdit.text()) self.outputFile.write('#Source=%s\n' % self.sourceCombo.currentText()) self.outputFile.write('#Pre-amp gain=%.5g\n' % self.preampGainSb.value()) self.outputFile.write('#Drive impedance=%.6g\n' % self.dcDriveImpedanceSb.value()) self.outputFile.write('#Inter-sweep delay=%d\n' % self.interSweepDelaySb.value()) self.outputFile.write('#Samples per point=%d\n' % self.samplesPerPointSb.value()) self.outputFile.write('#Discard samples=%d\n' % self.discardSamplesSb.value()) self.outputFile.write('#Threshold=%.5g\n' % self.thresholdVoltageSb.value()) self.outputFile.write('#Bipolar=%d\n' % int(self.bipolarCb.isChecked())) if self.coilAo is not None: self.outputFile.write('#Coil enabled=1\n') self.outputFile.write('#Coil driver=%s\n' % self.coilAo.name()) self.outputFile.write('#Coil drive=%.3g\n' % self.coilAo.dcDrive()) else: self.outputFile.write('#Coil enabled=0\n') if self.coilSweepCb.isChecked(): self.outputFile.write('#Coil sweep=1\n') self.outputFile.write('#Coil min=%.3f\n' % self.coilSweepMinSb.value()) self.outputFile.write('#Coil max=%.3f\n' % self.coilSweepMaxSb.value()) self.outputFile.write('#Coil steps=%d\n' % self.coilSweepStepsSb.value()) else: self.outputFile.write('#Coil sweep=0\n') self.ao = VoltageSourceDaq(str(self.aoDeviceCombo.currentText()), str(self.aoChannelCombo.currentText())) self.ai = VoltmeterDaq(str(self.aiDeviceCombo.currentText()), str(self.aiChannelCombo.currentText()), -10, 10, samples=self.samplesPerPointSb.value(), drop=self.discardSamplesSb.value()) self.msmThread = IVSweepMeasurement(self.ao, self.ai, self) self.msmThread.setFileName(fileName) self.msmThread.setThreshold(self.thresholdVoltageSb.value()) self.msmThread.readingAvailable.connect(self.updateRawData) self.msmThread.setMinimumVoltage(self.startVSb.value()) self.msmThread.setMaximumVoltage(self.stopVSb.value()) self.msmThread.setSteps(self.stepsSb.value()) self.msmThread.setInterSweepDelay(self.interSweepDelaySb.value()) self.msmThread.sweepComplete.connect(self.collectSweep) self.msmThread.enableBipolar(self.bipolarCb.isChecked()) self.hkSub.adrTemperatureReceived.connect( self.msmThread.updateTemperature) self.msmThread.finished.connect(self.finished) self.stopPb.clicked.connect(self.msmThread.stop) self.thresholdVoltageSb.valueChanged.connect( self.msmThread.setThreshold) self.outputFile.write( '#' + '\t'.join(['time', 'Vdrive', 'Vmeas', 'Vo', 'T', 'Vcoil']) + '\n') self.enableWidgets(False) self.msmThread.start() print "Thread started" def finished(self): self.ai.clear() self.ao.clear() self.outputFile.close() self.enableWidgets(True) self.msmThread.deleteLater() # self.updateStatus('Completed') def enableWidgets(self, enable=True): self.sampleLineEdit.setEnabled(enable) self.startPb.setEnabled(enable) self.dcDriveImpedanceSb.setEnabled(enable) self.aiChannelCombo.setEnabled(enable) self.aiDeviceCombo.setEnabled(enable) self.aoChannelCombo.setEnabled(enable) self.aoDeviceCombo.setEnabled(enable) self.startVSb.setEnabled(enable) self.stopVSb.setEnabled(enable) self.stepsSb.setEnabled(enable) self.interSweepDelaySb.setEnabled(enable) self.bipolarCb.setEnabled(enable) self.stopPb.setEnabled(not enable) def closeEvent(self, e): if self.msmThread: self.msmThread.stop() if self.hkSub: self.hkSub.stop() self.saveSettings() super(IVSweepDaqWidget, self).closeEvent(e) def saveSettings(self): s = QSettings() s.setValue('sampleId', self.sampleLineEdit.text()) s.setValue('dcDriveImpedance', self.dcDriveImpedanceSb.value()) s.setValue('bipolar', self.bipolarCb.isChecked()) s.setValue('startV', self.startVSb.value()) s.setValue('stopV', self.stopVSb.value()) s.setValue('steps', self.stepsSb.value()) s.setValue('interSweepDelay', self.interSweepDelaySb.value()) s.setValue('thresholdVoltage', self.thresholdVoltageSb.value()) s.setValue('samplesPerPoint', self.samplesPerPointSb.value()) s.setValue('discardSamples', self.discardSamplesSb.value()) s.setValue('preampGain', self.preampGainSb.value()) s.setValue('geometry', self.saveGeometry()) s.setValue('AI_Device', self.aiDeviceCombo.currentText()) s.setValue('AO_Device', self.aoDeviceCombo.currentText()) s.setValue('AI_Channel', self.aiChannelCombo.currentText()) s.setValue('AO_Channel', self.aoChannelCombo.currentText()) s.setValue('SourceType', self.sourceCombo.currentText()) s.setValue('coilVisa', self.coilVisaCombo.currentText()) s.setValue('CoilAuxOut', self.auxOutChannelSb.value()) s.setValue('CoilVoltage', self.coilVoltageSb.value()) def restoreSettings(self): s = QSettings() self.sampleLineEdit.setText(s.value('sampleId', '', type=QString)) self.dcDriveImpedanceSb.setValue( s.value('dcDriveImpedance', 10E3, type=float)) self.bipolarCb.setChecked(s.value('bipolar', False, type=bool)) self.startVSb.setValue(s.value('startV', 0, type=float)) self.stopVSb.setValue(s.value('stopV', 3, type=float)) self.stepsSb.setValue(s.value('steps', 10, type=int)) self.preampGainSb.setValue(s.value('preampGain', 1., type=float)) self.interSweepDelaySb.setValue(s.value('interSweepDelay', 0, type=int)) self.thresholdVoltageSb.setValue( s.value('thresholdVoltage', 0.010, type=float)) self.samplesPerPointSb.setValue(s.value('samplesPerPoint', 1, type=int)) self.discardSamplesSb.setValue(s.value('discardSamples', 0, type=int)) self.aiDeviceCombo.setCurrentIndex( self.aiDeviceCombo.findText(s.value('AI_Device', '', type=str))) self.aoDeviceCombo.setCurrentIndex( self.aoDeviceCombo.findText(s.value('AO_Device', '', type=str))) self.aiChannelCombo.setCurrentIndex( self.aiChannelCombo.findText(s.value('AI_Channel', '', type=str))) self.aoChannelCombo.setCurrentIndex( self.aoChannelCombo.findText(s.value('AO_Channel', '', type=str))) self.sourceCombo.setCurrentIndex( self.sourceCombo.findText(s.value('SourceType', '', type=str))) self.coilVisaCombo.setCurrentIndex( self.coilVisaCombo.findText(s.value('coilVisa', '', type=str))) self.auxOutChannelSb.setValue(s.value('CoilAuxOut', 1, type=int)) self.coilVoltageSb.setValue(s.value('CoilVoltage', 0.0, type=float)) geometry = s.value('geometry', QByteArray(), type=QByteArray) self.restoreGeometry(geometry)
class TESIVSweepDaqWidget(TES_IVSweepsDaqUi.Ui_Form, QWidget): def __init__(self, parent=None): super(TESIVSweepDaqWidget, self).__init__(parent) self.setupUi(self) self.setWindowTitle('TES IV Sweep (DAQ)') self.aoDeviceCombo.currentIndexChanged.connect( self.updateDaqChannelsAo) self.aiDeviceCombo.currentIndexChanged.connect( self.updateDaqChannelsAi) self.populateDaqCombos() self.msmThread = None self.startPb.clicked.connect(self.startPbClicked) self.hkSub = HousekeepingSubscriber(self) self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue) self.hkSub.adrResistanceReceived.connect( self.collectThermometerResistance) self.hkSub.start() self.plotRaw = pg.PlotWidget(title='IV sweeps') self.plotLayout.addWidget(self.plotRaw) self.curveRaw = pg.PlotCurveItem() self.plotRaw.addItem(self.curveRaw) self.plotCritical = pg.PlotWidget(title='Critical current') self.plotLayout.addWidget(self.plotCritical) self.plotCritical.addLegend() self.criticalCurve1 = pg.PlotCurveItem(name='+', symbol='o', pen='r') self.plotCritical.addItem(self.criticalCurve1) self.criticalCurve2 = pg.PlotCurveItem(name='o', symbol='o', pen='b') self.plotCritical.addItem(self.criticalCurve2) self.plotRaw.setLabel('bottom', 'Vbias', 'V') self.plotRaw.setLabel('left', 'I_TES', 'uA') self.plotCritical.setLabel('left', 'I_C', 'uA') self.clearData() self.clearCriticalData() self.coilAo = None self.clearPb.clicked.connect(self.clearData) self.coilSweepCb.toggled.connect(self.toggleCoilSweep) self.clearCriticalPb.clicked.connect(self.clearCriticalData) self.samplesPerPointSb.valueChanged.connect( lambda value: self.discardSamplesSb.setMaximum(value - 1)) self.coilEnableCb.toggled.connect(self.toggleCoil) self.coilVoltageSb.valueChanged.connect(self.updateCoilVoltage) # self.toggleCoil(self.coilEnableCb.isChecked()) # self.toggleCoilSweep(self.coilSweepCb.isChecked()) self.coilDriverCombo.currentIndexChanged.connect( self.coilDriverChanged) self.Vcoil = np.nan self.restoreSettings() self.Rthermometers = [] def coilDriverChanged(self): text = self.coilDriverCombo.currentText() self.coilVisaLabel.setText('%s VISA' % text) self.coilVisaCombo.clear() if text == 'SR830': suffix = ' V' maxDrive = 10 self.coilDriveLabel.setText('Voltage') self.coilVisaCombo.addItem('GPIB0::12') self.auxOutChannelSb.setEnabled(True) elif text == 'Keithley 6430': suffix = ' mA' maxDrive = 10 self.coilDriveLabel.setText('Current') self.coilVisaCombo.addItem('GPIB0::24') self.auxOutChannelSb.setEnabled(False) controls = [ self.coilVoltageSb, self.coilSweepMinSb, self.coilSweepMaxSb ] for control in controls: control.setSuffix(suffix) control.setMinimum(-maxDrive) control.setMaximum(+maxDrive) def toggleCoil(self, enabled): self.coilDriverCombo.setEnabled(not enabled) if enabled: drive = self.coilDriverCombo.currentText() if drive == 'SR830': self.sr830 = SR830(str(self.coilVisaCombo.currentText())) self.coilAo = VoltageSourceSR830(self.sr830, self.auxOutChannelSb.value()) elif drive == 'Keithley 6430': self.coilAo = CurrentSourceKeithley(str( self.coilVisaCombo.currentText()), currentRange=10.0E-3, complianceVoltage=20.0) self.Vcoil = self.coilAo.dcDrive() self.coilVoltageSb.setValue(self.Vcoil) self.auxOutChannelSb.setEnabled(False) self.coilVisaCombo.setEnabled(False) else: self.coilAo = None self.auxOutChannelSb.setEnabled(True) self.coilVisaCombo.setEnabled(True) def updateCoilVoltage(self, V): if self.coilAo is not None: self.coilAo.setDcDrive(V) self.Vcoil = self.coilAo.dcDrive() def updateDaqChannelsAi(self): dev = str(self.aiDeviceCombo.currentText()) self.aiChannelCombo.clear() if len(dev): device = daq.Device(dev) aiChannels = device.findAiChannels() for channel in aiChannels: self.aiChannelCombo.addItem(channel) def updateDaqChannelsAo(self): dev = str(self.aoDeviceCombo.currentText()) self.aoChannelCombo.clear() if len(dev): device = daq.Device(dev) aoChannels = device.findAoChannels() for channel in aoChannels: self.aoChannelCombo.addItem(channel) def populateDaqCombos(self): system = daq.System() devices = system.findDevices() for devName in devices: dev = daq.Device(devName) if len(dev.findAiChannels()): self.aiDeviceCombo.addItem(devName) if len(dev.findAoChannels()): self.aoDeviceCombo.addItem(devName) def clearData(self): self.Vdrive = [] self.Vmeas = [] self.curveRaw.setData(self.Vdrive, self.Vmeas) def clearCriticalData(self): self.VcritDrive1 = [] self.VcritDrive2 = [] self.VcritMeas1 = [] self.VcritMeas2 = [] self.Tcrit1 = [] self.Tcrit2 = [] self.VcoilCrit1 = [] self.VcoilCrit2 = [] self.updateCriticalPlot() def updateRawData(self, t, Vdrive, Vmeas, Vo, T): #string = "%.3f\t%.6f\t%.6f\t%.6f\t%.6f\t%.3f\n" % (t, Vdrive, Vmeas, Vo, T, self.Vcoil) #self.outputFile.write(string) self.Vdrive.append(Vdrive) self.Vmeas.append(Vmeas) maxLength = 10000 if len(self.Vdrive) > int( maxLength * 1.1): # Automatically expire old data self.Vdrive = self.Vdrive[-maxLength:] self.Vmeas = self.Vmeas[-maxLength:] self.curveRaw.setData(self.Vdrive, self.Vmeas, connect='finite') def toggleCoilSweep(self, checked): if checked: print "Making coil voltages" self.coilVoltages = np.linspace(self.coilSweepMinSb.value(), self.coilSweepMaxSb.value(), self.coilSweepStepsSb.value()) if self.coilSweepReverseCb.isChecked(): self.coilVoltages = np.append(self.coilVoltages, self.coilVoltages[::-1]) self.currentCoilStep = 0 self.stepCoil() else: self.coilVoltages = [] self.currentCoilStep = 0 def stepCoil(self): if self.coilAo is not None: self.coilVoltageSb.setValue( self.coilVoltages[self.currentCoilStep]) self.currentCoilStep += 1 if self.currentCoilStep >= len(self.coilVoltages): # Start over self.currentCoilStep = 0 def collectThermometerResistance(self, R): self.Rthermometers.append(R) def collectSweep(self, T, Vo, VcDrive, VcMeas, tStart, tEnd, Vdrives, Vmeas, t): with hdf.File(self.hdfFileName, mode='a') as f: grp = f.create_group('Sweeps/%d' % self.sweepNumber) self.sweepNumber += 1 grp.create_dataset('Vdrive', data=Vdrives, compression='gzip') grp.create_dataset('Vmeasured', data=Vmeas, compression='gzip') grp.create_dataset('t', data=t, compression='gzip') grp.attrs['tStart'] = tStart grp.attrs['tEnd'] = tEnd grp.attrs['Rthermometer'] = np.mean(self.Rthermometers) grp.attrs['Vcoil'] = self.Vcoil grp.attrs['T'] = T grp.attrs['VcritDrive'] = VcDrive grp.attrs['VcritMeas'] = VcMeas grp.attrs['Vo'] = Vo self.Rthermometers = [] self.updateRawData(0, np.nan, np.nan, np.nan, np.nan) #if np.max(VcDrive) >= 0: if np.max(Vdrives) > 0: self.Tcrit1.append(T) self.VcritDrive1.append(VcDrive) self.VcritMeas1.append(VcMeas) self.VcoilCrit1.append(self.Vcoil) else: self.Tcrit2.append(T) self.VcritDrive2.append(-VcDrive) self.VcritMeas2.append(-VcMeas) self.VcoilCrit2.append(self.Vcoil) self.updateCriticalPlot() if self.coilSweepCb.isChecked(): # Move on to next coil voltage if self.bipolarCb.isChecked(): if not len(self.Tcrit2) == len(self.Tcrit1): print "len(Tcrit2), len(Tcrit1)", len(self.Tcrit2), len( self.Tcrit1) print "Still need to do negative" return self.stepCoil() def updateCriticalPlot(self): xAxis = self.plotXaxisCombo.currentText() if xAxis == 'T': x1 = self.Tcrit1 x2 = self.Tcrit2 self.plotCritical.setLabel('bottom', 'T', 'K') elif xAxis == 'Coil V': x1 = self.VcoilCrit1 x2 = self.VcoilCrit2 self.plotCritical.setLabel('bottom', 'Coil', 'V/A') yAxis = self.plotYaxisCombo.currentText() if yAxis == 'V_c': y1 = self.VcritDrive1 y2 = self.VcritDrive2 elif yAxis == 'I_c': y1 = self.VcritMeas1 y2 = self.VcritMeas2 self.criticalCurve1.setData(x1, y1) self.criticalCurve2.setData(x2, y2) #self.criticalPlot.replot() # -1/3.256 +1/2.823 def writeHeader(self, item, value, hdfFile): #self.outputFile.write('#%s=%s\n' % (item, value)) if isinstance(value, QString): value = str(value) hdfFile.attrs[item] = value def startPbClicked(self): timeString = time.strftime('%Y%m%d-%H%M%S') fileName = self.sampleLineEdit.text() + '_%s_IV.dat' % timeString self.sweepNumber = 0 # self.outputFile = open(fileName, 'a+') self.hdfFileName = str(self.sampleLineEdit.text() + '_%s_IV.h5' % timeString) with hdf.File(self.hdfFileName, mode='w') as f: self.writeHeader('Program', 'TES_IVSweepsDaq.py', f) self.writeHeader('Date', timeString, f) self.writeHeader('Sample', self.sampleLineEdit.text(), f) self.writeHeader('Source', self.sourceCombo.currentText(), f) self.writeHeader('Pre-amp gain', self.preampGainSb.value(), f) self.writeHeader('Drive impedance', self.dcDriveImpedanceSb.value(), f) self.writeHeader('Inter-sweep delay', self.interSweepDelaySb.value(), f) self.writeHeader('Samples per point', self.samplesPerPointSb.value(), f) self.writeHeader('Discard samples', self.discardSamplesSb.value(), f) #self.writeHeader('Threshold', self.thresholdVoltageSb.value(), f) self.writeHeader('Bipolar', int(self.bipolarCb.isChecked()), f) self.writeHeader('ReverseSweep', int(self.reverseSweepCb.isChecked()), f) if self.coilAo is not None: self.writeHeader('Coil enabled', 1, f) self.writeHeader('Coil driver', self.coilAo.name(), f) self.writeHeader('Coil drive', self.coilAo.dcDrive(), f) else: self.writeHeader('Coil enabled', 0, f) if self.coilSweepCb.isChecked(): self.writeHeader('Coil sweep', 1, f) self.writeHeader('Coil min', self.coilSweepMinSb.value(), f) self.writeHeader('Coil max', self.coilSweepMaxSb.value(), f) self.writeHeader('Coil steps', self.coilSweepStepsSb.value(), f) else: self.writeHeader('Coil sweep', 0, f) self.ao = VoltageSourceDaq(str(self.aoDeviceCombo.currentText()), str(self.aoChannelCombo.currentText())) self.ai = VoltmeterDaq(str(self.aiDeviceCombo.currentText()), str(self.aiChannelCombo.currentText()), -10, 10, samples=self.samplesPerPointSb.value(), drop=self.discardSamplesSb.value()) self.msmThread = IVSweepMeasurement(self.ao, self.ai, self) self.msmThread.setFileName(fileName) self.msmThread.readingAvailable.connect(self.updateRawData) self.msmThread.setMinimumVoltage(self.startVSb.value()) self.msmThread.setMaximumVoltage(self.stopVSb.value()) self.msmThread.setSteps(self.stepsSb.value()) self.msmThread.setInterSweepDelay(self.interSweepDelaySb.value()) self.msmThread.sweepComplete.connect(self.collectSweep) self.msmThread.enableBipolar(self.bipolarCb.isChecked()) self.msmThread.enableReverseSweep(self.reverseSweepCb.isChecked()) self.msmThread.setAdaptiveLower(1E-2 * self.adaptiveLowerSb.value()) self.msmThread.setAdaptiveUpper(1E-2 * self.adaptiveUpperSb.value()) self.msmThread.enableAdaptiveSweep( self.adaptiveSweepingGroupBox.isChecked()) self.hkSub.adrTemperatureReceived.connect( self.msmThread.updateTemperature) self.msmThread.finished.connect(self.finished) self.stopPb.clicked.connect(self.msmThread.stop) self.reverseSweepCb.toggled.connect(self.msmThread.enableReverseSweep) self.adaptiveLowerSb.valueChanged.connect( lambda x: self.msmThread.setAdaptiveLower(1E-2 * x)) self.adaptiveUpperSb.valueChanged.connect( lambda x: self.msmThread.setAdaptiveUpper(1E-2 * x)) self.adaptiveSweepingGroupBox.toggled.connect( self.msmThread.enableAdaptiveSweep) #self.thresholdVoltageSb.valueChanged.connect(self.msmThread.setThreshold) #self.outputFile.write('#'+'\t'.join(['time', 'Vdrive', 'Vmeas', 'Vo', 'T', 'Vcoil' ])+'\n') self.enableWidgets(False) self.Rthermometers = [] self.msmThread.start() print "Thread started" def finished(self): self.ai.clear() self.ao.clear() #self.outputFile.close() with hdf.File(self.hdfFileName, mode='a') as f: grp = f.create_group('Results/Positive') grp.create_dataset('Tcrit', data=self.Tcrit1) grp.create_dataset('VcritDrive', data=self.VcritDrive1) grp.create_dataset('Vcritmeas', data=self.VcritMeas1) grp.create_dataset('Vcoil', data=self.VcoilCrit1) grp = f.create_group('Results/Negative') grp.create_dataset('Tcrit', data=self.Tcrit2) grp.create_dataset('VcritDrive', data=self.VcritDrive2) grp.create_dataset('Vcritmeas', data=self.VcritMeas2) grp.create_dataset('Vcoil', data=self.VcoilCrit2) self.enableWidgets(True) self.msmThread.deleteLater() # self.updateStatus('Completed') def enableWidgets(self, enable=True): self.sampleLineEdit.setEnabled(enable) self.startPb.setEnabled(enable) self.dcDriveImpedanceSb.setEnabled(enable) self.aiChannelCombo.setEnabled(enable) self.aiDeviceCombo.setEnabled(enable) self.aoChannelCombo.setEnabled(enable) self.aoDeviceCombo.setEnabled(enable) self.startVSb.setEnabled(enable) self.stopVSb.setEnabled(enable) self.stepsSb.setEnabled(enable) self.interSweepDelaySb.setEnabled(enable) self.bipolarCb.setEnabled(enable) self.stopPb.setEnabled(not enable) def closeEvent(self, e): if self.msmThread: self.msmThread.stop() if self.hkSub: self.hkSub.stop() self.saveSettings() super(TESIVSweepDaqWidget, self).closeEvent(e) def saveSettings(self): #widgets = [self.sampleLineEdit, self.dcDriveImpedanceSb, self.bipolarCb, self.startVSb] s = QSettings() print "Saving settings" s.setValue('sampleId', self.sampleLineEdit.text()) s.setValue('dcDriveImpedance', self.dcDriveImpedanceSb.value()) s.setValue('bipolar', self.bipolarCb.isChecked()) s.setValue('reverse', self.reverseSweepCb.isChecked()) s.setValue('startV', self.startVSb.value()) s.setValue('stopV', self.stopVSb.value()) s.setValue('steps', self.stepsSb.value()) s.setValue('interSweepDelay', self.interSweepDelaySb.value()) s.setValue('samplesPerPoint', self.samplesPerPointSb.value()) s.setValue('discardSamples', self.discardSamplesSb.value()) s.setValue('preampGain', self.preampGainSb.value()) s.setValue('geometry', self.saveGeometry()) s.setValue('AI_Device', self.aiDeviceCombo.currentText()) s.setValue('AO_Device', self.aoDeviceCombo.currentText()) s.setValue('AI_Channel', self.aiChannelCombo.currentText()) s.setValue('AO_Channel', self.aoChannelCombo.currentText()) s.setValue('SourceType', self.sourceCombo.currentText()) s.setValue('CoilDriver', self.coilDriverCombo.currentText()) s.setValue('coilVisa', self.coilVisaCombo.currentText()) s.setValue('coilEnable', self.coilEnableCb.isChecked()) s.setValue('CoilAuxOut', self.auxOutChannelSb.value()) s.setValue('CoilVoltage', self.coilVoltageSb.value()) s.setValue('CoilSweepEnable', self.coilSweepCb.isChecked()) s.setValue('CoilSweepSteps', self.coilSweepStepsSb.value()) s.setValue('CoilSweepMin', self.coilSweepMinSb.value()) s.setValue('CoilSweepMax', self.coilSweepMaxSb.value()) s.setValue('adaptiveLower', self.adaptiveLowerSb.value()) s.setValue('adaptiveUpper', self.adaptiveUpperSb.value()) s.setValue('adaptiveSweep', self.adaptiveSweepingGroupBox.isChecked()) def restoreSettings(self): s = QSettings() print "Restoring settings" self.sampleLineEdit.setText(s.value('sampleId', '', type=QString)) self.dcDriveImpedanceSb.setValue( s.value('dcDriveImpedance', 10E3, type=float)) self.bipolarCb.setChecked(s.value('bipolar', False, type=bool)) self.reverseSweepCb.setChecked(s.value('reverse', False, type=bool)) self.startVSb.setValue(s.value('startV', 0, type=float)) self.stopVSb.setValue(s.value('stopV', 3, type=float)) self.stepsSb.setValue(s.value('steps', 10, type=int)) self.preampGainSb.setValue(s.value('preampGain', 1., type=float)) self.interSweepDelaySb.setValue( s.value('interSweepDelay', 0.5, type=float)) self.samplesPerPointSb.setValue(s.value('samplesPerPoint', 1, type=int)) self.discardSamplesSb.setValue(s.value('discardSamples', 0, type=int)) self.aiDeviceCombo.setCurrentIndex( self.aiDeviceCombo.findText(s.value('AI_Device', '', type=str))) self.aoDeviceCombo.setCurrentIndex( self.aoDeviceCombo.findText(s.value('AO_Device', '', type=str))) self.aiChannelCombo.setCurrentIndex( self.aiChannelCombo.findText(s.value('AI_Channel', '', type=str))) self.aoChannelCombo.setCurrentIndex( self.aoChannelCombo.findText(s.value('AO_Channel', '', type=str))) self.sourceCombo.setCurrentIndex( self.sourceCombo.findText(s.value('SourceType', '', type=str))) self.coilDriverCombo.setCurrentIndex( self.coilDriverCombo.findText(s.value('CoilDriver', '', type=str))) self.coilVisaCombo.setCurrentIndex( self.coilVisaCombo.findText(s.value('coilVisa', '', type=str))) self.coilEnableCb.setChecked(s.value('coilEnable', False, type=bool)) self.auxOutChannelSb.setValue(s.value('CoilAuxOut', 1, type=int)) self.coilVoltageSb.setValue(s.value('CoilVoltage', 0.0, type=float)) self.coilSweepMinSb.setValue(s.value('CoilSweepMin', -1, type=float)) self.coilSweepMaxSb.setValue(s.value('CoilSweepMax', 1, type=float)) self.coilSweepStepsSb.setValue( s.value('CoilSweepSteps', 21, type=float)) self.coilSweepCb.setChecked(s.value('CoilSweepEnable', True, type=bool)) self.adaptiveLowerSb.setValue(s.value('adaptiveLower', 75, type=int)) self.adaptiveUpperSb.setValue(s.value('adaptiveUpper', 130, type=int)) self.adaptiveSweepingGroupBox.setChecked( s.value('adaptiveSweep', True, type=bool)) geometry = s.value('geometry', QByteArray(), type=QByteArray) self.restoreGeometry(geometry)
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 magnetReadingReceived(self, t, Vmagnet, ImagnetCoarse, ImagnetFine): #print('Magnet') self.magnetWriter.writeData(t=t, Vmagnet=Vmagnet, ImagnetCoarse=ImagnetCoarse, ImagnetFine=ImagnetFine) def tesBiasReceived(self, t, Vbias): #print('TES bias') self.tesBiasWriter.writeData(t=t, Vbias=Vbias) def fieldCoilBiasReceived(self, t, Vcoil): #print('Field coil bias') self.fieldCoilBiasWriter.writeData(t=t, Vcoil=Vcoil) if __name__ == '__main__': from PyQt4.QtGui import QApplication app = QApplication([]) import h5py as hdf from Zmq.Subscribers import HousekeepingSubscriber hkSub = HousekeepingSubscriber() hdfRoot = hdf.File('HKTest_2.h5', 'w') logger = HkLogger(hdfRoot, hkSub) hkSub.start() import time for i in range(50): time.sleep(1) app.processEvents() hkSub.stop() hdfRoot.close()
class Widget(gui.QWidget, ui.Ui_Form): def __init__(self, parent=None): super(Widget, self).__init__(parent) self.setupUi(self) self.BoSb.setToSiFactor(SI_uT) self.gCoilSb.setToSiFactor(SI_uT / SI_mA) self.RcoilSb.setToSiFactor(SI_kOhm) self.BcoilSb.setToSiFactor(SI_uT) self.RfbSb.setToSiFactor(SI_kOhm) self.invMiSb.setToSiFactor(SI_uA / SI_Phi0) self.invMfbSb.setToSiFactor(SI_uA / SI_Phi0) self.ApSb.setToSiFactor(SI_um**2) self.RbiasSb.setToSiFactor(SI_kOhm) self.gTesSb.setToSiFactor(SI_uT / SI_mA) self.RsSb.setToSiFactor(SI_mOhm) self.IbiasSb.setToSiFactor(SI_uA) self.ItesSb.setToSiFactor(SI_uA) self.RtesSb.setToSiFactor(SI_mOhm) self.setWindowTitle('TES Self-Field Cancellation') self.SettingsWidgets = [ self.BoSb, self.gCoilSb, self.RcoilSb, self.RfbSb, self.invMiSb, self.invMfbSb, self.ApSb, self.RbiasSb, self.gTesSb, self.IbiasSb, self.nameLe, self.aiSamplesSb, self.ItesTargetSb, self.disableBiasCb, self.RsSb ] self.liveWidgets = [ self.BoSb, self.gTesSb, self.IbiasSb, self.ItesTargetSb, self.tesCurrentControlCb, self.disableBiasCb ] #self.IbiasSb.valueChanged.connect(self.IbiasSb.setMaximum) self.restoreSettings() self.msmThread = None self.hkSub = HousekeepingSubscriber(self) self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue) self.hkSub.start() self.curveVsTime = pg.PlotCurveItem(pen='k') self.plotVsTime.addItem(self.curveVsTime) self.plotVsTime.plotItem.enableAutoRange(pg.ViewBox.XYAxes, True) self.curveVsTemp = pg.PlotCurveItem(pen='k') self.plotVsTemp.addItem(self.curveVsTemp) self.plotVsTemp.setBackground('w') self.plotVsTemp.plotItem.showGrid(x=True, y=True) self.plotVsTemp.plotItem.enableAutoRange(pg.ViewBox.XYAxes, True) self.clearData() self.runPb.clicked.connect(self.run) def run(self): # if self.TES1Cb.isChecked(): # self.invMiSb.setValue(5.7866) # self.invMfbSb.setValue(6.03355) # self.RbiasSb.setValue(1.6976) # self.RsSb.setValue(0.250) # whichTES = 'TES1' # # if self.TES2Cb.isChecked(): # self.invMiSb.setValue(5.673681) # self.invMfbSb.setValue(5.96605) # self.RbiasSb.setValue(1.6017) # self.RsSb.setValue(0.256) # whichTES = 'TES2' if self.msmThread is not None: self.msmThread.stop() self.msmThread.wait() self.file.close() self.msmThread.deleteLater() self.msmThread = None else: Mi = 1. / self.invMiSb.valueSi() Mfb = 1. / self.invMfbSb.valueSi() print Mi, Mfb, Mi / Mfb, Mfb / Mi Rfb = self.RfbSb.valueSi() gCoil = self.gCoilSb.valueSi() self.gCoil = gCoil Rcoil = self.RcoilSb.valueSi() self.Rcoil = Rcoil Ap = self.ApSb.valueSi() Rbias = self.RbiasSb.valueSi() print "Rbias:", Rbias Rs = self.RsSb.valueSi() gTes = self.gTesSb.valueSi() Bo = self.BoSb.valueSi() Ibias = self.IbiasSb.valueSi() print "Ibias:", Ibias nSamples = self.aiSamplesSb.value() fileName = 'D:/Users/Runs/G4C/RT/%s_%s.dat' % ( self.nameLe.text(), time.strftime('%Y%m%d_%H%M%S')) self.file = open(fileName, 'w') def writeParameter(name, value): self.file.write('#%s=%.6e\n' % (name, value)) writeParameter('Mi', Mi) writeParameter('Mfb', Mfb) writeParameter('Rfb', Rfb) writeParameter('gCoil', gCoil) writeParameter('Rcoil', Rcoil) writeParameter('Ap', Ap) writeParameter('Rbias', Rbias) writeParameter('Rs', Rs) writeParameter('gTes', gTes) writeParameter('Bo', Bo) writeParameter('gTes', gTes) writeParameter('Ibias', Ibias) self.file.write('#%s\n' % '\t'.join([ 't', 'Tadr', 'Vbias', 'Vcoil', 'Vo', 'Vsquid', 'Ites', 'Rtes' ])) msmThread = MsmThread(parent=self) msmThread.setFixedParameters(Mi=Mi, Mfb=Mfb, Rfb=Rfb, gCoil=gCoil, Rcoil=Rcoil, Ap=Ap, Rbias=Rbias, Rs=Rs) msmThread.setgTes(gTes) msmThread.setBo(Bo) msmThread.setIbias(Ibias) msmThread.setNumberOfSamples(nSamples) msmThread.finished.connect(self.threadFinished) msmThread.measurementAvailable.connect(self.collectData) msmThread.setDisableBias(self.disableBiasCb.isChecked()) self.disableBiasCb.toggled.connect(msmThread.setDisableBias) self.gTesSb.valueChangedSi.connect(msmThread.setgTes) self.BoSb.valueChangedSi.connect(msmThread.setBo) self.IbiasSb.valueChangedSi.connect(msmThread.setIbias) self.ItesTargetSb.valueChangedSi.connect(msmThread.setItes) self.msmThread = msmThread msmThread.start() msmThread.started.connect(self.threadStarted) def clearData(self): self.ts = [] self.T = [] self.Vbias = [] self.Vcoil = [] self.Vo = [] self.Vsquid = [] self.Ites = [] self.Rtes = [] self.updatePlots() def collectData(self, t, Vbias, Vcoil, Vo, Vsquid, Ites, Rtes): Tadr = self.temperatureSb.value() self.ts.append(t) self.T.append(Tadr) self.Vbias.append(Vbias) self.Vcoil.append(Vcoil) self.Vo.append(Vo) self.Vsquid.append(Vsquid) self.Ites.append(Ites) self.Rtes.append(Rtes) self.file.write('%.3f\t%.6e\t%.6f\t%.6f\t%.6f\t%.6f\t%.6e\t%.6e\n' % (t, Tadr, Vbias, Vcoil, Vo, Vsquid, Ites, Rtes)) l = len(self.ts) if l % 20 != 0: return i = min(l, 100) if i > 10: Dt = t - self.ts[-i] rate = float(i) / Dt self.loopRateSb.setValue(rate) self.lockLostLed.setValue(abs(Vsquid) > 10) self.ts = pruneData(self.ts) self.T = pruneData(self.T) self.Vbias = pruneData(self.Vbias) self.Vcoil = pruneData(self.Vcoil) self.Vo = pruneData(self.Vo) self.Vsquid = pruneData(self.Vsquid) self.Ites = pruneData(self.Ites) self.Rtes = pruneData(self.Rtes) self.VbiasSb.setValue(Vbias) self.VcoilSb.setValue(Vcoil) self.VoSb.setValue(Vo) self.VsquidSb.setValue(Vsquid) self.ItesSb.setValueSi(Ites) self.RtesSb.setValueSi(Rtes) Bcoil = self.gCoil * Vcoil / self.Rcoil self.BcoilSb.setValueSi(Bcoil) self.updatePlots() def updatePlots(self): yAxis = self.yAxisCombo.currentText() if yAxis == 'Vbias': y = self.Vbias elif yAxis == 'Vcoil': y = self.Vcoil elif yAxis == 'Vo': y = self.Vo elif yAxis == 'Vsquid': y = self.Vsquid elif yAxis == 'Ites': y = self.Ites elif yAxis == 'Rtes': y = self.Rtes if yAxis == 'Ites': unit = 'A' elif yAxis == 'Rtes': unit = 'Ohm' else: unit = 'V' yax = self.plotVsTime.getAxis('left') yax.setLabel(yAxis, unit) yax = self.plotVsTemp.getAxis('left') yax.setLabel(yAxis, unit) self.curveVsTime.setData(x=self.ts, y=y) self.curveVsTemp.setData(x=self.T, y=y) def threadStarted(self): self.enableControls(False) def threadFinished(self): self.enableControls(True) def enableControls(self, enable): for w in self.SettingsWidgets: if not w in self.liveWidgets: w.setEnabled(enable) self.runPb.setText('Run' if enable else 'Stop') def closeEvent(self, event): self.saveSettings() def restoreSettings(self): s = QSettings() for w in self.SettingsWidgets: ut.restoreWidgetFromSettings(s, w) def saveSettings(self): s = QSettings() for w in self.SettingsWidgets: ut.saveWidgetToSettings(s, w)
def __init__(self, parent = None): super(TemperatureControlMainWindow, self).__init__(parent) self.setupUi(self) self.setpointSb.setDecimals(7) self.setpointSb.setSingleStep(1E-7) self.rampRateSb.setDecimals(2) self.rampRateSb.setSingleStep(1E-2) self.serverThread = None self.selectedSensorName = '' self.clearData() self.pid = None self.outputFile = None self.magnetControlRemote = None self.rampEnableCb.toggled.connect(self.fixupRampRate) self.updatePlotsCb.toggled.connect(self.showPlots) self.widgetsForSettings = [self.thermometerCombo, self.setpointSb, self.KSb, self.TiSb, self.TdSb, self.TtSb, self.TfSb, self.betaSb, self.gammaSb, self.controlMinSb, self.controlMaxSb, self.rampRateSb, self.rampTargetSb, self.rampEnableCb, self.updatePlotsCb, self.useBaseTemperatureCb] axis = pg.DateAxisItem(orientation='bottom') self.pvPlot = pg.PlotWidget(axisItems={'bottom': axis}) self.mainVerticalLayout.addWidget(self.pvPlot) self.pvPlot.addLegend() self.curvePv = pg.PlotCurveItem(name='Actual', symbol='o', pen='g') self.curveSetpoint = pg.PlotCurveItem(name='Setpoint', symbol='o', pen='r') self.pvPlot.addItem(self.curvePv) self.pvPlot.addItem(self.curveSetpoint) axis = pg.DateAxisItem(orientation='bottom') self.pidPlot = pg.PlotWidget(axisItems={'bottom': axis}) self.mainVerticalLayout.addWidget(self.pidPlot) self.pidPlot.addLegend() self.curveP = pg.PlotCurveItem(name='P', symbol='o', pen='r') self.curveI = pg.PlotCurveItem(name='I', symbol='o', pen='g') self.curveD = pg.PlotCurveItem(name='D', symbol='o', pen='b') self.curveControl = pg.PlotCurveItem(name='control', symbol='o', pen='w') self.pidPlot.addItem(self.curveP) self.pidPlot.addItem(self.curveI) self.pidPlot.addItem(self.curveD) self.pidPlot.addItem(self.curveControl) self.KSb.setMinimum(-1E6) self.KSb.setMaximum(1E6) self.hkSub = HousekeepingSubscriber(self) self.hkSub.thermometerListUpdated.connect(self.updateThermometerList) self.hkSub.thermometerReadingReceived.connect(self.receiveTemperature) self.hkSub.start() self.startPb.clicked.connect(self.startPid) self.stopPb.clicked.connect(self.stopPid) self.timer = QTimer(self) # A 250 ms timer to implement ramping self.timer.timeout.connect(self.updateSetpoint) self.tOld = time.time() self.timer.start(250) self.thermometerCombo.currentIndexChanged.connect(self.updateThermometerSelection) self.restoreSettings() # Give the hkSub some time to update thermometer list before we update thermometer selection QTimer.singleShot(2000, self.restoreThermometerSelection)
class IvCurveWidget(ui.Ui_Form, QWidget): EXIT_CODE_REBOOT = -1234 logger = logging.getLogger('PulseCollectorMainWindow') def __init__(self, parent=None): super(IvCurveWidget, self).__init__(parent) self.setupUi(self) self.restartPb.clicked.connect(self.restart) self.thread = None self.hdfFile = None self.squid = None self._fileName = '' self.plot.addLegend() self.plot.setLabel('left', 'SQUID response', units='V') self.plot.setLabel('bottom', 'bias voltage', units='V') self.curves = [] self.startPb.clicked.connect(self.startMeasurement) self.stopPb.clicked.connect(self.stopMeasurement) # self.aoRangeCombo.currentIndexChanged.connect(self.updateAoRange) # self.auxAoChannelCombo.currentIndexChanged.connect(self.updateAuxAoRange) self.osr = OpenSquidRemote(port=7894) squids = self.osr.findSquids() self.tesCombo.addItem('None') self.tesCombo.addItems(squids) self.settingsWidgets = [ self.deviceCombo, self.aoChannelCombo, self.aoRangeCombo, self.aiChannelCombo, self.aiRangeCombo, self.aiTerminalConfigCombo, self.aiDriveChannelCombo, self.recordDriveCb, self.maxDriveSb, self.slewRateSb, self.zeroHoldTimeSb, self.peakHoldTimeSb, self.betweenHoldTimeSb, self.decimateCombo, self.sampleRateSb, self.sampleLe, self.commentLe, self.enablePlotCb, self.auxAoChannelCombo, self.auxAoRangeCombo, self.auxAoSb, self.auxAoEnableCb, self.polarityCombo, self.tesCombo, self.pflResetCb, self.driveOffsetSb ] self.deviceCombo.currentIndexChanged.connect(self.updateDevice) for w in [ self.maxDriveSb, self.slewRateSb, self.zeroHoldTimeSb, self.peakHoldTimeSb, self.betweenHoldTimeSb, self.sampleRateSb ]: w.valueChanged.connect(self.updateInfo) self.decimateCombo.currentIndexChanged.connect(self.updateInfo) self.polarityCombo.currentIndexChanged.connect(self.updateInfo) self.auxAoSb.valueChanged.connect(self.updateAuxOutputVoltage) self.auxAoEnableCb.toggled.connect(self.toggleAuxOut) self.auxAoRamper = None self.restoreSettings() self.hkSub = HousekeepingSubscriber(self) self.hkSub.adrTemperatureReceived.connect(self.temperatureSb.setValue) self.hkSub.adrResistanceReceived.connect(self.collectAdrResistance) self.hkSub.start() self.serverThread = RequestReplyThreadWithBindings( port=RequestReply.IvCurveDaq, parent=self) boundWidgets = { 'sampleName': self.sampleLe, 'auxAoEnable': self.auxAoEnableCb, 'auxVoltage': self.auxAoSb, 'maxDrive': self.maxDriveSb, 'slewRate': self.slewRateSb, 'start': self.startPb, 'stop': self.stopPb, 'totalTime': self.totalTimeSb, 'sweepCount': self.sweepCountSb, 'comment': self.commentLe, 'driveOffset': self.driveOffsetSb } for name in boundWidgets: self.serverThread.bindToWidget(name, boundWidgets[name]) self.serverThread.bindToFunction('fileName', self.fileName) self.serverThread.bindToFunction('restart', self.requestRestart) self.serverThread.start() pens = 'rgbc' for i in range(4): curve = pg.PlotDataItem(pen=pens[i], name='Curve %d' % i) self.plot.addItem(curve) self.curves.append(curve) self.restartRequested = False timer = QTimer(self) timer.timeout.connect(self.checkRestartRequested) timer.setInterval(1000) timer.start() self.timer = timer self.logger.info('IvCurveDaq started.') def checkRestartRequested(self): if self.restartRequested: self.restart() def requestRestart(self): print('Restarting soon!') self.restartRequested = True return True def restart(self): if self.thread is not None: result = QMessageBox.question( self, "Restart program", "The measurement thread is still running. Do you really want to restart the program?", QMessageBox.Yes, QMessageBox.No) if result != QMessageBox.Yes: return self.thread.stop() self.thread.wait(3000) self.close() self.logger.info('IvCurveDaq restarting.') qApp.exit(self.EXIT_CODE_REBOOT) def fileName(self): return self._fileName def toggleAuxOut(self, enabled): if enabled: deviceName = str(self.deviceCombo.currentText()) aoChannel = str(self.auxAoChannelCombo.currentText()) aoRange = self.aoRanges[self.auxAoRangeCombo.currentIndex()] self.auxAoRamper = AuxAoRamper(deviceName, aoChannel, aoRange) self.auxAoRamper.setTo(self.auxAoSb.value()) else: del self.auxAoRamper self.auxAoRamper = None def threadRunning(self): if self.thread is not None: return self.thread.isRunning() else: return False def updateAuxOutputVoltage(self): V = self.auxAoSb.value() if self.threadRunning(): self.thread.updateAoVoltage(V) elif self.auxAoRamper is not None: try: self.auxAoRamper.rampTo(V) except Exception: exceptionString = traceback.format_exc() self.reportError(exceptionString) # def updateAuxAoRange(self): # #auxAoChannel = str(self.auxAoChannelCombo.currentText()) # r = self.aoRanges[self.auxAoRangeCombo.currentIndex()] # #self.auxAoSb.setMaximum(r.max) # #self.auxAoSb.setMinimum(r.min) # if self.auxAoEnableCb.isChecked(): # If output is already on, we have to kill the old task and make a new one # self.toggleAuxOut(False) # self.toggleAuxOut(True) def collectAdrResistance(self, R): if self.hdfFile is None: return timeStamp = time.time() self.dsTimeStamps.resize((self.dsTimeStamps.shape[0] + 1, )) self.dsTimeStamps[-1] = timeStamp self.dsAdrResistance.resize((self.dsAdrResistance.shape[0] + 1, )) self.dsAdrResistance[-1] = R def populateDevices(self): self.deviceCombo.clear() system = daq.System() devices = system.findDevices() for dev in devices: self.deviceCombo.addItem(dev) # def updateAoRange(self): # aoRange = self.aoRanges[self.aoRangeCombo.currentIndex()] # self.maxDriveSb.setMaximum(aoRange.max) def updateDevice(self): self.aiChannelCombo.clear() self.aiDriveChannelCombo.clear() self.aoChannelCombo.clear() self.aiRangeCombo.clear() self.aoRangeCombo.clear() self.auxAoRangeCombo.clear() self.auxAoChannelCombo.clear() deviceName = str(self.deviceCombo.currentText()) if len(deviceName) < 1: return device = daq.Device(deviceName) aiChannels = device.findAiChannels() for channel in aiChannels: self.aiChannelCombo.addItem(channel) self.aiDriveChannelCombo.addItem(channel) aoChannels = device.findAoChannels() for channel in aoChannels: self.aoChannelCombo.addItem(channel) self.auxAoChannelCombo.addItem(channel) self.aiRanges = device.voltageRangesAi() for r in self.aiRanges: self.aiRangeCombo.addItem('%+.2f -> %+.2f V' % (r.min, r.max)) self.aoRanges = device.voltageRangesAo() for r in self.aoRanges: self.aoRangeCombo.addItem('%+.2f -> %+.2f V' % (r.min, r.max)) self.auxAoRangeCombo.addItem('%+.2f -> %+.2f V' % (r.min, r.max)) if len(aiChannels): aiChannel = daq.AiChannel('%s/%s' % (deviceName, aiChannels[0]), self.aiRanges[0].min, self.aiRanges[0].max) aiTask = daq.AiTask('TestInputSampleRate') aiTask.addChannel(aiChannel) aiSampleRate = aiTask.maxSampleClockRate() else: aiSampleRate = 0 if len(aoChannels): aoChannel = daq.AoChannel('%s/%s' % (deviceName, aoChannels[0]), self.aoRanges[0].min, self.aoRanges[0].max) aoTask = daq.AoTask('TestOutputSampleRate') aoTask.addChannel(aoChannel) aoSampleRate = aoTask.maxSampleClockRate() else: aoSampleRate = 0 rate = min(aiSampleRate, aoSampleRate) self.sampleRateSb.setMaximum(int(1E-3 * rate)) self.updateInfo() def terminalConfiguration(self): t = str(self.aiTerminalConfigCombo.currentText()) tc = daq.AiChannel.TerminalConfiguration terminalConfigDict = {'RSE': tc.RSE, 'DIFF': tc.DIFF, 'NRSE': tc.NRSE} return terminalConfigDict[t] def updateInfo(self): tz = self.zeroHoldTimeSb.value() tr = self.maxDriveSb.value() / self.slewRateSb.value() tp = self.peakHoldTimeSb.value() tb = self.betweenHoldTimeSb.value() f = self.sampleRateSb.value() * 1E3 polarity = self.polarityCombo.currentText() if polarity == 'bipolar': ttotal = 2 * tz + 2 * (2 * tr + tp) + tb elif polarity == 'IvsB': ttotal = 2 * tz + 4 * tr + tb else: ttotal = 2 * tz + 1 * (2 * tr + tp) self.ttotal = ttotal self.totalTimeSb.setValue(ttotal) samplesPerSweep = int( np.ceil(ttotal * f / float(self.decimateCombo.currentText()))) self.samplesPerSweepSb.setValue(samplesPerSweep) def restoreSettings(self): s = QSettings(OrganizationName, ApplicationName) self.populateDevices() for w in self.settingsWidgets: restoreWidgetFromSettings(s, w) def saveSettings(self): s = QSettings(OrganizationName, ApplicationName) for w in self.settingsWidgets: saveWidgetToSettings(s, w) def closeEvent(self, event): if self.thread is not None: event.ignore() return self.saveSettings() super(IvCurveWidget, self).closeEvent(event) def removeAllCurves(self): for curve in self.curves: curve.setData([], []) self.nSweeps = 0 self.sweepCountSb.setValue(self.nSweeps) def startMeasurement(self): self.removeAllCurves() f = self.sampleRateSb.value() * 1E3 Voffset = self.driveOffsetSb.value() Vmax = self.maxDriveSb.value() polarity = self.polarityCombo.currentText() if polarity == 'IvsB': whold1 = -Vmax * np.ones(int(self.zeroHoldTimeSb.value() * f)) wrs = np.linspace(-Vmax, +Vmax, int(2 * Vmax / self.slewRateSb.value() * f)) whold2 = Vmax * np.ones( (int(self.betweenHoldTimeSb.value() * f), ), dtype=float) wave = np.hstack([whold1, wrs, whold2, wrs[::-1], whold1]) else: wz = np.zeros((int(self.zeroHoldTimeSb.value() * f), ), dtype=float) # Zero wr = np.linspace(0, Vmax, int(Vmax / self.slewRateSb.value() * f)) # Ramp wp = np.ones((int(self.peakHoldTimeSb.value() * f), ), dtype=float) * Vmax # Hold at peak wb = np.zeros((int(self.betweenHoldTimeSb.value() * f), ), dtype=float) # Hold between if polarity == 'bipolar': wave = np.hstack( [wz, wr, wp, wr[::-1], wb, -wr, -wp, -wr[::-1], wz]) # Up down and back elif polarity == 'positive only': wave = np.hstack([wz, wr, wp, wr[::-1], wz]) # Positive only elif polarity == 'negative only': wave = np.hstack([wz, -wr, wp, -wr[::-1], wz]) # Positive only else: raise Exception('Unsupported polarity choice') Voffset = self.driveOffsetSb.value() wave += Voffset squidId = str(self.tesCombo.currentText()) if squidId != 'None': squid = Pfl102Remote(self.osr, squidId) else: squid = None self.squid = squid self.decimation = int(self.decimateCombo.currentText()) self.x = iterativeDecimate(wave, self.decimation) deviceName = str(self.deviceCombo.currentText()) aoChannel = str(self.aoChannelCombo.currentText()) aiChannel = str(self.aiChannelCombo.currentText()) aiTerminalConfig = self.terminalConfiguration() aiRange = self.aiRanges[self.aiRangeCombo.currentIndex()] aoRange = self.aoRanges[self.aoRangeCombo.currentIndex()] s = QSettings('WiscXrayAstro', application='ADR3RunInfo') path = str(s.value('runPath', '', type=str)) fileName = path + '/IV/%s_%s.h5' % (self.sampleLe.text(), time.strftime('%Y%m%d_%H%M%S')) self._fileName = fileName hdfFile = hdf.File(fileName, mode='w') hdfFile.attrs['Program'] = ApplicationName hdfFile.attrs['Version'] = Version hdfFile.attrs[ 'Decimation'] = 'Iterative decimate using customized scipy code with filtfilt.' hdfFile.attrs['Sample'] = str(self.sampleLe.text()) hdfFile.attrs['Comment'] = str(self.commentLe.text()) t = time.time() hdfFile.attrs['StartTime'] = t hdfFile.attrs['StartTimeLocal'] = time.strftime( '%Y-%m-%d %H:%M:%S', time.localtime(t)) hdfFile.attrs['StartTimeUTC'] = time.strftime('%Y-%m-%d %H:%M:%SZ', time.gmtime(t)) hdfFile.attrs['Voffset'] = Voffset hdfFile.attrs['Vmax'] = Vmax hdfFile.attrs['sampleRate'] = f hdfFile.attrs['decimation'] = self.decimation hdfFile.attrs['deviceName'] = deviceName hdfFile.attrs['aoChannel'] = aoChannel hdfFile.attrs['aoRangeMin'] = aoRange.min hdfFile.attrs['aoRangeMax'] = aoRange.max hdfFile.attrs['aiChannel'] = aiChannel hdfFile.attrs['aiRangeMin'] = aiRange.min hdfFile.attrs['aiRangeMax'] = aiRange.max hdfFile.attrs['aiTerminalConfig'] = str( self.aiTerminalConfigCombo.currentText()) hdfFile.attrs['zeroHoldTime'] = self.zeroHoldTimeSb.value() hdfFile.attrs['peakHoldTime'] = self.peakHoldTimeSb.value() hdfFile.attrs['betweenHoldTime'] = self.betweenHoldTimeSb.value() hdfFile.attrs['slewRate'] = self.slewRateSb.value() hdfFile.attrs['polarity'] = str(polarity) hdfFile.attrs['resetPflEverySweep'] = bool(self.pflResetCb.isChecked()) if squid is not None: hdfFile.attrs['pflReport'] = str(squid.report()) hdfFile.attrs['pflRfb'] = squid.feedbackR() hdfFile.attrs['pflCfb'] = squid.feedbackC() if self.auxAoRamper is not None: hdfFile.attrs['auxAoChannel'] = str(self.auxAoRamper.channel) auxAoRange = self.aoRanges[self.auxAoRangeCombo.currentIndex()] hdfFile.attrs['auxAoRangeMin'] = auxAoRange.min hdfFile.attrs['auxAoRangeMax'] = auxAoRange.max hdfFile.attrs['auxAoValue'] = self.auxAoSb.value() ds = hdfFile.create_dataset('excitationWave', data=wave, compression='lzf', shuffle=True, fletcher32=True) ds.attrs['units'] = 'V' ds = hdfFile.create_dataset('excitationWave_decimated', data=self.x, compression='lzf', shuffle=True, fletcher32=True) ds.attrs['units'] = 'V' g = hdfFile.create_group('HK') self.hkLogger = HkLogger( g, self.hkSub ) # Should remove stuff below soon - only kept for backwards compatibility self.dsTimeStamps = hdfFile.create_dataset('AdrResistance_TimeStamps', (0, ), maxshape=(None, ), chunks=(500, ), dtype=np.float64) self.dsTimeStamps.attrs['units'] = 's' self.dsAdrResistance = hdfFile.create_dataset('AdrResistance', (0, ), maxshape=(None, ), chunks=(500, ), dtype=np.float64) self.dsAdrResistance.attrs['units'] = 'Ohms' self.hdfFile = hdfFile thread = DaqThread(deviceName, aoChannel, aoRange, aiChannel, aiRange, aiTerminalConfig, parent=self) thread.setWave(wave) thread.setSampleRate(f) thread.dataReady.connect(self.collectData) thread.setAuxAoRamper(self.auxAoRamper) thread.updateAoVoltage(self.auxAoSb.value()) if self.recordDriveCb.isChecked(): aiDriveChannel = str(self.aiDriveChannelCombo.currentText()) hdfFile.attrs['aiDriveChannel'] = aiDriveChannel thread.enableDriveRecording(aiDriveChannel) thread.driveDataReady.connect(self.collectDriveData) if self.pflResetCb.isChecked(): thread.setSquid(squid) thread.error.connect(self.reportError) self.enableWidgets(False) self.thread = thread self.t0 = None #nCollected = gc.collect() #print('Garbarge collection before measurement:', nCollected) #gc.disable() thread.start() thread.finished.connect(self.threadFinished) #print guppyH.heap() def reportError(self, message): message = str(message) self.logger.error('Exception encountered: %s', message) from Utility.Gmail import sendMessage try: sendMessage(['*****@*****.**'], 'IvCurveDaq error', message) except Exception as e: self.logger.error('Unable to send email: %s', str(e)) QMessageBox.critical(self, 'Exception encountered!', message) def stopMeasurement(self): if self.thread is None: return if self.stopPb.text() == 'Stop': self.thread.stop() self.stopPb.setText('Abort') else: self.thread.abort() def threadFinished(self): print('Finished, cleaning up.') del self.hkLogger self.hkLogger = None del self.thread self.thread = None self.closeFile() del self.squid self.squid = None del self.dsAdrResistance self.dsAdrResistance = None del self.dsTimeStamps self.dsTimeStamps = None del self.x self.x = None self.stopPb.setText('Stop') self.enableWidgets(True) print('Done cleaning.') nCollected = gc.collect() print('Garbarge collection after measurement:', nCollected) #gc.enable() def closeFile(self): if self.hdfFile is not None: del self.hkLogger self.hkLogger = None t = time.time() self.hdfFile.attrs['StopTime'] = t self.hdfFile.attrs['StopTimeLocal'] = time.strftime( '%Y-%m-%d %H:%M:%S', time.localtime(t)) self.hdfFile.attrs['StopTimeUTC'] = time.strftime( '%Y-%m-%d %H:%M:%SZ', time.gmtime(t)) self.hdfFile.close() del self.hdfFile self.hdfFile = None def enableWidgets(self, enable): self.driveGroupBox.setEnabled(enable) self.inputGroupBox.setEnabled(enable) self.auxAoChannelCombo.setEnabled(enable) self.auxAoRangeCombo.setEnabled(enable) self.auxAoEnableCb.setEnabled(enable) self.startPb.setEnabled(enable) self.stopPb.setEnabled(not enable) def collectDriveData(self, timeStamp, dt, data): ds = self.hdfFile.create_dataset('DriveSignalRaw', data=data, compression='lzf', shuffle=True, fletcher32=True) ds.attrs['units'] = 'V' data = iterativeDecimate(data, self.decimation) ds = self.hdfFile.create_dataset('DriveSignalDecimated', data=data, compression='lzf', shuffle=True, fletcher32=True) ds.attrs['units'] = 'V' def collectData(self, timeStamp, auxAoVoltage, dt, data): Tadr = self.temperatureSb.value() if self.t0 is None: self.t0 = timeStamp data = iterativeDecimate(data, self.decimation) currentSweep = self.nSweeps self.nSweeps += 1 self.sweepCountSb.setValue(self.nSweeps) try: grp = self.hdfFile.create_group('Sweep_%06d' % currentSweep) grp.attrs['Time'] = timeStamp grp.attrs['TimeLocal'] = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(timeStamp)) grp.attrs['TimeUTC'] = time.strftime('%Y-%m-%d %H:%M:%SZ', time.gmtime(timeStamp)) grp.attrs['Tadr'] = Tadr grp.attrs['auxAoValue'] = auxAoVoltage ds = grp.create_dataset('Vsquid', data=data, compression='lzf', shuffle=True, fletcher32=True) ds.attrs['units'] = 'V' except Exception: exceptionString = traceback.format_exc() self.reportError(exceptionString) self.stopMeasurement() if self.enablePlotCb.isChecked(): self.curves[currentSweep % len(self.curves)].setData(self.x, data) else: for curve in self.curves: curve.setData([], [])