def measure_2D(self): if self.x_set_obj == None or self.y_set_obj == None: print 'axes parameters not properly set...aborting' return if self.ReadoutTrace: raise ValueError('ReadoutTrace is currently not supported for 2D measurements') qt.mstart() self.mode = 2 #1: 1D, 2: 2D, 3:1D_AWG/2D_AWG self._prepare_measurement_file() #self._create_dat_plots(mode='2d') if self.show_progress_bar: p = Progress_Bar(len(self.x_vec)*len(self.y_vec),name=self.dirname) try: # measurement loop for x in self.x_vec: self.x_set_obj(x) for y in self.y_vec: qt.msleep() self.y_set_obj(y) qt.msleep() self._append_data() if self.show_progress_bar: p.iterate() self._hdf_amp.next_matrix() self._hdf_pha.next_matrix() finally: self._end_measurement() qt.mend()
def measure_3D_AWG(self): ''' x_vec is sequence in AWG ''' if self.z_set_obj is None or self.y_set_obj is None: raise ValueError('x-axes parameters not properly set') if self.ReadoutTrace: raise ValueError( 'ReadoutTrace is currently not supported for 3D_AWG measurements' ) self.mode = 4 # 1: 1D, 2: 2D, 3:1D_AWG/2D_AWG, 4:3D_AWG self._prepare_measurement_file() if self.show_progress_bar: p = Progress_Bar(len(self.y_vec) * len(self.z_vec), name=self.dirname) try: # measurement loop for z in self.z_vec: self.z_set_obj(z) for y in self.y_vec: qkit.flow.sleep() self.y_set_obj(y) qkit.flow.sleep() self._append_data() if self.show_progress_bar: p.iterate() for i in range(self.ndev): self._hdf_amp[i].next_matrix() self._hdf_pha[i].next_matrix() finally: self._end_measurement()
def measure_2D_AWG(self): ''' x_vec is sequence in AWG ''' if self.y_set_obj == None: print 'axes parameters not properly set...aborting' return qt.mstart() qt.msleep() #if stop button was pressed by now, abort without creating data files self._prepare_measurement_dat_file(mode='2dAWG') self._create_dat_plots(mode='2dAWG') p = Progress_Bar(len(self.y_vec),name=self.dirname) try: # measurement loop for it in range(len(self.y_vec)): qt.msleep() # better done during measurement (waiting for trigger) self.y_set_obj(self.y_vec[it]) self._append_data([self.y_vec[it]],trace=True,it=it) self._update_plots() p.iterate() #except Exception as e: # print e finally: self._safe_plots() self._generate_avg_data(final=True) self._close_files() qt.mend()
def measure_2D_AWG(self): ''' x_vec is sequence in AWG ''' if self.y_set_obj == None: print 'axes parameters not properly set...aborting' return qt.mstart() qt.msleep() #if stop button was pressed by now, abort without creating data files self.mode = 3 #1: 1D, 2: 2D, 3:1D_AWG/2D_AWG self._prepare_measurement_file() if self.show_progress_bar: p = Progress_Bar(len(self.y_vec),name=self.dirname) try: # measurement loop for it in range(len(self.y_vec)): qt.msleep() # better done during measurement (waiting for trigger) self.y_set_obj(self.y_vec[it]) self._append_data(iteration=it) if self.show_progress_bar: p.iterate() finally: self._end_measurement() qt.mend()
def measure_2D_ddc_time_trace(self): """ Performs a digital down conversion for exactly one value in your awg sequence. But you can sweep other parameters, such as mw-power or so. :return: """ if self.y_set_obj is None: raise ValueError('y-axes parameters not properly set') time_end = float(self.sample.mspec.get_samples() ) / self.sample.mspec.get_samplerate() time_array = np.linspace(0, time_end, self.sample.mspec.get_samples()) self.set_x_parameters(time_array, 'time', True, 'sec') self.mode = 2 # 1: 1D, 2: 2D, 3:1D_AWG/2D_AWG self._prepare_measurement_file() if self.show_progress_bar: p = Progress_Bar(len(self.y_vec), name=self.dirname) try: for y in self.y_vec: qkit.flow.sleep() self.y_set_obj(y) qkit.flow.sleep() self._append_data(ddc=True) if self.show_progress_bar: p.iterate() finally: self._end_measurement()
def measure_2D(self): if self.x_set_obj == None or self.y_set_obj == None: print 'axes parameters not properly set...aborting' return qt.mstart() self._prepare_measurement_dat_file(mode='2d') self._create_dat_plots(mode='2d') p = Progress_Bar(len(self.x_vec)*len(self.y_vec),name=self.dirname) try: # measurement loop for x in self.x_vec: self.x_set_obj(x) if self.save_dat: self.data_raw.new_block() for y in self.y_vec: qt.msleep() # better done during measurement (waiting for trigge self.y_set_obj(y) #sleep(self.tdy) qt.msleep() # better done during measurement (waiting for trigger) self._append_data([x,y],trace=False) self._update_plots() p.iterate() finally: self._safe_plots() self._close_files() qt.mend()
def measure_2D_AWG(self, iterations=1): ''' x_vec is sequence in AWG ''' if self.y_set_obj is None: raise ValueError('y-axes parameters not properly set') qkit.flow.sleep( ) # if stop button was pressed by now, abort without creating data files if iterations > 1: self.z_vec = range(iterations) self.z_coordname = 'iteration' self.z_set_obj = lambda z: True self.z_unit = '' self.measure_3D_AWG() # For 3D measurements with iterations, averages are only created at the end to get a consistent averaging base. hdf_file = hdf.Data(self._hdf.get_filepath()) for j in range(self.ndev): amp = np.array(hdf_file["/entry/data0/amplitude_%i" % j]) pha = np.array(hdf_file["/entry/data0/phase_%i" % j]) amp_avg = sum(amp[i] for i in range(iterations)) / iterations pha_avg = sum(pha[i] for i in range(iterations)) / iterations hdf_amp_avg = hdf_file.add_value_matrix('amplitude_avg_%i' % i, x=self._hdf_y, y=self._hdf_x, unit='a.u.') hdf_pha_avg = hdf_file.add_value_matrix('phase_avg_%i' % i, x=self._hdf_y, y=self._hdf_x, unit='rad') for i in range(len(self.y_vec)): hdf_amp_avg.append(amp_avg[i]) hdf_pha_avg.append(pha_avg[i]) hdf_file.close_file() else: self.mode = 3 # 1: 1D, 2: 2D, 3:1D_AWG/2D_AWG, 4:3D_AWG self._prepare_measurement_file() if self.ndev > 1: raise ValueError( 'Multiplexed readout is currently not supported for 2D measurements' ) if self.show_progress_bar: p = Progress_Bar(len(self.y_vec), name=self.dirname) try: # measurement loop for it in range(len(self.y_vec)): qkit.flow.sleep( ) # better done during measurement (waiting for trigger) self.y_set_obj(self.y_vec[it]) self._append_data(iteration=it) if self.show_progress_bar: p.iterate() finally: self._end_measurement()
def measure_1D(self): ''' measure method to record a single (averaged) VNA trace, S11 or S21 according to the setting on the VNA ''' self._scan_1D = True self._scan_2D = False self._scan_3D = False if not self.dirname: self.dirname = 'VNA_tracedata' self._file_name = self.dirname.replace(' ', '').replace(',','_') if self.exp_name: self._file_name += '_' + self.exp_name self._prepare_measurement_vna() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" if self.open_qviewkit: qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase']) if self._fit_resonator: self._resonator = resonator(self._data_file.get_filepath()) print 'recording trace...' sys.stdout.flush() qt.mstart() self.vna.avg_clear() if self.vna.get_averages() == 1 or self.vna.get_Average() == False: #no averaging self._p = Progress_Bar(1,self.dirname,self.vna.get_sweeptime()) qt.msleep(self.vna.get_sweeptime()) #wait single sweep self._p.iterate() else: #with averaging self._p = Progress_Bar(self.vna.get_averages(),self.dirname,self.vna.get_sweeptime()) if "avg_status" in self.vna.get_function_names(): for a in range(self.vna.get_averages()): while self.vna.avg_status() <= a: qt.msleep(.2) #maybe one would like to adjust this at a later point self._p.iterate() else: #old style for a in range(self.vna.get_averages()): qt.msleep(self.vna.get_sweeptime()) #wait single sweep time self._p.iterate() data_amp, data_pha = self.vna.get_tracedata() data_real, data_imag = self.vna.get_tracedata('RealImag') self._data_amp.append(data_amp) self._data_pha.append(data_pha) self._data_real.append(data_real) self._data_imag.append(data_imag) if self._fit_resonator: self._do_fit_resonator() qt.mend() self._end_measurement()
def measure_3D(self, web_visible=True): ''' measure full window of vna while sweeping x_set_obj and y_set_obj with parameters x_vec/y_vec. sweep over y_set_obj is the inner loop, for every value x_vec[i] all values y_vec are measured. optional: measure method to perform the measurement according to landscape, if set self.span is the range (in units of the vertical plot axis) data is taken around the specified funtion(s) note: make sure to have properly set x,y vectors before generating traces ''' if not self.x_set_obj or not self.y_set_obj: logging.error('axes parameters not properly set...aborting') return self._scan_1D = False self._scan_2D = False self._scan_3D = True self._scan_time = False self._measurement_object.measurement_func = 'measure_3D' self._measurement_object.x_axis = self.x_coordname self._measurement_object.y_axis = self.y_coordname self._measurement_object.z_axis = 'frequency' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = self.x_coordname + ', ' + self.y_coordname self._file_name = '3D_' + self.dirname.replace(' ', '').replace( ',', '_') if self.exp_name: self._file_name += '_' + self.exp_name if self.progress_bar: self._p = Progress_Bar( len(self.x_vec) * len(self.y_vec), '3D VNA sweep ' + self.dirname, self.vna.get_sweeptime_averages()) self._prepare_measurement_vna() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" """only middle point in freq array is plotted vs x and y""" if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase']) if self._fit_resonator: self._resonator = resonator(self._data_file.get_filepath()) if self.landscape: self.center_freqs = np.array(self.landscape).T else: self.center_freqs = [] #load default sequence for i in range(len(self.x_vec)): self.center_freqs.append([0]) self._measure()
def measure_2D(self, web_visible=True): ''' measure method to record a (averaged) VNA trace, S11 or S21 according to the setting on the VNA for all parameters x_vec in x_obj ''' if not self.x_set_obj: logging.error('axes parameters not properly set...aborting') return if len(self.x_vec) == 0: logging.error( 'No points to measure given. Check your x vector... aborting') return self._scan_1D = False self._scan_2D = True self._scan_3D = False self._scan_time = False self._measurement_object.measurement_func = 'measure_2D' self._measurement_object.x_axis = self.x_coordname self._measurement_object.y_axis = 'frequency' self._measurement_object.z_axis = '' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = self.x_coordname self._file_name = '2D_' + self.dirname.replace(' ', '').replace( ',', '_') if self.exp_name: self._file_name += '_' + self.exp_name if self.progress_bar: self._p = Progress_Bar(len(self.x_vec), '2D VNA sweep ' + self.dirname, self.vna.get_sweeptime_averages()) self._prepare_measurement_vna() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" if self._nop < 10: if self.open_qviewkit: self._qvk_process = qviewkit.plot( self._data_file.get_filepath(), datasets=['amplitude_midpoint', 'phase_midpoint']) else: if self.open_qviewkit: self._qvk_process = qviewkit.plot( self._data_file.get_filepath(), datasets=['amplitude', 'phase']) if self._fit_resonator: self._resonator = resonator(self._data_file.get_filepath()) self._measure()
def _wait_progress_bar(self): ti = time() if self.progress_bar: self._p = Progress_Bar(self.IVD.get_averages(),self.dirname,self.IVD.get_sweeptime()) qt.msleep(.2) # wait for data while not self.IVD.ready(): if time()-ti > self.IVD.get_sweeptime(query=False): if self.progress_bar: self._p.iterate() ti = time() qt.msleep(.2) if self.progress_bar: while self._p.progr < self._p.max_it: self._p.iterate()
def ramp_to(self, target = 0, channel = 0, steps = 20, dt = 0.1): """ Ramp current to target Inputs: - target: target current value (mA) - channel: channel index (1..) - steps: number of steps - dt: wait time between two steps """ p = Progress_Bar(steps,'Ramping current') for c in np.linspace(self.do_get_current(channel),target,steps): self.do_set_current(c, channel, verbose=False) p.iterate('{:.3g}mA'.format(c)) time.sleep(dt) self.do_set_current(target, channel ,verbose=True)
def ramp_to(self, target = 0, ch = 1, steps = 100, dt = 0.05): """ ramp current to target Inputs: - target: target current value - ch: channel index (1..) - steps: number of steps - dt: wait time between two steps """ p = Progress_Bar(steps,'Ramping current') #init progress bar for c in np.linspace(self.do_get_current(ch),target,steps): self.do_set_current(c,ch,verbose=False) p.iterate("%.3gmA"%c) time.sleep(dt) self.do_set_current(c,ch,verbose=True)
def ramp_to(self, target=0, ch=1, steps=100, dt=0.05): """ ramp current to target Inputs: - target: target current value - ch: channel index (1..) - steps: number of steps - dt: wait time between two steps """ p = Progress_Bar(steps, 'Ramping current') #init progress bar for c in np.linspace(self.do_get_current(ch), target, steps): self.do_set_current(c, ch, verbose=False) p.iterate("%.3gmA" % c) time.sleep(dt) self.do_set_current(c, ch, verbose=True)
def measure_1D2(self): ''' measure full window of vna while sweeping x_set_obj with parameters x_vec ''' if not self.x_set_obj: logging.error('axes parameters not properly set...aborting') return self._scan_1D = False self._scan_1D2 = True self._scan_2D = False self.data_complex = False if self.dirname == None: self.dirname = self.x_coordname.replace() self._file_name = '1D2_' + self.dirname if self.exp_name: self._file_name += '_' + self.exp_name self._p = Progress_Bar(len(self.x_vec),self.dirname) self._prepare_measurement_vna() if self.save_dat: self._prepare_measurement_dat_file() if self.save_hdf: self._prepare_measurement_hdf_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" qviewkit.plot(self._data_hdf.get_filepath(), datasets=['amplitude', 'phase']) if self._fit_resonator: self._resonator = resonator(self._data_hdf) self._measure() self._end_measurement()
def measure_2D(self): ''' measure method to record a (averaged) VNA trace, S11 or S21 according to the setting on the VNA for all parameters x_vec in x_obj ''' if not self.x_set_obj: logging.error('axes parameters not properly set...aborting') return self._scan_1D = False self._scan_2D = True self._scan_3D = False if not self.dirname: self.dirname = self.x_coordname self._file_name = '2D_' + self.dirname.replace(' ', '').replace(',','_') if self.exp_name: self._file_name += '_' + self.exp_name self._p = Progress_Bar(len(self.x_vec),'2D VNA sweep '+self.dirname,self.vna.get_sweeptime()) self._prepare_measurement_vna() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" if self._nop < 10: qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude_midpoint', 'phase_midpoint']) else: qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase']) if self._fit_resonator: self._resonator = resonator(self._data_file.get_filepath()) self._measure()
def ramp_current(self, current, ramp_rate=1e-3, progress_bar=False): """ ramps the bias current to a specified value with the specified ramp_rate. current: float, end current ramp_rate: float, change of voltage per second progress_bar: bool, weather the progress_bar should be displayed """ start_current = self.do_get_measure_current() if np.sign(current - start_current) != np.sign(ramp_rate): ramp_rate *= (-1) current_values = np.arange(start_current, current, 0.1 * ramp_rate) if progress_bar: pb = Progress_Bar(len(current_values)) for c in current_values: self.do_set_bias_current(c) time.sleep(0.1) if progress_bar: pb.iterate()
def measure_IV(self): self._measure_IV_1D = True self._measure_IV_2D = False self._measure_IV_3D = False if not self._check_measurement: return self._scan_name = 'IV' if self.exp_name: self._scan_name += '_' + self.exp_name self._p = Progress_Bar(self._sweeps) self._prepare_measurement_file() self._save_settings() #qviewkit.plot_hdf(self._data.get_filepath() self._measure() self._end_measurement()
def measure_IV_3D(self): self._measure_IV_1D = False self._measure_IV_2D = False self._measure_IV_3D = True if not self._check_measurement: return self._scan_name = 'IV_vs_' + self.x_coordname + '_' + self.y_coordname if self.exp_name: self._scan_name += '_' + self.exp_name self._p = Progress_Bar(len(self.x_vec) * len(self.y_vec)) self._prepare_measurement_file() self._save_settings() #qviewkit.plot_hdf(self._data.filepath()) self._measure() self._end_measurement()
def measure_1D(self): if self.x_set_obj is None: raise ValueError('x-axes parameters not properly set') self.mode = 1 # 1: 1D, 2: 2D, 3:1D_AWG/2D_AWG, 4:3D_AWG self._prepare_measurement_file() if self.show_progress_bar: p = Progress_Bar(len(self.x_vec), name=self.dirname) try: # measurement loop for x in self.x_vec: self.x_set_obj(x) qkit.flow.sleep() self._append_data() if self.show_progress_bar: p.iterate() finally: self._end_measurement()
def ramp_voltage(self, voltage, ramp_rate=1e-3, progress_bar=False): """ ramps the bias voltage to a specified value with the specified ramp_rate. Keep in mind the defined current limit voltage: float, end voltage ramp_rate: float, change of voltage per second progress_bar: bool, weather the progress_bar should be displayed """ start_voltage = self.do_get_measure_voltage() if np.sign(voltage - start_voltage) != np.sign(ramp_rate): ramp_rate *= (-1) voltage_values = np.arange(start_voltage, voltage, 0.1 * ramp_rate) if progress_bar: pb = Progress_Bar(len(voltage_values)) for v in voltage_values: self.do_set_bias_voltage(v) time.sleep(0.1) if progress_bar: pb.iterate()
def measure_2D(self, web_visible=True): ''' measure method to record a (averaged) VNA trace, S11 or S21 according to the setting on the VNA for all parameters x_vec in x_obj ''' if not self.x_set_obj: logging.error('axes parameters not properly set...aborting') return self._scan_1D = False self._scan_2D = True self._scan_3D = False self._scan_time = False self._measurement_object.measurement_func = 'measure_2D' self._measurement_object.x_axis = self.x_coordname self._measurement_object.y_axis = 'frequency' self._measurement_object.z_axis = '' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = self.x_coordname self._file_name = '2D_' + self.dirname.replace(' ', '').replace( ',', '_') if self.exp_name: self._file_name += '_' + self.exp_name if self.progress_bar: self._p = Progress_Bar(len(self.x_vec), '2D signal analyzer sweep ' + self.dirname, self.sig_analyzer.get_sweeptime_averages()) self._prepare_measurement_sig_analyzer() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=self.traces_names) self._measure()
def measure_1D(self): if self.x_set_obj == None: print 'axes parameters not properly set...aborting' return qt.mstart() self.mode = 1 #1: 1D, 2: 2D, 3:1D_AWG/2D_AWG self._prepare_measurement_file() if self.show_progress_bar: p = Progress_Bar(len(self.x_vec),name=self.dirname) try: # measurement loop for x in self.x_vec: self.x_set_obj(x) qt.msleep() # better done during measurement (waiting for trigger) self._append_data() if self.show_progress_bar: p.iterate() finally: #self._safe_plots() self._end_measurement() qt.mend()
def measure_1D(self): if self.x_set_obj == None: print 'axes parameters not properly set...aborting' return qt.mstart() self.mode = 1 #1: 1D, 2: 2D, 3:1D_AWG/2D_AWG self._prepare_measurement_file() if self.show_progress_bar: p = Progress_Bar(len(self.x_vec), name=self.dirname) try: # measurement loop for x in self.x_vec: self.x_set_obj(x) qt.msleep( ) # better done during measurement (waiting for trigger) self._append_data() if self.show_progress_bar: p.iterate() finally: #self._safe_plots() self._end_measurement() qt.mend()
def measure_1D(self): if self.x_set_obj == None: print 'axes parameters not properly set...aborting' return self.time_data = False qt.mstart() self._prepare_measurement_dat_file(mode='1d') self._create_dat_plots(mode='1d') p = Progress_Bar(len(self.x_vec),name=self.dirname) try: # measurement loop for x in self.x_vec: self.x_set_obj(x) qt.msleep() # better done during measurement (waiting for trigger) self._append_data([x],trace=False) self._update_plots() p.iterate() finally: self._safe_plots() self._close_files() qt.mend()
def measure_3D(self, web_visible = True): ''' measure full window of vna while sweeping x_set_obj and y_set_obj with parameters x_vec/y_vec. sweep over y_set_obj is the inner loop, for every value x_vec[i] all values y_vec are measured. optional: measure method to perform the measurement according to landscape, if set self.span is the range (in units of the vertical plot axis) data is taken around the specified funtion(s) note: make sure to have properly set x,y vectors before generating traces ''' if not self.x_set_obj or not self.y_set_obj: logging.error('axes parameters not properly set...aborting') return self._scan_1D = False self._scan_2D = False self._scan_3D = True self._scan_time = False self._measurement_object.measurement_func = 'measure_3D' self._measurement_object.x_axis = self.x_coordname self._measurement_object.y_axis = self.y_coordname self._measurement_object.z_axis = 'frequency' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = self.x_coordname + ', ' + self.y_coordname self._file_name = '3D_' + self.dirname.replace(' ', '').replace(',','_') if self.exp_name: self._file_name += '_' + self.exp_name if self.progress_bar: self._p = Progress_Bar(len(self.x_vec)*len(self.y_vec),'3D VNA sweep '+self.dirname,self.vna.get_sweeptime_averages()) self._prepare_measurement_vna() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" """only middle point in freq array is plotted vs x and y""" if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase']) if self._fit_resonator: self._resonator = resonator(self._data_file.get_filepath()) if self.landscape: self.center_freqs = np.array(self.landscape).T else: self.center_freqs = [] #load default sequence for i in range(len(self.x_vec)): self.center_freqs.append([0]) self._measure()
def measure_IV_3D(self): self._measure_IV_1D = False self._measure_IV_2D = False self._measure_IV_3D = True if not self._check_measurement: return self._scan_name = 'IV_vs_'+ self.x_coordname + '_' + self.y_coordname if self.exp_name: self._scan_name += '_' + self.exp_name self._p = Progress_Bar(len(self.x_vec)*len(self.y_vec)) self._prepare_measurement_file() self._save_settings() #qviewkit.plot_hdf(self._data.filepath()) self._measure() self._end_measurement()
def measure_2D(self): ''' measure full window of vna while sweeping x_set_obj and y_set_obj with parameters x_vec/y_vec. sweep over y_set_obj is the inner loop, for every value x_vec[i] all values y_vec are measured. optional: measure method to perform the measurement according to landscape, if set self.span is the range (in units of the vertical plot axis) data is taken around the specified funtion(s) note: make sure to have properly set x,y vectors before generating traces ''' if not self.x_set_obj or not self.y_set_obj: logging.error('axes parameters not properly set...aborting') return self._scan_1D = False self._scan_1D2 = False self._scan_2D = True self.data_complex = False if self.dirname == None: self.dirname = self.x_coordname.replace() + '_' + self.y_coordname.replace() self._file_name = '2D_' + self.dirname if self.exp_name: self._file_name += '_' + self.exp_name self._p = Progress_Bar(len(self.x_vec)*len(self.y_vec),self.dirname) self._prepare_measurement_vna() if self.save_dat: self._prepare_measurement_dat_file() if self.save_hdf: self._prepare_measurement_hdf_file() if self._fit_resonator: self._resonator = resonator(self._data_hdf) if self.landscape: self.center_freqs = np.array(self.landscape).T else: self.center_freqs = [] #load default sequence for i in range(len(self.x_vec)): self.center_freqs.append([0]) self._measure() self._end_measurement()
def measure_timetrace(self, web_visible = True): ''' measure method to record a single VNA timetrace, this only makes sense when span is set to 0 Hz!, tested only with KEYSIGHT E5071C ENA and its corresponding qkit driver LGruenhaupt 11/2016 ''' if self.vna.get_span() > 0: print 'VNA span not set to 0 Hz... aborting' return self._scan_1D = False self._scan_2D = False self._scan_3D = False self._scan_time = True self._measurement_object.measurement_func = 'measure_timetrace' self._measurement_object.x_axis = 'time' self._measurement_object.y_axis = '' self._measurement_object.z_axis = '' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = 'VNA_timetrace' self._file_name = self.dirname.replace(' ', '').replace(',','_') if self.exp_name: self._file_name += '_' + self.exp_name self.x_vec = np.arange(0,self.number_of_timetraces,1) self._prepare_measurement_vna() self._prepare_measurement_file() if self.progress_bar: self._p = Progress_Bar(self.number_of_timetraces,'VNA timetrace '+self.dirname,self.vna.get_sweeptime_averages()) print 'recording timetrace(s)...' sys.stdout.flush() qt.mstart() try: """ loop: x_obj with parameters from x_vec """ for i, x in enumerate(self.x_vec): if self.log_function != None: for i,f in enumerate(self.log_function): self._log_value[i].append(float(f())) if self.averaging_start_ready: #LG 11/2016 self.vna.start_measurement() ti = time() #changed from time.time() to time() - LGruenhaupt OCT_2016 tp = time() #added to enable use of progress bar #self._p = Progress_Bar(self.vna.get_averages(),self.dirname,self.vna.get_sweeptime_averages()) moved to line 303 ''' This is to prevent the vna.ready() function from timing out. LG NOV/16 ''' if self.vna.get_Average(): print 'this function only makes sense without averaging' qt.mend() self._end_measuremt() else: #self._p = Progress_Bar(1,self.dirname,self.vna.get_sweeptime()) qt.msleep(self.vna.get_sweeptime()) #self._p.iterate() while not self.vna.ready(): qt.msleep(.2) #this is just to check if the measurement has finished data_amp, data_pha = self.vna.get_tracedata() self._data_amp.append(data_amp) self._data_pha.append(data_pha) qt.msleep() if self.progress_bar: self._p.iterate() else: print 'not implemented for this VNA, only works with Keysight ENA 5071C' qt.mend() self._end_measurement() except Exception as e: print e.__doc__ print e.message finally: self._end_measurement() qt.mend()
def update_sequence(ts, wfm_func, sample, iq=None, loop=False, drive='c:', path='\\waveforms', reset=True, marker=None, markerfunc=None, ch2_amp=2, chpair=1, awg=None, show_progress_bar=True): ''' set awg to sequence mode and push a number of waveforms into the sequencer inputs: ts: array of times, len(ts) = #sequenzes wfm_func: waveform function usually generated via generate_waveform using ts[i]; this can be a tuple of arrays (for channels 0,1, heterodyne mode) or a single array (homodyne mode) sample: sample object iq: Reference to iq mixer instrument. If None (default), the wfm will not be changed. Otherwise, the wfm will be converted via iq.convert() marker: marker array in the form [[ch1m1,ch1m2],[ch2m1,ch2m2]] and all entries arrays of sample length markerfunc: analog to wfm_func, set marker to None when used for the 6GS/s AWG, the waveform length must be divisible by 64 for the 1.2GS/s AWG, it must be divisible by 4 chpair: if you use the 4ch Tabor AWG as a single 2ch instrument, you can chose to take the second channel pair here (this can be either 1 or 2). ''' qkit.flow.start() if awg == None: awg = sample.awg clock = sample.clock wfm_func2 = wfm_func if iq != None: wfm_func2 = lambda t, sample: iq.convert(wfm_func(t, sample)) # create new sequence if reset: if "Tektronix" in awg.get_type(): awg.set_runmode('SEQ') awg.set_seq_length(0) #clear sequence, necessary? awg.set_seq_length(len(ts)) elif "Tabor" in awg.get_type(): awg.set('p%i_runmode' % chpair, 'SEQ') awg.define_sequence(chpair * 2 - 1, len(ts)) #amplitude settings of analog output awg.set_ch1_offset(0) awg.set_ch2_offset(0) awg.set_ch1_amplitude(2) awg.set_ch2_amplitude(ch2_amp) #generate empty tuples wfm_samples_prev = [None, None] wfm_fn = [None, None] wfm_pn = [None, None] if show_progress_bar: p = Progress_Bar( len(ts) * (2 if "Tektronix" in awg.get_type() else 1), 'Load AWG') #init progress bar #update all channels and times for ti, t in enumerate(ts): #run through all sequences qkit.flow.sleep() wfm_samples = wfm_func2(t, sample) #generate waveform if not isinstance(wfm_samples[0], (list, tuple, np.ndarray)): #homodyne wfm_samples = [ wfm_samples, np.zeros_like(wfm_samples, dtype=np.int8) ] for chan in [0, 1]: if markerfunc != None: #use markerfunc try: if markerfunc[chan][0] == None: marker1 = np.zeros_like(wfm_samples, dtype=np.int8)[0] else: marker1 = markerfunc[chan][0](t, sample) if markerfunc[chan][1] == None: marker2 = np.zeros_like(wfm_samples, dtype=np.int8)[0] else: marker2 = markerfunc[chan][1](t, sample) except TypeError: #only one markerfunc given marker1, marker2 = np.zeros_like(wfm_samples, dtype=np.int8) if chan == 0: marker1 = markerfunc(t, sample) elif marker == None: #fill up with zeros marker1, marker2 = np.zeros_like(wfm_samples, dtype=np.int8) else: #or set your own markers c_marker1, c_marker2 = marker[chan] marker1 = c_marker1[ti] marker2 = c_marker2[ti] if "Tektronix" in awg.get_type(): wfm_fn[chan] = 'ch%d_t%05d' % ( chan + 1, ti) # filename is kept until changed if len(wfm_samples) == 1 and chan == 1: wfm_pn[chan] = '%s%s\\%s' % ( drive, path, np.zeros_like( wfm_fn[0])) #create empty array else: wfm_pn[chan] = '%s%s\\%s' % (drive, path, wfm_fn[chan]) awg.wfm_send(wfm_samples[chan], marker1, marker2, wfm_pn[chan], clock) awg.wfm_import(wfm_fn[chan], wfm_pn[chan], 'WFM') # assign waveform to channel/time slot awg.wfm_assign(chan + 1, ti + 1, wfm_fn[chan]) if loop: awg.set_seq_loop(ti + 1, np.infty) elif "Tabor" in awg.get_type(): if chan == 1: #write out both together awg.wfm_send2(wfm_samples[0], wfm_samples[1], marker1, marker2, chpair * 2 - 1, ti + 1) else: continue else: raise ValueError("AWG type not known") if show_progress_bar: p.iterate() gc.collect() if reset and "Tektronix" in awg.get_type(): # enable channels awg.set_ch1_status(True) awg.set_ch2_status(True) awg.set_seq_goto(len(ts), 1) awg.run() awg.wait(10, False) elif reset and "Tabor" in awg.get_type(): # enable channels #awg.preset() awg.set_ch1_status(True) awg.set_ch2_status(True) qkit.flow.end() if sample.__dict__.has_key('mspec'): sample.mspec.spec_stop() sample.mspec.set_segments(len(ts)) return np.all([awg.get('ch%i_status' % i) for i in [1, 2]])
class spectrum(object): ''' useage: m = spectrum(vna = 'vna1') m2 = spectrum(vna = 'vna2', mw_src = 'mw_src1') #where 'vna2'/'mw_src1' is the qt.instruments name m.set_x_parameters(arange(-0.05,0.05,0.01),'flux coil current',coil.set_current, unit = 'mA') m.set_y_parameters(arange(4e9,7e9,10e6),'excitation frequency',mw_src1.set_frequency, unit = 'Hz') m.gen_fit_function(...) several times m.measure_XX() ''' def __init__(self, vna, exp_name = ''): self.vna = vna self.exp_name = exp_name self.landscape = None self.span = 200e6 #[Hz] self.tdx = 0.002 #[s] self.tdy = 0.002 #[s] self.data_complex = False self.comment = '' self.dirname = None self.plot3D = True self.plotlive = True self.x_set_obj = None self.y_set_obj = None self.return_dat = False self.save_dat = True self.save_hdf = False self.progress_bar = True self._fit_resonator = False def set_x_parameters(self, x_vec, x_coordname, x_instrument, x_unit = ""): ''' Sets parameters for sweep. In a 2D measurement, the x-parameters will be the "outer" sweep. For every x value all y values are swept Input: x_vec (array): conains the sweeping values x_coordname (string) x_instrument (obj): callable object to exectute with x_vec-values (i.e. vna.set_power()) x_unit (string): optional ''' self.x_vec = x_vec self.x_coordname = x_coordname self.x_set_obj = x_instrument self.delete_fit_function() self.x_unit = x_unit def set_y_parameters(self, y_vec, y_coordname, y_instrument, y_unit = ""): ''' Sets parameters for sweep. In a 2D measurement, the x-parameters will be the "outer" sweep. For every x value all y values are swept Input: x_vec (array): conains the sweeping values x_coordname (string) x_instrument (obj): callable object to exectute with x_vec-values (i.e. vna.set_power()) x_unit (string): optional ''' self.y_vec = y_vec self.y_coordname = y_coordname self.y_set_obj = y_instrument self.delete_fit_function() self.y_unit = y_unit def gen_fit_function(self, curve_f, curve_p, units = '', p0 = [-1,0.1,7]): ''' curve_f: 'parab', 'hyp', specifies the fit function to be employed curve_p: set of points that are the basis for the fit in the format [[x1,x2,x3,...],[y1,y2,y3,...]], frequencies in Hz units: set this to 'Hz' in order to avoid large values that cause the fit routine to diverge p0 (optional): start parameters for the fit, must be an 1D array of length 3 ([a,b,c]) adds a trace to landscape ''' if not self.landscape: self.landscape = [] x_fit = curve_p[0] if units == 'Hz': y_fit = np.array(curve_p[1])*1e-9 else: y_fit = np.array(curve_p[1]) try: if curve_f == 'parab': popt, pcov = curve_fit(self.f_parab, x_fit, y_fit, p0=p0) if units == 'Hz': self.landscape.append(1e9*self.f_parab(self.x_vec, *popt)) else: self.landscape.append(self.f_parab(self.x_vec, *popt)) elif curve_f == 'hyp': popt, pcov = curve_fit(self.f_hyp, x_fit, y_fit, p0=p0) if units == 'Hz': self.landscape.append(1e9*self.f_hyp(self.x_vec, *popt)) else: self.landscape.append(self.f_hyp(self.x_vec, *popt)) else: print 'function type not known...aborting' raise ValueError except Exception as message: print 'fit not successful:', message popt = p0 def delete_fit_function(self, n = None): ''' delete single fit function n (with 0 being the first one generated) or the complete landscape for n not specified ''' if n: self.landscape = np.delete(self.landscape, n, axis=0) else: self.landscape = None def plot_fit_function(self, num_points = 100): ''' try: x_coords = np.linspace(self.x_vec[0], self.x_vec[-1], num_points) except Exception as message: print 'no x axis information specified', message return ''' if self.landscape: for trace in self.landscape: try: #plt.clear() plt.plot(self.x_vec, trace) plt.fill_between(self.x_vec, trace+float(self.span)/2, trace-float(self.span)/2, alpha=0.5) except Exception: print 'invalid trace...skip' plt.axhspan(self.y_vec[0], self.y_vec[-1], facecolor='0.5', alpha=0.5) plt.show() else: print 'No trace generated.' def measure_1D(self): ''' measure central point of awg window while sweeping one parameter x ''' if not self.x_set_obj: logging.error('axes parameters not properly set...aborting') return self._scan_1D = True self._scan_1D2 = False self._scan_2D = False self.data_complex = False if self.dirname == None: self.dirname = self.x_coordname.replace() self._file_name = '1D_' + self.dirname if self.exp_name: self._file_name += '_' + self.exp_name self._p = Progress_Bar(len(self.x_vec),self.dirname) self._prepare_measurement_vna() if self.save_dat: self._prepare_measurement_dat_file() if self.save_hdf: self._prepare_measurement_hdf_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" qviewkit.plot(self._data_hdf.get_filepath(), datasets=['amplitude', 'phase']) if self._fit_resonator: self._resonator = resonator(self._data_hdf) self._measure() self._end_measurement() def measure_1D2(self): ''' measure full window of vna while sweeping x_set_obj with parameters x_vec ''' if not self.x_set_obj: logging.error('axes parameters not properly set...aborting') return self._scan_1D = False self._scan_1D2 = True self._scan_2D = False self.data_complex = False if self.dirname == None: self.dirname = self.x_coordname.replace() self._file_name = '1D2_' + self.dirname if self.exp_name: self._file_name += '_' + self.exp_name self._p = Progress_Bar(len(self.x_vec),self.dirname) self._prepare_measurement_vna() if self.save_dat: self._prepare_measurement_dat_file() if self.save_hdf: self._prepare_measurement_hdf_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" qviewkit.plot(self._data_hdf.get_filepath(), datasets=['amplitude', 'phase']) if self._fit_resonator: self._resonator = resonator(self._data_hdf) self._measure() self._end_measurement() def measure_2D(self): ''' measure full window of vna while sweeping x_set_obj and y_set_obj with parameters x_vec/y_vec. sweep over y_set_obj is the inner loop, for every value x_vec[i] all values y_vec are measured. optional: measure method to perform the measurement according to landscape, if set self.span is the range (in units of the vertical plot axis) data is taken around the specified funtion(s) note: make sure to have properly set x,y vectors before generating traces ''' if not self.x_set_obj or not self.y_set_obj: logging.error('axes parameters not properly set...aborting') return self._scan_1D = False self._scan_1D2 = False self._scan_2D = True self.data_complex = False if self.dirname == None: self.dirname = self.x_coordname.replace() + '_' + self.y_coordname.replace() self._file_name = '2D_' + self.dirname if self.exp_name: self._file_name += '_' + self.exp_name self._p = Progress_Bar(len(self.x_vec)*len(self.y_vec),self.dirname) self._prepare_measurement_vna() if self.save_dat: self._prepare_measurement_dat_file() if self.save_hdf: self._prepare_measurement_hdf_file() if self._fit_resonator: self._resonator = resonator(self._data_hdf) if self.landscape: self.center_freqs = np.array(self.landscape).T else: self.center_freqs = [] #load default sequence for i in range(len(self.x_vec)): self.center_freqs.append([0]) self._measure() self._end_measurement() def set_fit(self,fit_resonator=True,fit_function='',f_min=None,f_max=None): ''' sets fit parameter for resonator fit_resonator (bool): True or False, default: True (optional) fit_function (string): function which will be fitted to the data (optional) f_min (float): lower frequency boundary for the fitting function, default: None (optional) f_max (float): upper frequency boundary for the fitting function, default: None (optional) ''' if not fit_resonator: self._fit_resonator = False return self._functions = {'lorentzian':0,'skewed_lorentzian':1,'circle_fit':2,'fano':3,'all_fits':4} try: self._fit_function = self._functions[fit_function] except KeyError: logging.error('Fit function not properly set. Must be either \'lorentzian\', \'skewed_lorentzian\', \'circle_fit\', \'fano\', or \'all_fits\'.') else: self._fit_resonator = True self._f_min = f_min self._f_max = f_max def _do_fit_resonator(self): ''' calls fit function in resonator class fit function is specified in self.set_fit, with boundaries f_mim and f_max only the last 'slice' of data is fitted, since we fit live while measuring. ''' if self._fit_function == self._function['lorentzian']: self._resonator.fit_lorentzian(f_min=self._f_min, f_max = self._f_max) if self._fit_function == self._function['skewed_lorentzian']: self._resonator.fit_skewed_lorentzian(f_min=self._f_min, f_max = self._f_max) if self._fit_function == self._function['circle']: self._resonator.fit_circle(f_min=self._f_min, f_max = self._f_max) if self._fit_function == self._function['fano']: self._resonator.fit_fano(f_min=self._f_min, f_max = self._f_max) #if self._fit_function == self._function['all_fits']: #self._resonator.fit_all_fits(f_min=self._f_min, f_max = self._f_max) def _prepare_measurement_vna(self): ''' all the relevant settings from the vna are updated and called ''' self.vna.get_all() #ttip.get_temperature() self._nop = self.vna.get_nop() self._sweeptime_averages = self.vna.get_sweeptime_averages() self._freqpoints = self.vna.get_freqpoints() def _prepare_measurement_dat_file(self): ''' creates the output .dat-file with distict structure for each meaurement type. ''' self._data_dat = qt.Data(name=self._file_name) if self.comment: self._data_dat.add_comment(self.comment) self._data_dat.add_coordinate(self.x_coordname + ' '+self.x_unit) if self._scan_1D2: self._data_dat.add_coordinate('Frequency (Hz)') self._data_dat.add_value('Amplitude (V)') self._data_dat.add_value('Phase (pi)') if self.data_complex: self._data_dat.add_value('Real') self._data_dat.add_value('Img') else: if self._scan_2D: self._data_dat.add_coordinate(self.y_coordname + ' ' +self.y_unit) for i in range(1,self._nop+1): self._data_dat.add_value(('Point %i Amp' %i)) for i in range(1,self._nop+1): self._data_dat.add_value(('Point %i Pha' %i)) self._data_dat.create_file() def _prepare_measurement_hdf_file(self): ''' creates the output .h5-file with distinct dataset structures for each measurement type. the filename is borrowed from the .dat-file to put them into the same folder. at this point all measurement parameters are known and put in the output file ''' if self.save_dat: filename = str(self._data_dat.get_filepath()).replace('.dat','.h5') else: filename = str(self._file_name) + '.h5' self._data_hdf = hdf.Data(name=self._file_name, path=filename) self._hdf_freq = self._data_hdf.add_coordinate('frequency', unit = 'Hz') self._hdf_freq.add(self._freqpoints) self._hdf_x = self._data_hdf.add_coordinate(self.x_coordname, unit = self.x_unit) self._hdf_x.add(self.x_vec) self._hdf_real = self._data_hdf.add_value_vector('real', x = self._hdf_freq, unit = '') self._hdf_imag = self._data_hdf.add_value_vector('imag', x = self._hdf_freq, unit = '') if self._scan_2D: self._hdf_y = self._data_hdf.add_coordinate(self.y_coordname, unit = self.y_unit) self._hdf_y.add(self.y_vec) self._hdf_amp = self._data_hdf.add_value_box('amplitudes', x = self._hdf_x, y = self._hdf_y, z = self._hdf_freq, unit = 'a.u.') self._hdf_pha = self._data_hdf.add_value_box('phases', x = self._hdf_x, y = self._hdf_y, z = self._hdf_freq, unit = 'rad') else: self._hdf_amp = self._data_hdf.add_value_matrix('amplitude', x = self._hdf_x, y = self._hdf_freq, unit = 'a.u.') self._hdf_pha = self._data_hdf.add_value_matrix('phase', x = self._hdf_x, y = self._hdf_freq, unit='rad') if self.comment: self._data_hdf.add_comment(self.comment) def _measure(self): ''' measures and plots the data depending on the measurement type. the measurement loops feature the setting of the objects and saving the data in the .dat and/or .h5 files. ''' qt.mstart() if not self.save_hdf or self._scan_2D: ''' we use our own qviewkit for liveplotting the data. plotting a 2D scan is not yet implemented. ''' if self.plotlive: self._plot_dat_file() try: """ loop: x_obj with data from x_vec """ for i, x in enumerate(self.x_vec): self.x_set_obj(x) sleep(self.tdx) dat=[] if self._scan_2D: for y in self.y_vec: """ loop: x_obj with data from x_vec (only 2D measurement) """ if (np.min(np.abs(self.center_freqs[i]-y*np.ones(len(self.center_freqs[i])))) > self.span/2.) and self.landscape: #if point is not of interest (not close to one of the functions) data_amp = np.zeros(int(self._nop)) data_pha = np.zeros(int(self._nop)) #fill with zeros else: self.y_set_obj(y) sleep(self.tdy) self.vna.avg_clear() sleep(self._sweeptime_averages) """ measurement """ data_amp, data_pha = self.vna.get_tracedata() if self.save_dat: dat = np.append(x, y) dat = np.append(dat,data_amp) dat = np.append(dat,data_pha) self._data_dat.add_data_point(*dat) self._data_dat.new_block() if self.save_hdf: self._hdf_amp.append(data_amp) self._hdf_pha.append(data_pha) if self._fit_resonator: self._do_fit_resonator() if self.plotlive and not self.save_hdf: qt.msleep(0.1) self._plot_amp.update() self._plot_pha.update() if self.progress_bar: self._p.iterate() if self._scan_1D: self.vna.avg_clear() sleep(self._sweeptime_averages) """ measurement """ data_amp, data_pha = self.vna.get_tracedata() if self.save_dat: dat = np.append(x, data_amp) dat = np.append(dat, data_pha) self._data_dat.add_data_point(*dat) if self.save_hdf: self._hdf_amp.append(data_amp) self._hdf_pha.append(data_pha) if self._fit_resonator: self._do_fit_resonator() if self.progress_bar: self._p.iterate() if self._scan_1D2: self.vna.avg_clear() sleep(self._sweeptime_averages) """ measurement """ data_amp, data_pha = self.vna.get_tracedata() if self.save_dat: dat = np.append([x*np.ones(self._nop)],[self._freqpoints], axis = 0) dat = np.append(dat,[data_amp],axis = 0) dat = np.append(dat,[data_pha],axis = 0) self._data_dat.add_data_point(*dat) self._data_dat.new_block() if self.save_hdf: self._hdf_amp.append(data_amp) self._hdf_pha.append(data_pha) if self._fit_resonator: self._do_fit_resonator() if self.progress_bar: self._p.iterate() if not self.save_hdf and self.plotlive: qt.msleep(0.1) self._plot_amp.update() self._plot_pha.update() finally: if self.save_dat: if not self.plotlive: self._plot_dat_file() self._plot_amp.update() self._plot_pha.update() self._plot_amp.save_png() self._plot_amp.save_gp() self._plot_pha.save_png() self._plot_pha.save_gp() qt.mend() def _end_measurement(self): ''' the data files are closed and their filepaths are printed ''' if self.save_dat: print self._data_dat.get_filepath() self._data_dat.close_file() if self.save_hdf: print self._data_hdf.get_filepath() qviewkit.save_plots(self._data_hdf.get_filepath()) self._data_hdf.close_file() def _plot_dat_file(self): ''' plots measured data in gnuplot: 1D: amp/pha vs x_vec (1D plot for middle freqpoint) 1D2: freq vs x_vec (color plot with amp/pha trace color coded) 2D: x_vec vs y_vec (color plot with amp/pha of middle freqpoint color coded) ''' if self._scan_1D: self._plot_amp = qt.Plot2D(self._data_dat, name='Amplitude', coorddim=0, valdim=int(self._nop/2)+1) self._plot_pha = qt.Plot2D(self._data_dat, name='Phase', coorddim=0, valdim=self._nop+int(self._nop/2)+1) if self._scan_1D2: self._plot_amp = qt.Plot3D(self._data_dat, name='Amplitude 1D2', coorddims=(0,1), valdim=2, style=qt.Plot3D.STYLE_IMAGE) self._plot_amp.set_palette('bluewhitered') self._plot_pha = qt.Plot3D(self._data_dat, name='Phase 1D2', coorddims=(0,1), valdim=3, style=qt.Plot3D.STYLE_IMAGE) self._plot_pha.set_palette('bluewhitered') if self._scan_2D: if self.plot3D: self._plot_amp = qt.Plot3D(self._data_dat, name='Amplitude', coorddims=(0,1), valdim=int(self._nop/2)+2, style=qt.Plot3D.STYLE_IMAGE) self._plot_amp.set_palette('bluewhitered') self._plot_pha = qt.Plot3D(self._data_dat, name='Phase', coorddims=(0,1), valdim=int(self._nop/2)+2+self._nop, style=qt.Plot3D.STYLE_IMAGE) self._plot_pha.set_palette('bluewhitered') else: self._plot_amp = qt.Plot2D(self._data_dat, name='Amplitude', coorddim=1, valdim=int(self._nop/2)+2) self._plot_pha = qt.Plot2D(self._data_dat, name='Phase', coorddim=1, valdim=int(self._nop/2)+2+self._nop) def record_trace(self): ''' measure method to record a single (averaged) VNA trace, S11 or S21 according to the setting on the VNA returns frequency points, data_amp and data_pha when self.return_dat is set ''' qt.mstart() self._prepare_measurement_vna() self.vna.hold(0) #switch VNA to continuous mode print 'recording trace...' sys.stdout.flush() #use 1D2 functions self._scan_1D = False self._scan_1D2 = True self._scan_2D = False self.data_complex = True #creating data object self.dirname = 'VNA_tracedata' self._file_name = self.dirname if self.exp_name: self._file_name += '_' + self.exp_name self._prepare_measurement_dat_file() p = Progress_Bar(self.vna.get_averages(),self.dirname) self.vna.avg_clear() if self.vna.get_averages() == 1 or self.vna.get_Average == False: #no averaging qt.msleep(self.vna.get_sweeptime()) #wait single sweep else: p = Progress_Bar(self.vna.get_averages(),self.dirname) for a in range(self.vna.get_averages()): qt.msleep(self.vna.get_sweeptime()) #wait single sweep time p.iterate() data_amp, data_pha = self.vna.get_tracedata() data_real, data_imag = self.vna.get_tracedata('RealImag') if self.save_dat: for i in np.arange(self._nop): self._data_dat.add_data_point(self._freqpoints[i], data_amp[i], data_pha[i], data_real[i], data_imag[i]) if self.save_hdf: self._hdf_amp.append(data_amp) self._hdf_pha.append(data_pha) self._hdf_real.append(data_real) self._hdf_imag.append(data_imag) if self._fit_resonator: self._do_fit_resonator() plot_amp = qt.Plot2D(self._data.dat, name='amplitude', clear=True, needtempfile=True, autoupdate=True, coorddim=0, valdim=1) plot_pha = qt.Plot2D(self._data.dat, name='phase', clear=True, needtempfile=True, autoupdate=True, coorddim=0, valdim=2) plot_complex = qt.Plot2D(self._data.dat, name='Complex Plane', clear=True, needtempfile=True, autoupdate=True, coorddim=3, valdim=4) plot_amp.save_png() plot_amp.save_gp() plot_pha.save_png() plot_pha.save_gp() plot_complex.save_png() plot_complex.save_png() self._data.dat.close_file() qt.mend() #print 'Done.' if self.return_dat: return self._freqpoints, data_amp, data_pha def set_span(self, span): self.span = span def get_span(self): return self.span def set_tdx(self, tdx): self.tdx = tdx def set_tdy(self, tdy): self.tdy = tdy def get_tdx(self): return self.tdx def get_tdy(self): return self.tdy def f_parab(self,x,a,b,c): return a*(x-b)**2+c def f_hyp(self,x,a,b,c): return a*np.sqrt((x/b)**2+c)
class transport(object): ''' useage: m = transport(daq = 'DAQ') m.set_voltage_bias('True') m.set_measurement_setup_parameters(conversion_IV = 1e7, V_amp=1, I_div=1, V_divider=1000) m.set_measurement_parameters(start=-100e-6, stop=100e-7, sample_count=1000, sample_rate=200, sweeps = 3) m.set_x_parameters(arange(0.,1.5,0.05),'magnetic coil current', yoko.set_current, 'A') #m.set_y_parameters(arange(4e9,7e9,10e6),'excitation frequency', mw_src.set_frequency, 'Hz') #sweep-parameter only active for x.measure_IV()!! m.measure_IV_2D() ''' def __init__(self, daq, exp_name=''): self.daq = daq self.exp_name = exp_name self._chan_out = self.daq._ins._get_output_channels()[0] self._chan_in = self.daq._ins._get_input_channels()[0] self._tdx = 0.002 self._tdy = 0.002 self.comment = '' self._voltage_bias = False self._current_bias = True self._voltage_offset = 0. self._current_offset = 0. def set_x_parameters(self, x_vec, x_coordname, x_instrument, x_unit): self.x_vec = x_vec self.x_coordname = x_coordname self.x_set_obj = x_instrument self.x_unit = x_unit self._set_x_parameters = True def set_y_parameters(self, y_vec, y_coordname, y_instrument, y_unit): self.y_vec = y_vec self.y_coordname = y_coordname self.y_set_obj = y_instrument self.y_unit = y_unit self._set_y_parameters = True def set_measurement_setup_parameters(self, conversion_IV, V_amp, I_div, V_divider): self._conversion_IV = conversion_IV self._V_amp = V_amp self._I_div = I_div self._V_divider = V_divider self._set_measurement_setup_parameters = True def set_measurement_parameters(self, start, stop, sample_count, sample_rate, sweeps=1): self._start = start self._stop = stop self._sample_count = sample_count self._sample_rate = sample_rate self._sweeps = sweeps self._vec_fw = np.linspace(start, stop, sample_count) self._vec_bw = np.linspace(stop, start, sample_count) self._set_measurement_parameters = True def _check_measurement(self): if self._voltage_bias == self._current_bias: logging.error( 'Please specify current- or voltage-bias, both are %s...aborting' % (str(self._voltage_bias))) return False if not self._set_measurement_setup_parameters: logging.error('Please set_measurement_setup_parameters...aborting') return False if not self._set_measurement_parameters: logging.error('Please set_measurement_parameters...aborting') return False if self._measure_IV_2D or self._measure_IV_3D and not self._set_x_parameters: logging.error('Please set_x_parameters...aborting') return False if self._measure_IV_3D and not self._set_y_parameters: logging.error('Please set_y_parameters...aborting') return False return True def _save_settings(self): settings = "## Settings for measurement " + self._scan_name + ' ##\n' settings += "A per V = %f\nV_amp = %f\nI_div = %f\nV_div = %f\nsamples = %f\nrate = %f\nsweeps = %f\n" % ( float(self._conversion_factor), float(self._V_amp), float(self._I_div), float(self._V_div), float(self._sample_count), float(self._sample_rate), float(self._sweeps)) settings += 'Voltage bias = %s, Current bias = %s\n' % (str( self._voltage_bias), str(self._current_bias)) settings += "Min = %f \nMax = %f \n" % (self._start, self._stop) if not self._measure_IV: settings += "%s, %f-%f %s, step = %f\n" % ( self.x_instrument, self.x_vec[0], self.x_vec[len(self.x_vec) - 1], (self.x_vec[len(self.x_vec) - 1] - self.x_vec[0]) / (len(self.x_vec) - 1)) if self._measure_IV_3D: settings += "%s, %f-%f %s, step = %f\n" % ( self.y_instrument, self.y_vec[0], self.y_vec[len(self.y_vec) - 1], (self.y_vec[len(self.y_vec) - 1] - self.y_vec[0]) / (len(self.y_vec) - 1)) settings += "Current offset %f A\n" % (self._current_offset) settings += "Voltage offset %f V\n" % (self._voltage_offset) self._data.add_comment(settings) def measure_IV(self): self._measure_IV_1D = True self._measure_IV_2D = False self._measure_IV_3D = False if not self._check_measurement: return self._scan_name = 'IV' if self.exp_name: self._scan_name += '_' + self.exp_name self._p = Progress_Bar(self._sweeps) self._prepare_measurement_file() self._save_settings() #qviewkit.plot_hdf(self._data.get_filepath() self._measure() self._end_measurement() def measure_IV_2D(self): self._measure_IV_1D = False self._measure_IV_2D = True self._measure_IV_3D = False if not self._check_measurement: return self._scan_name = 'IV_vs_' + self.x_coordname if self.exp_name: self._scan_name += '_' + self.exp_name self._p = Progress_Bar(len(self.x_vec)) self._prepare_measurement_file() self._save_settings() #qviewkit.plot_hdf(self._data.get_filepath()) self._measure() self._end_measurement() def measure_IV_3D(self): self._measure_IV_1D = False self._measure_IV_2D = False self._measure_IV_3D = True if not self._check_measurement: return self._scan_name = 'IV_vs_' + self.x_coordname + '_' + self.y_coordname if self.exp_name: self._scan_name += '_' + self.exp_name self._p = Progress_Bar(len(self.x_vec) * len(self.y_vec)) self._prepare_measurement_file() self._save_settings() #qviewkit.plot_hdf(self._data.filepath()) self._measure() self._end_measurement() def _prepare_measurement_file(self): self._data = hdf.Data(name=self._scan_name) if self._voltage_bias: bias_name = 'Voltage' bias_unit = 'V' measurement_name = 'Current' measurement_unit = 'A' if self._current_bias: bias_name = 'Current' bias_unit = 'A' measurement_name = 'Voltage' measurement_unit = 'V' self._data_bias = self._data.add_coordinate(bias_name, unit=bias_unit) bias_vec = np.append(self._vec_fw, self._vec_bw) self._data_bias.add(bias_vec) if self._measure_IV_1D: if self._sweeps == 1: self._data_measure = self._data.add_value_vector( measurement_name, x=self._hdf_bias, unit=measurement_unit) else: self._data_sweep = self._data.add_coordinate('sweep') self._data_sweep.add([sweep for sweep in self._sweeps]) self._data_measure = self._data.add_value_matrix( measurement_name, x=self._data_sweep, y=self._hdf_bias, unit=measurement_unit) if self._measure_IV_2D: self._data_x = self._dat.add_coordinate(self.x_coordname, unit=self.x_unit) self._data_x.add(self.x_vec) self._data_measure = self._data.add_value_matrix( measurement_name, x=self._x, y=self._hdf_bias, unit=measurement_unit) if self._measure_IV_3D: self._data_x = self._data.add_coordinate(self.x_coordname, unit=self.x_unit) self._data_x.add(self.x_vec) self._data_y = self._data.add_coordinate(self.y_coordname, unit=self.y_unit) self._data_y.add(self.y_vec) self._data_measure = self._data.add_value_box( measurement_name, x=self._data_x, y=self._data_y, z=self._hdf_bias, unit=measurement_unit) if self.comment: self._data.add_comment(self.comment) def _measure(self): qt.mstart() plt.gca().set_xlabel("V [uV]") plt.gca().set_ylabel("I [nA]") try: if self._measure_IV_1D: for self._sweep in np.arange(self._sweeps): if self._current_bias: self._take_IV( out_conversion_factor=self._conversion_IV, in_amplification=self._V_amp, out_divider=self._V_divider) if self._voltage_bias: self._take_IV(out_conversion_factor=1, in_amplification=self._conversion_IV, out_divider=self._V_divider) self._p_iterate() if self._measure_IV_2D or self._measure_IV_3D: for self._x in self.x_vec: self.x_set_obj(self._x) sleep(self._tdx) if self._measure_IV_2D: if self._current_bias: self._take_IV( out_conversion_factor=self._conversion_IV, in_amplification=self._V_amp, out_divider=self._V_divider) if self._voltage_bias: self._take_IV(out_conversion_factor=1, in_amplification=self._conversion_IV, out_divider=self._V_divider) self._p.iterate() if self._measure_IV_3D: for self._y in self.y_vec: self.y_set_obj(self._y) sleep(self._tdy) if self._current_bias: self._take_IV( out_conversion_factor=self._conversion_IV, in_amplification=self._V_amp, out_divider=self._V_divider) if self._voltage_bias: self._take_IV( out_conversion_factor=1, in_amplification=self._conversion_IV, out_divider=self._V_divider) self._p.iterate() finally: self.daq.set_ao1(0) qt.mend() def _end_measurement(self): print self._data.get_filepath() self._data.close_file() def _take_IV(self, out_conversion_factor, in_amplification, out_divider=1): """ IV measurement with current or voltage (vec_fw, vec_bw)!""" data_IV = self.daq.sync_output_input(self._chan_out, self._chan_in, self._vec_fw * out_divider / out_conversion_factor, rate=self._sample_rate) if self._current_bias: self._pl1 = plt.plot((data_IV / in_amplification) * 1e6, self._vec_fw * 1e6, "o") else: self._pl1 = plt.plot(self._vec_fw * 1e6, (data_IV / in_amplification) * 1e9, "-") qt.msleep(0.1) data_measure = [] data_measure = np.append(data_measure, data_IV / in_amplification) data_IV = self.daq.sync_output_input(self._chan_out, self._chan_in, self._vec_bw * out_divider / out_conversion_factor, rate=self._sample_rate) if self._current_bias: self._pl1 = plt.plot((data_IV / in_amplification) * 1e6, self._vec_fw * 1e6, "o") else: self._pl1 = plt.plot(self._vec_fw * 1e6, (data_IV / in_amplification) * 1e9, "-") qt.msleep(0.1) data_measure = np.append(data_measure, data_IV / in_amplification) self._data_measure.append(data_measure) def set_tdx(self, tdx): self._tdx = tdx def set_tdy(self, tdy): self._tdy = tdy def get_tdx(self): return self.tdx def get_tdy(self): return self.tdy def set_voltage_bias(self, voltage_bias): self._voltage_bias = voltage_bias self._current_bias = not voltage_bias def get_voltage_bias(self): return self._voltage_bias def set_current_bias(self, current_bias): self._current_bias = current_bias self._voltage_bias = not current_bias def get_current_bias(self): return self._current_bias def set_voltage_offset(self, voltage_offset): self._voltage_offset = voltage_offset def set_current_offset(self, voltage_offset): self._current_offset = voltage_offset def get_voltage_offset(self): return self._voltgae_offset def get_current_offset(self): return self._current_offset
def record_trace(self): ''' measure method to record a single (averaged) VNA trace, S11 or S21 according to the setting on the VNA returns frequency points, data_amp and data_pha when self.return_dat is set ''' qt.mstart() self._prepare_measurement_vna() self.vna.hold(0) #switch VNA to continuous mode print 'recording trace...' sys.stdout.flush() #use 1D2 functions self._scan_1D = False self._scan_1D2 = True self._scan_2D = False self.data_complex = True #creating data object self.dirname = 'VNA_tracedata' self._file_name = self.dirname if self.exp_name: self._file_name += '_' + self.exp_name self._prepare_measurement_dat_file() p = Progress_Bar(self.vna.get_averages(),self.dirname) self.vna.avg_clear() if self.vna.get_averages() == 1 or self.vna.get_Average == False: #no averaging qt.msleep(self.vna.get_sweeptime()) #wait single sweep else: p = Progress_Bar(self.vna.get_averages(),self.dirname) for a in range(self.vna.get_averages()): qt.msleep(self.vna.get_sweeptime()) #wait single sweep time p.iterate() data_amp, data_pha = self.vna.get_tracedata() data_real, data_imag = self.vna.get_tracedata('RealImag') if self.save_dat: for i in np.arange(self._nop): self._data_dat.add_data_point(self._freqpoints[i], data_amp[i], data_pha[i], data_real[i], data_imag[i]) if self.save_hdf: self._hdf_amp.append(data_amp) self._hdf_pha.append(data_pha) self._hdf_real.append(data_real) self._hdf_imag.append(data_imag) if self._fit_resonator: self._do_fit_resonator() plot_amp = qt.Plot2D(self._data.dat, name='amplitude', clear=True, needtempfile=True, autoupdate=True, coorddim=0, valdim=1) plot_pha = qt.Plot2D(self._data.dat, name='phase', clear=True, needtempfile=True, autoupdate=True, coorddim=0, valdim=2) plot_complex = qt.Plot2D(self._data.dat, name='Complex Plane', clear=True, needtempfile=True, autoupdate=True, coorddim=3, valdim=4) plot_amp.save_png() plot_amp.save_gp() plot_pha.save_png() plot_pha.save_gp() plot_complex.save_png() plot_complex.save_png() self._data.dat.close_file() qt.mend() #print 'Done.' if self.return_dat: return self._freqpoints, data_amp, data_pha
class spectrum(object): ''' usage: m = spectrum(vna = vna1) m.set_x_parameters(arange(-0.05,0.05,0.01),'flux coil current',coil.set_current, unit = 'mA') m.set_y_parameters(arange(4e9,7e9,10e6),'excitation frequency',mw_src1.set_frequency, unit = 'Hz') m.gen_fit_function(...) several times m.measure_XX() ''' def __init__(self, vna, exp_name = '', sample = None): self.vna = vna self.averaging_start_ready = "start_measurement" in self.vna.get_function_names() and "ready" in self.vna.get_function_names() if not self.averaging_start_ready: logging.warning(__name__ + ': With your VNA instrument driver (' + self.vna.get_type() + '), I can not see when a measurement is complete. So I only wait for a specific time and hope the VNA has finished. Please consider implemeting the necessary functions into your driver.') self.exp_name = exp_name self._sample = sample self.landscape = None self.span = 200e6 #[Hz] self.tdx = 0.002 #[s] self.tdy = 0.002 #[s] self.comment = '' self.dirname = None self.x_set_obj = None self.y_set_obj = None self.progress_bar = True self._fit_resonator = False self._plot_comment="" self.set_log_function() self.open_qviewkit = True self.qviewkit_singleInstance = False self._measurement_object = Measurement() self._measurement_object.measurement_type = 'spectroscopy' self._measurement_object.sample = self._sample self._qvk_process = False self.number_of_timetraces = 1 #relevant in time domain mode def set_log_function(self, func=None, name = None, unit = None, log_dtype = None): ''' A function (object) can be passed to the measurement loop which is excecuted before every x iteration but after executing the x_object setter in 2D measurements and before every line (but after setting the x value) in 3D measurements. The return value of the function of type float or similar is stored in a value vector in the h5 file. Call without any arguments to delete all log functions. The timestamp is automatically saved. func: function object in list form name: name of logging parameter appearing in h5 file, default: 'log_param' unit: unit of logging parameter, default: '' log_dtype: h5 data type, default: 'f' (float32) ''' if name == None: try: name = ['log_param']*len(func) except Exception: name = None if unit == None: try: unit = ['']*len(func) except Exception: unit = None if log_dtype == None: try: log_dtype = ['f']*len(func) except Exception: log_dtype = None self.log_function = [] self.log_name = [] self.log_unit = [] self.log_dtype = [] if func != None: for i,f in enumerate(func): self.log_function.append(f) self.log_name.append(name[i]) self.log_unit.append(unit[i]) self.log_dtype.append(log_dtype[i]) def set_x_parameters(self, x_vec, x_coordname, x_set_obj, x_unit = ""): ''' Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep. For every x value all y values are swept Input: x_vec (array): conains the sweeping values x_coordname (string) x_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power()) x_unit (string): optional ''' self.x_vec = x_vec self.x_coordname = x_coordname self.x_set_obj = x_set_obj self.delete_fit_function() self.x_unit = x_unit def set_y_parameters(self, y_vec, y_coordname, y_set_obj, y_unit = ""): ''' Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep. For every x value all y values are swept Input: y_vec (array): contains the sweeping values y_coordname (string) y_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power()) y_unit (string): optional ''' self.y_vec = y_vec self.y_coordname = y_coordname self.y_set_obj = y_set_obj self.delete_fit_function() self.y_unit = y_unit def gen_fit_function(self, curve_f, curve_p, units = '', p0 = [-1,0.1,7]): ''' curve_f: 'parab', 'hyp', specifies the fit function to be employed curve_p: set of points that are the basis for the fit in the format [[x1,x2,x3,...],[y1,y2,y3,...]], frequencies in Hz units: set this to 'Hz' in order to avoid large values that cause the fit routine to diverge p0 (optional): start parameters for the fit, must be an 1D array of length 3 ([a,b,c,d]), where for the parabula p0[3] will be ignored The parabolic function takes the form y = a*(x-b)**2 + c , where (a,b,c) = p0 The hyperbolic function takes the form y = sqrt[ a*(x-b)**2 + c ], where (a,b,c) = p0 adds a trace to landscape ''' if not self.landscape: self.landscape = [] x_fit = curve_p[0] if units == 'Hz': y_fit = np.array(curve_p[1])*1e-9 else: y_fit = np.array(curve_p[1]) try: if curve_f == 'parab': "remove the last item" #p0.pop() popt, pcov = curve_fit(self.f_parab, x_fit, y_fit, p0=p0) if units == 'Hz': self.landscape.append(1e9*self.f_parab(self.x_vec, *popt)) else: self.landscape.append(self.f_parab(self.x_vec, *popt)) elif curve_f == 'hyp': popt, pcov = curve_fit(self.f_hyp, x_fit, y_fit, p0=p0) print popt if units == 'Hz': self.landscape.append(1e9*self.f_hyp(self.x_vec, *popt)) else: self.landscape.append(self.f_hyp(self.x_vec, *popt)) else: print 'function type not known...aborting' raise ValueError except Exception as message: print 'fit not successful:', message popt = p0 def _prepare_measurement_vna(self): ''' all the relevant settings from the vna are updated and called ''' self.vna.get_all() #ttip.get_temperature() self._nop = self.vna.get_nop() self._sweeptime_averages = self.vna.get_sweeptime_averages() self._freqpoints = self.vna.get_freqpoints() if self.averaging_start_ready: self.vna.pre_measurement() def _prepare_measurement_file(self): ''' creates the output .h5-file with distinct dataset structures for each measurement type. at this point all measurement parameters are known and put in the output file ''' self._data_file = hdf.Data(name=self._file_name) self._measurement_object.uuid = self._data_file._uuid self._measurement_object.hdf_relpath = self._data_file._relpath self._measurement_object.instruments = qt.instruments.get_instruments() self._measurement_object.save() self._mo = self._data_file.add_textlist('measurement') self._mo.append(self._measurement_object.get_JSON()) # write logfile and instrument settings self._write_settings_dataset() self._log = waf.open_log_file(self._data_file.get_filepath()) if not self._scan_time: self._data_freq = self._data_file.add_coordinate('frequency', unit = 'Hz') self._data_freq.add(self._freqpoints) if self._scan_1D: self._data_real = self._data_file.add_value_vector('real', x = self._data_freq, unit = '', save_timestamp = True) self._data_imag = self._data_file.add_value_vector('imag', x = self._data_freq, unit = '', save_timestamp = True) self._data_amp = self._data_file.add_value_vector('amplitude', x = self._data_freq, unit = 'arb. unit', save_timestamp = True) self._data_pha = self._data_file.add_value_vector('phase', x = self._data_freq, unit = 'rad', save_timestamp = True) if self._scan_2D: self._data_x = self._data_file.add_coordinate(self.x_coordname, unit = self.x_unit) self._data_x.add(self.x_vec) self._data_amp = self._data_file.add_value_matrix('amplitude', x = self._data_x, y = self._data_freq, unit = 'arb. unit', save_timestamp = True) self._data_pha = self._data_file.add_value_matrix('phase', x = self._data_x, y = self._data_freq, unit='rad', save_timestamp = True) if self.log_function != None: #use logging self._log_value = [] for i in range(len(self.log_function)): self._log_value.append(self._data_file.add_value_vector(self.log_name[i], x = self._data_x, unit = self.log_unit[i], dtype=self.log_dtype[i])) if self._nop < 10: """creates view: plot middle point vs x-parameter, for qubit measurements""" self._data_amp_mid = self._data_file.add_value_vector('amplitude_midpoint', unit = 'arb. unit', x = self._data_x, save_timestamp = True) self._data_pha_mid = self._data_file.add_value_vector('phase_midpoint', unit = 'rad', x = self._data_x, save_timestamp = True) #self._view = self._data_file.add_view("amplitude vs. " + self.x_coordname, x = self._data_x, y = self._data_amp[self._nop/2]) if self._scan_3D: self._data_x = self._data_file.add_coordinate(self.x_coordname, unit = self.x_unit) self._data_x.add(self.x_vec) self._data_y = self._data_file.add_coordinate(self.y_coordname, unit = self.y_unit) self._data_y.add(self.y_vec) if self._nop == 0: #saving in a 2D matrix instead of a 3D box HR: does not work yet !!! test things before you put them online. self._data_amp = self._data_file.add_value_matrix('amplitude', x = self._data_x, y = self._data_y, unit = 'arb. unit', save_timestamp = False) self._data_pha = self._data_file.add_value_matrix('phase', x = self._data_x, y = self._data_y, unit = 'rad', save_timestamp = False) else: self._data_amp = self._data_file.add_value_box('amplitude', x = self._data_x, y = self._data_y, z = self._data_freq, unit = 'arb. unit', save_timestamp = False) self._data_pha = self._data_file.add_value_box('phase', x = self._data_x, y = self._data_y, z = self._data_freq, unit = 'rad', save_timestamp = False) if self.log_function != None: #use logging self._log_value = [] for i in range(len(self.log_function)): self._log_value.append(self._data_file.add_value_vector(self.log_name[i], x = self._data_x, unit = self.log_unit[i], dtype=self.log_dtype[i])) if self._scan_time: self._data_freq = self._data_file.add_coordinate('frequency', unit = 'Hz') self._data_freq.add([self.vna.get_centerfreq()]) self._data_time = self._data_file.add_coordinate('time', unit = 's') self._data_time.add(np.arange(0,self._nop,1)*self.vna.get_sweeptime()/(self._nop-1)) self._data_x = self._data_file.add_coordinate('trace_number', unit = '') self._data_x.add(np.arange(0, self.number_of_timetraces, 1)) self._data_amp = self._data_file.add_value_matrix('amplitude', x = self._data_x, y = self._data_time, unit = 'lin. mag.', save_timestamp = False) self._data_pha = self._data_file.add_value_matrix('phase', x = self._data_x, y = self._data_time, unit = 'rad.', save_timestamp = False) if self.comment: self._data_file.add_comment(self.comment) if self.qviewkit_singleInstance and self.open_qviewkit and self._qvk_process: self._qvk_process.terminate() #terminate an old qviewkit instance def _write_settings_dataset(self): self._settings = self._data_file.add_textlist('settings') settings = waf.get_instrument_settings(self._data_file.get_filepath()) self._settings.append(settings) def measure_1D(self, rescan = True, web_visible = True): ''' measure method to record a single (averaged) VNA trace, S11 or S21 according to the setting on the VNA rescan: If True (default), the averages on the VNA are cleared and a new measurement is started. If False, it will directly take the data from the VNA without waiting. ''' self._scan_1D = True self._scan_2D = False self._scan_3D = False self._scan_time = False self._measurement_object.measurement_func = 'measure_1D' self._measurement_object.x_axis = 'frequency' self._measurement_object.y_axis = '' self._measurement_object.z_axis = '' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = 'VNA_tracedata' self._file_name = self.dirname.replace(' ', '').replace(',','_') if self.exp_name: self._file_name += '_' + self.exp_name self._prepare_measurement_vna() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase']) if self._fit_resonator: self._resonator = resonator(self._data_file.get_filepath()) print 'recording trace...' sys.stdout.flush() qt.mstart() if rescan: if self.averaging_start_ready: self.vna.start_measurement() ti = time() if self.progress_bar: self._p = Progress_Bar(self.vna.get_averages(),self.dirname,self.vna.get_sweeptime()) qt.msleep(.2) while not self.vna.ready(): if time()-ti > self.vna.get_sweeptime(query=False): if self.progress_bar: self._p.iterate() ti = time() qt.msleep(.2) if self.progress_bar: while self._p.progr < self._p.max_it: self._p.iterate() else: self.vna.avg_clear() if self.vna.get_averages() == 1 or self.vna.get_Average() == False: #no averaging if self.progress_bar:self._p = Progress_Bar(1,self.dirname,self.vna.get_sweeptime()) qt.msleep(self.vna.get_sweeptime()) #wait single sweep if self.progress_bar: self._p.iterate() else: #with averaging if self.progress_bar: self._p = Progress_Bar(self.vna.get_averages(),self.dirname,self.vna.get_sweeptime()) if "avg_status" in self.vna.get_function_names(): for a in range(self.vna.get_averages()): while self.vna.avg_status() <= a: qt.msleep(.2) #maybe one would like to adjust this at a later point if self.progress_bar: self._p.iterate() else: #old style for a in range(self.vna.get_averages()): qt.msleep(self.vna.get_sweeptime()) #wait single sweep time if self.progress_bar: self._p.iterate() data_amp, data_pha = self.vna.get_tracedata() data_real, data_imag = self.vna.get_tracedata('RealImag') self._data_amp.append(data_amp) self._data_pha.append(data_pha) self._data_real.append(data_real) self._data_imag.append(data_imag) if self._fit_resonator: self._do_fit_resonator() qt.mend() self._end_measurement() def measure_2D(self, web_visible = True): ''' measure method to record a (averaged) VNA trace, S11 or S21 according to the setting on the VNA for all parameters x_vec in x_obj ''' if not self.x_set_obj: logging.error('axes parameters not properly set...aborting') return self._scan_1D = False self._scan_2D = True self._scan_3D = False self._scan_time = False self._measurement_object.measurement_func = 'measure_2D' self._measurement_object.x_axis = self.x_coordname self._measurement_object.y_axis = 'frequency' self._measurement_object.z_axis = '' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = self.x_coordname self._file_name = '2D_' + self.dirname.replace(' ', '').replace(',','_') if self.exp_name: self._file_name += '_' + self.exp_name if self.progress_bar: self._p = Progress_Bar(len(self.x_vec),'2D VNA sweep '+self.dirname,self.vna.get_sweeptime_averages()) self._prepare_measurement_vna() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" if self._nop < 10: if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude_midpoint', 'phase_midpoint']) else: if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase']) if self._fit_resonator: self._resonator = resonator(self._data_file.get_filepath()) self._measure() def measure_3D(self, web_visible = True): ''' measure full window of vna while sweeping x_set_obj and y_set_obj with parameters x_vec/y_vec. sweep over y_set_obj is the inner loop, for every value x_vec[i] all values y_vec are measured. optional: measure method to perform the measurement according to landscape, if set self.span is the range (in units of the vertical plot axis) data is taken around the specified funtion(s) note: make sure to have properly set x,y vectors before generating traces ''' if not self.x_set_obj or not self.y_set_obj: logging.error('axes parameters not properly set...aborting') return self._scan_1D = False self._scan_2D = False self._scan_3D = True self._scan_time = False self._measurement_object.measurement_func = 'measure_3D' self._measurement_object.x_axis = self.x_coordname self._measurement_object.y_axis = self.y_coordname self._measurement_object.z_axis = 'frequency' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = self.x_coordname + ', ' + self.y_coordname self._file_name = '3D_' + self.dirname.replace(' ', '').replace(',','_') if self.exp_name: self._file_name += '_' + self.exp_name if self.progress_bar: self._p = Progress_Bar(len(self.x_vec)*len(self.y_vec),'3D VNA sweep '+self.dirname,self.vna.get_sweeptime_averages()) self._prepare_measurement_vna() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" """only middle point in freq array is plotted vs x and y""" if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase']) if self._fit_resonator: self._resonator = resonator(self._data_file.get_filepath()) if self.landscape: self.center_freqs = np.array(self.landscape).T else: self.center_freqs = [] #load default sequence for i in range(len(self.x_vec)): self.center_freqs.append([0]) self._measure() def measure_timetrace(self, web_visible = True): ''' measure method to record a single VNA timetrace, this only makes sense when span is set to 0 Hz!, tested only with KEYSIGHT E5071C ENA and its corresponding qkit driver LGruenhaupt 11/2016 ''' if self.vna.get_span() > 0: print 'VNA span not set to 0 Hz... aborting' return self._scan_1D = False self._scan_2D = False self._scan_3D = False self._scan_time = True self._measurement_object.measurement_func = 'measure_timetrace' self._measurement_object.x_axis = 'time' self._measurement_object.y_axis = '' self._measurement_object.z_axis = '' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = 'VNA_timetrace' self._file_name = self.dirname.replace(' ', '').replace(',','_') if self.exp_name: self._file_name += '_' + self.exp_name self.x_vec = np.arange(0,self.number_of_timetraces,1) self._prepare_measurement_vna() self._prepare_measurement_file() if self.progress_bar: self._p = Progress_Bar(self.number_of_timetraces,'VNA timetrace '+self.dirname,self.vna.get_sweeptime_averages()) print 'recording timetrace(s)...' sys.stdout.flush() qt.mstart() try: """ loop: x_obj with parameters from x_vec """ for i, x in enumerate(self.x_vec): if self.log_function != None: for i,f in enumerate(self.log_function): self._log_value[i].append(float(f())) if self.averaging_start_ready: #LG 11/2016 self.vna.start_measurement() ti = time() #changed from time.time() to time() - LGruenhaupt OCT_2016 tp = time() #added to enable use of progress bar #self._p = Progress_Bar(self.vna.get_averages(),self.dirname,self.vna.get_sweeptime_averages()) moved to line 303 ''' This is to prevent the vna.ready() function from timing out. LG NOV/16 ''' if self.vna.get_Average(): print 'this function only makes sense without averaging' qt.mend() self._end_measuremt() else: #self._p = Progress_Bar(1,self.dirname,self.vna.get_sweeptime()) qt.msleep(self.vna.get_sweeptime()) #self._p.iterate() while not self.vna.ready(): qt.msleep(.2) #this is just to check if the measurement has finished data_amp, data_pha = self.vna.get_tracedata() self._data_amp.append(data_amp) self._data_pha.append(data_pha) qt.msleep() if self.progress_bar: self._p.iterate() else: print 'not implemented for this VNA, only works with Keysight ENA 5071C' qt.mend() self._end_measurement() except Exception as e: print e.__doc__ print e.message finally: self._end_measurement() qt.mend() def _measure(self): ''' measures and plots the data depending on the measurement type. the measurement loops feature the setting of the objects and saving the data in the .h5 file. ''' qt.mstart() try: """ loop: x_obj with parameters from x_vec """ for ix, x in enumerate(self.x_vec): self.x_set_obj(x) sleep(self.tdx) if self.log_function != None: for i,f in enumerate(self.log_function): self._log_value[i].append(float(f())) if self._scan_3D: for y in self.y_vec: """ loop: y_obj with parameters from y_vec (only 3D measurement) """ if (np.min(np.abs(self.center_freqs[ix]-y*np.ones(len(self.center_freqs[ix])))) > self.span/2.) and self.landscape: #if point is not of interest (not close to one of the functions) data_amp = np.zeros(int(self._nop)) data_pha = np.zeros(int(self._nop)) #fill with zeros else: self.y_set_obj(y) sleep(self.tdy) if self.averaging_start_ready: self.vna.start_measurement() qt.msleep(.2) #just to make sure, the ready command does not *still* show ready while not self.vna.ready(): qt.msleep(.2) else: self.vna.avg_clear() qt.msleep(self._sweeptime_averages) #if "avg_status" in self.vna.get_function_names(): # while self.vna.avg_status() < self.vna.get_averages(): # qt.msleep(.2) #maybe one would like to adjust this at a later point """ measurement """ data_amp, data_pha = self.vna.get_tracedata() if self._nop == 0: # this does not work yet. print data_amp[0], data_amp, self._nop self._data_amp.append(data_amp[0]) self._data_pha.append(data_pha[0]) else: self._data_amp.append(data_amp) self._data_pha.append(data_pha) if self._fit_resonator: self._do_fit_resonator() if self.progress_bar: self._p.iterate() qt.msleep() """ filling of value-box is done here. after every y-loop the data is stored the next 2d structure """ self._data_amp.next_matrix() self._data_pha.next_matrix() if self._scan_2D: if self.averaging_start_ready: self.vna.start_measurement() qt.msleep(.2) #just to make sure, the ready command does not *still* show ready while not self.vna.ready(): qt.msleep(.2) else: self.vna.avg_clear() qt.msleep(self._sweeptime_averages) """ measurement """ data_amp, data_pha = self.vna.get_tracedata() self._data_amp.append(data_amp) self._data_pha.append(data_pha) if self._nop < 10: #print data_amp[self._nop/2] self._data_amp_mid.append(float(data_amp[self._nop/2])) self._data_pha_mid.append(float(data_pha[self._nop/2])) if self._fit_resonator: self._do_fit_resonator() if self.progress_bar: self._p.iterate() qt.msleep() except Exception as e: print e.__doc__ print e.message finally: self._end_measurement() qt.mend() def _end_measurement(self): ''' the data file is closed and filepath is printed ''' print self._data_file.get_filepath() #qviewkit.save_plots(self._data_file.get_filepath(),comment=self._plot_comment) #old version where we have to wait for the plots t = threading.Thread(target=qviewkit.save_plots,args=[self._data_file.get_filepath(),self._plot_comment]) t.start() self._data_file.close_file() waf.close_log_file(self._log) self.dirname = None if self.averaging_start_ready: self.vna.post_measurement() def set_resonator_fit(self,fit_resonator=True,fit_function='',f_min=None,f_max=None): ''' sets fit parameter for resonator fit_resonator (bool): True or False, default: True (optional) fit_function (string): function which will be fitted to the data (optional) f_min (float): lower frequency boundary for the fitting function, default: None (optional) f_max (float): upper frequency boundary for the fitting function, default: None (optional) fit types: 'lorentzian','skewed_lorentzian','circle_fit_reflection', 'circle_fit_notch','fano' ''' if not fit_resonator: self._fit_resonator = False return self._functions = {'lorentzian':0,'skewed_lorentzian':1,'circle_fit_reflection':2,'circle_fit_notch':3,'fano':5,'all_fits':5} try: self._fit_function = self._functions[fit_function] except KeyError: logging.error('Fit function not properly set. Must be either \'lorentzian\', \'skewed_lorentzian\', \'circle_fit_reflection\', \'circle_fit_notch\', \'fano\', or \'all_fits\'.') else: self._fit_resonator = True self._f_min = f_min self._f_max = f_max def _do_fit_resonator(self): ''' calls fit function in resonator class fit function is specified in self.set_fit, with boundaries f_mim and f_max only the last 'slice' of data is fitted, since we fit live while measuring. ''' if self._fit_function == 0: #lorentzian self._resonator.fit_lorentzian(f_min=self._f_min, f_max = self._f_max) if self._fit_function == 1: #skewed_lorentzian self._resonator.fit_skewed_lorentzian(f_min=self._f_min, f_max = self._f_max) if self._fit_function == 2: #circle_reflection self._resonator.fit_circle(reflection = True, f_min=self._f_min, f_max = self._f_max) if self._fit_function == 3: #circle_notch self._resonator.fit_circle(notch = True, f_min=self._f_min, f_max = self._f_max) if self._fit_function == 4: #fano self._resonator.fit_fano(f_min=self._f_min, f_max = self._f_max) #if self._fit_function == 5: #all fits #self._resonator.fit_all_fits(f_min=self._f_min, f_max = self._f_max) def delete_fit_function(self, n = None): ''' delete single fit function n (with 0 being the first one generated) or the complete landscape for n not specified ''' if n: self.landscape = np.delete(self.landscape, n, axis=0) else: self.landscape = None def plot_fit_function(self, num_points = 100): ''' try: x_coords = np.linspace(self.x_vec[0], self.x_vec[-1], num_points) except Exception as message: print 'no x axis information specified', message return ''' if self.landscape: for trace in self.landscape: try: #plt.clear() plt.plot(self.x_vec, trace) plt.fill_between(self.x_vec, trace+float(self.span)/2, trace-float(self.span)/2, alpha=0.5) except Exception: print 'invalid trace...skip' plt.axhspan(self.y_vec[0], self.y_vec[-1], facecolor='0.5', alpha=0.5) plt.show() else: print 'No trace generated.' def set_span(self, span): self.span = span def get_span(self): return self.span def set_tdx(self, tdx): self.tdx = tdx def set_tdy(self, tdy): self.tdy = tdy def get_tdx(self): return self.tdx def get_tdy(self): return self.tdy def f_parab(self,x,a,b,c): return a*(x-b)**2+c def f_hyp(self,x,a,b,c): "hyperbolic function with the form y = sqrt[ a*(x-b)**2 + c ]" return np.sqrt(a*(x-b)**2+c) def set_plot_comment(self, comment): ''' Small comment to add at the end of plot pics for more information i.e. good for wiki entries. ''' self._plot_comment=comment
def measure_timetrace(self, web_visible=True): ''' measure method to record a single VNA timetrace, this only makes sense when span is set to 0 Hz!, tested only with KEYSIGHT E5071C ENA and its corresponding qkit driver LGruenhaupt 11/2016 ''' if self.vna.get_span() > 0: print 'VNA span not set to 0 Hz... aborting' return self._scan_1D = False self._scan_2D = False self._scan_3D = False self._scan_time = True self._measurement_object.measurement_func = 'measure_timetrace' self._measurement_object.x_axis = 'time' self._measurement_object.y_axis = '' self._measurement_object.z_axis = '' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = 'VNA_timetrace' self._file_name = self.dirname.replace(' ', '').replace(',', '_') if self.exp_name: self._file_name += '_' + self.exp_name self.x_vec = np.arange(0, self.number_of_timetraces, 1) self._prepare_measurement_vna() self._prepare_measurement_file() if self.progress_bar: self._p = Progress_Bar(self.number_of_timetraces, 'VNA timetrace ' + self.dirname, self.vna.get_sweeptime_averages()) print 'recording timetrace(s)...' sys.stdout.flush() qt.mstart() try: """ loop: x_obj with parameters from x_vec """ for i, x in enumerate(self.x_vec): if self.log_function != None: for i, f in enumerate(self.log_function): self._log_value[i].append(float(f())) if self.averaging_start_ready: #LG 11/2016 self.vna.start_measurement() ti = time( ) #changed from time.time() to time() - LGruenhaupt OCT_2016 tp = time() #added to enable use of progress bar #self._p = Progress_Bar(self.vna.get_averages(),self.dirname,self.vna.get_sweeptime_averages()) moved to line 303 ''' This is to prevent the vna.ready() function from timing out. LG NOV/16 ''' if self.vna.get_Average(): print 'this function only makes sense without averaging' qt.mend() self._end_measuremt() else: #self._p = Progress_Bar(1,self.dirname,self.vna.get_sweeptime()) qt.msleep(self.vna.get_sweeptime()) #self._p.iterate() while not self.vna.ready(): qt.msleep( .2 ) #this is just to check if the measurement has finished data_amp, data_pha = self.vna.get_tracedata() self._data_amp.append(data_amp) self._data_pha.append(data_pha) qt.msleep() if self.progress_bar: self._p.iterate() else: print 'not implemented for this VNA, only works with Keysight ENA 5071C' qt.mend() self._end_measurement() except Exception as e: print e.__doc__ print e.message finally: self._end_measurement() qt.mend()
def measure_1D(self, rescan=True, web_visible=True): ''' measure method to record a single (averaged) VNA trace, S11 or S21 according to the setting on the VNA rescan: If True (default), the averages on the VNA are cleared and a new measurement is started. If False, it will directly take the data from the VNA without waiting. ''' self._scan_1D = True self._scan_2D = False self._scan_3D = False self._scan_time = False self._measurement_object.measurement_func = 'measure_1D' self._measurement_object.x_axis = 'frequency' self._measurement_object.y_axis = '' self._measurement_object.z_axis = '' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = 'VNA_tracedata' self._file_name = self.dirname.replace(' ', '').replace(',', '_') if self.exp_name: self._file_name += '_' + self.exp_name self._prepare_measurement_vna() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase']) if self._fit_resonator: self._resonator = resonator(self._data_file.get_filepath()) print 'recording trace...' sys.stdout.flush() qt.mstart() if rescan: if self.averaging_start_ready: self.vna.start_measurement() ti = time() if self.progress_bar: self._p = Progress_Bar(self.vna.get_averages(), self.dirname, self.vna.get_sweeptime()) qt.msleep(.2) while not self.vna.ready(): if time() - ti > self.vna.get_sweeptime(query=False): if self.progress_bar: self._p.iterate() ti = time() qt.msleep(.2) if self.progress_bar: while self._p.progr < self._p.max_it: self._p.iterate() else: self.vna.avg_clear() if self.vna.get_averages() == 1 or self.vna.get_Average( ) == False: #no averaging if self.progress_bar: self._p = Progress_Bar(1, self.dirname, self.vna.get_sweeptime()) qt.msleep(self.vna.get_sweeptime()) #wait single sweep if self.progress_bar: self._p.iterate() else: #with averaging if self.progress_bar: self._p = Progress_Bar(self.vna.get_averages(), self.dirname, self.vna.get_sweeptime()) if "avg_status" in self.vna.get_function_names(): for a in range(self.vna.get_averages()): while self.vna.avg_status() <= a: qt.msleep( .2 ) #maybe one would like to adjust this at a later point if self.progress_bar: self._p.iterate() else: #old style for a in range(self.vna.get_averages()): qt.msleep(self.vna.get_sweeptime() ) #wait single sweep time if self.progress_bar: self._p.iterate() data_amp, data_pha = self.vna.get_tracedata() data_real, data_imag = self.vna.get_tracedata('RealImag') self._data_amp.append(data_amp) self._data_pha.append(data_pha) self._data_real.append(data_real) self._data_imag.append(data_imag) if self._fit_resonator: self._do_fit_resonator() qt.mend() self._end_measurement()
def load_tabor(channel_sequences, ro_index, sample, reset=True, show_progress_bar=True): """ This function takes the data, coming from virtual awg, and loads them into the awg :param channel_sequences: This must be a list of list, i.e., a list of channels each containing the sequences :param ro_index: coming from virtual awg, for each sequence there is a ro_index letting the awg know, when to send the trigger. :param sample: you should know this :param reset: simply sets the awg_channel active, probably not needed :param show_progress_bar: enables the progress bar :return: """ awg = sample.awg awg.clear_waveforms() number_of_channels = 0 complex_channel = [] for chan in channel_sequences: if True in (s.dtype == np.complex128 for s in chan): number_of_channels += 2 complex_channel.append(True) else: number_of_channels += 1 complex_channel.append(False) if number_of_channels > awg._numchannels: raise ValueError('more sequences than channels') # reordering channels if necessary if number_of_channels > 2: if complex_channel[:2] == [False, True]: logging.warning( 'Channels reordered! Please do not split complex channels on two different channelpairs.' 'Complex channel is on chpair 1') channel_sequences[:2] = [ channel_sequences[1], channel_sequences[0] ] complex_channel[:2] = [True, False] #qkit.flow.start() if reset: _reset(awg, number_of_channels, ro_index) # Loading the waveforms into the AWG, differentiating between all cases if show_progress_bar: p = Progress_Bar(len(ro_index), 'Load AWG') if number_of_channels == 1: for j, seq in enumerate(channel_sequences[0]): _adjust_wfs_for_tabor(seq, [0], ro_index, 1, j, sample) if show_progress_bar: p.iterate() elif number_of_channels == 2: if complex_channel[0]: for j, seq in enumerate(channel_sequences[0]): _adjust_wfs_for_tabor(seq.real, seq.imag, ro_index, 1, j, sample) if show_progress_bar: p.iterate() else: for j, seq in enumerate( zip(channel_sequences[0], channel_sequences[1])): _adjust_wfs_for_tabor(seq[0], seq[1], ro_index, 1, j, sample) if show_progress_bar: p.iterate() elif number_of_channels == 3: if complex_channel[0]: for j, seq in enumerate( zip(channel_sequences[0], channel_sequences[1])): _adjust_wfs_for_tabor(seq[0].real, seq[0].imag, ro_index, 1, j, sample) _adjust_wfs_for_tabor(seq[1], [0], ro_index, 2, j, sample) if show_progress_bar: p.iterate() else: for j, seq in enumerate( zip(channel_sequences[0], channel_sequences[1], channel_sequences[2])): _adjust_wfs_for_tabor(seq[0], seq[1], ro_index, 1, j, sample) _adjust_wfs_for_tabor(seq[2], [0], ro_index, 2, j, sample) if show_progress_bar: p.iterate() else: # 4 channels if complex_channel == [True, True]: for j, seq in enumerate( zip(channel_sequences[0], channel_sequences[1])): _adjust_wfs_for_tabor(seq[0].real, seq[0].imag, ro_index, 1, j, sample) _adjust_wfs_for_tabor(seq[1].real, seq[1].imag, ro_index, 2, j, sample) if show_progress_bar: p.iterate() elif complex_channel == [True, False, False]: for j, seq in enumerate( zip(channel_sequences[0], channel_sequences[1], channel_sequences[2])): _adjust_wfs_for_tabor(seq[0].real, seq[0].imag, ro_index, 1, j, sample) _adjust_wfs_for_tabor(seq[1], seq[2], ro_index, 2, j, sample) if show_progress_bar: p.iterate() elif complex_channel == [False, False, True]: for j, seq in enumerate( zip(channel_sequences[0], channel_sequences[1], channel_sequences[2])): _adjust_wfs_for_tabor(seq[0], seq[1], ro_index, 1, j, sample) _adjust_wfs_for_tabor(seq[2].real, seq[2].imag, ro_index, 2, j, sample) if show_progress_bar: p.iterate() else: for j, seq in enumerate( zip(channel_sequences[0], channel_sequences[1], channel_sequences[2], channel_sequences[3])): _adjust_wfs_for_tabor(seq[0], seq[1], ro_index, 1, j, sample) _adjust_wfs_for_tabor(seq[2], seq[3], ro_index, 2, j, sample) if show_progress_bar: p.iterate() gc.collect() if number_of_channels <= 2: num_channels = [1, 2] else: num_channels = [1, 2, 3, 4] return np.all([awg.get('ch%i_status' % i) for i in num_channels])
class SputterMonitor(object): """ Sputter monitoring class, derived from spectroscopy.py monitors and stores sputter parameters live during deposition facilitates dynamic flow adjustment Example: m = SPUTTER_Monitor(quartz, ohmmeter, film_name, sample) m.set_filmparameters() ... see in corresponding docstring m.monitor_depo() Args: quartz (Instrument): The quartz oscillator instrument measuring thickness and rate. ohmmeter (Instrument): The resistance measurement instrument. mfc (Instrument): The mass flow controller instrument. film_name (str): A name can be given, that is added to the directory and filename. sample (Sample): A sample object can be given that is stored in the h5 file. Attributes: Should be listed here... Until then, use tabbing. """ def __init__(self, quartz=None, ohmmeter=None, mfc=None, film_name='', sample=None): self.quartz = quartz self.ohmmeter = ohmmeter self.mfc = mfc self.film_name = film_name self._sample = sample self.comment = '' self.dirname = None self.x_set_obj = None self.y_set_obj = None self.progress_bar = False self._show_ideal = False self._fit_resistance = False self._fit_every = 1 self._fit_points = 5 self._p0 = None self._reference_uid = None self._plot_comment = "" self.open_qviewkit = True self.qviewkit_singleInstance = False self._measurement_object = Measurement() self._measurement_object.measurement_type = 'sputter_deposition' self._measurement_object.sample = self._sample self._qvk_process = True self._duration = 60. # Duration of monitoring self._resolution = 2. # How often values should be taken self._target_resistance = 1000. self._target_thickness = 20. self._target_marker = False self._depmon_queue = Queue( ) # for communication with the depmon thread def set_duration(self, duration=60.): """ Set the duration for the monitoring. Better too long than too short. Args: duration (float): Monitoring duration in seconds. """ self._duration = duration def get_duration(self): """ Get the duration for the monitoring. Returns: duration (float): Monitoring duration in seconds. """ return self._duration def set_resolution(self, resolution=2.0): """ Set the resolution for the monitoring. Every x second, the parameters will be read and stored. Args: resolution (float): Monitoring resolution in seconds. """ self._resolution = resolution def get_resolution(self): """ Get the resolution for the monitoring. Every x second, the parameters will be read and stored. Returns: resolution (float): Monitoring resolution in seconds. """ return self._resolution def set_filmparameters(self, resistance=1000., thickness=20., target_marker=False): """ Set the target parameters of the film to be deposited. They are used to calculate the ideal trend of the measured parameters and their deviation from the ideal. Args: resistance (float): The target sheet resistance in Ohm. thickness (float): The target thickness in nm. target_marker (bool): Display a cross marking the target resistance and thickness. """ self._target_resistance = resistance self._target_thickness = thickness self._target_marker = target_marker def get_filmparameters(self): """ Get the set film parameters. Returns: Tuple containing: [0]: target_resistance (float): The target sheet resistance in Ohm. [1]: target_thickness (float): The target thickness in nm. """ return self._target_resistance, self._target_thickness def ideal_resistance(self, thickness): """ Calculates the ideal resistance at a given thickness depending on the set film parameters. Args: thickness (float): Film thickness in nm for which the ideal resistance should be calculated. Returns: The ideal resistance as a float. """ try: return 1. / (thickness * (1. / self._target_resistance) / self._target_thickness) except: return np.nan def ideal_trend(self): """ Calculates a list of ideal resistances that is used as comparison in a view together with the real values. """ t = np.linspace(1, self._target_thickness, 300) return [t, self.ideal_resistance(t)] def set_fit(self, fit=False, fit_every=1, fit_points=5, p0=None): """ Settings for the fit used to estimate final resistance at target thickness and thickness at target resistance. Args: fit (bool): Turn on the fit. fit_every (int): Fit after every x measurement. fit_points (int): Fit to the last x points. p0 (list): Provide initial values for the curvefit. """ self._fit_resistance = fit self._fit_every = fit_every self._fit_points = fit_points self._p0 = p0 def set_reference_uid(self, uid=None): """ Set a UID to be shown as a reference trace in the "resistance_thickness" view. Args: uid (str): The UID of the h5 file from which the resistance and thickness data is to be displayed. """ try: ref = hdf.Data(qkit.fid[uid]) self._ref_resistance = ref.data.resistance[:] self._ref_thickness = ref.data.thickness[:] self._reference_uid = uid ref.close() return self._reference_uid except: self._reference_uid = None return "Invalid UID" def set_resistance_4W(self, four_wire=False): """ Sets 2 (default) or 4 wire measurement mode, if the device supports it. Args: four_wire = False """ self.ohmmeter.set_measure_4W(four_wire) def get_rate(self, test_thickness): """ Tests deposition rate. Args: test_thickness (in nm) """ self.quartz.set_timer_zero() self.quartz.set_thickness_zero() time.sleep(3) while self.quartz.get_thickness() < test_thickness * 1e-2: time.sleep(1) mins, secs = self.quartz.get_time().split(':') t = 60 * float(mins) + float(secs) rate = test_thickness / t return rate, t def _theory(self, thickness, thickness_start, conductivity, cond_per_layer): """ Calculate a prediction for the resistance at a certain film thickness, given the current thickness, current conductivity and added conductivity per layer. Args: thickness (float): Film thickness for which the resistance shall be predicted thickness_start (float): Film thickness at the start of the prediction. conductivity (float): Film conductivity at the start of the prediction. cond_per_layer (float): Added conductivity per layer for the remaining film growth. Returns: The projected film resistance (float). """ t_added = thickness - thickness_start try: return 1. / (conductivity + t_added * cond_per_layer) except: return np.nan def _linear(self, x, a, b): """ Helper function providing a linear fitting function. """ return a * x + b def _fit_layer(self, t_points, R_points): """ Fit the changing film resistance to extract the gained conductivity per layer. This is then used to project the final film resistance with the provided target film parameters. Args: t_points (numpy array): Thickness data from which the conductivity per layer is to be extracted. R_points (numpy array): Resistance data from which the conductivity per layer is to be extracted. Returns: Tuple containing: [0]: t_final (float): The predicted thickness at which the target resistance will be reached. [1]: R_final (float): The predicted resistance that will be reached at the target thickness. [2]: popt[0] (float): Conductivity per layer as obtained by linear fit. """ # ToDo: Store all fits, Store time of fit to sync it to other datasets, revise theory. for i in R_points: if i == 0: return [np.nan, np.nan, np.nan] try: c_target = 1. / self._target_resistance c_points = 1. / R_points popt, _ = curve_fit(self._linear, t_points, c_points, p0=self._p0) R_final = self._theory(self._target_thickness, t_points[0], c_points[0], popt[0]) t_final = ((c_target - c_points[-1]) / popt[0]) + t_points[-1] return [t_final, R_final, popt[0]] except: return [np.nan, np.nan, np.nan] def _reciprocal(self, thickness, cond_per_layer): """ Function used by the curvefit routine. Args: thickness: Film thickness in nm. cond_per_layer: Film conductance per layer. Returns: The resistance for the given parameters as a float. """ try: return 1. / (thickness * cond_per_layer) except: return np.nan def _fit_trend(self, t_points, R_points): """ Do the fit used to estimate final resistance at target thickness and thickness at target resistance. Args: t_points: The last x points of film thickness in nm. R_points: The last x points of film resistivity in Ohm. Returns: List of [R_final, t_final] with R_final (float): Estimated final resistance at target thickness. t_final (float): Estimated thickness at target resistance. """ for i in t_points: if i == 0: return [np.nan, np.nan] try: self._popt, _ = curve_fit(self._reciprocal, t_points, R_points, p0=self._p0) t_final = 1. / (self._target_resistance * self._popt[0]) R_final = 1. / (self._target_thickness * self._popt[0]) return [t_final, R_final] except: return [np.nan, np.nan] def _prepare_measurement_quartz(self): """ All the relevant settings from the quartz are updated and called. """ # self.quartz.get_all() # self.quartz.get_frequency() # Store it somewhere pass def _prepare_measurement_ohmmeter(self): """ All the relevant settings from the ohmmeter are updated and called. """ # self.ohmmeter.get_all() pass def _prepare_measurement_mfc(self): """ All the relevant settings from the ohmmeter are updated and called. """ # self.mfc.get_all() self.Ar_channel = self.mfc.predef_channels['Ar'] self.ArO_channel = self.mfc.predef_channels['ArO'] pass def _prepare_monitoring_file(self): """ Creates the output .h5-file with distinct the required datasets and views. At this point all measurement parameters are known and put in the output file. """ self._data_file = hdf.Data(name=self._file_name, mode='a') self._measurement_object.uuid = self._data_file._uuid self._measurement_object.hdf_relpath = self._data_file._relpath self._measurement_object.instruments = qkit.instruments.get_instrument_names( ) self._measurement_object.save() self._mo = self._data_file.add_textlist('measurement') self._mo.append(self._measurement_object.get_JSON()) # write logfile and instrument settings self._write_settings_dataset() self._log = waf.open_log_file(self._data_file.get_filepath()) ''' Time record ''' self._data_time = self._data_file.add_value_vector('time', x=None, unit='s') ''' Quartz datasets ''' self._data_rate = self._data_file.add_value_vector( 'rate', x=self._data_time, unit='nm/s', save_timestamp=False) self._data_thickness = self._data_file.add_value_vector( 'thickness', x=self._data_time, unit='nm', save_timestamp=False) ''' Ohmmeter datasets ''' self._data_resistance = self._data_file.add_value_vector( 'resistance', x=self._data_time, unit='Ohm', save_timestamp=False) self._data_conductance = self._data_file.add_value_vector( 'conductance', x=self._data_time, unit='S', save_timestamp=False) self._data_deviation_abs = self._data_file.add_value_vector( 'deviation_absolute', x=self._data_time, unit='Ohm', save_timestamp=False) self._data_deviation_rel = self._data_file.add_value_vector( 'deviation_relative', x=self._data_time, unit='relative', save_timestamp=False) ''' MFC datasets ''' # FIXME: units? if self.mfc: self._data_pressure = self._data_file.add_value_vector( 'pressure', x=self._data_time, unit='ubar', save_timestamp=False) self._data_Ar_flow = self._data_file.add_value_vector( 'Ar_flow', x=self._data_time, unit='sccm', save_timestamp=False) self._data_ArO_flow = self._data_file.add_value_vector( 'ArO_flow', x=self._data_time, unit='sccm', save_timestamp=False) ''' Calculate ideal trend and create record ''' self._thickness_coord = self._data_file.add_coordinate( 'thickness_coord', unit='nm') self._thickness_coord.add(self.ideal_trend()[0]) if self._show_ideal: self._data_ideal = self._data_file.add_value_vector( 'ideal_resistance', x=self._thickness_coord, unit='Ohm', save_timestamp=False) self._data_ideal.append(self.ideal_trend()[1]) ''' Create target marker ''' if self._target_marker: self._target_thickness_line = self._data_file.add_value_vector( 'target_thickness', x=None, unit='nm', save_timestamp=False) self._target_thickness_line.append([ 0.8 * self._target_thickness, self._target_thickness, self._target_thickness, self._target_thickness, self._target_thickness, 1.2 * self._target_thickness ]) self._target_resistance_line = self._data_file.add_value_vector( 'target_resistance', x=None, unit='Ohm', save_timestamp=False) self._target_resistance_line.append([ self._target_resistance, self._target_resistance, 1.2 * self._target_resistance, 0.8 * self._target_resistance, self._target_resistance, self._target_resistance ]) self._target_conductance_line = self._data_file.add_value_vector( 'target_conductance', x=None, unit='S', save_timestamp=False) self._target_conductance_line.append([ 1. / self._target_resistance, 1. / self._target_resistance, 1. / 1.2 / self._target_resistance, 1. / 0.8 / self._target_resistance, 1. / self._target_resistance, 1. / self._target_resistance ]) ''' Estimation datasets ''' if self._fit_resistance: self._thickness_estimation = self._data_file.add_value_vector( 'thickness_estimation', x=None, unit='nm', save_timestamp=False) self._resistance_estimation = self._data_file.add_value_vector( 'resistance_estimation', x=None, unit='Ohm', save_timestamp=False) self._last_resistance_fit = self._data_file.add_value_vector( 'last_resistance_fit', x=None, unit='Ohm', save_timestamp=False) ''' Reference dataset ''' if not self._reference_uid == None: self._thickness_reference = self._data_file.add_value_vector( 'thickness_reference', x=None, unit='nm', save_timestamp=False) self._thickness_reference.append(self._ref_thickness) self._resistance_reference = self._data_file.add_value_vector( 'reference_' + self._reference_uid, x=None, unit='Ohm', save_timestamp=False) self._resistance_reference.append(self._ref_resistance) ''' Create Views ''' self._resist_view = self._data_file.add_view('resistance_thickness', x=self._data_thickness, y=self._data_resistance) if self._show_ideal: self._resist_view.add(x=self._thickness_coord, y=self._data_ideal) if self._target_marker: self._resist_view.add(x=self._target_thickness_line, y=self._target_resistance_line) if not self._reference_uid == None: self._resist_view.add(x=self._thickness_reference, y=self._resistance_reference) if self._fit_resistance: self._resist_view.add(x=self._thickness_coord, y=self._last_resistance_fit) self._conductance_view = self._data_file.add_view( 'conductance_thickness', x=self._data_thickness, y=self._data_conductance) if self._target_marker: self._conductance_view.add(x=self._target_thickness_line, y=self._target_conductance_line) self._deviation_abs_view = self._data_file.add_view( 'deviation_absolute', x=self._data_thickness, y=self._data_deviation_abs) self._deviation_rel_view = self._data_file.add_view( 'deviation_relative', x=self._data_thickness, y=self._data_deviation_rel) ''' Create comment ''' if self.comment: self._data_file.add_comment(self.comment) ''' Open GUI ''' if self.qviewkit_singleInstance and self.open_qviewkit and self._qvk_process: self._qvk_process.terminate() # terminate an old qviewkit instance def _write_settings_dataset(self): """ Writes a dataset containing the settings of the measurement instruments. """ self._settings = self._data_file.add_textlist('settings') settings = waf.get_instrument_settings(self._data_file.get_filepath()) self._settings.append(settings) def _init_depmon(self): """ Initializes the measurement, the required files and instruments. Should only be called by the main functions monitor_depo or start_depmon. """ self._measurement_object.measurement_func = 'sputter_monitoring' self._measurement_object.x_axis = 'time' self._measurement_object.y_axis = '' self._measurement_object.z_axis = '' self._measurement_object.web_visible = True if not self.dirname: self.dirname = 'SPUTTER_monitoring' self._file_name = self.dirname.replace(' ', '').replace(',', '_') if self.film_name: self._file_name += '_' + self.film_name self.x_vec = np.arange(0, self._duration, self._resolution) self._prepare_measurement_quartz() self._prepare_measurement_ohmmeter() if self.mfc: self._prepare_measurement_mfc() self._prepare_monitoring_file() def _acquire(self, mon_data): """ Acquires and stores current information from the instruments. Should only be called by main functions monitor_depo or start_depmon. """ self._data_time.append(time.time() - mon_data.t0) # mon_data.resistance.append(self.ohmmeter.get_resistance()) mon_data.resistance = np.append(mon_data.resistance, self.ohmmeter.get_resistance()) if self.quartz: rate = self.quartz.get_rate(nm=True) # mon_data.thickness.append(self.quartz.get_thickness(nm=True)) mon_data.thickness = np.append(mon_data.thickness, self.quartz.get_thickness(nm=True)) if self.mfc: pressure = self.mfc.get_pressure() Ar_flow = self.mfc.get_flow(self.Ar_channel) ArO_flow = self.mfc.get_flow(self.ArO_channel) if mon_data.resistance[-1] > 1.e9: mon_data.resistance[-1] = np.nan self._data_resistance.append(mon_data.resistance[-1]) if not mon_data.resistance[-1] == 0: self._data_conductance.append(1. / mon_data.resistance[-1]) else: self._data_conductance.append(np.nan) if self.quartz: self._data_rate.append(rate) self._data_thickness.append(mon_data.thickness[-1]) if self.mfc: self._data_pressure.append(pressure) self._data_Ar_flow.append(Ar_flow) self._data_ArO_flow.append(ArO_flow) if self.quartz: deviation_abs = mon_data.resistance[-1] - self.ideal_resistance( mon_data.thickness[-1]) deviation_rel = deviation_abs / self._target_resistance self._data_deviation_abs.append(deviation_abs) self._data_deviation_rel.append(deviation_rel) if ((self._fit_resistance) and (mon_data.it % self._fit_every == 0) and (len(mon_data.resistance) >= self._fit_points)): """ # OLD ROUTINE estimation = self._fit_trend(mon_data.thickness[-self._fit_points:None], mon_data.resistance[-self._fit_points:None]) self._thickness_estimation.append(estimation[0]) self._resistance_estimation.append(estimation[1]) self._last_resistance_fit.append(self._reciprocal(self.ideal_trend()[0],self._popt[0]), reset=True) """ estimation = self._fit_layer( mon_data.thickness[-self._fit_points:None], mon_data.resistance[-self._fit_points:None]) self._thickness_estimation.append(estimation[0]) self._resistance_estimation.append(estimation[1]) self._last_resistance_fit.append(self._theory( self.ideal_trend()[0], mon_data.thickness[-1], 1. / mon_data.resistance[-1], estimation[2]), reset=True) def monitor_depo(self): """ Main sputter deposition monitoring function. Consider using the threaded version by calling start_depmon(). Records the film resistance, thickness and deposition rate live during the sputter process. Stores everything into a h5 file. Provides measures to estimate the resulting resistance of the final film and thereby facilitates live adjustments to the sputter parameters. Note: set_duration and set_resolution should be called before to set the monitoring length and time resolution. set_filmparameters should be called before to provide the actual target values. """ self._init_depmon() if self.progress_bar: self._p = Progress_Bar( self._duration / self._resolution, 'EVAP_timetrace ' + self.dirname, self._resolution) # FIXME: Doesn't make much sense... print('Monitoring deposition...') sys.stdout.flush() if self.open_qviewkit: self._qvk_process = qviewkit.plot( self._data_file.get_filepath(), datasets=['views/resistance_thickness']) try: """ Initialize the data lists. """ class MonData(object): resistance = np.array([]) thickness = np.array([]) mon_data = MonData() """ Main loop: """ mon_data.t0 = time.time( ) # note: Windows has limitations on time precision (about 16ms) for mon_data.it, _ in enumerate(self.x_vec): self._acquire(mon_data) if self.progress_bar: self._p.iterate() # calculate the time when the next iteration should take place ti = mon_data.t0 + (float(mon_data.it) + 1) * self._resolution # wait until the total dt(iteration) has elapsed while time.time() < ti: time.sleep(0.05) except KeyboardInterrupt: print('Monitoring interrupted by user.') except Exception as e: print(e) print(e.__doc__) print(e.message) finally: self._end_measurement() def _monitor_depo_bg(self): """ Main sputter deposition monitoring function, threaded version Should only be called by main function start_depmon. Records the film resistance, thickness and deposition rate live during the sputter process. Stores everything into a h5 file. Provides measures to estimate the resulting resistance of the final film and thereby facilitates live adjustments to the sputter parameters. Note: set_duration and set_resolution should be called before to set the monitoring length and time resolution. set_filmparameters should be called before to provide the actual target values. """ class StopPauseExeption(Exception): pass class StartPauseExeption(Exception): pass """ A few boilerplate functions for the thread management: continue, stop, status, etc... """ def stop(): raise StopIteration def do_nothing(): pass def status(): self._depmon_queue.put(loop_status) def cont(): raise StopPauseExeption def pause(): raise StartPauseExeption loop_status = 0 tasks = {} tasks[0] = stop tasks[1] = do_nothing tasks[2] = status tasks[3] = pause tasks[4] = cont """ Initialize the data lists. """ class MonData(object): resistance = np.array([]) thickness = np.array([]) mon_data = MonData() """ Main loop: """ mon_data.it = 0 mon_data.t0 = time.time( ) # note: Windows has limitations on time precision (about 16ms)? while True: self._acquire(mon_data) # Handle external commands try: tasks.get(self._depmon_queue.get(False), do_nothing)() self._depmon_queue.task_done() except Empty: pass except StartPauseExeption: print('monitoring paused') while True: try: time.sleep(0.1) tasks.get(self._depmon_queue.get(False), do_nothing)() self._depmon_queue.task_done() except StopPauseExeption: print('monitoring restarted') break except Empty: pass except StopPauseExeption: pass except StopIteration: break # calculate the time when the next iteration should take place mon_data.it += 1 ti = mon_data.t0 + (float(mon_data.it)) * self._resolution loop_status = ti # for now we simply return the runtime # wait until the total dt(iteration) has elapsed while time.time() < ti: time.sleep(0.05) self._end_measurement() def start_depmon(self): """ Starts the main sputter deposition monitoring function background process (depmon). Records the film resistance, thickness and deposition rate live during the sputter process. Stores everything into a h5 file. Provides measures to estimate the resulting resistance of the final film and thereby facilitates live adjustments to the sputter parameters. Note: set_duration and set_resolution should be called before to set the monitoring length and time resolution. set_filmparameters should be called before to provide the actual target values. Hint: Check with 'list_depmon_threads()' Stop with 'stop_depmon()' """ print('Monitoring deposition...') sys.stdout.flush() self._init_depmon() if self.open_qviewkit: self._qvk_process = qviewkit.plot( self._data_file.get_filepath(), datasets=['views/resistance_thickness'], refresh=1.) self._depmon = Thread(target=self._monitor_depo_bg, name="depmon-1") self._depmon.daemon = True self._depmon.start() def stop_depmon(self): """ Stop the deposition monitoring thread. Can be executed several times if more than one monitoring thread is running. Hint: Check with 'list_depmon_threads()' """ self._depmon_queue.put(0) def status_depmon(self): """ Give a heartbeat of the deposition monitoring thread (in the moment just the runtime). """ self._depmon_queue.put(2) print(self._depmon_queue.get()) self._depmon_queue.task_done() def pause_depmon(self): print('Pause Monitoring deposition...') self._depmon_queue.put(3) def continue_depmon(self): print('Continue Monitoring deposition...') self._depmon_queue.put(4) def list_depmon_threads(self): """ List all bg deposition monitoring threads. """ for thread in threading.enumerate(): if thread.getName()[:3] == "dep": print(thread) def _end_measurement(self): """ The data file is closed and file path is printed. """ print(self._data_file.get_filepath()) # qviewkit.save_plots(self._data_file.get_filepath(),comment=self._plot_comment) # #old version where we have to wait for the plots t = threading.Thread( target=qviewkit.save_plots, args=[self._data_file.get_filepath(), self._plot_comment]) t.start() self._data_file.close_file() waf.close_log_file(self._log) self.dirname = None
class spectrum(object): ''' usage: m = spectrum(vna = vna1) m.set_x_parameters(arange(-0.05,0.05,0.01),'flux coil current',coil.set_current, unit = 'mA') m.set_y_parameters(arange(4e9,7e9,10e6),'excitation frequency',mw_src1.set_frequency, unit = 'Hz') m.gen_fit_function(...) several times m.measure_XX() ''' def __init__(self, vna, exp_name='', sample=None): self.vna = vna self.averaging_start_ready = "start_measurement" in self.vna.get_function_names( ) and "ready" in self.vna.get_function_names() if not self.averaging_start_ready: logging.warning( __name__ + ': With your VNA instrument driver (' + self.vna.get_type() + '), I can not see when a measurement is complete. So I only wait for a specific time and hope the VNA has finished. Please consider implemeting the necessary functions into your driver.' ) self.exp_name = exp_name self._sample = sample self.landscape = None self.span = 200e6 #[Hz] self.tdx = 0.002 #[s] self.tdy = 0.002 #[s] self.comment = '' self.dirname = None self.x_set_obj = None self.y_set_obj = None self.progress_bar = True self._fit_resonator = False self._plot_comment = "" self.set_log_function() self.open_qviewkit = True self.qviewkit_singleInstance = False self._measurement_object = Measurement() self._measurement_object.measurement_type = 'spectroscopy' self._measurement_object.sample = self._sample self._qvk_process = False self.number_of_timetraces = 1 #relevant in time domain mode def set_log_function(self, func=None, name=None, unit=None, log_dtype=None): ''' A function (object) can be passed to the measurement loop which is excecuted before every x iteration but after executing the x_object setter in 2D measurements and before every line (but after setting the x value) in 3D measurements. The return value of the function of type float or similar is stored in a value vector in the h5 file. Call without any arguments to delete all log functions. The timestamp is automatically saved. func: function object in list form name: name of logging parameter appearing in h5 file, default: 'log_param' unit: unit of logging parameter, default: '' log_dtype: h5 data type, default: 'f' (float32) ''' if name == None: try: name = ['log_param'] * len(func) except Exception: name = None if unit == None: try: unit = [''] * len(func) except Exception: unit = None if log_dtype == None: try: log_dtype = ['f'] * len(func) except Exception: log_dtype = None self.log_function = [] self.log_name = [] self.log_unit = [] self.log_dtype = [] if func != None: for i, f in enumerate(func): self.log_function.append(f) self.log_name.append(name[i]) self.log_unit.append(unit[i]) self.log_dtype.append(log_dtype[i]) def set_x_parameters(self, x_vec, x_coordname, x_set_obj, x_unit=""): ''' Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep. For every x value all y values are swept Input: x_vec (array): conains the sweeping values x_coordname (string) x_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power()) x_unit (string): optional ''' self.x_vec = x_vec self.x_coordname = x_coordname self.x_set_obj = x_set_obj self.delete_fit_function() self.x_unit = x_unit def set_y_parameters(self, y_vec, y_coordname, y_set_obj, y_unit=""): ''' Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep. For every x value all y values are swept Input: y_vec (array): contains the sweeping values y_coordname (string) y_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power()) y_unit (string): optional ''' self.y_vec = y_vec self.y_coordname = y_coordname self.y_set_obj = y_set_obj self.delete_fit_function() self.y_unit = y_unit def gen_fit_function(self, curve_f, curve_p, units='', p0=[-1, 0.1, 7]): ''' curve_f: 'parab', 'hyp', specifies the fit function to be employed curve_p: set of points that are the basis for the fit in the format [[x1,x2,x3,...],[y1,y2,y3,...]], frequencies in Hz units: set this to 'Hz' in order to avoid large values that cause the fit routine to diverge p0 (optional): start parameters for the fit, must be an 1D array of length 3 ([a,b,c,d]), where for the parabula p0[3] will be ignored The parabolic function takes the form y = a*(x-b)**2 + c , where (a,b,c) = p0 The hyperbolic function takes the form y = sqrt[ a*(x-b)**2 + c ], where (a,b,c) = p0 adds a trace to landscape ''' if not self.landscape: self.landscape = [] x_fit = curve_p[0] if units == 'Hz': y_fit = np.array(curve_p[1]) * 1e-9 else: y_fit = np.array(curve_p[1]) try: multiplier = 1 if units == 'Hz': multiplier = 1e9 fit_fct = None if curve_f == 'parab': fit_fct = self.f_parab elif curve_f == 'hyp': fit_fct = self.f_hyp elif curve_f == 'lin': fit_fct = self.f_lin p0 = p0[:2] else: print 'function type not known...aborting' raise ValueError popt, pcov = curve_fit(fit_fct, x_fit, y_fit, p0=p0) self.landscape.append(multiplier * fit_fct(self.x_vec, *popt)) except Exception as message: print 'fit not successful:', message popt = p0 def _prepare_measurement_vna(self): ''' all the relevant settings from the vna are updated and called ''' self.vna.get_all() #ttip.get_temperature() self._nop = self.vna.get_nop() self._sweeptime_averages = self.vna.get_sweeptime_averages() self._freqpoints = self.vna.get_freqpoints() if self.averaging_start_ready: self.vna.pre_measurement() def _prepare_measurement_file(self): ''' creates the output .h5-file with distinct dataset structures for each measurement type. at this point all measurement parameters are known and put in the output file ''' self._data_file = hdf.Data(name=self._file_name) self._measurement_object.uuid = self._data_file._uuid self._measurement_object.hdf_relpath = self._data_file._relpath self._measurement_object.instruments = qt.instruments.get_instruments() self._measurement_object.save() self._mo = self._data_file.add_textlist('measurement') self._mo.append(self._measurement_object.get_JSON()) # write logfile and instrument settings self._write_settings_dataset() self._log = waf.open_log_file(self._data_file.get_filepath()) if not self._scan_time: self._data_freq = self._data_file.add_coordinate('frequency', unit='Hz') self._data_freq.add(self._freqpoints) if self._scan_1D: self._data_real = self._data_file.add_value_vector( 'real', x=self._data_freq, unit='', save_timestamp=True) self._data_imag = self._data_file.add_value_vector( 'imag', x=self._data_freq, unit='', save_timestamp=True) self._data_amp = self._data_file.add_value_vector( 'amplitude', x=self._data_freq, unit='arb. unit', save_timestamp=True) self._data_pha = self._data_file.add_value_vector( 'phase', x=self._data_freq, unit='rad', save_timestamp=True) if self._scan_2D: self._data_x = self._data_file.add_coordinate(self.x_coordname, unit=self.x_unit) self._data_x.add(self.x_vec) self._data_amp = self._data_file.add_value_matrix( 'amplitude', x=self._data_x, y=self._data_freq, unit='arb. unit', save_timestamp=True) self._data_pha = self._data_file.add_value_matrix( 'phase', x=self._data_x, y=self._data_freq, unit='rad', save_timestamp=True) if self.log_function != None: #use logging self._log_value = [] for i in range(len(self.log_function)): self._log_value.append( self._data_file.add_value_vector( self.log_name[i], x=self._data_x, unit=self.log_unit[i], dtype=self.log_dtype[i])) if self._nop < 10: """creates view: plot middle point vs x-parameter, for qubit measurements""" self._data_amp_mid = self._data_file.add_value_vector( 'amplitude_midpoint', unit='arb. unit', x=self._data_x, save_timestamp=True) self._data_pha_mid = self._data_file.add_value_vector( 'phase_midpoint', unit='rad', x=self._data_x, save_timestamp=True) #self._view = self._data_file.add_view("amplitude vs. " + self.x_coordname, x = self._data_x, y = self._data_amp[self._nop/2]) if self._scan_3D: self._data_x = self._data_file.add_coordinate(self.x_coordname, unit=self.x_unit) self._data_x.add(self.x_vec) self._data_y = self._data_file.add_coordinate(self.y_coordname, unit=self.y_unit) self._data_y.add(self.y_vec) if self._nop == 0: #saving in a 2D matrix instead of a 3D box HR: does not work yet !!! test things before you put them online. self._data_amp = self._data_file.add_value_matrix( 'amplitude', x=self._data_x, y=self._data_y, unit='arb. unit', save_timestamp=False) self._data_pha = self._data_file.add_value_matrix( 'phase', x=self._data_x, y=self._data_y, unit='rad', save_timestamp=False) else: self._data_amp = self._data_file.add_value_box( 'amplitude', x=self._data_x, y=self._data_y, z=self._data_freq, unit='arb. unit', save_timestamp=False) self._data_pha = self._data_file.add_value_box( 'phase', x=self._data_x, y=self._data_y, z=self._data_freq, unit='rad', save_timestamp=False) if self.log_function != None: #use logging self._log_value = [] for i in range(len(self.log_function)): self._log_value.append( self._data_file.add_value_vector( self.log_name[i], x=self._data_x, unit=self.log_unit[i], dtype=self.log_dtype[i])) if self._scan_time: self._data_freq = self._data_file.add_coordinate('frequency', unit='Hz') self._data_freq.add([self.vna.get_centerfreq()]) self._data_time = self._data_file.add_coordinate('time', unit='s') self._data_time.add( np.arange(0, self._nop, 1) * self.vna.get_sweeptime() / (self._nop - 1)) self._data_x = self._data_file.add_coordinate('trace_number', unit='') self._data_x.add(np.arange(0, self.number_of_timetraces, 1)) self._data_amp = self._data_file.add_value_matrix( 'amplitude', x=self._data_x, y=self._data_time, unit='lin. mag.', save_timestamp=False) self._data_pha = self._data_file.add_value_matrix( 'phase', x=self._data_x, y=self._data_time, unit='rad.', save_timestamp=False) if self.comment: self._data_file.add_comment(self.comment) if self.qviewkit_singleInstance and self.open_qviewkit and self._qvk_process: self._qvk_process.terminate() #terminate an old qviewkit instance def _write_settings_dataset(self): self._settings = self._data_file.add_textlist('settings') settings = waf.get_instrument_settings(self._data_file.get_filepath()) self._settings.append(settings) def measure_1D(self, rescan=True, web_visible=True): ''' measure method to record a single (averaged) VNA trace, S11 or S21 according to the setting on the VNA rescan: If True (default), the averages on the VNA are cleared and a new measurement is started. If False, it will directly take the data from the VNA without waiting. ''' self._scan_1D = True self._scan_2D = False self._scan_3D = False self._scan_time = False self._measurement_object.measurement_func = 'measure_1D' self._measurement_object.x_axis = 'frequency' self._measurement_object.y_axis = '' self._measurement_object.z_axis = '' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = 'VNA_tracedata' self._file_name = self.dirname.replace(' ', '').replace(',', '_') if self.exp_name: self._file_name += '_' + self.exp_name self._prepare_measurement_vna() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase']) if self._fit_resonator: self._resonator = resonator(self._data_file.get_filepath()) print 'recording trace...' sys.stdout.flush() qt.mstart() if rescan: if self.averaging_start_ready: self.vna.start_measurement() ti = time() if self.progress_bar: self._p = Progress_Bar(self.vna.get_averages(), self.dirname, self.vna.get_sweeptime()) qt.msleep(.2) while not self.vna.ready(): if time() - ti > self.vna.get_sweeptime(query=False): if self.progress_bar: self._p.iterate() ti = time() qt.msleep(.2) if self.progress_bar: while self._p.progr < self._p.max_it: self._p.iterate() else: self.vna.avg_clear() if self.vna.get_averages() == 1 or self.vna.get_Average( ) == False: #no averaging if self.progress_bar: self._p = Progress_Bar(1, self.dirname, self.vna.get_sweeptime()) qt.msleep(self.vna.get_sweeptime()) #wait single sweep if self.progress_bar: self._p.iterate() else: #with averaging if self.progress_bar: self._p = Progress_Bar(self.vna.get_averages(), self.dirname, self.vna.get_sweeptime()) if "avg_status" in self.vna.get_function_names(): for a in range(self.vna.get_averages()): while self.vna.avg_status() <= a: qt.msleep( .2 ) #maybe one would like to adjust this at a later point if self.progress_bar: self._p.iterate() else: #old style for a in range(self.vna.get_averages()): qt.msleep(self.vna.get_sweeptime() ) #wait single sweep time if self.progress_bar: self._p.iterate() data_amp, data_pha = self.vna.get_tracedata() data_real, data_imag = self.vna.get_tracedata('RealImag') self._data_amp.append(data_amp) self._data_pha.append(data_pha) self._data_real.append(data_real) self._data_imag.append(data_imag) if self._fit_resonator: self._do_fit_resonator() qt.mend() self._end_measurement() def measure_2D(self, web_visible=True): ''' measure method to record a (averaged) VNA trace, S11 or S21 according to the setting on the VNA for all parameters x_vec in x_obj ''' if not self.x_set_obj: logging.error('axes parameters not properly set...aborting') return self._scan_1D = False self._scan_2D = True self._scan_3D = False self._scan_time = False self._measurement_object.measurement_func = 'measure_2D' self._measurement_object.x_axis = self.x_coordname self._measurement_object.y_axis = 'frequency' self._measurement_object.z_axis = '' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = self.x_coordname self._file_name = '2D_' + self.dirname.replace(' ', '').replace( ',', '_') if self.exp_name: self._file_name += '_' + self.exp_name if self.progress_bar: self._p = Progress_Bar(len(self.x_vec), '2D VNA sweep ' + self.dirname, self.vna.get_sweeptime_averages()) self._prepare_measurement_vna() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" if self._nop < 10: if self.open_qviewkit: self._qvk_process = qviewkit.plot( self._data_file.get_filepath(), datasets=['amplitude_midpoint', 'phase_midpoint']) else: if self.open_qviewkit: self._qvk_process = qviewkit.plot( self._data_file.get_filepath(), datasets=['amplitude', 'phase']) if self._fit_resonator: self._resonator = resonator(self._data_file.get_filepath()) self._measure() def measure_3D(self, web_visible=True): ''' measure full window of vna while sweeping x_set_obj and y_set_obj with parameters x_vec/y_vec. sweep over y_set_obj is the inner loop, for every value x_vec[i] all values y_vec are measured. optional: measure method to perform the measurement according to landscape, if set self.span is the range (in units of the vertical plot axis) data is taken around the specified funtion(s) note: make sure to have properly set x,y vectors before generating traces ''' if not self.x_set_obj or not self.y_set_obj: logging.error('axes parameters not properly set...aborting') return self._scan_1D = False self._scan_2D = False self._scan_3D = True self._scan_time = False self._measurement_object.measurement_func = 'measure_3D' self._measurement_object.x_axis = self.x_coordname self._measurement_object.y_axis = self.y_coordname self._measurement_object.z_axis = 'frequency' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = self.x_coordname + ', ' + self.y_coordname self._file_name = '3D_' + self.dirname.replace(' ', '').replace( ',', '_') if self.exp_name: self._file_name += '_' + self.exp_name if self.progress_bar: self._p = Progress_Bar( len(self.x_vec) * len(self.y_vec), '3D VNA sweep ' + self.dirname, self.vna.get_sweeptime_averages()) self._prepare_measurement_vna() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" """only middle point in freq array is plotted vs x and y""" if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase']) if self._fit_resonator: self._resonator = resonator(self._data_file.get_filepath()) if self.landscape: self.center_freqs = np.array(self.landscape).T else: self.center_freqs = [] #load default sequence for i in range(len(self.x_vec)): self.center_freqs.append([0]) self._measure() def measure_timetrace(self, web_visible=True): ''' measure method to record a single VNA timetrace, this only makes sense when span is set to 0 Hz!, tested only with KEYSIGHT E5071C ENA and its corresponding qkit driver LGruenhaupt 11/2016 ''' if self.vna.get_span() > 0: print 'VNA span not set to 0 Hz... aborting' return self._scan_1D = False self._scan_2D = False self._scan_3D = False self._scan_time = True self._measurement_object.measurement_func = 'measure_timetrace' self._measurement_object.x_axis = 'time' self._measurement_object.y_axis = '' self._measurement_object.z_axis = '' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = 'VNA_timetrace' self._file_name = self.dirname.replace(' ', '').replace(',', '_') if self.exp_name: self._file_name += '_' + self.exp_name self.x_vec = np.arange(0, self.number_of_timetraces, 1) self._prepare_measurement_vna() self._prepare_measurement_file() if self.progress_bar: self._p = Progress_Bar(self.number_of_timetraces, 'VNA timetrace ' + self.dirname, self.vna.get_sweeptime_averages()) print 'recording timetrace(s)...' sys.stdout.flush() qt.mstart() try: """ loop: x_obj with parameters from x_vec """ for i, x in enumerate(self.x_vec): if self.log_function != None: for i, f in enumerate(self.log_function): self._log_value[i].append(float(f())) if self.averaging_start_ready: #LG 11/2016 self.vna.start_measurement() ti = time( ) #changed from time.time() to time() - LGruenhaupt OCT_2016 tp = time() #added to enable use of progress bar #self._p = Progress_Bar(self.vna.get_averages(),self.dirname,self.vna.get_sweeptime_averages()) moved to line 303 ''' This is to prevent the vna.ready() function from timing out. LG NOV/16 ''' if self.vna.get_Average(): print 'this function only makes sense without averaging' qt.mend() self._end_measuremt() else: #self._p = Progress_Bar(1,self.dirname,self.vna.get_sweeptime()) qt.msleep(self.vna.get_sweeptime()) #self._p.iterate() while not self.vna.ready(): qt.msleep( .2 ) #this is just to check if the measurement has finished data_amp, data_pha = self.vna.get_tracedata() self._data_amp.append(data_amp) self._data_pha.append(data_pha) qt.msleep() if self.progress_bar: self._p.iterate() else: print 'not implemented for this VNA, only works with Keysight ENA 5071C' qt.mend() self._end_measurement() except Exception as e: print e.__doc__ print e.message finally: self._end_measurement() qt.mend() def _measure(self): ''' measures and plots the data depending on the measurement type. the measurement loops feature the setting of the objects and saving the data in the .h5 file. ''' qt.mstart() try: """ loop: x_obj with parameters from x_vec """ for ix, x in enumerate(self.x_vec): self.x_set_obj(x) sleep(self.tdx) if self.log_function != None: for i, f in enumerate(self.log_function): self._log_value[i].append(float(f())) if self._scan_3D: for y in self.y_vec: """ loop: y_obj with parameters from y_vec (only 3D measurement) """ if ( np.min( np.abs(self.center_freqs[ix] - y * np.ones(len(self.center_freqs[ix]))) ) > self.span / 2. ) and self.landscape: #if point is not of interest (not close to one of the functions) data_amp = np.zeros(int(self._nop)) data_pha = np.zeros(int( self._nop)) #fill with zeros else: self.y_set_obj(y) sleep(self.tdy) if self.averaging_start_ready: self.vna.start_measurement() qt.msleep( .2 ) #just to make sure, the ready command does not *still* show ready while not self.vna.ready(): qt.msleep(.2) else: self.vna.avg_clear() qt.msleep(self._sweeptime_averages) #if "avg_status" in self.vna.get_function_names(): # while self.vna.avg_status() < self.vna.get_averages(): # qt.msleep(.2) #maybe one would like to adjust this at a later point """ measurement """ data_amp, data_pha = self.vna.get_tracedata() if self._nop == 0: # this does not work yet. print data_amp[0], data_amp, self._nop self._data_amp.append(data_amp[0]) self._data_pha.append(data_pha[0]) else: self._data_amp.append(data_amp) self._data_pha.append(data_pha) if self._fit_resonator: self._do_fit_resonator() if self.progress_bar: self._p.iterate() qt.msleep() """ filling of value-box is done here. after every y-loop the data is stored the next 2d structure """ self._data_amp.next_matrix() self._data_pha.next_matrix() if self._scan_2D: if self.averaging_start_ready: self.vna.start_measurement() qt.msleep( .2 ) #just to make sure, the ready command does not *still* show ready while not self.vna.ready(): qt.msleep(.2) else: self.vna.avg_clear() qt.msleep(self._sweeptime_averages) """ measurement """ data_amp, data_pha = self.vna.get_tracedata() self._data_amp.append(data_amp) self._data_pha.append(data_pha) if self._nop < 10: #print data_amp[self._nop/2] self._data_amp_mid.append( float(data_amp[self._nop / 2])) self._data_pha_mid.append( float(data_pha[self._nop / 2])) if self._fit_resonator: self._do_fit_resonator() if self.progress_bar: self._p.iterate() qt.msleep() except Exception as e: print e.__doc__ print e.message finally: self._end_measurement() qt.mend() def _end_measurement(self): ''' the data file is closed and filepath is printed ''' print self._data_file.get_filepath() #qviewkit.save_plots(self._data_file.get_filepath(),comment=self._plot_comment) #old version where we have to wait for the plots t = threading.Thread( target=qviewkit.save_plots, args=[self._data_file.get_filepath(), self._plot_comment]) t.start() self._data_file.close_file() waf.close_log_file(self._log) self.dirname = None if self.averaging_start_ready: self.vna.post_measurement() def set_resonator_fit(self, fit_resonator=True, fit_function='', f_min=None, f_max=None): ''' sets fit parameter for resonator fit_resonator (bool): True or False, default: True (optional) fit_function (string): function which will be fitted to the data (optional) f_min (float): lower frequency boundary for the fitting function, default: None (optional) f_max (float): upper frequency boundary for the fitting function, default: None (optional) fit types: 'lorentzian','skewed_lorentzian','circle_fit_reflection', 'circle_fit_notch','fano' ''' if not fit_resonator: self._fit_resonator = False return self._functions = { 'lorentzian': 0, 'skewed_lorentzian': 1, 'circle_fit_reflection': 2, 'circle_fit_notch': 3, 'fano': 5, 'all_fits': 5 } try: self._fit_function = self._functions[fit_function] except KeyError: logging.error( 'Fit function not properly set. Must be either \'lorentzian\', \'skewed_lorentzian\', \'circle_fit_reflection\', \'circle_fit_notch\', \'fano\', or \'all_fits\'.' ) else: self._fit_resonator = True self._f_min = f_min self._f_max = f_max def _do_fit_resonator(self): ''' calls fit function in resonator class fit function is specified in self.set_fit, with boundaries f_mim and f_max only the last 'slice' of data is fitted, since we fit live while measuring. ''' if self._fit_function == 0: #lorentzian self._resonator.fit_lorentzian(f_min=self._f_min, f_max=self._f_max) if self._fit_function == 1: #skewed_lorentzian self._resonator.fit_skewed_lorentzian(f_min=self._f_min, f_max=self._f_max) if self._fit_function == 2: #circle_reflection self._resonator.fit_circle(reflection=True, f_min=self._f_min, f_max=self._f_max) if self._fit_function == 3: #circle_notch self._resonator.fit_circle(notch=True, f_min=self._f_min, f_max=self._f_max) if self._fit_function == 4: #fano self._resonator.fit_fano(f_min=self._f_min, f_max=self._f_max) #if self._fit_function == 5: #all fits #self._resonator.fit_all_fits(f_min=self._f_min, f_max = self._f_max) def delete_fit_function(self, n=None): ''' delete single fit function n (with 0 being the first one generated) or the complete landscape for n not specified ''' if n: self.landscape = np.delete(self.landscape, n, axis=0) else: self.landscape = None def plot_fit_function(self, num_points=100): ''' try: x_coords = np.linspace(self.x_vec[0], self.x_vec[-1], num_points) except Exception as message: print 'no x axis information specified', message return ''' if self.landscape: for trace in self.landscape: try: #plt.clear() plt.plot(self.x_vec, trace) plt.fill_between(self.x_vec, trace + float(self.span) / 2, trace - float(self.span) / 2, alpha=0.5) except Exception: print 'invalid trace...skip' plt.axhspan(self.y_vec[0], self.y_vec[-1], facecolor='0.5', alpha=0.5) plt.show() else: print 'No trace generated.' def set_span(self, span): self.span = span def get_span(self): return self.span def set_tdx(self, tdx): self.tdx = tdx def set_tdy(self, tdy): self.tdy = tdy def get_tdx(self): return self.tdx def get_tdy(self): return self.tdy def f_parab(self, x, a, b, c): return a * (x - b)**2 + c def f_hyp(self, x, a, b, c): "hyperbolic function with the form y = sqrt[ a*(x-b)**2 + c ]" return np.sqrt(a * (x - b)**2 + c) def f_lin(self, x, a, b): return a * x + b def set_plot_comment(self, comment): ''' Small comment to add at the end of plot pics for more information i.e. good for wiki entries. ''' self._plot_comment = comment
class transport(object): ''' useage: m = transport(daq = 'DAQ') m.set_voltage_bias('True') m.set_measurement_setup_parameters(conversion_IV = 1e7, V_amp=1, I_div=1, V_divider=1000) m.set_measurement_parameters(start=-100e-6, stop=100e-7, sample_count=1000, sample_rate=200, sweeps = 3) m.set_x_parameters(arange(0.,1.5,0.05),'magnetic coil current', yoko.set_current, 'A') #m.set_y_parameters(arange(4e9,7e9,10e6),'excitation frequency', mw_src.set_frequency, 'Hz') #sweep-parameter only active for x.measure_IV()!! m.measure_IV_2D() ''' def __init__(self, daq, exp_name = ''): self.daq = daq self.exp_name = exp_name self._chan_out = self.daq._ins._get_output_channels()[0] self._chan_in = self.daq._ins._get_input_channels()[0] self._tdx = 0.002 self._tdy = 0.002 self.comment = '' self._voltage_bias = False self._current_bias = True self._voltage_offset = 0. self._current_offset = 0. def set_x_parameters(self, x_vec, x_coordname, x_instrument, x_unit): self.x_vec = x_vec self.x_coordname = x_coordname self.x_set_obj = x_instrument self.x_unit = x_unit self._set_x_parameters = True def set_y_parameters(self, y_vec, y_coordname, y_instrument, y_unit): self.y_vec = y_vec self.y_coordname = y_coordname self.y_set_obj = y_instrument self.y_unit = y_unit self._set_y_parameters = True def set_measurement_setup_parameters(self, conversion_IV, V_amp, I_div, V_divider): self._conversion_IV = conversion_IV self._V_amp = V_amp self._I_div = I_div self._V_divider = V_divider self._set_measurement_setup_parameters = True def set_measurement_parameters(self, start, stop, sample_count, sample_rate, sweeps=1): self._start = start self._stop = stop self._sample_count = sample_count self._sample_rate = sample_rate self._sweeps = sweeps self._vec_fw = np.linspace(start, stop, sample_count) self._vec_bw = np.linspace(stop, start, sample_count) self._set_measurement_parameters = True def _check_measurement(self): if self._voltage_bias == self._current_bias: logging.error('Please specify current- or voltage-bias, both are %s...aborting' % (str(self._voltage_bias))) return False if not self._set_measurement_setup_parameters: logging.error('Please set_measurement_setup_parameters...aborting') return False if not self._set_measurement_parameters: logging.error('Please set_measurement_parameters...aborting') return False if self._measure_IV_2D or self._measure_IV_3D and not self._set_x_parameters: logging.error('Please set_x_parameters...aborting') return False if self._measure_IV_3D and not self._set_y_parameters: logging.error('Please set_y_parameters...aborting') return False return True def _save_settings(self): settings = "## Settings for measurement "+self._scan_name+' ##\n' settings += "A per V = %f\nV_amp = %f\nI_div = %f\nV_div = %f\nsamples = %f\nrate = %f\nsweeps = %f\n" % (float(self._conversion_factor), float(self._V_amp), float(self._I_div), float(self._V_div), float(self._sample_count), float(self._sample_rate), float(self._sweeps)) settings += 'Voltage bias = %s, Current bias = %s\n' %(str(self._voltage_bias), str(self._current_bias)) settings += "Min = %f \nMax = %f \n" % (self._start, self._stop) if not self._measure_IV: settings += "%s, %f-%f %s, step = %f\n" % (self.x_instrument, self.x_vec[0], self.x_vec[len(self.x_vec)-1], (self.x_vec[len(self.x_vec)-1]-self.x_vec[0])/(len(self.x_vec)-1)) if self._measure_IV_3D: settings += "%s, %f-%f %s, step = %f\n" % (self.y_instrument, self.y_vec[0], self.y_vec[len(self.y_vec)-1], (self.y_vec[len(self.y_vec)-1]-self.y_vec[0])/(len(self.y_vec)-1)) settings += "Current offset %f A\n" %(self._current_offset) settings += "Voltage offset %f V\n" %(self._voltage_offset) self._data.add_comment(settings) def measure_IV(self): self._measure_IV_1D = True self._measure_IV_2D = False self._measure_IV_3D = False if not self._check_measurement: return self._scan_name = 'IV' if self.exp_name: self._scan_name += '_' + self.exp_name self._p = Progress_Bar(self._sweeps) self._prepare_measurement_file() self._save_settings() #qviewkit.plot_hdf(self._data.get_filepath() self._measure() self._end_measurement() def measure_IV_2D(self): self._measure_IV_1D = False self._measure_IV_2D = True self._measure_IV_3D = False if not self._check_measurement: return self._scan_name = 'IV_vs_'+ self.x_coordname if self.exp_name: self._scan_name += '_' + self.exp_name self._p = Progress_Bar(len(self.x_vec)) self._prepare_measurement_file() self._save_settings() #qviewkit.plot_hdf(self._data.get_filepath()) self._measure() self._end_measurement() def measure_IV_3D(self): self._measure_IV_1D = False self._measure_IV_2D = False self._measure_IV_3D = True if not self._check_measurement: return self._scan_name = 'IV_vs_'+ self.x_coordname + '_' + self.y_coordname if self.exp_name: self._scan_name += '_' + self.exp_name self._p = Progress_Bar(len(self.x_vec)*len(self.y_vec)) self._prepare_measurement_file() self._save_settings() #qviewkit.plot_hdf(self._data.filepath()) self._measure() self._end_measurement() def _prepare_measurement_file(self): self._data = hdf.Data(name=self._scan_name) if self._voltage_bias: bias_name = 'Voltage' bias_unit = 'V' measurement_name = 'Current' measurement_unit = 'A' if self._current_bias: bias_name = 'Current' bias_unit = 'A' measurement_name = 'Voltage' measurement_unit = 'V' self._data_bias = self._data.add_coordinate(bias_name, unit = bias_unit) bias_vec = np.append(self._vec_fw, self._vec_bw) self._data_bias.add(bias_vec) if self._measure_IV_1D: if self._sweeps == 1: self._data_measure = self._data.add_value_vector(measurement_name, x = self._hdf_bias, unit = measurement_unit) else: self._data_sweep = self._data.add_coordinate('sweep') self._data_sweep.add([sweep for sweep in self._sweeps]) self._data_measure = self._data.add_value_matrix(measurement_name, x = self._data_sweep, y = self._hdf_bias, unit = measurement_unit) if self._measure_IV_2D: self._data_x = self._dat.add_coordinate(self.x_coordname, unit = self.x_unit) self._data_x.add(self.x_vec) self._data_measure = self._data.add_value_matrix(measurement_name, x = self._x, y = self._hdf_bias, unit = measurement_unit) if self._measure_IV_3D: self._data_x = self._data.add_coordinate(self.x_coordname, unit = self.x_unit) self._data_x.add(self.x_vec) self._data_y = self._data.add_coordinate(self.y_coordname, unit = self.y_unit) self._data_y.add(self.y_vec) self._data_measure = self._data.add_value_box(measurement_name, x = self._data_x, y = self._data_y, z = self._hdf_bias, unit = measurement_unit) if self.comment: self._data.add_comment(self.comment) def _measure(self): qt.mstart() plt.gca().set_xlabel("V [uV]") plt.gca().set_ylabel("I [nA]") try: if self._measure_IV_1D: for self._sweep in np.arange(self._sweeps): if self._current_bias: self._take_IV(out_conversion_factor = self._conversion_IV, in_amplification = self._V_amp, out_divider = self._V_divider) if self._voltage_bias: self._take_IV(out_conversion_factor = 1, in_amplification = self._conversion_IV, out_divider = self._V_divider) self._p_iterate() if self._measure_IV_2D or self._measure_IV_3D: for self._x in self.x_vec: self.x_set_obj(self._x) sleep(self._tdx) if self._measure_IV_2D: if self._current_bias: self._take_IV(out_conversion_factor = self._conversion_IV, in_amplification = self._V_amp, out_divider = self._V_divider) if self._voltage_bias: self._take_IV(out_conversion_factor = 1, in_amplification = self._conversion_IV, out_divider = self._V_divider) self._p.iterate() if self._measure_IV_3D: for self._y in self.y_vec: self.y_set_obj(self._y) sleep(self._tdy) if self._current_bias: self._take_IV(out_conversion_factor = self._conversion_IV, in_amplification = self._V_amp, out_divider = self._V_divider) if self._voltage_bias: self._take_IV(out_conversion_factor = 1, in_amplification = self._conversion_IV, out_divider = self._V_divider) self._p.iterate() finally: self.daq.set_ao1(0) qt.mend() def _end_measurement(self): print self._data.get_filepath() self._data.close_file() def _take_IV(self, out_conversion_factor,in_amplification,out_divider=1): """ IV measurement with current or voltage (vec_fw, vec_bw)!""" data_IV = self.daq.sync_output_input(self._chan_out, self._chan_in, self._vec_fw * out_divider / out_conversion_factor, rate=self._sample_rate) if self._current_bias: self._pl1 = plt.plot((data_IV/in_amplification)*1e6,self._vec_fw*1e6,"o") else: self._pl1 = plt.plot(self._vec_fw*1e6, (data_IV/in_amplification)*1e9,"-") qt.msleep(0.1) data_measure = [] data_measure = np.append(data_measure, data_IV/in_amplification) data_IV = self.daq.sync_output_input(self._chan_out, self._chan_in, self._vec_bw * out_divider / out_conversion_factor, rate=self._sample_rate) if self._current_bias: self._pl1 = plt.plot((data_IV/in_amplification)*1e6,self._vec_fw*1e6,"o") else: self._pl1 = plt.plot(self._vec_fw*1e6, (data_IV/in_amplification)*1e9,"-") qt.msleep(0.1) data_measure = np.append(data_measure, data_IV/in_amplification) self._data_measure.append(data_measure) def set_tdx(self, tdx): self._tdx = tdx def set_tdy(self, tdy): self._tdy = tdy def get_tdx(self): return self.tdx def get_tdy(self): return self.tdy def set_voltage_bias(self, voltage_bias): self._voltage_bias = voltage_bias self._current_bias = not voltage_bias def get_voltage_bias(self): return self._voltage_bias def set_current_bias(self, current_bias): self._current_bias = current_bias self._voltage_bias = not current_bias def get_current_bias(self): return self._current_bias def set_voltage_offset(self, voltage_offset): self._voltage_offset = voltage_offset def set_current_offset(self, voltage_offset): self._current_offset = voltage_offset def get_voltage_offset(self): return self._voltgae_offset def get_current_offset(self): return self._current_offset
class transport(object): ''' usage: m = spectrum(vna = vna1) m.set_x_parameters(arange(-0.05,0.05,0.01),'flux coil current',coil.set_current, unit = 'mA') m.set_y_parameters(arange(4e9,7e9,10e6),'excitation frequency',mw_src1.set_frequency, unit = 'Hz') m.gen_fit_function(...) several times m.measure_XX() ''' def __init__(self, IV_Device, exp_name = '', sample = None): self.IVD = IV_Device self.exp_name = exp_name self._sample = sample self.comment = '' self.dirname = None self.x_set_obj = None self.y_set_obj = None self.progress_bar = True self._plot_comment="" self.set_log_function() self.open_qviewkit = True self.qviewkit_singleInstance = False self._measurement_object = Measurement() self._measurement_object.measurement_type = 'transport' self._measurement_object.sample = self._sample self._qvk_process = False self.number_of_timetraces = 1 #relevant in time domain mode self._web_visible = True self.sweep = self.sweeps() self._Fraunhofer = True def add_sweep_4quadrants(self, start, stop, step, offset=0): self.sweep.add_sweep(start+offset, stop+offset, step) self.sweep.add_sweep(stop+offset, start+offset, -step) self.sweep.add_sweep(start+offset, -stop+offset, -step) self.sweep.add_sweep(-stop+offset, start+offset, step) def set_log_function(self, func=None, name = None, unit = None, log_dtype = None): ''' A function (object) can be passed to the measurement loop which is excecuted before every x iteration but after executing the x_object setter in 2D measurements and before every line (but after setting the x value) in 3D measurements. The return value of the function of type float or similar is stored in a value vector in the h5 file. Call without any arguments to delete all log functions. The timestamp is automatically saved. func: function object in list form name: name of logging parameter appearing in h5 file, default: 'log_param' unit: unit of logging parameter, default: '' log_dtype: h5 data type, default: 'f' (float32) ''' if name == None: try: name = ['log_param']*len(func) except Exception: name = None if unit == None: try: unit = ['']*len(func) except Exception: unit = None if log_dtype == None: try: log_dtype = ['f']*len(func) except Exception: log_dtype = None self.log_function = [] self.log_name = [] self.log_unit = [] self.log_dtype = [] if func != None: for i,f in enumerate(func): self.log_function.append(f) self.log_name.append(name[i]) self.log_unit.append(unit[i]) self.log_dtype.append(log_dtype[i]) def set_x_parameters(self, x_vec, x_coordname, x_set_obj, x_unit = ""): ''' Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep. For every x value all y values are swept Input: x_vec (array): conains the sweeping values x_coordname (string) x_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power()) x_unit (string): optional ''' self.x_vec = x_vec self.x_coordname = x_coordname self.x_set_obj = x_set_obj #self.delete_fit_function() self.x_unit = x_unit def set_y_parameters(self, y_vec, y_coordname, y_set_obj, y_unit = ""): ''' Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep. For every x value all y values are swept Input: y_vec (array): contains the sweeping values y_coordname (string) y_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power()) y_unit (string): optional ''' self.y_vec = y_vec self.y_coordname = y_coordname self.y_set_obj = y_set_obj self.delete_fit_function() self.y_unit = y_unit def set_web_visible(self, web_visible = True): ''' Sets the web_visible parameter for the measurement class Input: web_visible = True (Default) | False ''' self._web_visible = web_visible def set_sweep_type(self, sweep_type = 1): ''' # FIXME: HR this should go into the IVD driver Sets the sweep type, in the moment only simple sweep types are defined: Input: sweep_type: 0: single sweep START -> END 1: double sweep START -> END -> START (default) 2: triple sweep START -> END -> START -> -END 3: quad sweep START -> END -> START -> -END -> START ... ''' # define the number of datasets for each sweep type self.IV_sweep_types = { 0:1 , 1:2, 2:3, 3:4 } self.IV_sweep_type = sweep_type def get_sweep_type(self): return self.IV_sweep_type def get_num_ds_from_sweep_type(self,sweep_type): # should be self.IVD.IV_sweep_types[sweep_type] return self.IV_sweep_types[sweep_type] def _prepare_measurement_IVD(self): ''' all the relevant settings from the vna are updated and called ''' self.IVD.get_all() #ttip.get_temperature() # bias mode is either current=0 or voltage=1 self._bias_mode = self.IVD.get_bias_mode() #self._nop = self.IVD.get_nop() #self._sweeptime_averages = self.IVD.get_sweeptime_averages() #self._freqpoints = self.IVD.get_freqpoints() #if self.averaging_start_ready: self.vna.pre_measurement() def _prepare_measurement_file(self): ''' creates the output .h5-file with distinct dataset structures for each measurement type. at this point all measurement parameters are known and put in the output file ''' print ('filename '+self._file_name) self._data_file = hdf.Data(name=self._file_name) self._measurement_object.uuid = self._data_file._uuid self._measurement_object.hdf_relpath = self._data_file._relpath self._measurement_object.instruments = qt.instruments.get_instruments() #self._measurement_object.save() self._mo = self._data_file.add_textlist('measurement') self._mo.append(self._measurement_object.get_JSON()) # write logfile and instrument settings #self._write_settings_dataset() #self._log = waf.open_log_file(self._data_file.get_filepath()) #if not self._scan_time: # self._data_freq = self._data_file.add_coordinate('frequency', unit = 'Hz') # self._data_freq.add(self._freqpoints) self._data_I = [] self._data_V = [] #self._data_dVdI = [] if self._scan_1D: if self._bias_mode:# current bias #self._data_freq = self._data_file.add_coordinate('frequency', unit = 'Hz') for st in range(self.sweep.get_nos()): self._data_V.append(self._data_file.add_value_vector('V_'+str(st), unit = 'V', save_timestamp = False)) self._data_I.append(self._data_file.add_value_vector('I_'+str(st), x = self._data_V[st], unit = 'A', save_timestamp = False)) #self._data_dVdI.append(self._data_file.add_value_vector('_data_dVdI_'+str(st), x = self._data_V[st], unit = 'V/A', save_timestamp = False)) IV = self._data_file.add_view('IV', x = self._data_V[0], y = self._data_I[0]) #dVdI = self._data_file.add_view('dVdI', x = self._data_I[0] , y = self._data_dVdI[0]) for i in range(1, self.sweep.get_nos()): IV.add(x=self._data_V[i],y=self._data_I[i]) #dVdI.add(x=self._data_I[i],y=self._data_dVdI[i]) if self._scan_2D: self._data_x = self._data_file.add_coordinate(self.x_coordname, unit = self.x_unit) self._data_x.add(self.x_vec) for st in range(self.sweep.get_nos()): self._data_I.append(self._data_file.add_value_vector('I_'+str(st), unit = 'A', save_timestamp = False)) self._data_V.append(self._data_file.add_value_matrix('V_'+str(st), x = self._data_x, y = self._data_I[st], unit = 'V', save_timestamp = False)) #self._data_dVdI.append(self._data_file.add_value_matrix('dVdI_'+str(st), x = self._data_x, y = self._data_I[st], unit = 'V/A', save_timestamp = False)) if self._Fraunhofer: self._data_Ic = [] for st in range(self.sweep.get_nos()): self._data_Ic.append(self._data_file.add_value_vector('Ic_'+str(st), x = self._data_x, unit = 'A', save_timestamp = False)) Fraunhofer = self._data_file.add_view('Fraunhofer', x=self._data_x, y=self._data_Ic[0]) for i in range(1, self.sweep.get_nos()): Fraunhofer.add(x=self._data_x, y=self._data_Ic[i]) #if self.log_function != None: #use logging # self._log_value = [] # for i in range(len(self.log_function)): # self._log_value.append(self._data_file.add_value_vector(self.log_name[i], x = self._data_x, unit = self.log_unit[i], dtype=self.log_dtype[i])) if self._scan_3D: self._data_x = self._data_file.add_coordinate(self.x_coordname, unit = self.x_unit) self._data_x.add(self.x_vec) self._data_y = self._data_file.add_coordinate(self.y_coordname, unit = self.y_unit) self._data_y.add(self.y_vec) if self._nop == 0: #saving in a 2D matrix instead of a 3D box HR: does not work yet !!! test things before you put them online. self._data_amp = self._data_file.add_value_matrix('amplitude', x = self._data_x, y = self._data_y, unit = 'arb. unit', save_timestamp = False) self._data_pha = self._data_file.add_value_matrix('phase', x = self._data_x, y = self._data_y, unit = 'rad', save_timestamp = False) else: self._data_amp = self._data_file.add_value_box('amplitude', x = self._data_x, y = self._data_y, z = self._data_freq, unit = 'arb. unit', save_timestamp = False) self._data_pha = self._data_file.add_value_box('phase', x = self._data_x, y = self._data_y, z = self._data_freq, unit = 'rad', save_timestamp = False) if self.log_function != None: #use logging self._log_value = [] for i in range(len(self.log_function)): self._log_value.append(self._data_file.add_value_vector(self.log_name[i], x = self._data_x, unit = self.log_unit[i], dtype=self.log_dtype[i])) if self.comment: self._data_file.add_comment(self.comment) if self.qviewkit_singleInstance and self.open_qviewkit and self._qvk_process: self._qvk_process.terminate() #terminate an old qviewkit instance def _write_settings_dataset(self): self._settings = self._data_file.add_textlist('settings') settings = waf.get_instrument_settings(self._data_file.get_filepath()) self._settings.append(settings) def _wait_progress_bar(self): ti = time() if self.progress_bar: self._p = Progress_Bar(self.IVD.get_averages(),self.dirname,self.IVD.get_sweeptime()) qt.msleep(.2) # wait for data while not self.IVD.ready(): if time()-ti > self.IVD.get_sweeptime(query=False): if self.progress_bar: self._p.iterate() ti = time() qt.msleep(.2) if self.progress_bar: while self._p.progr < self._p.max_it: self._p.iterate() def measure_1D(self, sweep_type=1): ''' measure method to record a single (averaged) VNA trace, S11 or S21 according to the setting on the VNA rescan: If True (default), the averages on the VNA are cleared and a new measurement is started. If False, it will directly take the data from the VNA without waiting. ''' self._sweep_type = sweep_type self._scan_1D = True self._scan_2D = False self._scan_3D = False self._scan_time = False self._measurement_object.measurement_func = 'measure_1D' self._measurement_object.x_axis = 'frequency' self._measurement_object.y_axis = '' self._measurement_object.z_axis = '' self._measurement_object.web_visible = self._web_visible if not self.dirname: self.dirname = 'IVD_tracedata' self._file_name = self.dirname.replace(' ', '').replace(',','_') if self.exp_name: self._file_name += '_' + self.exp_name # prepare storage self._prepare_measurement_IVD() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['I_0', 'V_0']) print('recording trace...') sys.stdout.flush() qt.mstart() self.sweep.create_iterator() self.IVD.set_status(True) for st in range(self.sweep.get_nos()): #print st self.IVD.set_sweep_parameters(self.sweep.get_sweep()) data_bias, data_sense = self.IVD.take_IV() self._data_I[st].append(data_bias) self._data_V[st].append(data_sense) #self._data_dVdI[st].append(np.gradient(self._data_V[st])/np.gradient(self._data_I[st])) self.IVD.set_status(False) qt.mend() self._end_measurement() def measure_2D(self): ''' measure method to record a (averaged) VNA trace, S11 or S21 according to the setting on the VNA for all parameters x_vec in x_obj ''' if not self.x_set_obj: logging.error('axes parameters not properly set...aborting') return self._scan_1D = False self._scan_2D = True self._scan_3D = False self._measurement_object.measurement_func = 'measure_2D' self._measurement_object.x_axis = self.x_coordname self._measurement_object.y_axis = 'current' self._measurement_object.z_axis = '' self._measurement_object.web_visible = self._web_visible if not self.dirname: self.dirname = self.x_coordname self._file_name = '2D_' + self.dirname.replace(' ', '').replace(',','_') if self.exp_name: self._file_name += '_' + self.exp_name #if self.progress_bar: self._p = Progress_Bar(len(self.x_vec),'2D VNA sweep '+self.dirname,self.vna.get_sweeptime_averages()) self._prepare_measurement_IVD() self._prepare_measurement_file() '''opens qviewkit to plot measurement, amp and pha are opened by default''' if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['I_0', 'V_0']) self._measure() # # # def measure_3D(self): # ''' # measure full window of vna while sweeping x_set_obj and y_set_obj with parameters x_vec/y_vec. sweep over y_set_obj is the inner loop, for every value x_vec[i] all values y_vec are measured. # # optional: measure method to perform the measurement according to landscape, if set # self.span is the range (in units of the vertical plot axis) data is taken around the specified funtion(s) # note: make sure to have properly set x,y vectors before generating traces # ''' # if not self.x_set_obj or not self.y_set_obj: # logging.error('axes parameters not properly set...aborting') # return # self._scan_1D = False # self._scan_2D = False # self._scan_3D = True # self._scan_time = False # # self._measurement_object.measurement_func = 'measure_3D' # self._measurement_object.x_axis = self.x_coordname # self._measurement_object.y_axis = self.y_coordname # self._measurement_object.z_axis = 'frequency' # self._measurement_object.web_visible = self._web_visible # # if not self.dirname: # self.dirname = self.x_coordname + ', ' + self.y_coordname # self._file_name = '3D_' + self.dirname.replace(' ', '').replace(',','_') # if self.exp_name: # self._file_name += '_' + self.exp_name # # if self.progress_bar: self._p = Progress_Bar(len(self.x_vec)*len(self.y_vec),'3D VNA sweep '+self.dirname,self.vna.get_sweeptime_averages()) # # self._prepare_measurement_vna() # self._prepare_measurement_file() # '''opens qviewkit to plot measurement, amp and pha are opened by default''' # '''only middle point in freq array is plotted vs x and y''' # if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase']) # if self._fit_resonator: # self._resonator = resonator(self._data_file.get_filepath()) # # if self.landscape: # self.center_freqs = np.array(self.landscape).T # else: # self.center_freqs = [] #load default sequence # for i in range(len(self.x_vec)): # self.center_freqs.append([0]) # # self._measure() def _measure(self): ''' measures and plots the data depending on the measurement type. the measurement loops feature the setting of the objects and saving the data in the .h5 file. ''' qt.mstart() try: """ loop: x_obj with parameters from x_vec """ self.IVD.set_status(True) for ix, x in enumerate(self.x_vec): self.x_set_obj(x) sleep(1) #if self.log_function != None: # for i,f in enumerate(self.log_function): # self._log_value[i].append(float(f())) if self._scan_2D: """ measurement """ self.sweep.create_iterator() for st in range(self.sweep.get_nos()): self.IVD.set_sweep_parameters(self.sweep.get_sweep()) data_bias, data_sense = self.IVD.take_IV(channel=1) self._data_I[st].add(data_bias) self._data_V[st].append(data_sense) #self._data_dVdI[st].append(np.array(np.gradient(self._data_V[st]))[-1]/np.gradient(self._data_I[st])) if self._Fraunhofer: self._IVC = IV_curve() self._data_Ic[st].append(self._IVC.get_Ic(V=data_sense, I=data_bias, direction=self.IVD._direction)) #if self.progress_bar: # self._p.iterate() qt.msleep() self.IVD.set_status(False) except Exception as e: print e.__doc__ print e.message finally: self._end_measurement() self.IVD.set_status(False, 1) #self.IVD.ramp_current(0, 100e-6, channel=2) self.IVD.set_status(False, 2) qt.mend() def _end_measurement(self): ''' the data file is closed and filepath is printed ''' print self._data_file.get_filepath() #qviewkit.save_plots(self._data_file.get_filepath(),comment=self._plot_comment) #old version where we have to wait for the plots t = threading.Thread(target=qviewkit.save_plots,args=[self._data_file.get_filepath(),self._plot_comment]) t.start() self._data_file.close_file() #waf.close_log_file(self._log) self.dirname = None def set_span(self, span): self.span = span def get_span(self): return self.span def set_tdx(self, tdx): self.tdx = tdx def set_tdy(self, tdy): self.tdy = tdy def get_tdx(self): return self.tdx def get_tdy(self): return self.tdy def f_parab(self,x,a,b,c): return a*(x-b)**2+c def f_hyp(self,x,a,b,c): "hyperbolic function with the form y = sqrt[ a*(x-b)**2 + c ]" return np.sqrt(a*(x-b)**2+c) def set_plot_comment(self, comment): ''' Small comment to add at the end of plot pics for more information i.e. good for wiki entries. ''' self._plot_comment=comment class sweeps(object): def __init__(self, name='default'): self._starts = [] self._stops = [] self._steps = [] self.create_iterator() def create_iterator(self): self._start_iter = iter(self._starts) self._stop_iter = iter(self._stops) self._step_iter = iter(self._steps) def add_sweep(self, start, stop, step): self._starts.append(start) self._stops.append(stop) self._steps.append(step) def reset_sweeps(self): self._starts = [] self._stops = [] self._stops = [] def get_sweep(self): return (self._start_iter.next(), self._stop_iter.next(), self._step_iter.next()) def get_nos(self): return len(self._starts) def print_sweeps(self): print(self._starts, self._stops, self._steps)
class spectrum(object): """ usage: similar to the old spectroscopy file.However class generates hdf vectors/matrices depending on the number of traces of your signal_analyzer. m = spectrum(sig_analyzer = specki) m.set_x_parameters(arange(-0.05,0.05,0.01),'flux coil current',coil.set_current, unit = 'mA') m.set_y_parameters(arange(4e9,7e9,10e6),'excitation frequency',mw_src1.set_frequency, unit = 'Hz') m.gen_fit_function(...) several times m.measure_XX() functions that must be additionally implemented in your signal analyzer's driver get_y_unit(trace) - returns y-unit of your trace get_active_traces() - returns number of traces get_trace_name(trace) - returns name of your trace """ def __init__(self, sig_analyzer, exp_name='', sample=None): self.sig_analyzer = sig_analyzer self.averaging_start_ready = "start_measurement" in self.sig_analyzer.get_function_names() \ and "ready" in self.sig_analyzer.get_function_names() if not self.averaging_start_ready: logging.warning( __name__ + ': With your signal analyzer driver (' + self.sig_analyzer.get_type() + '),' ' I can not see when a measurement is complete. ' 'So I only wait for a specific time and hope the VNA has finished. ' 'Please consider implemeting the necessary functions into your driver.' ) self.exp_name = exp_name self._sample = sample self.landscape = None self.span = 200e6 # [Hz] self.tdx = 0.002 # [s] self.tdy = 0.002 # [s] self.comment = '' self.dirname = None self.x_set_obj = None self.y_set_obj = None self.progress_bar = True self._plot_comment = "" self.set_log_function() self.open_qviewkit = True self.qviewkit_singleInstance = False self._measurement_object = Measurement() self._measurement_object.measurement_type = 'spectroscopy' self._measurement_object.sample = self._sample self._qvk_process = False def set_log_function(self, func=None, name=None, unit=None, log_dtype=None): ''' A function (object) can be passed to the measurement loop which is excecuted before every x iteration but after executing the x_object setter in 2D measurements and before every line (but after setting the x value) in 3D measurements. The return value of the function of type float or similar is stored in a value vector in the h5 file. Call without any arguments to delete all log functions. The timestamp is automatically saved. func: function object in list form name: name of logging parameter appearing in h5 file, default: 'log_param' unit: unit of logging parameter, default: '' log_dtype: h5 data type, default: 'f' (float32) ''' if name == None: try: name = ['log_param'] * len(func) except Exception: name = None if unit == None: try: unit = [''] * len(func) except Exception: unit = None if log_dtype == None: try: log_dtype = ['f'] * len(func) except Exception: log_dtype = None self.log_function = [] self.log_name = [] self.log_unit = [] self.log_dtype = [] if func != None: for i, f in enumerate(func): self.log_function.append(f) self.log_name.append(name[i]) self.log_unit.append(unit[i]) self.log_dtype.append(log_dtype[i]) def set_x_parameters(self, x_vec, x_coordname, x_set_obj, x_unit=""): ''' Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep. For every x value all y values are swept Input: x_vec (array): conains the sweeping values x_coordname (string) x_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power()) x_unit (string): optional ''' self.x_vec = x_vec self.x_coordname = x_coordname self.x_set_obj = x_set_obj self.delete_fit_function() self.x_unit = x_unit def set_y_parameters(self, y_vec, y_coordname, y_set_obj, y_unit=""): ''' Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep. For every x value all y values are swept Input: y_vec (array): contains the sweeping values y_coordname (string) y_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power()) y_unit (string): optional ''' self.y_vec = y_vec self.y_coordname = y_coordname self.y_set_obj = y_set_obj self.delete_fit_function() self.y_unit = y_unit def gen_fit_function(self, curve_f, curve_p, units='', p0=[-1, 0.1, 7]): ''' curve_f: 'parab', 'hyp', specifies the fit function to be employed curve_p: set of points that are the basis for the fit in the format [[x1,x2,x3,...],[y1,y2,y3,...]], frequencies in Hz units: set this to 'Hz' in order to avoid large values that cause the fit routine to diverge p0 (optional): start parameters for the fit, must be an 1D array of length 3 ([a,b,c,d]), where for the parabula p0[3] will be ignored The parabolic function takes the form y = a*(x-b)**2 + c , where (a,b,c) = p0 The hyperbolic function takes the form y = sqrt[ a*(x-b)**2 + c ], where (a,b,c) = p0 adds a trace to landscape ''' if not qkit.module_available("scipy"): raise ImportError('scipy not available.') if not self.landscape: self.landscape = [] x_fit = curve_p[0] if units == 'Hz': y_fit = np.array(curve_p[1]) * 1e-9 else: y_fit = np.array(curve_p[1]) try: multiplier = 1 if units == 'Hz': multiplier = 1e9 fit_fct = None if curve_f == 'parab': fit_fct = self.f_parab elif curve_f == 'hyp': fit_fct = self.f_hyp elif curve_f == 'lin': fit_fct = self.f_lin p0 = p0[:2] else: print('function type not known...aborting') raise ValueError popt, pcov = curve_fit(fit_fct, x_fit, y_fit, p0=p0) self.landscape.append(multiplier * fit_fct(self.x_vec, *popt)) except Exception as message: print('fit not successful:', message) popt = p0 def _prepare_measurement_sig_analyzer(self): ''' all the relevant settings from the vna are updated and called ''' self.sig_analyzer.get_all() # ttip.get_temperature() self._nop = self.sig_analyzer.get_nop() self._sweeptime_averages = self.sig_analyzer.get_sweeptime_averages() self._freqpoints = self.sig_analyzer.get_freqpoints() self.num_traces = self.sig_analyzer.get_active_traces() self.traces_names = [] self.units = [] for i in range(self.num_traces): self.traces_names.append(self.sig_analyzer.get_trace_name( i + 1)) # must be implemented in driver self.units.append( self.sig_analyzer.get_y_unit(i + 1)) # must also be impelmented if self.averaging_start_ready: self.sig_analyzer.pre_measurement() def _prepare_measurement_file(self): ''' creates the output .h5-file with distinct dataset structures for each measurement type. at this point all measurement parameters are known and put in the output file ''' self._data_file = hdf.Data(name=self._file_name, mode='a') self._measurement_object.uuid = self._data_file._uuid self._measurement_object.hdf_relpath = self._data_file._relpath self._measurement_object.instruments = qkit.instruments.get_instrument_names( ) self._measurement_object.save() self._mo = self._data_file.add_textlist('measurement') self._mo.append(self._measurement_object.get_JSON()) # write logfile and instrument settings self._write_settings_dataset() self._log = waf.open_log_file(self._data_file.get_filepath()) self._data_freq = self._data_file.add_coordinate('frequency', unit='Hz') self._data_freq.add(self._freqpoints) self._data = [] # empty list as we have a variable number of channels if self._scan_1D: for i in range(self.num_traces): self._data.append( self._data_file.add_value_vector(self.traces_names[i], x=self._data_freq, unit=self.units[i], save_timestamp=True)) if self._scan_2D: self._data_x = self._data_file.add_coordinate(self.x_coordname, unit=self.x_unit) self._data_x.add(self.x_vec) for i in range(self.num_traces): self._data.append( self._data_file.add_value_matrix(self.traces_names[i], x=self._data_x, y=self._data_freq, unit=self.units[i], save_timestamp=True)) if self.log_function != None: # use logging self._log_value = [] for i in range(len(self.log_function)): self._log_value.append( self._data_file.add_value_vector( self.log_name[i], x=self._data_x, unit=self.log_unit[i], dtype=self.log_dtype[i])) if self._scan_3D: self._data_x = self._data_file.add_coordinate(self.x_coordname, unit=self.x_unit) self._data_x.add(self.x_vec) self._data_y = self._data_file.add_coordinate(self.y_coordname, unit=self.y_unit) self._data_y.add(self.y_vec) for i in range(self.num_traces): self._data.append( self._data_file.add_value_box(self.traces_names[i], x=self._data_x, y=self.data_y, z=self._data_freq, unit=self.units[i], save_timestamp=True)) if self.log_function != None: # use logging self._log_value = [] for i in range(len(self.log_function)): self._log_value.append( self._data_file.add_value_vector( self.log_name[i], x=self._data_x, unit=self.log_unit[i], dtype=self.log_dtype[i])) if self.comment: self._data_file.add_comment(self.comment) if self.qviewkit_singleInstance and self.open_qviewkit and self._qvk_process: self._qvk_process.terminate() # terminate an old qviewkit instance def _write_settings_dataset(self): self._settings = self._data_file.add_textlist('settings') settings = waf.get_instrument_settings(self._data_file.get_filepath()) self._settings.append(settings) def measure_1D(self, rescan=True, web_visible=True): ''' measure method to record a single (averaged) VNA trace, S11 or S21 according to the setting on the VNA rescan: If True (default), the averages on the VNA are cleared and a new measurement is started. If False, it will directly take the data from the VNA without waiting. ''' self._scan_1D = True self._scan_2D = False self._scan_3D = False self._scan_time = False self._measurement_object.measurement_func = 'measure_1D' self._measurement_object.x_axis = 'frequency' self._measurement_object.y_axis = '' self._measurement_object.z_axis = '' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = 'signal_analyzer_tracedata' self._file_name = self.dirname.replace(' ', '').replace(',', '_') if self.exp_name: self._file_name += '_' + self.exp_name self._prepare_measurement_sig_analyzer() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=self.traces_names) print('recording trace...') sys.stdout.flush() qkit.flow.start() if rescan: if self.averaging_start_ready: self.sig_analyzer.start_measurement() ti = time() if self.progress_bar: self._p = Progress_Bar(self.sig_analyzer.get_averages(), self.dirname, self.sig_analyzer.get_sweeptime()) qkit.flow.sleep(.2) while not self.sig_analyzer.ready(): if time() - ti > self.sig_analyzer.get_sweeptime( query=False): if self.progress_bar: self._p.iterate() ti = time() qkit.flow.sleep(.2) if self.progress_bar: while self._p.progr < self._p.max_it: self._p.iterate() else: # not tested!!!!!! self.sig_analyzer.avg_clear() if self.sig_analyzer.get_averages( ) == 1 or self.sig_analyzer.get_Average( ) == False: # no averaging if self.progress_bar: self._p = Progress_Bar( 1, self.dirname, self.sig_analyzer.get_sweeptime()) qkit.flow.sleep( self.sig_analyzer.get_sweeptime()) # wait single sweep if self.progress_bar: self._p.iterate() else: # with averaging if self.progress_bar: self._p = Progress_Bar( self.sig_analyzer.get_averages(), self.dirname, self.sig_analyzer.get_sweeptime()) if "avg_status" in self.sig_analyzer.get_function_names(): for a in range(self.sig_analyzer.get_averages()): while self.sig_analyzer.avg_status() <= a: qkit.flow.sleep( .2 ) # maybe one would like to adjust this at a later point if self.progress_bar: self._p.iterate() else: # old style for a in range(self.sig_analyzer.get_averages()): qkit.flow.sleep(self.sig_analyzer.get_sweeptime() ) # wait single sweep time if self.progress_bar: self._p.iterate() for i in range(self.num_traces): self._data[i].append(self.sig_analyzer.get_tracedata(i + 1)) qkit.flow.end() self._end_measurement() def measure_2D(self, web_visible=True): ''' measure method to record a (averaged) VNA trace, S11 or S21 according to the setting on the VNA for all parameters x_vec in x_obj ''' if not self.x_set_obj: logging.error('axes parameters not properly set...aborting') return self._scan_1D = False self._scan_2D = True self._scan_3D = False self._scan_time = False self._measurement_object.measurement_func = 'measure_2D' self._measurement_object.x_axis = self.x_coordname self._measurement_object.y_axis = 'frequency' self._measurement_object.z_axis = '' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = self.x_coordname self._file_name = '2D_' + self.dirname.replace(' ', '').replace( ',', '_') if self.exp_name: self._file_name += '_' + self.exp_name if self.progress_bar: self._p = Progress_Bar(len(self.x_vec), '2D signal analyzer sweep ' + self.dirname, self.sig_analyzer.get_sweeptime_averages()) self._prepare_measurement_sig_analyzer() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=self.traces_names) self._measure() def measure_3D(self, web_visible=True): ''' measure full window of vna while sweeping x_set_obj and y_set_obj with parameters x_vec/y_vec. sweep over y_set_obj is the inner loop, for every value x_vec[i] all values y_vec are measured. optional: measure method to perform the measurement according to landscape, if set self.span is the range (in units of the vertical plot axis) data is taken around the specified funtion(s) note: make sure to have properly set x,y vectors before generating traces ''' if not self.x_set_obj or not self.y_set_obj: logging.error('axes parameters not properly set...aborting') return self._scan_1D = False self._scan_2D = False self._scan_3D = True self._scan_time = False self._measurement_object.measurement_func = 'measure_3D' self._measurement_object.x_axis = self.x_coordname self._measurement_object.y_axis = self.y_coordname self._measurement_object.z_axis = 'frequency' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = self.x_coordname + ', ' + self.y_coordname self._file_name = '3D_' + self.dirname.replace(' ', '').replace( ',', '_') if self.exp_name: self._file_name += '_' + self.exp_name if self.progress_bar: self._p = Progress_Bar( len(self.x_vec) * len(self.y_vec), '3D signal analyzer sweep ' + self.dirname, self.sig_analyzer.get_sweeptime_averages()) self._prepare_measurement_sig_analyzer() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" """only middle point in freq array is plotted vs x and y""" if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=self.traces_names) ### ??? if self.landscape: self.center_freqs = np.array(self.landscape).T else: self.center_freqs = [] # load default sequence for i in range(len(self.x_vec)): self.center_freqs.append([0]) self._measure() def _measure(self): ''' measures and plots the data depending on the measurement type. the measurement loops feature the setting of the objects and saving the data in the .h5 file. ''' qkit.flow.start() try: """ loop: x_obj with parameters from x_vec """ for ix, x in enumerate(self.x_vec): self.x_set_obj(x) sleep(self.tdx) if self.log_function != None: for i, f in enumerate(self.log_function): self._log_value[i].append(float(f())) #### not tested if self._scan_3D: for y in self.y_vec: """ loop: y_obj with parameters from y_vec (only 3D measurement) """ if ( np.min( np.abs(self.center_freqs[ix] - y * np.ones(len(self.center_freqs[ix]))) ) > self.span / 2. ) and self.landscape: # if point is not of interest (not close to one of the functions) data = [] for i in range(self.num_traces): data.append(np.zeros(int(self._nop))) else: self.y_set_obj(y) sleep(self.tdy) if self.averaging_start_ready: self.sig_analyzer.start_measurement() qkit.flow.sleep( .2 ) # just to make sure, the ready command does not *still* show ready while not self.sig_analyzer.ready(): qkit.flow.sleep(.2) else: self.sig_analyzer.avg_clear() qkit.flow.sleep(self._sweeptime_averages) # if "avg_status" in self.sig_analyzer.get_function_names(): # while self.sig_analyzer.avg_status() < self.sig_analyzer.get_averages(): # qkit.flow.sleep(.2) #maybe one would like to adjust this at a later point """ measurement """ for i in range(self.num_traces): data[i] = self.sig_analyzer.get_tracedata(i + 1) self._data[i].append(data[i]) if self.progress_bar: self._p.iterate() qkit.flow.sleep() """ filling of value-box is done here. after every y-loop the data is stored the next 2d structure """ for i in range(self.num_traces): self._data[i].next_matrix() if self._scan_2D: if self.averaging_start_ready: self.sig_analyzer.start_measurement() qkit.flow.sleep( .2 ) # just to make sure, the ready command does not *still* show ready while not self.sig_analyzer.ready(): qkit.flow.sleep(.2) else: self.sig_analyzer.avg_clear() qkit.flow.sleep(self._sweeptime_averages) """ measurement """ for i in range(self.num_traces): self._data[i].append( self.sig_analyzer.get_tracedata(i + 1)) if self.progress_bar: self._p.iterate() qkit.flow.sleep() finally: self._end_measurement() qkit.flow.end() def _end_measurement(self): ''' the data file is closed and filepath is printed ''' print(self._data_file.get_filepath()) # qviewkit.save_plots(self._data_file.get_filepath(),comment=self._plot_comment) #old version where we have to wait for the plots t = threading.Thread( target=qviewkit.save_plots, args=[self._data_file.get_filepath(), self._plot_comment]) t.start() self._data_file.close_file() waf.close_log_file(self._log) self.dirname = None if self.averaging_start_ready: self.sig_analyzer.post_measurement() def delete_fit_function(self, n=None): ''' delete single fit function n (with 0 being the first one generated) or the complete landscape for n not specified ''' if n: self.landscape = np.delete(self.landscape, n, axis=0) else: self.landscape = None def plot_fit_function(self, num_points=100): ''' try: x_coords = np.linspace(self.x_vec[0], self.x_vec[-1], num_points) except Exception as message: print 'no x axis information specified', message return ''' if not qkit.module_available("matplotlib"): raise ImportError("matplotlib not found.") if self.landscape: for trace in self.landscape: try: # plt.clear() plt.plot(self.x_vec, trace) plt.fill_between(self.x_vec, trace + float(self.span) / 2, trace - float(self.span) / 2, alpha=0.5) except Exception: print('invalid trace...skip') plt.axhspan(self.y_vec[0], self.y_vec[-1], facecolor='0.5', alpha=0.5) plt.show() else: print('No trace generated.') def set_span(self, span): self.span = span def get_span(self): return self.span def set_tdx(self, tdx): self.tdx = tdx def set_tdy(self, tdy): self.tdy = tdy def get_tdx(self): return self.tdx def get_tdy(self): return self.tdy def f_parab(self, x, a, b, c): return a * (x - b)**2 + c def f_hyp(self, x, a, b, c): "hyperbolic function with the form y = sqrt[ a*(x-b)**2 + c ]" return np.sqrt(a * (x - b)**2 + c) def f_lin(self, x, a, b): return a * x + b def set_plot_comment(self, comment): ''' Small comment to add at the end of plot pics for more information i.e. good for wiki entries. ''' self._plot_comment = comment
def update_sequence(ts, wfm_func, sample, iq = None, loop = False, drive = 'c:', path = '\\waveforms', reset = True, marker=None, markerfunc=None, ch2_amp = 2,chpair=1,awg= None): ''' set awg to sequence mode and push a number of waveforms into the sequencer inputs: ts: array of times, len(ts) = #sequenzes wfm_func: waveform function usually generated via generate_waveform using ts[i]; this can be a touple of arrays (for channels 0,1, heterodyne mode) or a single array (homodyne mode) sample: sample object iq: Reference to iq mixer instrument. If None (default), the wfm will not be changed. Otherwise, the wfm will be converted via iq.convert() marker: marker array in the form [[ch1m1,ch1m2],[ch2m1,ch2m2]] and all entries arrays of sample length markerfunc: analog to wfm_func, set marker to None when used for the 6GS/s AWG, the waveform length must be divisible by 64 for the 1.2GS/s AWG, it must be divisible by 4 chpair: if you use the 4ch Tabor AWG as a single 5ch instrument, you can chose to take the second channel pair here (this can be either 1 or 2). ''' qt.mstart() if awg==None: awg = sample.get_awg() clock = sample.get_clock() wfm_func2 = wfm_func if iq != None: wfm_func2 = lambda t, sample: iq.convert(wfm_func(t,sample)) # create new sequence if reset: if "Tektronix" in awg.get_type(): awg.set_runmode('SEQ') awg.set_seq_length(0) #clear sequence, necessary? awg.set_seq_length(len(ts)) elif "Tabor" in awg.get_type(): awg.set('p%i_runmode'%chpair,'SEQ') awg.define_sequence(chpair*2-1,len(ts)) #amplitude settings of analog output awg.set_ch1_offset(0) awg.set_ch2_offset(0) awg.set_ch1_amplitude(2) awg.set_ch2_amplitude(ch2_amp) #generate empty tuples wfm_samples_prev = [None,None] wfm_fn = [None,None] wfm_pn = [None,None] p = Progress_Bar(len(ts)*(2 if "Tektronix" in awg.get_type() else 1),'Load AWG') #init progress bar #update all channels and times for ti, t in enumerate(ts): #run through all sequences qt.msleep() wfm_samples = wfm_func2(t,sample) #generate waveform if not isinstance(wfm_samples[0],(list, tuple, np.ndarray)): #homodyne wfm_samples = [wfm_samples,np.zeros_like(wfm_samples, dtype=np.int8)] for chan in [0,1]: if markerfunc != None: #use markerfunc try: if markerfunc[chan][0] == None: marker1 = np.zeros_like(wfm_samples, dtype=np.int8)[0] else: marker1 = markerfunc[chan][0](t,sample) if markerfunc[chan][1] == None: marker2 = np.zeros_like(wfm_samples, dtype=np.int8)[0] else: marker2 = markerfunc[chan][1](t,sample) except TypeError: #only one markerfunc given marker1, marker2 = np.zeros_like(wfm_samples, dtype=np.int8) if chan == 0: marker1 = markerfunc(t,sample) elif marker == None: #fill up with zeros marker1, marker2 = np.zeros_like(wfm_samples, dtype=np.int8) else: #or set your own markers c_marker1, c_marker2 = marker[chan] marker1 = c_marker1[ti] marker2 = c_marker2[ti] if "Tektronix" in awg.get_type(): wfm_fn[chan] = 'ch%d_t%05d'%(chan+1, ti) # filename is kept until changed if len(wfm_samples) == 1 and chan == 1: wfm_pn[chan] = '%s%s\\%s'%(drive, path, np.zeros_like(wfm_fn[0])) #create empty array else: wfm_pn[chan] = '%s%s\\%s'%(drive, path, wfm_fn[chan]) awg.wfm_send(wfm_samples[chan], marker1, marker2, wfm_pn[chan], clock) awg.wfm_import(wfm_fn[chan], wfm_pn[chan], 'WFM') # assign waveform to channel/time slot awg.wfm_assign(chan+1, ti+1, wfm_fn[chan]) if loop: awg.set_seq_loop(ti+1, np.infty) elif "Tabor" in awg.get_type(): if chan == 0: awg.wfm_send2(wfm_samples[0],wfm_samples[1],marker1,marker2,chpair*2-1+chan+1,ti+1) else: continue else: raise ValueError("AWG type not known") p.iterate() gc.collect() if reset and "Tektronix" in awg.get_type(): # enable channels awg.set_ch1_status(True) awg.set_ch2_status(True) awg.set_seq_goto(len(ts), 1) awg.run() awg.wait(10,False) elif reset and "Tabor" in awg.get_type(): # enable channels #awg.preset() awg.set_ch1_status(True) awg.set_ch2_status(True) qt.mend() return np.all([awg.get('ch%i_status'%i) for i in [1,2]])
def measure_1D(self, rescan = True, web_visible = True): ''' measure method to record a single (averaged) VNA trace, S11 or S21 according to the setting on the VNA rescan: If True (default), the averages on the VNA are cleared and a new measurement is started. If False, it will directly take the data from the VNA without waiting. ''' self._scan_1D = True self._scan_2D = False self._scan_3D = False self._scan_time = False self._measurement_object.measurement_func = 'measure_1D' self._measurement_object.x_axis = 'frequency' self._measurement_object.y_axis = '' self._measurement_object.z_axis = '' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = 'VNA_tracedata' self._file_name = self.dirname.replace(' ', '').replace(',','_') if self.exp_name: self._file_name += '_' + self.exp_name self._prepare_measurement_vna() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase']) if self._fit_resonator: self._resonator = resonator(self._data_file.get_filepath()) print 'recording trace...' sys.stdout.flush() qt.mstart() if rescan: if self.averaging_start_ready: self.vna.start_measurement() ti = time() if self.progress_bar: self._p = Progress_Bar(self.vna.get_averages(),self.dirname,self.vna.get_sweeptime()) qt.msleep(.2) while not self.vna.ready(): if time()-ti > self.vna.get_sweeptime(query=False): if self.progress_bar: self._p.iterate() ti = time() qt.msleep(.2) if self.progress_bar: while self._p.progr < self._p.max_it: self._p.iterate() else: self.vna.avg_clear() if self.vna.get_averages() == 1 or self.vna.get_Average() == False: #no averaging if self.progress_bar:self._p = Progress_Bar(1,self.dirname,self.vna.get_sweeptime()) qt.msleep(self.vna.get_sweeptime()) #wait single sweep if self.progress_bar: self._p.iterate() else: #with averaging if self.progress_bar: self._p = Progress_Bar(self.vna.get_averages(),self.dirname,self.vna.get_sweeptime()) if "avg_status" in self.vna.get_function_names(): for a in range(self.vna.get_averages()): while self.vna.avg_status() <= a: qt.msleep(.2) #maybe one would like to adjust this at a later point if self.progress_bar: self._p.iterate() else: #old style for a in range(self.vna.get_averages()): qt.msleep(self.vna.get_sweeptime()) #wait single sweep time if self.progress_bar: self._p.iterate() data_amp, data_pha = self.vna.get_tracedata() data_real, data_imag = self.vna.get_tracedata('RealImag') self._data_amp.append(data_amp) self._data_pha.append(data_pha) self._data_real.append(data_real) self._data_imag.append(data_imag) if self._fit_resonator: self._do_fit_resonator() qt.mend() self._end_measurement()
def monitor_depo(self): """ Main sputter deposition monitoring function. Consider using the threaded version by calling start_depmon(). Records the film resistance, thickness and deposition rate live during the sputter process. Stores everything into a h5 file. Provides measures to estimate the resulting resistance of the final film and thereby facilitates live adjustments to the sputter parameters. Note: set_duration and set_resolution should be called before to set the monitoring length and time resolution. set_filmparameters should be called before to provide the actual target values. """ self._init_depmon() if self.progress_bar: self._p = Progress_Bar( self._duration / self._resolution, 'EVAP_timetrace ' + self.dirname, self._resolution) # FIXME: Doesn't make much sense... print('Monitoring deposition...') sys.stdout.flush() if self.open_qviewkit: self._qvk_process = qviewkit.plot( self._data_file.get_filepath(), datasets=['views/resistance_thickness']) try: """ Initialize the data lists. """ class MonData(object): resistance = np.array([]) thickness = np.array([]) mon_data = MonData() """ Main loop: """ mon_data.t0 = time.time( ) # note: Windows has limitations on time precision (about 16ms) for mon_data.it, _ in enumerate(self.x_vec): self._acquire(mon_data) if self.progress_bar: self._p.iterate() # calculate the time when the next iteration should take place ti = mon_data.t0 + (float(mon_data.it) + 1) * self._resolution # wait until the total dt(iteration) has elapsed while time.time() < ti: time.sleep(0.05) except KeyboardInterrupt: print('Monitoring interrupted by user.') except Exception as e: print(e) print(e.__doc__) print(e.message) finally: self._end_measurement()
class spectrum(object): ''' usage: m = spectrum(vna = vna1) m.set_x_parameters(arange(-0.05,0.05,0.01),'flux coil current',coil.set_current, unit = 'mA') m.set_y_parameters(arange(4e9,7e9,10e6),'excitation frequency',mw_src1.set_frequency, unit = 'Hz') m.gen_fit_function(...) several times m.measure_XX() ''' def __init__(self, IV_Device, exp_name = '', sample = None): self.IVD = IV_Device self.exp_name = exp_name self._sample = sample self.comment = '' self.dirname = None self.x_set_obj = None self.y_set_obj = None self.progress_bar = True self._plot_comment="" self.set_log_function() self.open_qviewkit = True self.qviewkit_singleInstance = False self._measurement_object = Measurement() self._measurement_object.measurement_type = 'transport' self._measurement_object.sample = self._sample self._qvk_process = False self.number_of_timetraces = 1 #relevant in time domain mode def set_log_function(self, func=None, name = None, unit = None, log_dtype = None): ''' A function (object) can be passed to the measurement loop which is excecuted before every x iteration but after executing the x_object setter in 2D measurements and before every line (but after setting the x value) in 3D measurements. The return value of the function of type float or similar is stored in a value vector in the h5 file. Call without any arguments to delete all log functions. The timestamp is automatically saved. func: function object in list form name: name of logging parameter appearing in h5 file, default: 'log_param' unit: unit of logging parameter, default: '' log_dtype: h5 data type, default: 'f' (float32) ''' if name == None: try: name = ['log_param']*len(func) except Exception: name = None if unit == None: try: unit = ['']*len(func) except Exception: unit = None if log_dtype == None: try: log_dtype = ['f']*len(func) except Exception: log_dtype = None self.log_function = [] self.log_name = [] self.log_unit = [] self.log_dtype = [] if func != None: for i,f in enumerate(func): self.log_function.append(f) self.log_name.append(name[i]) self.log_unit.append(unit[i]) self.log_dtype.append(log_dtype[i]) def set_x_parameters(self, x_vec, x_coordname, x_set_obj, x_unit = ""): ''' Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep. For every x value all y values are swept Input: x_vec (array): conains the sweeping values x_coordname (string) x_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power()) x_unit (string): optional ''' self.x_vec = x_vec self.x_coordname = x_coordname self.x_set_obj = x_set_obj self.delete_fit_function() self.x_unit = x_unit def set_y_parameters(self, y_vec, y_coordname, y_set_obj, y_unit = ""): ''' Sets parameters for sweep. In a 3D measurement, the x-parameters will be the "outer" sweep. For every x value all y values are swept Input: y_vec (array): contains the sweeping values y_coordname (string) y_instrument (obj): callable object to execute with x_vec-values (i.e. vna.set_power()) y_unit (string): optional ''' self.y_vec = y_vec self.y_coordname = y_coordname self.y_set_obj = y_set_obj self.delete_fit_function() self.y_unit = y_unit def set_sweep_type(self, sweep_type = 1): ''' Sets the sweep type, in the moment only simple sweep types are defined: Input: sweep_type: 0: single sweep START -> END 1: double sweep START -> END -> START (default) 2: triple sweep START -> END -> START -> -END 3: quad sweep START -> END -> START -> -END -> START ''' self.IV_sweep_type = sweep_type def _prepare_measurement_IVD(self): ''' all the relevant settings from the vna are updated and called ''' self.IVD.get_all() #ttip.get_temperature() # bias mode is either current=0 or voltage=1 self._bias_mode = self.IVD.get_bias_mode() self._nop = self.IVD.get_nop() self._sweeptime_averages = self.IVD.get_sweeptime_averages() #self._freqpoints = self.IVD.get_freqpoints() if self.averaging_start_ready: self.vna.pre_measurement() def _prepare_measurement_file(self): ''' creates the output .h5-file with distinct dataset structures for each measurement type. at this point all measurement parameters are known and put in the output file ''' self._data_file = hdf.Data(name=self._file_name) self._measurement_object.uuid = self._data_file._uuid self._measurement_object.hdf_relpath = self._data_file._relpath self._measurement_object.instruments = qt.instruments.get_instruments() self._measurement_object.save() self._mo = self._data_file.add_textlist('measurement') self._mo.append(self._measurement_object.get_JSON()) # write logfile and instrument settings self._write_settings_dataset() self._log = waf.open_log_file(self._data_file.get_filepath()) #if not self._scan_time: # self._data_freq = self._data_file.add_coordinate('frequency', unit = 'Hz') # self._data_freq.add(self._freqpoints) if self._scan_1D: if self._bias_mode:# current bias #self._data_freq = self._data_file.add_coordinate('frequency', unit = 'Hz') for st in range(self.sweep_type): self._data_I[st] = self._data_file.add_value_vector('I_'+str(st), unit = 'A', save_timestamp = True) self._data_V[st] = self._data_file.add_value_vector('V_'+str(st), x = self._data_I, unit = 'V', save_timestamp = True) if self._scan_2D: self._data_x = self._data_file.add_coordinate(self.x_coordname, unit = self.x_unit) self._data_x.add(self.x_vec) self._data_amp = self._data_file.add_value_matrix('amplitude', x = self._data_x, y = self._data_freq, unit = 'arb. unit', save_timestamp = True) self._data_pha = self._data_file.add_value_matrix('phase', x = self._data_x, y = self._data_freq, unit='rad', save_timestamp = True) if self.log_function != None: #use logging self._log_value = [] for i in range(len(self.log_function)): self._log_value.append(self._data_file.add_value_vector(self.log_name[i], x = self._data_x, unit = self.log_unit[i], dtype=self.log_dtype[i])) if self._nop < 10: """creates view: plot middle point vs x-parameter, for qubit measurements""" self._data_amp_mid = self._data_file.add_value_vector('amplitude_midpoint', unit = 'arb. unit', x = self._data_x, save_timestamp = True) self._data_pha_mid = self._data_file.add_value_vector('phase_midpoint', unit = 'rad', x = self._data_x, save_timestamp = True) #self._view = self._data_file.add_view("amplitude vs. " + self.x_coordname, x = self._data_x, y = self._data_amp[self._nop/2]) if self._scan_3D: self._data_x = self._data_file.add_coordinate(self.x_coordname, unit = self.x_unit) self._data_x.add(self.x_vec) self._data_y = self._data_file.add_coordinate(self.y_coordname, unit = self.y_unit) self._data_y.add(self.y_vec) if self._nop == 0: #saving in a 2D matrix instead of a 3D box HR: does not work yet !!! test things before you put them online. self._data_amp = self._data_file.add_value_matrix('amplitude', x = self._data_x, y = self._data_y, unit = 'arb. unit', save_timestamp = False) self._data_pha = self._data_file.add_value_matrix('phase', x = self._data_x, y = self._data_y, unit = 'rad', save_timestamp = False) else: self._data_amp = self._data_file.add_value_box('amplitude', x = self._data_x, y = self._data_y, z = self._data_freq, unit = 'arb. unit', save_timestamp = False) self._data_pha = self._data_file.add_value_box('phase', x = self._data_x, y = self._data_y, z = self._data_freq, unit = 'rad', save_timestamp = False) if self.log_function != None: #use logging self._log_value = [] for i in range(len(self.log_function)): self._log_value.append(self._data_file.add_value_vector(self.log_name[i], x = self._data_x, unit = self.log_unit[i], dtype=self.log_dtype[i])) if self._scan_time: self._data_freq = self._data_file.add_coordinate('frequency', unit = 'Hz') self._data_freq.add([self.vna.get_centerfreq()]) self._data_time = self._data_file.add_coordinate('time', unit = 's') self._data_time.add(np.arange(0,self._nop,1)*self.vna.get_sweeptime()/(self._nop-1)) self._data_x = self._data_file.add_coordinate('trace_number', unit = '') self._data_x.add(np.arange(0, self.number_of_timetraces, 1)) self._data_amp = self._data_file.add_value_matrix('amplitude', x = self._data_x, y = self._data_time, unit = 'lin. mag.', save_timestamp = False) self._data_pha = self._data_file.add_value_matrix('phase', x = self._data_x, y = self._data_time, unit = 'rad.', save_timestamp = False) if self.comment: self._data_file.add_comment(self.comment) if self.qviewkit_singleInstance and self.open_qviewkit and self._qvk_process: self._qvk_process.terminate() #terminate an old qviewkit instance def _write_settings_dataset(self): self._settings = self._data_file.add_textlist('settings') settings = waf.get_instrument_settings(self._data_file.get_filepath()) self._settings.append(settings) def measure_1D(self, rescan = True, web_visible = True): ''' measure method to record a single (averaged) VNA trace, S11 or S21 according to the setting on the VNA rescan: If True (default), the averages on the VNA are cleared and a new measurement is started. If False, it will directly take the data from the VNA without waiting. ''' self._scan_1D = True self._scan_2D = False self._scan_3D = False self._scan_time = False self._measurement_object.measurement_func = 'measure_1D' self._measurement_object.x_axis = 'frequency' self._measurement_object.y_axis = '' self._measurement_object.z_axis = '' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = 'IVD_tracedata' self._file_name = self.dirname.replace(' ', '').replace(',','_') if self.exp_name: self._file_name += '_' + self.exp_name # prepare storage self._prepare_measurement_IVD() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['I_0', 'V_0']) print('recording trace...') sys.stdout.flush() qt.mstart() if rescan: if self.averaging_start_ready: self.vna.start_measurement() ti = time() if self.progress_bar: self._p = Progress_Bar(self.vna.get_averages(),self.dirname,self.vna.get_sweeptime()) qt.msleep(.2) while not self.vna.ready(): if time()-ti > self.vna.get_sweeptime(query=False): if self.progress_bar: self._p.iterate() ti = time() qt.msleep(.2) if self.progress_bar: while self._p.progr < self._p.max_it: self._p.iterate() else: self.vna.avg_clear() if self.vna.get_averages() == 1 or self.vna.get_Average() == False: #no averaging if self.progress_bar:self._p = Progress_Bar(1,self.dirname,self.vna.get_sweeptime()) qt.msleep(self.vna.get_sweeptime()) #wait single sweep if self.progress_bar: self._p.iterate() else: #with averaging if self.progress_bar: self._p = Progress_Bar(self.vna.get_averages(),self.dirname,self.vna.get_sweeptime()) if "avg_status" in self.vna.get_function_names(): for a in range(self.vna.get_averages()): while self.vna.avg_status() <= a: qt.msleep(.2) #maybe one would like to adjust this at a later point if self.progress_bar: self._p.iterate() else: #old style for a in range(self.vna.get_averages()): qt.msleep(self.vna.get_sweeptime()) #wait single sweep time if self.progress_bar: self._p.iterate() data_amp, data_pha = self.vna.get_tracedata() data_real, data_imag = self.vna.get_tracedata('RealImag') self._data_amp.append(data_amp) self._data_pha.append(data_pha) self._data_real.append(data_real) self._data_imag.append(data_imag) if self._fit_resonator: self._do_fit_resonator() qt.mend() self._end_measurement() """ def measure_2D(self, web_visible = True): ''' measure method to record a (averaged) VNA trace, S11 or S21 according to the setting on the VNA for all parameters x_vec in x_obj ''' if not self.x_set_obj: logging.error('axes parameters not properly set...aborting') return self._scan_1D = False self._scan_2D = True self._scan_3D = False self._scan_time = False self._measurement_object.measurement_func = 'measure_2D' self._measurement_object.x_axis = self.x_coordname self._measurement_object.y_axis = 'frequency' self._measurement_object.z_axis = '' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = self.x_coordname self._file_name = '2D_' + self.dirname.replace(' ', '').replace(',','_') if self.exp_name: self._file_name += '_' + self.exp_name if self.progress_bar: self._p = Progress_Bar(len(self.x_vec),'2D VNA sweep '+self.dirname,self.vna.get_sweeptime_averages()) self._prepare_measurement_vna() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" if self._nop < 10: if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude_midpoint', 'phase_midpoint']) else: if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase']) if self._fit_resonator: self._resonator = resonator(self._data_file.get_filepath()) self._measure() def measure_3D(self, web_visible = True): ''' measure full window of vna while sweeping x_set_obj and y_set_obj with parameters x_vec/y_vec. sweep over y_set_obj is the inner loop, for every value x_vec[i] all values y_vec are measured. optional: measure method to perform the measurement according to landscape, if set self.span is the range (in units of the vertical plot axis) data is taken around the specified funtion(s) note: make sure to have properly set x,y vectors before generating traces ''' if not self.x_set_obj or not self.y_set_obj: logging.error('axes parameters not properly set...aborting') return self._scan_1D = False self._scan_2D = False self._scan_3D = True self._scan_time = False self._measurement_object.measurement_func = 'measure_3D' self._measurement_object.x_axis = self.x_coordname self._measurement_object.y_axis = self.y_coordname self._measurement_object.z_axis = 'frequency' self._measurement_object.web_visible = web_visible if not self.dirname: self.dirname = self.x_coordname + ', ' + self.y_coordname self._file_name = '3D_' + self.dirname.replace(' ', '').replace(',','_') if self.exp_name: self._file_name += '_' + self.exp_name if self.progress_bar: self._p = Progress_Bar(len(self.x_vec)*len(self.y_vec),'3D VNA sweep '+self.dirname,self.vna.get_sweeptime_averages()) self._prepare_measurement_vna() self._prepare_measurement_file() """opens qviewkit to plot measurement, amp and pha are opened by default""" """only middle point in freq array is plotted vs x and y""" if self.open_qviewkit: self._qvk_process = qviewkit.plot(self._data_file.get_filepath(), datasets=['amplitude', 'phase']) if self._fit_resonator: self._resonator = resonator(self._data_file.get_filepath()) if self.landscape: self.center_freqs = np.array(self.landscape).T else: self.center_freqs = [] #load default sequence for i in range(len(self.x_vec)): self.center_freqs.append([0]) self._measure() """ def _measure(self): ''' measures and plots the data depending on the measurement type. the measurement loops feature the setting of the objects and saving the data in the .h5 file. ''' qt.mstart() try: """ loop: x_obj with parameters from x_vec """ for ix, x in enumerate(self.x_vec): self.x_set_obj(x) sleep(self.tdx) if self.log_function != None: for i,f in enumerate(self.log_function): self._log_value[i].append(float(f())) if self._scan_3D: for y in self.y_vec: """ loop: y_obj with parameters from y_vec (only 3D measurement) """ if (np.min(np.abs(self.center_freqs[ix]-y*np.ones(len(self.center_freqs[ix])))) > self.span/2.) and self.landscape: #if point is not of interest (not close to one of the functions) data_amp = np.zeros(int(self._nop)) data_pha = np.zeros(int(self._nop)) #fill with zeros else: self.y_set_obj(y) sleep(self.tdy) if self.averaging_start_ready: self.vna.start_measurement() qt.msleep(.2) #just to make sure, the ready command does not *still* show ready while not self.vna.ready(): qt.msleep(.2) else: self.vna.avg_clear() qt.msleep(self._sweeptime_averages) #if "avg_status" in self.vna.get_function_names(): # while self.vna.avg_status() < self.vna.get_averages(): # qt.msleep(.2) #maybe one would like to adjust this at a later point """ measurement """ data_amp, data_pha = self.vna.get_tracedata() if self._nop == 0: # this does not work yet. print data_amp[0], data_amp, self._nop self._data_amp.append(data_amp[0]) self._data_pha.append(data_pha[0]) else: self._data_amp.append(data_amp) self._data_pha.append(data_pha) if self._fit_resonator: self._do_fit_resonator() if self.progress_bar: self._p.iterate() qt.msleep() """ filling of value-box is done here. after every y-loop the data is stored the next 2d structure """ self._data_amp.next_matrix() self._data_pha.next_matrix() if self._scan_2D: if self.averaging_start_ready: self.vna.start_measurement() qt.msleep(.2) #just to make sure, the ready command does not *still* show ready while not self.vna.ready(): qt.msleep(.2) else: self.vna.avg_clear() qt.msleep(self._sweeptime_averages) """ measurement """ data_amp, data_pha = self.vna.get_tracedata() self._data_amp.append(data_amp) self._data_pha.append(data_pha) if self._nop < 10: #print data_amp[self._nop/2] self._data_amp_mid.append(float(data_amp[self._nop/2])) self._data_pha_mid.append(float(data_pha[self._nop/2])) if self._fit_resonator: self._do_fit_resonator() if self.progress_bar: self._p.iterate() qt.msleep() except Exception as e: print e.__doc__ print e.message finally: self._end_measurement() qt.mend() def _end_measurement(self): ''' the data file is closed and filepath is printed ''' print self._data_file.get_filepath() #qviewkit.save_plots(self._data_file.get_filepath(),comment=self._plot_comment) #old version where we have to wait for the plots t = threading.Thread(target=qviewkit.save_plots,args=[self._data_file.get_filepath(),self._plot_comment]) t.start() self._data_file.close_file() waf.close_log_file(self._log) self.dirname = None def set_span(self, span): self.span = span def get_span(self): return self.span def set_tdx(self, tdx): self.tdx = tdx def set_tdy(self, tdy): self.tdy = tdy def get_tdx(self): return self.tdx def get_tdy(self): return self.tdy def f_parab(self,x,a,b,c): return a*(x-b)**2+c def f_hyp(self,x,a,b,c): "hyperbolic function with the form y = sqrt[ a*(x-b)**2 + c ]" return np.sqrt(a*(x-b)**2+c) def set_plot_comment(self, comment): ''' Small comment to add at the end of plot pics for more information i.e. good for wiki entries. ''' self._plot_comment=comment