Пример #1
0
class IVExperiment(Experiment):

    awg = Agilent33220A("192.168.5.198")

    amplitude = FloatParameter(default=0.1, unit="V")
    frequency  = 167.0 # FloatParameter(default=167.0, unit="Hz")

    sample_rate = 5e5
    num_bursts  = 10

    preamp_gain = 1
    r_ref       = 1e3

    current_input  = OutputConnector(unit="V")
    voltage_sample = OutputConnector(unit="V")

    def init_streams(self):
        descrip = DataStreamDescriptor()
        descrip.data_name='current_input'
        descrip.add_axis(DataAxis("time", np.arange(int(self.sample_rate*self.num_bursts/self.frequency))/self.sample_rate))
        self.current_input.set_descriptor(descrip)

        descrip = DataStreamDescriptor()
        descrip.data_name='voltage_sample'
        descrip.add_axis(DataAxis("time", np.arange(int(self.sample_rate*self.num_bursts/self.frequency))/self.sample_rate))
        self.voltage_sample.set_descriptor(descrip)

    def init_instruments(self):
        # Configure the AWG
        self.awg.output         = False
        self.awg.function       = 'Sine'
        self.awg.load_resistance = self.r_ref
        self.awg.auto_range     = True
        self.awg.amplitude      = self.amplitude.value # Preset to avoid danger
        self.awg.dc_offset      = 0.0
        self.awg.frequency      = self.frequency
        self.awg.burst_state    = True
        self.awg.burst_cycles   = self.num_bursts + 2
        self.awg.trigger_source = "Bus"
        self.awg.output         = True

        self.amplitude.assign_method(self.awg.set_amplitude)
        # self.frequency.assign_method(self.awg.set_frequency)

        # Setup the NIDAQ
        max_voltage = 2.0 #self.amplitude.value*2.0
        self.num_samples_total = int(self.sample_rate*(self.num_bursts+2)/self.frequency)
        self.num_samples_trimmed = int(self.sample_rate*(self.num_bursts)/self.frequency)
        self.trim_len = int(self.sample_rate/self.frequency)
        self.analog_input = Task()
        self.read = int32()
        self.analog_input.CreateAIVoltageChan("Dev1/ai0", "", DAQmx_Val_Diff,
            -max_voltage, max_voltage, DAQmx_Val_Volts, None)
        self.analog_input.CreateAIVoltageChan("Dev1/ai1", "", DAQmx_Val_Diff,
            -max_voltage, max_voltage, DAQmx_Val_Volts, None)
        self.analog_input.CfgSampClkTiming("", self.sample_rate, DAQmx_Val_Rising,
            DAQmx_Val_FiniteSamps , self.num_samples_total)
        self.analog_input.CfgInputBuffer(2*self.num_samples_total)
        self.analog_input.CfgDigEdgeStartTrig("/Dev1/PFI0", DAQmx_Val_Rising)
        self.analog_input.StartTask()
        # self.analog_input.SetStartTrigRetriggerable(1)

    def shutdown_instruments(self):
        self.awg.output     = False
        # self.awg.auto_range = True
        try:
            self.analog_input.StopTask()
            self.analog_input.ClearTask()
        except Exception as e:
            logger.warning("Failed to clear DAQ task!")

    async def run(self):
        """This is run for each step in a sweep."""

        self.awg.trigger()

        buf = np.empty(2*self.num_samples_total)
        self.analog_input.ReadAnalogF64(self.num_samples_total, -1, DAQmx_Val_GroupByChannel,
                                        buf, 2*self.num_samples_total, byref(self.read), None)
        await self.current_input.push(buf[self.num_samples_total+self.trim_len:self.num_samples_total+self.trim_len+self.num_samples_trimmed]/self.r_ref)
        await self.voltage_sample.push(buf[self.trim_len:self.trim_len+self.num_samples_trimmed]/self.preamp_gain)
        await asyncio.sleep(0.02)
