示例#1
0
def test_skip_in_loop(task):
    """Skip testing the field if the task is embedded in a LoopTask.

    """
    task.feval = ''
    val, res, msg = validators.SkipLoop().check(task, 'feval')
    assert val is None
    assert res
    assert not msg

    task.feval = '2*{Loop_val}'
    root = task.root
    task.parent.task = None
    root.add_child_task(0, task)
    val, res, msg = validators.SkipLoop().check(task, 'feval')
    assert val == 2
    assert res
    assert not msg
示例#2
0
class SetPulseModulationTask(InterfaceableTaskMixin, InstrumentTask):
    """Switch on/off the pulse modulation of the source.

    """
    # Desired state of the output, runtime value can be 0 or 1.
    switch = Unicode('Off').tag(pref=True, feval=validators.SkipLoop())

    database_entries = set_default({'pm_state': 0})

    def check(self, *args, **kwargs):
        """Validate the value of the switch.

        """
        test, traceback = super(SetPulseModulationTask, self).check(*args,
                                                                    **kwargs)

        if test and self.switch:
            try:
                switch = self.format_and_eval_string(self.switch)
            except Exception:
                return False, traceback

            if switch not in ('Off', 'On', 0, 1):
                test = False
                traceback[self.get_error_path() + '-switch'] =\
                    '{} is not an acceptable value.'.format(self.switch)

        return test, traceback

    def i_perform(self, switch=None):
        """Default interface behavior.

        """
        if switch is None:
            switch = self.format_and_eval_string(self.switch)

        if switch == 'On' or switch == 1:
            self.driver.pm_state = 'On'
            self.write_in_database('pm_state', 1)
        else:
            self.driver.pm_state = 'Off'
            self.write_in_database('pm_state', 0)
示例#3
0
class ApplyMagFieldAndDropTask(InstrumentTask):
    """Use a supraconducting magnet to apply a magnetic field. Parallel task.

    """
    # Target magnetic field (dynamically evaluated)
    field = Unicode().tag(pref=True,
                          feval=validators.SkipLoop(types=numbers.Real))

    # Rate at which to sweep the field.
    rate = Float(0.01).tag(pref=True)

    parallel = set_default({'activated': True, 'pool': 'instr'})
    database_entries = set_default({'field': 0.01})

    def check_for_interruption(self):
        """Check if the user required an interruption.

        """
        return self.root.should_stop.is_set()

    def perform(self, target_value=None):
        """Apply the specified magnetic field.

        """
        # make ready
        if (self.driver.owner != self.name
                or not self.driver.check_connection()):
            self.driver.owner = self.name

        driver = self.driver
        if driver.heater_state == 'Off':
            raise ValueError(cleandoc(''' Switch heater must be on'''))

        if target_value is None:
            target_value = self.format_and_eval_string(self.field)

        driver.field_sweep_rate = self.rate
        driver.target_field = target_value
        driver.activity = 'To set point'

        self.write_in_database('field', target_value)
示例#4
0
class SetDCFunctionTask(InterfaceableTaskMixin, InstrumentTask):
    """Set a DC source function to the specified value: VOLT or CURR

    """
    #: Target value for the source (dynamically evaluated)
    switch = Str('VOLT').tag(pref=True, feval=validators.SkipLoop())

    database_entries = set_default({'function': 'VOLT'})

    def i_perform(self, switch=None):
        """Default interface.

        """
        if switch is None:
            switch = self.format_and_eval_string(self.switch)

        if switch == 'VOLT':
            self.driver.function = 'VOLT'
            self.write_in_database('function', 'VOLT')

        if switch == 'CURR':
            self.driver.function = 'CURR'
            self.write_in_database('function', 'CURR')
示例#5
0
class SetDCOutputTask(InterfaceableTaskMixin, InstrumentTask):
    """Set a DC source output to the specified value: ON or OFF

    """
    #: Target value for the source output
    switch = Unicode('OFF').tag(pref=True, feval=validators.SkipLoop())

    database_entries = set_default({'output': 'OFF'})

    def i_perform(self, switch=None):
        """Default interface.

        """
        if switch is None:
            switch = self.format_and_eval_string(self.switch)

        if switch == 'ON':
            self.driver.output = 'ON'
            self.write_in_database('output', 'ON')

        if switch == 'OFF':
            self.driver.output = 'OFF'
            self.write_in_database('output', 'OFF')
