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

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

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

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

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

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

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

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

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

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

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

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

    def closeEvent(self, e):
        if self.thread is not None:
            self.endThread()
        self.saveSettings()
        super(TektronixScopeWidget, self).closeEvent(e)
示例#2
0
class MagnetControlThread(QThread):
    '''Magnet control thread'''
    DIODE_I0 = 4.2575E-11 # A
    DIODE_A  = 37.699     # 1/V (=q_q/(k_B*T))
    quenchDetected = pyqtSignal()
    supplyVoltageUpdated = pyqtSignal(float)
    programmedSupplyVoltageUpdated = pyqtSignal(float)
    supplyCurrentUpdated = pyqtSignal(float)
    magnetVoltageUpdated = pyqtSignal(float)
    diodeVoltageUpdated = pyqtSignal(float)
    resistiveVoltageUpdated = pyqtSignal(float)
    resistanceUpdated = pyqtSignal(float)
    rampRateUpdated = pyqtSignal(float)
    measurementAvailable = pyqtSignal(float, float, float, float)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                # Log all measurements
                self.log(t)

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

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

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

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

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

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

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

        except Exception:
            logger.warn("Exception:", exc_info=True)
        logger.info("MagnetControl ending")