Пример #2
0
class nTronSwitchingExperiment(Experiment):

    # Parameters
    channel_bias = FloatParameter(default=0.05, unit="V")  # On the 33220A
    gate_bias = FloatParameter(default=0.0, unit="V")  # On the M8190A

    # Constants (set with attribute access if you want to change these!)
    attempts = 1 << 8
    samples = 384  #1024 + 16*20
    measure_amplitude = 0.1
    measure_duration = 250.0e-9
    measure_frequency = 100e6

    # arb
    sample_rate = 12e9
    repeat_time = 4 * 2.4e-6  # Picked very carefully for 100ns alignment

    # Things coming back
    voltage = OutputConnector()

    # Instrument resources
    arb = KeysightM8190A("192.168.5.108")
    awg = Agilent33220A("192.168.5.198")
    alz = AlazarATS9870("1")

    def __init__(self, gate_amps, gate_durs):
        self.gate_amps = gate_amps
        self.gate_durs = gate_durs
        super(nTronSwitchingExperiment, self).__init__()

    def init_instruments(self):

        self.awg.function = 'Pulse'
        self.awg.frequency = 0.5e6
        self.awg.pulse_width = 1e-6
        self.awg.low_voltage = 0.0
        self.awg.high_voltage = self.channel_bias.value
        self.awg.burst_state = True
        self.awg.burst_cycles = 1
        self.awg.trigger_source = "External"
        self.awg.output = True

        self.ch = AlazarChannel({'channel': 1})
        self.alz.add_channel(self.ch)
        alz_cfg = {
            'acquire_mode': 'digitizer',
            'bandwidth': 'Full',
            'clock_type': 'ref',
            'delay': 850e-9,
            'enabled': True,
            'label': "Alazar",
            'record_length': self.samples,
            'nbr_segments': len(self.gate_amps) * len(self.gate_durs),
            'nbr_waveforms': 1,
            'nbr_round_robins': self.attempts,
            'sampling_rate': 1e9,
            'trigger_coupling': 'DC',
            'trigger_level': 125,
            'trigger_slope': 'rising',
            'trigger_source': 'Ext',
            'vertical_coupling': 'AC',
            'vertical_offset': 0.0,
            'vertical_scale': 0.1,
        }
        self.alz.set_all(alz_cfg)
        self.loop.add_reader(self.alz.get_socket(self.ch),
                             self.alz.receive_data, self.ch, self.voltage)

        self.arb.set_output(True, channel=2)
        self.arb.set_output(True, channel=1)
        self.arb.sample_freq = self.sample_rate
        self.arb.set_waveform_output_mode("WSPEED", channel=1)
        self.arb.set_waveform_output_mode("WSPEED", channel=2)
        self.arb.set_output_route("DC", channel=1)
        self.arb.set_output_route("DC", channel=2)
        self.arb.set_output_complement(False, channel=1)
        self.arb.set_output_complement(False, channel=2)
        self.arb.set_voltage_amplitude(1.0, channel=1)
        self.arb.set_voltage_amplitude(1.0, channel=2)
        self.arb.continuous_mode = False
        self.arb.gate_mode = False
        self.arb.set_marker_level_low(0.0, channel=1, marker_type="sync")
        self.arb.set_marker_level_high(1.5, channel=1, marker_type="sync")
        self.arb.set_marker_level_low(0.0, channel=2, marker_type="sync")
        self.arb.set_marker_level_high(1.5, channel=2, marker_type="sync")

        self.setup_arb(
        )  #self.gate_bias.value, self.gate_pulse_amplitude.value, self.gate_pulse_duration.value) # Sequencing goes here

    def setup_arb(
            self):  #, gate_bias, gate_pulse_amplitude, gate_pulse_duration):
        self.arb.abort()
        self.arb.delete_all_waveforms()
        self.arb.reset_sequence_table()

        seg_ids_ch1 = []
        seg_ids_ch2 = []

        # For the measurements pulses along the channel
        wf = measure_pulse(amplitude=self.measure_amplitude,
                           duration=self.measure_duration,
                           frequency=self.measure_frequency)
        wf_data = KeysightM8190A.create_binary_wf_data(wf, sync_mkr=1)
        seg_id = self.arb.define_waveform(len(wf_data), channel=1)
        self.arb.upload_waveform(wf_data, seg_id, channel=1)
        seg_ids_ch1.append(seg_id)

        # Build in a delay between sequences
        settle_pts = 640 * np.int(
            np.ceil(self.repeat_time * self.sample_rate / 640))
        # settle_pts2 = 640*np.ceil(8*2.4e-9 * self.sample_rate / 640)

        scenario = Scenario()
        seq = Sequence(sequence_loop_ct=self.attempts * len(self.gate_amps) *
                       len(self.gate_durs))
        for si in seg_ids_ch1:
            seq.add_waveform(si)
            seq.add_idle(settle_pts, 0.0)
        scenario.sequences.append(seq)
        self.arb.upload_scenario(scenario, start_idx=0, channel=1)

        for amp in self.gate_amps:
            for dur in self.gate_durs:
                # For the switching pulses along the gate
                wf = switching_pulse(
                    amplitude=amp,
                    duration=dur)  #self.gate_pulse_duration.value)
                wf_data = KeysightM8190A.create_binary_wf_data(wf)
                seg_id = self.arb.define_waveform(len(wf_data), channel=2)
                self.arb.upload_waveform(wf_data, seg_id, channel=2)
                seg_ids_ch2.append(seg_id)

        scenario = Scenario()
        seq = Sequence(sequence_loop_ct=self.attempts)
        for si in seg_ids_ch2:
            seq.add_waveform(si)
            seq.add_idle(settle_pts, 0.0)
        scenario.sequences.append(seq)
        self.arb.upload_scenario(scenario, start_idx=0, channel=2)

        self.arb.set_sequence_mode("SCENARIO", channel=1)
        self.arb.set_scenario_advance_mode("SINGLE", channel=1)
        self.arb.set_scenario_start_index(0, channel=1)
        self.arb.set_sequence_mode("SCENARIO", channel=2)
        self.arb.set_scenario_advance_mode("SINGLE", channel=2)
        self.arb.set_scenario_start_index(0, channel=2)
        self.arb.initiate(channel=1)
        self.arb.initiate(channel=2)
        self.arb.advance()

    def init_streams(self):
        # Baked in data axes
        descrip = DataStreamDescriptor()
        descrip.add_axis(DataAxis("time", 1e-9 * np.arange(self.samples)))
        if len(self.gate_durs) > 1:
            descrip.add_axis(DataAxis("gate_pulse_duration", self.gate_durs))
        descrip.add_axis(DataAxis("gate_pulse_amplitude", self.gate_amps))
        descrip.add_axis(DataAxis("attempt", range(self.attempts)))

        self.voltage.set_descriptor(descrip)

    async def run(self):
        # self.arb.stop()
        self.arb.set_scenario_start_index(0, channel=1)
        self.arb.set_scenario_start_index(0, channel=2)
        self.arb.advance()
        await asyncio.sleep(0.3)
        self.alz.acquire()
        await asyncio.sleep(0.3)
        self.arb.trigger()
        await self.alz.wait_for_acquisition(10.0)
        await asyncio.sleep(0.8)
        self.alz.stop()
        # Seemingly we need to give the filters some time to catch up here...
        await asyncio.sleep(0.02)
        logger.info("Stream has filled {} of {} points".format(
            self.voltage.points_taken, self.voltage.num_points()))

    def shutdown_instruments(self):
        self.awg.output = False

        self.arb.stop()
        self.loop.remove_reader(self.alz.get_socket(self.ch))

        for name, instr in self._instruments.items():
            instr.disconnect()