示例#6
0
class RunAWGTask(InstrumentTask):
    """ Task to set AWG run mode

    """
    #: Switch to choose the AWG run mode: on or off
    switch = Str('Off').tag(pref=True, feval=validators.SkipLoop())
    database_entries = set_default({'output': 0})

    def perform(self, switch=None):
        """Default interface behavior.

        """
        if switch is None:
            switch = self.format_and_eval_string(self.switch)

        if switch == 'On' or switch == 1:
            self.driver.running = 1
            self.write_in_database('output', 1)
        else:
            self.driver.running = 0
            self.write_in_database('output', 0)
        log = logging.getLogger(__name__)
        msg = 'AWG running state OK'
        log.debug(msg)
示例#7
0
"""
import numbers

from atom.api import (Unicode, Bool, set_default, Enum)

from exopy.tasks.api import (InstrumentTask, InterfaceableTaskMixin,
                            validators)

CONVERSION_FACTORS = {'GHz': {'Hz': 1e9, 'kHz': 1e6, 'MHz': 1e3, 'GHz': 1},
                      'MHz': {'Hz': 1e6, 'kHz': 1e3, 'MHz': 1, 'GHz': 1e-3},
                      'kHz': {'Hz': 1e3, 'kHz': 1, 'MHz': 1e-3, 'GHz': 1e-6},
                      'Hz': {'Hz': 1, 'kHz': 1e-3, 'MHz': 1e-6, 'GHz': 1e-9}}


LOOP_REAL = validators.SkipLoop(types=numbers.Real)


class SetRFFrequencyTask(InterfaceableTaskMixin, InstrumentTask):
    """Set the frequency of the signal delivered by a RF source.

    """
    # Target frequency (dynamically evaluated)
    frequency = Unicode().tag(pref=True, feval=LOOP_REAL)

    # Unit of the frequency
    unit = Enum('GHz', 'MHz', 'kHz', 'Hz').tag(pref=True)

    # Whether to start the source if its output is off.
    auto_start = Bool(False).tag(pref=True)
示例#8
0
class SetDCVoltageTask(InterfaceableTaskMixin, InstrumentTask):
    """Set a DC voltage to the specified value.

    The user can choose to limit the rate by choosing an appropriate back step
    (larger step allowed), and a waiting time between each step.

    """
    #: Target value for the source (dynamically evaluated)
    target_value = Str().tag(pref=True,
                             feval=validators.SkipLoop(types=numbers.Real))

    #: Largest allowed step when changing the output of the instr.
    back_step = Float().tag(pref=True)

    #: Largest allowed voltage
    safe_max = Float(0.0).tag(pref=True)

    #: Largest allowed delta compared to current voltage. 0 = ignored
    safe_delta = Float(0.0).tag(pref=True)

    #: Time to wait between changes of the output of the instr.
    delay = Float(0.01).tag(pref=True)

    parallel = set_default({'activated': True, 'pool': 'instr'})
    database_entries = set_default({'voltage': 0.01})

    def i_perform(self, value=None):
        """Default interface.

        """
        if self.driver.owner != self.name:
            self.driver.owner = self.name
            if hasattr(self.driver, 'function') and\
                    self.driver.function != 'VOLT':
                msg = ('Instrument assigned to task {} is not configured to '
                       'output a voltage')
                raise ValueError(msg.format(self.name))

        setter = lambda value: setattr(self.driver, 'voltage', value)
        current_value = getattr(self.driver, 'voltage')

        self.smooth_set(value, setter, current_value)

    def smooth_set(self, target_value, setter, current_value):
        """ Smoothly set the voltage.

        target_value : float
            Voltage to reach.

        setter : callable
            Function to set the voltage, should take as single argument the
            value.

        current_value: float
            Current voltage.

        """
        if target_value is not None:
            value = target_value
        else:
            value = self.format_and_eval_string(self.target_value)

        if self.safe_delta and abs(current_value - value) > self.safe_delta:
            msg = (
                'Requested voltage {} is too far away from the current voltage {}!'
            )
            raise ValueError(msg.format(value, current_value))

        if self.safe_max and self.safe_max < abs(value):
            msg = 'Requested voltage {} exceeds safe max : {}'
            raise ValueError(msg.format(value, self.safe_max))

        last_value = current_value

        if abs(last_value - value) < 1e-12:
            self.write_in_database('voltage', value)
            return

        elif self.back_step == 0:
            self.write_in_database('voltage', value)
            setter(value)
            return

        else:
            if (value - last_value) / self.back_step > 0:
                step = self.back_step
            else:
                step = -self.back_step

        if abs(value - last_value) > abs(step):
            while not self.root.should_stop.is_set():
                # Avoid the accumulation of rounding errors
                last_value = round(last_value + step, 9)
                setter(last_value)
                if abs(value - last_value) > abs(step):
                    time.sleep(self.delay)
                else:
                    break

        if not self.root.should_stop.is_set():
            setter(value)
            self.write_in_database('voltage', value)
            return

        self.write_in_database('voltage', last_value)
示例#9
0
class TuneIQMixerTask(InstrumentTask):
    """ Task to tune an IQ mixer in SSB
        Implicit use of a SignalHound spectrum analyzer
        Tunes channels I and Q DC offset, relative delay and voltage
        to suppress LO leakage and unwanted sideband
        TODO: handle task with two instruments: AWG AND Spectrum analyzer
        TODO: implement realtime sweep for better SNR

    """

    # Get user inputs
    channelI = Enum('Ch1', 'Ch2', 'Ch3', 'Ch4').tag(pref=True)
    channelQ = Enum('Ch1', 'Ch2', 'Ch3', 'Ch4').tag(pref=True)

    # LO frequency
    freq = Str('0.0').tag(pref=True,
                              feval=validators.SkipLoop(types=numbers.Real))
    # modulation frequency
    det = Str('0.0').tag(pref=True,
                             feval=validators.SkipLoop(types=numbers.Real))
    # LO power frequency
    power = Str('0.0').tag(pref=True,
                             feval=validators.SkipLoop(types=numbers.Real))
    # Desired sideband, e.g. if Lower, suppress freq and freq+det
    SB = Enum('Lower', 'Upper').tag(pref=True)

    my_sa = Value()  # signal analyzer
    chI = Value()
    chQ = Value()
    freq_Hz = Value()
    det_Hz = Value()
    SB_sgn = Value()
    LO_pow = Value()

    def check(self, *args, **kwargs):
        ''' Default checks and check different AWG channels
        '''
        test, traceback = super(TuneIQMixerTask, self).check(*args, **kwargs)
        if not test:
            return test, traceback

        if self.channelI == self.channelQ:
            test = False
            msg = 'I and Q channels need to be different !'
            traceback[self.get_error_path()] = msg
        return test, traceback

    def perform(self):
        """Default interface behavior.

        """
        # open signal analyzer Rhode&Schwarz
        visa_address = 'TCPIP0::192.168.0.52::inst0::INSTR'
        connection_infos = {'resource_name': visa_address}
        self.my_sa = sa.RohdeAndSchwarzPSA(connection_infos)#, mode=SA_SWEEPING)

        # AWG channels
        awg = self.driver
        awg.run_mode = 'CONT'
        awg.output_mode = 'FIX'
        self.chI = awg.get_channel(int(self.channelI[-1]))
        self.chQ = awg.get_channel(int(self.channelQ[-1]))

        # convert user inputs into adequate units
        self.LO_pow = self.format_and_eval_string(self.power)
        self.freq_Hz = self.format_and_eval_string(self.freq)*1e9
        self.det_Hz = self.format_and_eval_string(self.det)*1e6 #modulation freq
        self.SB_sgn = 1 if self.SB == 'Lower' else -1
        
        # setting the modulation frequency for each channel
        self.chI.set_frequency(self.det_Hz)
        self.chQ.set_frequency(self.det_Hz)

        # Initialize AWG params          
        # set parameters to minima or centers everywhere
        # initialisation
        self.chI_vpp(0.15)
        self.chQ_vpp(0.15)
        self.chI_offset(0)
        self.chQ_offset(0)
        self.chQ_delay(0)
        
        # perform optimization twice
        self.tune_ssb('lo')
        self.tune_ssb('sb')
        pos_lo, cost = self.tune_ssb('lo')
        pos_sb, cost = self.tune_ssb('sb')

        # get power for optimal parameters at sig, leakage and sideband
        # get_single_freq(self,freq,reflevel,rbw,vbw,avrg_num)
        sig = self.my_sa.get_single_freq(self.freq_Hz-self.SB_sgn*self.det_Hz,
                                         self.LO_pow,int(1e3),int(1e3),10)
        lo = self.my_sa.get_single_freq(self.freq_Hz,self.LO_pow,1e3,1e3,10)
        sb = self.my_sa.get_single_freq(self.freq_Hz+self.SB_sgn*self.det_Hz,
                                         self.LO_pow,int(1e3),int(1e3),10)

        # close signal analyzer
        self.my_sa._close()

        # log values
        log = logging.getLogger(__name__)
        msg1 = 'Tuned IQ mixer at LO = %s GHz, IF = %s MHz, \
                Signal: %s dBm, LO: %s dBm, SB: %s dBm' % \
               (1e-9*self.freq_Hz, 1e-6*self.det_Hz, sig, lo, sb)
        log.info(msg1)
        msg2 = 'chI offset: %s V, chQ offset: %s V, chQvpp: %s V, \
                chQphase: %s °' % \
               (pos_lo[0], pos_lo[1], pos_sb[0], pos_sb[1])
        log.info(msg2)

    # optimization procedure
    def tune_ssb(self, mode):
        # suppress lo leakage params
        if mode == 'lo':
            param1 = self.chI_offset
            param2 = self.chQ_offset
            f = self.freq_Hz
            minvals = np.array([-1,-1])
            maxvals = np.array([1,1])
            precision = np.array([0.001,0.001])
            pos0 = np.array([self.get_chI_offset(), self.get_chQ_offset()])

        # suppress other sideband params
        elif mode == 'sb':
            param1 = self.chQ_vpp
            param2 = self.chQ_delay
            f = self.freq_Hz + self.SB_sgn*self.det_Hz
            minvals = np.array([0.05,0])
            maxvals = np.array([0.15,360])
            precision = np.array([0.01,0.1])
            pos0 = np.array([self.get_chQ_vpp(), self.get_chQ_delay()])
            
        else:
            msg = '''param has wrong value, should be lo or sb,
                     received %s''' % mode
            raise ValueError(msg)

        # 4 directions in parameter search space
        sens = [np.array([1, 0]), np.array([0, 1]),
                np.array([-1, 0]), np.array([0, -1])]

        # initial cost (cost = power of sa at f)
        cost0 = self.cost(param1, param2, pos0[0], pos0[1], f)
        
        ### Qcircuits STARTS HERE ###
        dec = 0.1
        s = 0
        c = 0
        counter = 0
        poslist = [pos0]
        precision_reached = False
        
        # stop search when step_size < AWG resolution
        while dec >= 0.0001:
            step_sizes = dec*(maxvals-minvals)
            
            #check that we aren't lower than instrument precision
            if ((step_sizes[0] - precision[0]) < 0) and \
            ((step_sizes[1] - precision[1]) < 0):
                precision_reached = True
                step_sizes = precision
            elif (step_sizes[0] - precision[0]) < 0:
                step_sizes[0] = precision[0]
            elif (step_sizes[1] - precision[1]) < 0:
                step_sizes[1] = precision[1]
            
            # break when max eval count has reach or
            # all 4 directions have been explored
            while c < 4 and counter < 1000:
                # probe cost at new pos: pos1
                pos1 = pos0 + step_sizes*sens[s]
                
                # check that we aren't out of bounds
                if (not (minvals[0] <= pos1[0] <= maxvals[0])) and \
                (not (minvals[1] <= pos1[1] <= maxvals[1])):
                    boundaries = np.array([minvals,
                                           np.array([minvals[0],maxvals[1]]),
                                           np.array([minvals[1],maxvals[0]]),
                                           maxvals])
                    # find bounardy closest to current value
                    pos1 = boundaries[np.argmin(list(map(abs,
                                                         boundaries-pos1)))]
            
                elif not (minvals[0] <= pos1[0] <= maxvals[0]):
                    boundaries = np.array([minvals[0],maxvals[0]])
                    # find bounardy closest to current value
                    pos1[0] = boundaries[np.argmin(list(map(abs,
                                                         boundaries-pos1[0])))]
                
                elif not (minvals[1] <= pos1[1] <= maxvals[1]):
                    boundaries = np.array([minvals[1],maxvals[1]])
                    # find bounardy closest to current value
                    pos1[1] = boundaries[np.argmin(list(map(abs,
                                                         boundaries-pos1[1])))]
                
                # evaluate cost of new position
                cost1 = self.cost(param1, param2, pos1[0], pos1[1], f)
                counter += 1
                
                # if lower cost, update pos
                if cost1 < cost0:
                    cost0 = cost1
                    pos0 = pos1
                    c = 0
                    poslist.append(pos0)
                else:
                    c += 1
                    s = np.mod(s+1, 4)
            c = 0
            # decrease dec if all explored directions give higher cost
            dec /= 10
            if precision_reached:
                break
        return pos0, cost0
        
    # optimization cost function: get power in dBm at f from signal_analyzer
    def cost(self, param1, param2, val1, val2, f):
        param1(val1)
        param2(val2)
        return self.my_sa.get_single_freq(f,self.LO_pow,1e3,1e3,10)

    # define AWG getter and setter functions to pass into cost function
    def chI_offset(self, value):
        self.chI.set_DC_offset(value)

    def chQ_offset(self, value):
        self.chQ.set_DC_offset(value)

    def get_chI_offset(self):
        return self.chI.DC_offset()

    def get_chQ_offset(self):
        return self.chQ.DC_offset()

    def chI_vpp(self, value):
        self.chI.set_Vpp(value)

    def chQ_vpp(self, value):
        self.chQ.set_Vpp(value)

    def get_chI_vpp(self):
        return self.chI.Vpp()

    def get_chQ_vpp(self):
        return self.chQ.Vpp()

    def chQ_delay(self, value):
        self.chQ.set_phase(value)

    def get_chQ_delay(self):
        return self.chQ.phase()
示例#10
0
class ApplyMagFieldTask(InstrumentTask):
    """Use a supraconducting magnet to apply a magnetic field. Parallel task.

    """
    # Target magnetic field (dynamically evaluated)
    field = Unicode().tag(pref=True,
                          feval=validators.SkipLoop(types=numbers.Real))

    # Rate at which to sweep the field.
    rate = Float(0.01).tag(pref=True)

    # Whether to stop the switch heater after setting the field.
    auto_stop_heater = Bool(True).tag(pref=True)

    # Time to wait before bringing the field to zero after closing the switch
    # heater.
    post_switch_wait = Float(30.0).tag(pref=True)

    parallel = set_default({'activated': True, 'pool': 'instr'})
    database_entries = set_default({'field': 0.01})

    def check_for_interruption(self):
        """Check if the user required an interruption.

        """
        return self.root.should_stop.is_set()

    def perform(self, target_value=None):
        """Apply the specified magnetic field.

        """
        # make ready
        if (self.driver.owner != self.name
                or not self.driver.check_connection()):
            self.driver.owner = self.name

        if target_value is None:
            target_value = self.format_and_eval_string(self.field)

        driver = self.driver
        normal_end = True
        if (abs(driver.read_persistent_field() - target_value) >
                driver.output_fluctuations):
            job = driver.sweep_to_persistent_field()
            if job.wait_for_completion(self.check_for_interruption,
                                       timeout=60,
                                       refresh_time=1):
                driver.heater_state = 'On'
                sleep(self.post_switch_wait)
            else:
                return False

            # set the magnetic field
            job = driver.sweep_to_field(target_value, self.rate)
            normal_end = job.wait_for_completion(self.check_for_interruption,
                                                 timeout=60,
                                                 refresh_time=10)

        # Always close the switch heater when the ramp was interrupted.
        if not normal_end:
            job.cancel()
            driver.heater_state = 'Off'
            sleep(self.post_switch_wait)
            self.write_in_database('field', driver.read_persistent_field())
            return False

        # turn off heater
        if self.auto_stop_heater:
            driver.heater_state = 'Off'
            sleep(self.post_switch_wait)
            job = driver.sweep_to_field(0)
            job.wait_for_completion(self.check_for_interruption,
                                    timeout=60,
                                    refresh_time=1)

        self.write_in_database('field', target_value)