Пример #3
0
class ShortProcessSwitchingExperiment(Experiment):

    # Parameters and outputs
    pulse_duration = FloatParameter(default=5.0e-9, unit="s")
    pulse_voltage  = FloatParameter(default=1, unit="V")
    bias_current   = FloatParameter(default=60e-6, unit="A")
    field          = FloatParameter(default=0.0, unit="T")
    voltage_chan   = OutputConnector()
    #voltage_MTJ    = OutputConnector()
    resistance_MTJ = OutputConnector()

    # Constants (set with attribute access if you want to change these!)
    attempts           = 1 << 4

    # MTJ measurements
    measure_current = 3.0e-6

    # Reference resistances and attenuations
    matching_ref_res = 50
    chan_bias_ref_res = 1e4
    pspl_base_attenuation = 10
    circuit_attenuation = 0

    # Min/Max NIDAQ AI Voltage
    min_daq_voltage = -1
    max_daq_voltage = 1

    # Measurement Sequence timing, clocks in Hz times in seconds
    ai_clock = 0.25e6
    do_clock = 1e5
    run_time = 2e-4
    settle_time = 0.5*run_time
    integration_time = 0.1*run_time
    pspl_delay = 0.25*0.5*run_time

    # Instrument resources, NIDAQ called via PyDAQmx
    arb_cb = Agilent33220A("192.168.5.199") # Channel Bias
    pspl  = Picosecond10070A("GPIB0::24::INSTR") # Gate Pulse
    atten = RFMDAttenuator("calibration/RFSA2113SB_HPD_20160901.csv") # Gate Amplitude control
    lock  = SR865("USB0::0xB506::0x2000::002638::INSTR") # Gate Amplitude control
    keith = Keithley2400("GPIB0::25::INSTR")
    mag   = AMI430("192.168.5.109")

    def init_instruments(self):
        # Channel bias arb
        self.arb_cb.output          = False
        self.arb_cb.load_resistance = (self.chan_bias_ref_res+self.matching_ref_res)
        self.arb_cb.function        = 'Pulse'
        self.arb_cb.pulse_period    = 0.99*self.run_time
        self.arb_cb.pulse_width     = self.run_time - self.settle_time
        self.arb_cb.pulse_edge      = 100e-9
        self.arb_cb.low_voltage     = 0.0
        self.arb_cb.high_voltage    = self.bias_current.value*(self.chan_bias_ref_res+self.matching_ref_res)
        self.arb_cb.polarity        = 1
        self.arb_cb.burst_state     = True
        self.arb_cb.burst_cycles    = 1
        self.arb_cb.trigger_source  = "External"
        self.arb_cb.burst_mode      = "Triggered"
        self.arb_cb.output          = True

        # Setup the Keithley
        self.keith.triad()
        self.keith.conf_meas_res(res_range=1e5)
        self.keith.conf_src_curr(comp_voltage=0.5, curr_range=1.0e-5)
        self.keith.current = self.measure_current

        # Setup the magnet
        self.mag.ramp()

        # Setup the NIDAQ tasks
        DAQmxResetDevice("Dev1")
        self.nidaq = CallbackTask(["Dev1/ai0"], asyncio.get_event_loop(), int(self.integration_time*self.ai_clock), self.attempts, [self.voltage_chan],
                                 min_voltage=self.min_daq_voltage, max_voltage=self.max_daq_voltage, ai_clock=self.ai_clock)
        self.nidaq.StartTask()


        # Setup DO Triggers, P1:0 triggers AWG, P1:1 triggers PSPL and DAQ analog input
        self.digital_output = Task()
        data_p0 = np.zeros(int(self.do_clock*self.run_time),dtype=np.uint8)
        data_p0[0]=1

        data_p1 = np.zeros(int(self.do_clock*self.run_time),dtype=np.uint8)
        data_p1[int(self.do_clock*(self.pspl_delay))]=1 # PSPL delay

        data = np.hstack([data_p0,data_p1])

        self.digital_output.CreateDOChan("/Dev1/port0/line0:1","",DAQmx_Val_ChanPerLine)
        self.digital_output.CfgSampClkTiming("",self.do_clock, DAQmx_Val_Rising, DAQmx_Val_FiniteSamps, int(data.size/2)*self.attempts)
        self.digital_output.WriteDigitalLines(int(self.do_clock*self.run_time),0,1,DAQmx_Val_GroupByChannel,data,None,None)

        # Setup the PSPL
        self.pspl.amplitude = 7.5*np.power(10, (-self.pspl_base_attenuation)/20.0)
        self.pspl.trigger_source = "EXT"
        self.pspl.trigger_level = 0.5
        self.pspl.output = True

        # Setup PSPL Attenuator Control
        self.atten.set_supply_method(self.lock.set_ao2)
        self.atten.set_control_method(self.lock.set_ao3)

        def set_pulse_voltage(voltage, base_atten=self.pspl_base_attenuation):
            # Calculate the voltage controller attenuator setting
            amplitude = 7.5*np.power(10, -base_atten/20.0)
            vc_atten = abs(20.0 * np.log10(abs(voltage)/7.5)) - base_atten - self.circuit_attenuation

            if vc_atten <= self.atten.minimum_atten():
                base_atten -= 1
                if base_atten < 0:
                    logger.error("Voltage controlled attenuation {} under range, PSPL at Max. Decrease circuit attenuation.".format(vc_atten))
                    raise ValueError("Voltage controlled attenuation {} under range, PSPL at Max. Decrease circuit attenuation.".format(vc_atten))
                set_pulse_voltage(voltage, base_atten=base_atten)
                return

            if self.atten.maximum_atten() < vc_atten:
                base_atten += 1
                if base_atten > 80:
                    logger.error("Voltage controlled attenuation {} over range, PSPL at Min. Increase circuit attenuation.".format(vc_atten))
                    raise ValueError("Voltage controlled attenuation {} over range, PSPL at Min. Increase circuit attenuation.".format(vc_atten))
                set_pulse_voltage(voltage, base_atten=base_atten)
                return

            #print("PSPL Amplitude: {}, Attenuator: {}".format(amplitude,vc_atten))
            self.atten.set_attenuation(vc_atten)
            self.pspl.amplitude = self.arb_cb.polarity*abs(amplitude)
            time.sleep(0.04)

        def set_awg_current(current):

            if 0 <= current:
                self.arb_cb.polarity = 1
                self.arb_cb.low_voltage = 0
                self.arb_cb.high_voltage = current*self.chan_bias_ref_res
            else :
                self.arb_cb.polarity = -1
                self.arb_cb.low_voltage = current*self.chan_bias_ref_res
                self.arb_cb.high_voltage = 0

            self.pspl.amplitude = self.arb_cb.polarity*abs(self.pspl.amplitude)

            time.sleep(0.04)

        # Assign Methods
        self.bias_current.assign_method(set_awg_current)
        self.pulse_duration.add_post_push_hook(lambda: time.sleep(0.1))
        self.pulse_duration.assign_method(self.pspl.set_duration)
        self.pulse_voltage.assign_method(set_pulse_voltage)
        self.field.assign_method(self.mag.set_field)

    def init_streams(self):
        # Baked in data axes
        descrip = DataStreamDescriptor()
        descrip.data_name='voltage'
        descrip.add_axis(DataAxis("sample", range(int(self.integration_time*self.ai_clock))))
        descrip.add_axis(DataAxis("attempt", range(self.attempts)))
        self.voltage_chan.set_descriptor(descrip)

        # descrip = DataStreamDescriptor()
        # descrip.data_name='voltage'
        # descrip.add_axis(DataAxis("sample", range(int(self.integration_time*self.ai_clock))))
        # descrip.add_axis(DataAxis("attempt", range(self.attempts)))
        # self.voltage_MTJ.set_descriptor(descrip)

        descrip = DataStreamDescriptor()
        descrip.data_name='resistance'
        self.resistance_MTJ.set_descriptor(descrip)

    async def run(self):
        self.digital_output.StartTask()
        self.digital_output.WaitUntilTaskDone(2*self.attempts*self.run_time)
        self.digital_output.StopTask()
        await self.resistance_MTJ.push(self.keith.resistance)
        await asyncio.sleep(0.05)
        # print("\t now ", self.nidaq.points_read)
        # logger.debug("Stream has filled {} of {} points".format(self.voltage.points_taken, self.voltage.num_points() ))

    def shutdown_instruments(self):
        self.keith.current = 0.0e-5
        self.mag.zero()
        try:
            for ch in [self.nidaq, self.nidaq_MTJ]:
                ch.StopTask()
                ch.ClearTask()
                self.digital_output.StopTask()
                del ch
        except Exception as e:
            print("Warning: failed to stop task (this normally happens with no consequences when taking multiple samples per trigger).")
            pass
        self.arb_cb.output = False
        self.pspl.output = False
        for name, instr in self._instruments.items():
            instr.disconnect()
Пример #4
0
class nTronSwitchingExperimentFast(Experiment):

    # Parameters and outputs
    # channel_bias   = FloatParameter(default=100e-6,  unit="A") # On the 33220A
    pulse_duration = FloatParameter(default=5.0e-9, unit="s")
    pulse_voltage = FloatParameter(default=1, unit="V")
    channel_bias = FloatParameter(default=500e-6, unit="A")
    voltage = OutputConnector()

    # Constants (set with attribute access if you want to change these!)
    attempts = 1 << 6

    # Reference resistances and attenuations
    matching_ref_res = 50
    chan_bias_ref_res = 1e4
    pspl_base_attenuation = 10
    circuit_attenuation = 0
    pulse_polarity = 1

    # Min/Max NIDAQ AI Voltage
    min_daq_voltage = 0
    max_daq_voltage = 10

    # Measurement Sequence timing, clocks in Hz times in seconds
    ai_clock = 0.25e6
    do_clock = 0.5e6
    trig_interval = 0.2e-3  # The repetition rate for switching attempts
    bias_pulse_width = 40e-6  # This is how long the bias pulse is
    pspl_trig_time = 4e-6  # When we trigger the gate pulse
    integration_time = 20e-6  # How long to measure for
    ai_delay = 2e-6  # How long before the measurement begins

    # Instrument resources, NIDAQ called via PyDAQmx
    arb_cb = Agilent33220A("192.168.5.199")  # Channel Bias
    pspl = Picosecond10070A("GPIB0::24::INSTR")  # Gate Pulse
    atten = RFMDAttenuator(
        "calibration/RFSA2113SB_HPD_20160901.csv")  # Gate Amplitude control
    lock = SR865(
        "USB0::0xB506::0x2000::002638::INSTR")  # Gate Amplitude control

    def init_instruments(self):
        # Channel bias arb
        self.arb_cb.output = False
        self.arb_cb.load_resistance = (self.chan_bias_ref_res +
                                       self.matching_ref_res)
        self.arb_cb.function = 'Pulse'
        self.arb_cb.pulse_period = 0.99 * self.trig_interval  # Slightly under the trig interval since we are driving with another instrument
        self.arb_cb.pulse_width = self.bias_pulse_width
        self.arb_cb.pulse_edge = 100e-9  # Going through the DC port of a bias-tee, no need for fast edges
        self.arb_cb.low_voltage = 0.0
        self.arb_cb.high_voltage = self.channel_bias.value * (
            self.chan_bias_ref_res + self.matching_ref_res)
        self.arb_cb.burst_state = True
        self.arb_cb.burst_cycles = 1
        self.arb_cb.trigger_source = "External"
        self.arb_cb.burst_mode = "Triggered"
        self.arb_cb.output = True
        self.arb_cb.polarity = 1

        # Setup the NIDAQ
        DAQmxResetDevice("Dev1")
        measure_points = int(self.integration_time * self.ai_clock)
        self.nidaq = CallbackTask(
            asyncio.get_event_loop(),
            measure_points,
            self.attempts,
            self.voltage,
            #  chunk_size=measure_points*(self.attempts>>2),
            min_voltage=self.min_daq_voltage,
            max_voltage=self.max_daq_voltage,
            ai_clock=self.ai_clock)
        self.nidaq.StartTask()

        # Setup DO Triggers, P1:0 triggers AWG, P1:1 triggers PSPL and DAQ analog input
        self.digital_output = Task()
        data_p0 = np.zeros(int(self.do_clock * self.trig_interval),
                           dtype=np.uint8)
        data_p0[0] = 1

        data_p1 = np.zeros(int(self.do_clock * self.trig_interval),
                           dtype=np.uint8)
        data_p1[int(self.do_clock * (self.pspl_trig_time))] = 1

        data = np.append(data_p0, data_p1)

        self.digital_output.CreateDOChan("/Dev1/port0/line0:1", "",
                                         DAQmx_Val_ChanPerLine)
        self.digital_output.CfgSampClkTiming(
            "", self.do_clock, DAQmx_Val_Rising, DAQmx_Val_FiniteSamps,
            int(data.size / 2) * self.attempts)
        self.digital_output.WriteDigitalLines(
            int(self.do_clock * self.trig_interval), 0, 1,
            DAQmx_Val_GroupByChannel, data, None, None)

        # Setup the PSPL`
        self.pspl.amplitude = 7.5 * np.power(
            10, (-self.pspl_base_attenuation) / 20.0)
        self.pspl.trigger_source = "EXT"
        self.pspl.trigger_level = 0.5
        self.pspl.output = True

        # Setup PSPL Attenuator Control
        self.atten.set_supply_method(self.lock.set_ao2)
        self.atten.set_control_method(self.lock.set_ao3)

        # Setup bias current method
        def set_bias(value):
            self.arb_cb.high_voltage = self.channel_bias.value * (
                self.chan_bias_ref_res + self.matching_ref_res)
            self.arb_cb.low_voltage = 0.0

        self.channel_bias.assign_method(set_bias)

        def set_voltage(voltage, base_atten=self.pspl_base_attenuation):
            # Calculate the voltage controller attenuator setting
            amplitude = 7.5 * np.power(10, -base_atten / 20.0)
            vc_atten = abs(20.0 * np.log10(
                abs(voltage) / 7.5)) - base_atten - self.circuit_attenuation

            if vc_atten <= self.atten.minimum_atten():
                base_atten -= 1
                if base_atten < 0:
                    logger.error(
                        "Voltage controlled attenuation {} under range, PSPL at Max. Decrease circuit attenuation."
                        .format(vc_atten))
                    raise ValueError(
                        "Voltage controlled attenuation {} under range, PSPL at Max. Decrease circuit attenuation."
                        .format(vc_atten))
                set_voltage(voltage, base_atten=base_atten)
                return

            if self.atten.maximum_atten() < vc_atten:
                base_atten += 1
                if base_atten > 80:
                    logger.error(
                        "Voltage controlled attenuation {} over range, PSPL at Min. Increase circuit attenuation."
                        .format(vc_atten))
                    raise ValueError(
                        "Voltage controlled attenuation {} over range, PSPL at Min. Increase circuit attenuation."
                        .format(vc_atten))
                set_voltage(voltage, base_atten=base_atten)
                return

            #print("PSPL Amplitude: {}, Attenuator: {}".format(amplitude,vc_atten))
            self.atten.set_attenuation(vc_atten)
            self.pspl.amplitude = self.pulse_polarity * amplitude
            time.sleep(0.04)

        # Assign Methods
        #self.channel_bias.assign_method(lambda i: self.arb_cb.set_high_voltage(i*self.chan_bias_ref_res))
        self.pulse_duration.add_post_push_hook(lambda: time.sleep(0.1))
        self.pulse_duration.assign_method(self.pspl.set_duration)
        self.pulse_voltage.assign_method(set_voltage)

    def init_streams(self):
        # Baked in data axes
        descrip = DataStreamDescriptor()
        descrip.data_name = 'voltage'
        descrip.add_axis(
            DataAxis("sample",
                     range(int(self.integration_time * self.ai_clock))))
        descrip.add_axis(DataAxis("attempt", range(self.attempts)))
        self.voltage.set_descriptor(descrip)

    async def run(self):

        self.digital_output.StartTask()
        self.digital_output.WaitUntilTaskDone(2 * self.attempts *
                                              self.trig_interval)
        self.digital_output.StopTask()
        await asyncio.sleep(0.05)
        # print("\t now ", self.nidaq.points_read)
        logger.debug("Stream has filled {} of {} points".format(
            self.voltage.points_taken, self.voltage.num_points()))

    def shutdown_instruments(self):
        try:
            # self.analog_input.StopTask()
            self.nidaq.StopTask()
            self.nidaq.ClearTask()
            self.digital_output.StopTask()
            del self.nidaq
        except Exception as e:
            print(
                "Warning: failed to stop task (this normally happens with no consequences when taking multiple samples per trigger)."
            )
            pass
        self.arb_cb.output = False
        self.pspl.output = False
        for name, instr in self._instruments.items():
            instr.disconnect()
Пример #5
0
class ShortProcessSwitchingExperimentReset(Experiment):

    # Parameters and outputs
    pulse_duration = FloatParameter(default=5.0e-9, unit="s")
    pulse_voltage  = FloatParameter(default=1, unit="V")
    bias_current   = FloatParameter(default=60e-6, unit="A")
    field          = FloatParameter(default=0.0, unit="T")
    voltage        = OutputConnector()

    polarity        = 1
    attempts        = 1 << 4 # How many times to try switching per set of conditions
    measure_current = 10.0e-6 # MTJ sense current
    reset_current   = 0.5e-3 # AWG reset current (remember division)
    MTJ_res         = 100e3 # MTJ Resistance

    # Reference resistances and attenuations
    matching_ref_res      = 50
    chan_bias_ref_res     = 1e4
    reset_bias_ref_res    = 5e3
    pspl_base_attenuation = 10
    circuit_attenuation   = 0

    # Reset Pulse          Switching Pulse
    #                         ___/\____
    # \_______/             /         \
    # A          B         C     D
    #            |------------------------------------->
    #             MEAS Begins (ignore middle) MEAS Ends
    #            | Integ. time|              |integ time|

    # Measurement Sequence timing, clocks in Hz times in seconds
    ai_clock          = 0.25e6
    do_clock          = 0.5e6

    trig_interval     = 200e-6  # The repetition rate for switching attempts
    bias_pulse_width  = 40e-6   # This is how long the bias pulse is
    reset_pulse_width = 40e-6   # This is how long the reset pulse is
    meas_duration     = 140e-6  # This is how long the meas is

    reset_trig_time   = 0e-6    # (A) When we trigger the reset pulse
    meas_trig_time    = 50e-6   # (B) When the measurement trigger begins
    switch_trig_time  = 4e-6    # (C) When we trigger the reset pulse
    pspl_trig_time    = 4e-6    # (D) When we trigger the gate pulse
    integration_time  = 20e-6   # How long to measure for at the beginning and end

    # Instrument resources, NIDAQ called via PyDAQmx
    arb_cb    = Agilent33220A("192.168.5.199") # Channel Bias
    arb_reset = Agilent33220A("192.168.5.198") # Channel reset pulses
    pspl      = Picosecond10070A("GPIB0::24::INSTR") # Gate Pulse
    atten     = RFMDAttenuator("calibration/RFSA2113SB_HPD_20160901.csv") # Gate Amplitude control
    lock      = SR865("USB0::0xB506::0x2000::002638::INSTR") # Gate Amplitude control
    keith     = Keithley2400("GPIB0::25::INSTR")
    mag       = AMI430("192.168.5.109")

    def init_instruments(self):
        # Channel bias arb
        self.arb_cb.output          = False
        self.arb_cb.load_resistance = (self.chan_bias_ref_res+self.matching_ref_res)
        self.arb_cb.function        = 'Pulse'
        self.arb_cb.pulse_period    = 0.99*self.trig_interval
        self.arb_cb.pulse_width     = self.bias_pulse_width
        self.arb_cb.pulse_edge      = 100e-9
        self.arb_cb.low_voltage     = 0.0
        self.arb_cb.high_voltage    = self.bias_current.value*(self.chan_bias_ref_res+self.matching_ref_res)
        self.arb_cb.polarity        = self.polarity
        self.arb_cb.burst_state     = True
        self.arb_cb.burst_cycles    = 1
        self.arb_cb.trigger_source  = "External"
        self.arb_cb.burst_mode      = "Triggered"
        self.arb_cb.output          = True

        # MTJ reset arb
        self.arb_reset.output          = False
        self.arb_reset.load_resistance = (self.reset_bias_ref_res+self.matching_ref_res)
        self.arb_reset.function        = 'Pulse'
        self.arb_reset.pulse_period    = 0.99*self.trig_interval
        self.arb_reset.pulse_width     = self.reset_pulse_width
        self.arb_reset.pulse_edge      = 100e-9
        self.arb_reset.low_voltage     = 0.0
        self.arb_reset.high_voltage    = self.reset_current*(self.reset_bias_ref_res+self.matching_ref_res)
        self.arb_reset.polarity        = -self.polarity
        self.arb_reset.burst_state     = True
        self.arb_reset.burst_cycles    = 1
        self.arb_reset.trigger_source  = "External"
        self.arb_reset.burst_mode      = "Triggered"
        self.arb_reset.output          = True

        # Setup the Keithley
        self.keith.triad()
        self.keith.conf_meas_res(res_range=1e5)
        self.keith.conf_src_curr(comp_voltage=0.5, curr_range=1.0e-4)
        self.keith.current = self.measure_current

        # Setup the magnet
        self.mag.ramp()

        # Setup the NIDAQ tasks
        DAQmxResetDevice("Dev1")
        self.nidaq = CallbackTask(["Dev1/ai2"], asyncio.get_event_loop(), int(self.meas_duration*self.ai_clock), self.attempts, [self.voltage],
                                 trigger="/Dev1/PFI12", min_voltage=-self.MTJ_res*self.measure_current, max_voltage=self.MTJ_res*self.measure_current,
                                 ai_clock=self.ai_clock)
        self.nidaq.StartTask()

        self.digital_output = Task()
        do_reset = np.zeros(int(self.do_clock*self.trig_interval),dtype=np.uint8)
        do_reset[int(self.do_clock*self.reset_trig_time)]=1
        do_bias = np.zeros(int(self.do_clock*self.trig_interval),dtype=np.uint8)
        do_bias[int(self.do_clock*self.switch_trig_time)]=1
        do_pspl = np.zeros(int(self.do_clock*self.trig_interval),dtype=np.uint8)
        do_pspl[int(self.do_clock*self.pspl_trig_time)]=1
        do_meas = np.zeros(int(self.do_clock*self.trig_interval),dtype=np.uint8)
        do_meas[int(self.do_clock*self.meas_trig_time)]=1
        data = np.hstack([do_reset, do_bias, do_pspl, do_meas])

        self.digital_output.CreateDOChan("/Dev1/port0/line0:3","",DAQmx_Val_ChanPerLine)
        self.digital_output.CfgSampClkTiming("",self.do_clock, DAQmx_Val_Rising, DAQmx_Val_FiniteSamps, int(data.size/4)*self.attempts)
        self.digital_output.WriteDigitalLines(int(self.do_clock*self.trig_interval),0,1,DAQmx_Val_GroupByChannel,data,None,None)

        # Setup the PSPL
        self.pspl.amplitude = 7.5*np.power(10, (-self.pspl_base_attenuation)/20.0)
        self.pspl.trigger_source = "EXT"
        self.pspl.trigger_level = 0.5
        self.pspl.output = True

        # Setup PSPL Attenuator Control
        self.atten.set_supply_method(self.lock.set_ao2)
        self.atten.set_control_method(self.lock.set_ao3)

        def set_pulse_voltage(voltage, base_atten=self.pspl_base_attenuation):
            # Calculate the voltage controller attenuator setting
            amplitude = 7.5*np.power(10, -base_atten/20.0)
            vc_atten = abs(20.0 * np.log10(abs(voltage)/7.5)) - base_atten - self.circuit_attenuation

            if vc_atten <= self.atten.minimum_atten():
                base_atten -= 1
                if base_atten < 0:
                    logger.error("Voltage controlled attenuation {} under range, PSPL at Max. Decrease circuit attenuation.".format(vc_atten))
                    raise ValueError("Voltage controlled attenuation {} under range, PSPL at Max. Decrease circuit attenuation.".format(vc_atten))
                set_pulse_voltage(voltage, base_atten=base_atten)
                return

            if self.atten.maximum_atten() < vc_atten:
                base_atten += 1
                if base_atten > 80:
                    logger.error("Voltage controlled attenuation {} over range, PSPL at Min. Increase circuit attenuation.".format(vc_atten))
                    raise ValueError("Voltage controlled attenuation {} over range, PSPL at Min. Increase circuit attenuation.".format(vc_atten))
                set_pulse_voltage(voltage, base_atten=base_atten)
                return

            #print("PSPL Amplitude: {}, Attenuator: {}".format(amplitude,vc_atten))
            self.atten.set_attenuation(vc_atten)
            self.pspl.amplitude = self.arb_cb.polarity*abs(amplitude)
            time.sleep(0.04)

        def set_awg_current(current):

            if 0 <= current:
                self.arb_cb.polarity = 1
                self.arb_cb.low_voltage = 0
                self.arb_cb.high_voltage = current*self.chan_bias_ref_res
            else :
                self.arb_cb.polarity = -1
                self.arb_cb.low_voltage = current*self.chan_bias_ref_res
                self.arb_cb.high_voltage = 0

            self.pspl.amplitude = self.arb_cb.polarity*abs(self.pspl.amplitude)

            time.sleep(0.04)

        # Assign Methods
        self.bias_current.assign_method(set_awg_current)
        self.pulse_duration.assign_method(self.pspl.set_duration)
        self.pulse_voltage.assign_method(set_pulse_voltage)
        self.field.assign_method(self.mag.set_field)

        self.pulse_duration.add_post_push_hook(lambda: time.sleep(0.05))
        self.pulse_voltage.add_post_push_hook(lambda: time.sleep(0.05))
        self.bias_current.add_post_push_hook(lambda: time.sleep(0.05))

    def init_streams(self):
        descrip = DataStreamDescriptor()
        descrip.data_name='voltage'
        descrip.add_axis(DataAxis("sample", range(int(self.meas_duration*self.ai_clock))))
        descrip.add_axis(DataAxis("attempt", range(self.attempts)))
        self.voltage.set_descriptor(descrip)

    async def run(self):
        self.digital_output.StartTask()
        self.digital_output.WaitUntilTaskDone(2*self.attempts*self.trig_interval)
        self.digital_output.StopTask()
        await asyncio.sleep(0.05)

    def shutdown_instruments(self):
        self.keith.current = 0.0e-5
        self.keith.triad(down=True)
        self.mag.zero()
        try:
            for ch in [self.nidaq, self.nidaq_MTJ]:
                ch.StopTask()
                ch.ClearTask()
                self.digital_output.StopTask()
                del ch
        except Exception as e:
            print("Warning: failed to stop task (this normally happens with no consequences when taking multiple samples per trigger).")
            pass
        self.arb_cb.output = False
        self.pspl.output = False
        for name, instr in self._instruments.items():
            instr.disconnect()