def _upload_awg_program(self, awg_program, core=0): # Transfer the AWG sequence program. Compilation starts automatically. # Create an instance of the AWG Module awgModule = self.daq.awgModule() awgModule.set('awgModule/device', self.device) awgModule.set('awgModule/index', int(core)) awgModule.execute() awgModule.set('awgModule/compiler/sourcestring', awg_program) while awgModule.getInt('awgModule/compiler/status') == -1: time.sleep(0.1) if self.isStopped(): return if awgModule.getInt('awgModule/compiler/status') == 1: # compilation failed, raise an exception raise Error('Upload failed:\n' + awgModule.getString('awgModule/compiler/statusstring')) if awgModule.getInt('awgModule/compiler/status') == 2: self.log("Compiler warning: ", awgModule.getString('awgModule/compiler/statusstring')) # Wait for the waveform upload to finish time.sleep(0.1) while ((awgModule.getDouble('awgModule/progress') < 1.0) and (awgModule.getInt('awgModule/elf/status') != 1)): time.sleep(0.1) if self.isStopped(): return if awgModule.getInt('awgModule/elf/status') == 1: raise Error("Uploading the AWG program failed.")
def _get_node_datatype(self, node): """Get datatype for object at node""" # used cached value, if available if node in self._node_datatypes: return self._node_datatypes[node] # find datatype from returned data d = self.daq.get(node, True) if len(d) == 0: raise Error('No value defined at node %s.' % node) data = next(iter(d.values())) # if returning dict, strip timing information (API level 6) if isinstance(data, dict) and 'value' in data: data = data['value'] # get first item, if python list assume string if isinstance(data, list): dtype = str # not string, should be np array, check dtype elif data.dtype in (int, np.int_, np.int64, np.int32, np.int16): dtype = int elif data.dtype in (float, np.float_, np.float64, np.float32): dtype = float elif data.dtype in (complex, np.complex_, np.complex64, np.complex128): dtype = complex else: raise Error('Undefined datatype for node %s.' % node) # keep track of datatype for future use self._node_datatypes[node] = dtype self.log('Datatype:', node, dtype) return dtype
def performOpen(self, options={}): """Perform the operation of opening the instrument connection""" # number of demod blocks in the FPGA self.num_of_demods = 5 # self.demod_n_pts = self.num_of_demods * 15 self.demod_n_pts = 80 self.bit_stream_name = '' # set time step and resolution self.nBit = 16 self.bitRange = float(2**(self.nBit - 1) - 1) # timeout self.timeout_ms = int(1000 * self.dComCfg['Timeout']) # get PXI chassis self.chassis = int(self.dComCfg.get('PXI chassis', 1)) # create AWG instance self.dig = keysightSD1.SD_AIN() AWGPart = self.dig.getProductNameBySlot( self.chassis, int(self.comCfg.address)) self.log('Serial:', self.dig.getSerialNumberBySlot( self.chassis, int(self.comCfg.address))) if not isinstance(AWGPart, str): raise Error('Unit not available') # check that model is supported dOptionCfg = self.dInstrCfg['options'] for validId, validName in zip( dOptionCfg['model_id'], dOptionCfg['model_str']): if AWGPart.find(validId) >= 0: # id found, stop searching break else: # loop fell through, raise ID error raise IdError(AWGPart, dOptionCfg['model_id']) # set model self.setModel(validName) # sampling rate and number of channles is set by model if validName in ('M3102', 'M3302'): # 500 MHz models self.dt = 2E-9 self.nCh = 4 else: # assume 100 MHz for all other models self.dt = 10E-9 self.nCh = 4 # create list of sampled data self.lTrace = [np.array([])] * self.nCh self.demod_output_ssb = np.zeros((0,), dtype='complex') self.demod_buffer = np.zeros((0,), dtype=np.int16) self.dig.openWithSlot(AWGPart, self.chassis, int(self.comCfg.address)) # get hardware version - changes numbering of channels hw_version = self.dig.getHardwareVersion() if hw_version >= 4: # KEYSIGHT - channel numbers start with 1 self.ch_index_zero = 1 else: # SIGNADYNE - channel numbers start with 0 self.ch_index_zero = 0 self.log('HW:', hw_version) self.configure_FPGA()
def configure_FPGA(self, reset=False): """Load FPGA bitstream and setup triggers""" self.fpga_config = self.getValue('FPGA Hardware') if reset or self.fpga_config == 'Only signals': bitstream = os.path.join( os.path.dirname(__file__), 'firmware_FPGAFlow_Clean_2018-05-31T22_22_11.sbp') elif self.fpga_config in ('FPGA I/Q and signals', 'Only FPGA I/Q'): bitstream = os.path.join( os.path.dirname(__file__), 'firmware_FPGAFlow_Demod_v4_IQx5_2018-09-02T19_14_50.sbp') # don't reload if correct bitstream is already loaded if bitstream == self.bit_stream_name: return if (self.dig.FPGAload(bitstream)) < 0: if self.fpga_config != 'Only signals': raise Error('FPGA not loaded, check FPGA version...') self.bit_stream_name = bitstream if self.fpga_config != 'Only signals': for n in range(self.num_of_demods): LO_freq = self.getValue('LO freq %d' % (n + 1)) self.setFPGALOfreq(n + 1, LO_freq) self.setFPGATrigger()
def performOpen(self, options={}): """Perform the operation of opening the instrument connection""" # set time step and resolution self.nBit = 16 self.bitRange = float(2**(self.nBit-1)-1) # timeout self.timeout_ms = int(1000 * self.dComCfg['Timeout']) # create AWG instance self.dig = keysightSD1.SD_AIN() AWGPart = self.dig.getProductNameBySlot(1, int(self.comCfg.address)) if not isinstance(AWGPart, str): raise Error('Unit not available') # check that model is supported dOptionCfg = self.dInstrCfg['options'] for validId, validName in zip(dOptionCfg['model_id'], dOptionCfg['model_str']): if AWGPart.find(validId)>=0: # id found, stop searching break else: # loop fell through, raise ID error raise IdError(AWGPart, dOptionCfg['model_id']) # set model self.setModel(validName) # sampling rate and number of channles is set by model if validName in ('M3102', 'M3302'): # 500 MHz models self.dt = 2E-9 self.nCh = 4 else: # assume 100 MHz for all other models self.dt = 10E-9 self.nCh = 4 # create list of sampled data self.lTrace = [np.array([])] * self.nCh self.dig.openWithSlot(AWGPart, 1, int(self.comCfg.address))
def performOpen(self, options={}): """Perform the operation of opening the instrument connection""" # add compatibility with pre-1.5.4 version of Labber if not hasattr(self, 'getTrigChannel'): self.getTrigChannel = self._getTrigChannel # timeout self.timeout_ms = int(1000 * self.dComCfg['Timeout']) # get PXI chassis self.chassis = int(self.dComCfg.get('PXI chassis', 1)) # create AWG instance # get PXI chassis self.chassis = int(self.dComCfg.get('PXI chassis', 1)) self.AWG = keysightSD1.SD_AOU() AWGPart = self.AWG.getProductNameBySlot(self.chassis, int(self.comCfg.address)) if not isinstance(AWGPart, str): raise Error('Unit not available') # check that model is supported dOptionCfg = self.dInstrCfg['options'] for validId, validName in zip(dOptionCfg['model_id'], dOptionCfg['model_str']): if AWGPart.find(validId) >= 0: # id found, stop searching break else: # loop fell through, raise ID error raise IdError(AWGPart, dOptionCfg['model_id']) # set model self.setModel(validName) self.AWG.openWithSlot(AWGPart, self.chassis, int(self.comCfg.address)) # sampling rate and number of channles is set by model if validName in ('M3202', 'H3344'): # 1GS/s models self.dt = 1E-9 self.nCh = 4 elif validName == 'M3302': # two-channel, 500 MS/s model self.dt = 2E-9 self.nCh = 2 else: # assume 500 MS/s for all other models self.dt = 2E-9 self.nCh = 4 # keep track of if waveform was updated self.waveform_updated = [False] * self.nCh self.previous_upload = dict() self.waveform_sizes = dict() # get hardware version - changes numbering of channels hw_version = self.AWG.getHardwareVersion() if hw_version >= 4: # KEYSIGHT - channel numbers start with 1 self.ch_index_zero = 1 else: # SIGNADYNE - channel numbers start with 0 self.ch_index_zero = 0 # clear old waveforms self.clearOldWaveforms()
def scaleWaveformToU16(self, vData, dVpp, ch): """Scales the waveform and returns data in a string of U16""" # make sure waveform data is within the voltage range if np.sum(vData > dVpp / 2) or np.sum(vData < -dVpp / 2): raise Error(('Waveform for channel %d contains values that are ' % ch) + 'outside the channel voltage range.') # clip waveform and store in-place np.clip(vData, -dVpp / 2., dVpp / 2., vData) vU16 = np.array(4094 * (vData + dVpp / 2.) / dVpp, dtype=np.uint16) return vU16
def open_modules(self, slots, type): ''' slots = list of slot numbers of device type = string 'awg' to open awg modules, 'dig' to open digitizer modules open_modules creates and returns a list keysightSD1 module objects ''' log_string = "" log_status = 20 options = "channelNumbering=keysight" model = "" modules = [] if slots: for slot in slots: if type == 'awg': module = keysightSD1.SD_AOU() elif type == 'dig': module = keysightSD1.SD_AIN() else: raise Error('Only AWGs and digitizers are supported') # check that we haven't already assigned module to this slot if self.slot_free[slot - 1] == True: id_num = module.openWithOptions(model, self.chassis, slot, options) if id_num < 0: raise Error( "Error opening module in chassis {}, slot {}, opened with ID: {}" .format(self.chassis, slot, id_num)) if not module.hvi: raise Error( "Module in chassis {} and slot {} does not support HVI2.0... exiting" .format(awgModule.getChassis(), awgModule.getSlot())) modules.append(module) self.slot_free[slot - 1] = False log_string += 'slots status: ' + str(self.slot_free) + '\n' else: log_string += 'slot taken, check behavior' + '\n' log_status = 30 return modules, log_string, log_status
def sendWaveformToAWG(self, channel, vData, vMark1, vMark2): """Send waveform to Tek""" # turn off output and clear old traces self.writeAndLog(':INST %d;:OUTP 0' % channel) self.writeAndLog(':TRAC:DEL:ALL') # if output is disabled, stop here if not self.getValue('Ch%d - Enabled' % channel): return False # channels are named 1-2 n = channel - 1 if len(vData) == 0: if len(vMark1) == 0 and len(vMark2) == 0: # turn off, clear, go to next channel self.lInUse[n] = False return False else: # no data, but markers exist, output zeros for data nMark = max(len(vMark1), len(vMark2)) vData = np.zeros((nMark, ), dtype=float) # make sure length of data is the same if (len(vMark1) > 0 and len(vData) != len(vMark1)) or \ (len(vMark2) > 0 and len(vData) != len(vMark2)): raise Error( 'All channels need to have the same number of elements') self.nPrevData = len(vData) # channel in use, mark self.lInUse[n] = True # get range and scale to U16 Vpp = self.getValue('Ch%d - Range' % channel) vU16 = self.scaleWaveformToU16(vData, Vpp, channel) # check for marker traces for m, marker in enumerate([vMark1, vMark2]): if len(marker) == len(vU16): # get marker trace vMU16 = np.array(marker != 0, dtype=np.uint16) # add marker trace to data trace, with bit shift vU16 += 2**(12 + m) * vMU16 # granularity of the awg is 32 if len(vU16) % 32 > 0: vU16 = np.pad(vU16, (0, 32 - (len(vU16) % 32)), 'constant', constant_values=2047) self.writeAndLog(':TRAC:DEF 1, %d' % len(vU16)) self.writeAndLog(':TRAC:SEL 1') download_binary_data(self.com, 'TRAC:DATA', vU16, len(vU16) * 2, paranoia_level=0) return True
def performOpen(self, options={}): """Perform the operation of opening the instrument connection""" # timeout self.timeout_ms = int(1000 * self.dComCfg['Timeout']) # create AWG instance self.AWG = keysightSD1.SD_AOU() AWGPart = self.AWG.getProductNameBySlot(1, int(self.comCfg.address)) if not isinstance(AWGPart, str): raise Error('Unit not available') # check that model is supported dOptionCfg = self.dInstrCfg['options'] for validId, validName in zip(dOptionCfg['model_id'], dOptionCfg['model_str']): if AWGPart.find(validId)>=0: # id found, stop searching break else: # loop fell through, raise ID error raise IdError(AWGPart, dOptionCfg['model_id']) # set model self.setModel(validName) self.AWG.openWithSlot(AWGPart, 1, int(self.comCfg.address)) # sampling rate and number of channles is set by model if validName in ('M3202', 'H3344'): # 1GS/s models self.dt = 1E-9 self.nCh = 4 elif validName in ('M3302',): # two-channel, 500 MS/s model self.dt = 2E-9 self.nCh = 2 else: # assume 500 MS/s for all other models self.dt = 2E-9 self.nCh = 4 # keep track of if waveform was updated self.lWaveUpdated = [False]*self.nCh # get hardware version - changes numbering of channels hw_version = self.AWG.getHardwareVersion() if hw_version >= 4: # KEYSIGHT - channel numbers start with 1 self.ch_index_zero = 1 else: # SIGNADYNE - channel numbers start with 0 self.ch_index_zero = 0 self.log('HW:', hw_version) # clear old waveforms self.AWG.waveformFlush() for ch in range(self.nCh): self.AWG.AWGflush(self.get_hw_ch(ch))
def _get_node_value(self, quant): """Get instrument value using ZI node hierarchy""" # get node definition node = self._get_node(quant) dtype = self._get_node_datatype(node) # read data from ZI d = self.daq.get(node, True) if len(d) == 0: raise Error('No value defined at node %s.' % node) # extract and return data data = next(iter(d.values())) # if returning dict, strip timing information (API level 6) if isinstance(data, dict) and 'value' in data: data = data['value'] value = dtype(data[0]) # convert to index for combo datatypes if quant.datatype == quant.COMBO: # if no command options are given, use index if len(quant.cmd_def) == 0: cmd_options = list(range(len(quant.combo_defs))) else: # convert option list to correct datatype cmd_options = [dtype(x) for x in quant.cmd_def] # look for correct option try: index = cmd_options.index(value) value = quant.combo_defs[index] except Exception: raise Error( 'Invalid value %s for quantity %s, should be one of %s.' % (str(value), quant.name, str(cmd_options))) self.log('Get value', quant.name, node, data, value) return value
def queueWaveform(self, ch, waveform_id): """Queue waveform to AWG channel""" # get trig parameters trigMode = int(self.getChannelCmd(ch, 'Trig mode')) if self.getChannelValue(ch, 'Trig mode') in ('Software / HVI', 'External'): cycles = int(self.getChannelValue(ch, 'Cycles')) else: cycles = 1 prescaler = 0 delay = int(round(self.getValue('Trig delay') / 10E-9)) # if aligning waveform to end of trig, adjust delay if self.getValue('Waveform alignment') == 'End at trig': delay -= round(self.waveform_sizes[waveform_id] * self.dt / 10E-9) # add extra after waveform ends delay -= int(round(self.getValue('Delay after end') / 10E-9)) # raise error if delay is negative if delay < 0: raise Error('"Trig delay" must be larger than waveform length') if self.getChannelValue( ch, 'Trig mode') in ('Software / HVI', 'External') and self.getChannelValue( ch, 'Immediate'): nbr_awg_channels = len(self.getAWGChannelsInUse()) #if waveform_id > -1: if waveform_id > nbr_awg_channels: trigMode = 0 # if delay is longer than 50 us, fix bug by using empty waveform if delay > 5000: # empty waveform is 10 us long, find number of empty waves needed (n_empty, final_delay) = divmod(delay, 5000) # queue empty waveforms s = self.AWG.AWGqueueWaveform(self.getHwCh(ch), 0, trigMode, 0, n_empty, prescaler) self.check_keysight_error(s) # queue the actual waveform s = self.AWG.AWGqueueWaveform(self.getHwCh(ch), waveform_id, 0, final_delay, cycles, prescaler) self.check_keysight_error(s) else: # queue waveform, inform user if an error happens s = self.AWG.AWGqueueWaveform(self.getHwCh(ch), waveform_id, trigMode, delay, cycles, prescaler) self.check_keysight_error(s)
def __init__(self, chassis): ''' set up chassis the main function connects to hardware, writes a trigger instruction sequence to all hardware, then runs triggering until user interrupt ''' # modules chassis and slot numbers if self.__initialized: return self.__initialized = True self.awg_slots = [] self.dig_slots = [] self.slot_free = [True] * 18 # Ext trigger module (TODO: not sure if these values might ever change) self.chassis = chassis slotNumber = 6 partNumber = "" extTrigModule = keysightSD1.SD_AOU() status = extTrigModule.openWithSlot(partNumber, self.chassis, slotNumber) if (status < 0): raise Error( "Invalid external trigger module. Name, Chassis or Slot numbers might be invalid!" ) # Create HVI instance moduleResourceName = "KtHvi" self.hvi = pyhvi.KtHvi(moduleResourceName) # Add chassis self.hvi.platform.chassis.add_auto_detect() # initialize lists for clarity self.awgs = [] self.digs = [] # ensure that when program quits, the hardware resources will be released atexit.register(self.close)
def configure_hvi(self): """Configure and start/stop HVI depending on UI settings""" # get units units = self.get_pxi_config_from_ui() n_awg = len([x for x in units if x == 1]) n_dig = len([x for x in units if x == 2]) # if no units in use, just stop if (n_awg + n_dig) == 0: self.HVI.stop() return # check if unit configuration changed, if so reload HVI if units != self.units: # stop current HVI, may not even be running self.HVI.stop() self.HVI.close() self.units = units self.log('-----------') self.log('HVI CONFIGURE!') self.log('-----------') # we need at least one AWG if n_awg == 0: raise Error('This driver requires at least one AWG.') # currently only support 2 digitizers if n_dig > 2: raise Error('This driver only supports up to two digitizers.') # get HVI name and open hvi_name = 'InternalTrigger_%d_%d.HVI' % (n_awg, n_dig) dir_path = os.path.dirname(os.path.realpath(__file__)) self.HVI.open(os.path.join(dir_path, 'HVI_Delay', hvi_name)) # assign units, run twice to ignore errors before all units are set for m in range(2): awg_number = 0 dig_number = 0 for n, unit in enumerate(units): # if unit in use, assign to module if unit == 0: continue elif unit == 1: # AWG module_name = 'Module %d' % awg_number awg_number += 1 elif unit == 2: # digitizer module_name = 'DAQ %d' % dig_number dig_number += 1 r = self.HVI.assignHardwareWithUserNameAndSlot( module_name, self.chassis, n + 1) # only check for errors after second run if m > 0: self.check_keysight_error(r) # clear old trig period to force update self.old_trig_period = 0.0 # only update trig period if necessary, takes time to re-compile if (self.getValue('Trig period') != self.old_trig_period or self.getValue('Digitizer delay') != self.old_dig_delay): self.old_trig_period = self.getValue('Trig period') self.old_dig_delay = self.getValue('Digitizer delay') # update trig period, include 460 ns delay in HVI wait = round(self.getValue('Trig period') / 10E-9) - 46 digi_wait = round(self.getValue('Digitizer delay') / 10E-9) # special case if only one module: add 240 ns extra delay if (n_awg + n_dig) == 1: wait += 24 # r = self.HVI.writeIntegerConstantWithIndex(0, 'Wait time', wait) r = self.HVI.writeIntegerConstantWithUserName( 'Module 0', 'Wait time', wait) self.check_keysight_error(r) self.log('Number of modules', self.HVI.getNumberOfModules()) for n in range(n_dig): r = self.HVI.writeIntegerConstantWithUserName( 'DAQ %d' % n, 'Digi wait', digi_wait) self.check_keysight_error(r) # need to recompile after setting wait time, not sure why self.check_keysight_error(self.HVI.compile()) # try to load a few times, sometimes hangs on first try n_try = 5 while True: try: self.check_keysight_error(self.HVI.load()) break except Exception: n_try -= 1 if n_try <= 0: raise # start or stop the HVI, depending on output state if self.getValue('Output'): self.check_keysight_error(self.HVI.start()) else: self.HVI.stop()
def check_keysight_error(self, code): """Check and raise error""" if code >= 0: return # get error message raise Error(keysightSD1.SD_Error.getErrorMessage(code))
def write_instructions(self, wait, dig_wait=0): ''' creates instruction sequences for all devices wait = global AWG wait time dig_wait = (optional) digitizer wait time both need to be multiples of 10 ''' NANOSECONDS_PER_CYCLE = 10 # Check experiment parameters values timeElapsedJumping = 170 if wait % 10 != 0: # Validate that we received values that are multiples of 10 ns. raise Error( 'Invalid wait time. Value must be a multiple of 10 ns.') if wait < timeElapsedJumping: raise Error('Invalid wait time. The delay must be at least ' + str(timeElapsedJumping + 900) + ' ns') if wait < 2000: raise Error( "warning: you might get unexpected behavior with wait times less than 2 us" ) if dig_wait % 10 != 0: raise Error( 'Invalid digitizer wait time. Value must be a multiple of 10 ns' ) wait = wait - timeElapsedJumping - 900 wait = int(wait / NANOSECONDS_PER_CYCLE) dig_wait = int(dig_wait / NANOSECONDS_PER_CYCLE) # Add registers self.awgs[0].add_register("wait", wait) self.awgs[0].add_register("true_reg", 1) self.awgs[0].add_register("loop", 0) if dig_wait > 0: self.digs[0].add_register("dig_wait", wait) self.digs[0].assign_register_value(self.hvi, "write dig wait", "dig_wait", dig_wait) # Write instruction sequences # Assign to register "wait" its initial value self.awgs[0].assign_register_value(self.hvi, "write wait", "wait", wait) # Add global synchronized junction junctionName = "SJunc1" junctionTime_ns = 100 #not sure how long this should be self.hvi.programming.add_junction(junctionName, junctionTime_ns) # Add triggers awg0 = True for awg in self.awgs: awg.trigger_all(self.hvi) if awg0: # adds Wait length contained in Wait register self.awgs[0].add_wait("wait for", "wait") self.awgs[0].increment_register_value(self.hvi, "loop inc", "loop") awg0 = False dig0 = True for dig in self.digs: dig.trigger_all(self.hvi) if dig_wait > 0 and dig0: self.digs[0].add_wait("dig wait for", "dig_wait") dig0 = False # === Conditional jumps ===== master_seq_name = self.awgs[0].return_sequence_name() timeElapsedBeforeJump = 200 #Internal Loop - Conditional Jump jump_destination = "SJunc1" sync_conditional = self.hvi.programming.add_conditional_jump( "SCond1", timeElapsedBeforeJump, timeElapsedJumping, jump_destination, master_seq_name) # Set up the condition: condition = sync_conditional.conditions.add_register_condition("") comparison_operator = pyhvi.ComparisonMode.SMALLER condition.register_evaluations.add( "true", self.awgs[0].return_register("true_reg"), 10, comparison_operator) # === END ===== # Add global synchronized end to close HVI execution (close all sequences - using hvi-programming interface) self.hvi.programming.add_end("EndOfSequence", 100)
def _configure_sequencer(self, group=1, buffer_size=None): """Configure sequencer and upload program for given group""" # currently only supports one AWG group if self.getValue('Channel grouping') != '1 x 8': raise Error('Driver currently only support 1x8 channel group mode') self._map_awg_to_channel() # create waveforms, one per channel awg_program = ''.join( ['wave w%d = zeros(_n_);\n' % (n + 1) for n in range(self.n_ch)]) # create list of link between AWGs and channel outputs x = [] for awg, in_use in enumerate(self.awg_in_use): if in_use: # awg in use, create string for playing wave y = ','.join([('%d' % (ch + 1)) for ch in self.awg_to_ch[awg]]) y += ', w%d' % (awg + 1) x.append(y) else: # not in use, still add to corresponding channel, will be empty x.append('%d, w%d' % (awg + 1, awg + 1)) channels = ', '.join(x) self.log(channels) # check version of API new_style = hasattr(self.daq, 'setVector') # proceed depending on run mode run_mode = self.getValue('Run mode, group %d' % group) # in internal trigger mode, make buffer as long as trig interval if run_mode == 'Internal trigger': trig_period = self.getValue('Trig period, group %d' % group) sampling_rate = 2.4E9 / (2**self.getValueIndex( 'Sampling rate, group %d' % group)) # timing changed for new API version if new_style: # new style - large delays buffer_size = int( round((trig_period - 30E-9 - 3.9E-6) * sampling_rate)) # wait time is in units of 10/3 ns wait_time = int(round(3 * (30E-9 / 10E-9))) # remove a few clock cycles to account for while/wait time wait_time -= 7 awg_program += textwrap.dedent("""\ setUserReg(0, 0); while(true){ while(getUserReg(0) == 0){ playWave(%s); waitWave(); wait(%d); } }""") % (channels, wait_time) else: buffer_size = int(round(trig_period * sampling_rate)) # wait time is in units of 10/3 ns wait_time = int(round(3 * (trig_period / 10E-9))) # remove a few clock cycles to account for while/wait time wait_time -= 7 awg_program += textwrap.dedent("""\ setUserReg(0, 0); while(true){ while(getUserReg(0) == 0){ playWave(%s); wait(%d); } }""") % (channels, wait_time) # limit to max memory of unit buffer_size = min(buffer_size, 64E6) awg_program = awg_program.replace('_n_', ('%d' % buffer_size)) elif run_mode == 'External trigger': buffer_length = self.getValue('Buffer length, group %d' % group) sampling_rate = 2.4E9 / (2**self.getValueIndex( 'Sampling rate, group %d' % group)) buffer_size = int(round(buffer_length * sampling_rate)) # limit to max memory of unit buffer_size = min(buffer_size, 64E6) awg_program += textwrap.dedent("""\ setUserReg(0, 0); while(true){ waitDigTrigger(1); playWave(%s); }""") % (channels) # the code below trigs faster, but only works for <400 ns waveforms # setUserReg(0, 0); # while(true){ # playWaveDigTrigger(1, %s); awg_program = awg_program.replace('_n_', ('%d' % buffer_size)) # keep track of buffer size self.buffer_sizes[group - 1] = buffer_size # stop current AWG base = '/%s/awgs/0/' % self.device self.daq.setInt(base + 'enable', 0) # compile and upload self._upload_awg_program(awg_program) # set to single-shot mode and enable self.daq.setInt(base + 'single', 1) self.daq.setInt(base + 'enable', 1)
def configure_hvi(self): """Configure and start/stop HVI depending on UI settings""" # get units units = self.get_pxi_config_from_ui() n_awg = len([x for x in units if x == 1]) n_dig = len([x for x in units if x == 2]) # if no units in use, just stop if (n_awg + n_dig) == 0: return # check if unit configuration changed, if so reload HVI if units != self.units: # stop current HVI, may not even be running self.units = units # we need at least one AWG if n_awg == 0: raise Error('This driver requires at least one AWG.') # assign units, run twice to ignore errors before all units are set awg_slots = [] dig_slots = [] for m in range(2): for n, unit in enumerate(units): # if unit in use, assign to module if unit == 0: continue elif unit == 1: # AWG awg_slots.append(n + 1) elif unit == 2: # digitizer dig_slots.append(n + 1) # only update trig period or slots if necessary, takes time to re-compile if (awg_slots != self.awg_slots or dig_slots != self.dig_slots): # hardcoding chassis number = 1 because pyhvi won't work # with multiple chassis self.awg_slots = awg_slots self.dig_slots = dig_slots #awgModules = self.open_modules(1, awg_slots, 'awg') #digModules = self.open_modules(1, dig_slots, 'dig') #close_log, awg_info, dig_info = self.trigger_loop.set_slots(self.awg_slots, self.dig_slots, slot_free) close_log = self.trigger_loop.close_modules() self.log('closed: ' + close_log) self.trigger_loop.awg_slots = awg_slots self.trigger_loop.dig_slots = dig_slots awg_info, dig_info = self.trigger_loop.init_hw() self.log('awg: ' + awg_info[0], awg_info[1]) self.log('dig: ' + dig_info[0], dig_info[1]) # always check trig period now because we have to recompile anyway self.old_trig_period = self.getValue('Trig period') self.old_dig_delay = self.getValue('Digitizer delay') wait = round(self.getValue('Trig period') / 10E-9) - 46 digi_wait = round(self.getValue('Digitizer delay') / 10E-9) # special case if only one module: add 240 ns extra delay if (n_awg + n_dig) == 1: wait += 24 self.trigger_loop.write_instructions(wait * 10, digi_wait) self.trigger_loop.prepare_hw() if (self.getValue('Trig period') != self.old_trig_period or self.getValue('Digitizer delay') != self.old_dig_delay): self.old_trig_period = self.getValue('Trig period') self.old_dig_delay = self.getValue('Digitizer delay') # update trig period, include 460 ns delay in HVI wait = round(self.getValue('Trig period') / 10E-9) - 46 digi_wait = round(self.getValue('Digitizer delay') / 10E-9) # special case if only one module: add 240 ns extra delay if (n_awg + n_dig) == 1: wait += 24 self.trigger_loop.write_instructions(wait, digi_wait) self.trigger_loop.prepare_hw() # start or stop the HVI, depending on output state if self.getValue('Output'): self.trigger_loop.run() else: self.trigger_loop.close()
def configure_hvi(self): """Configure and start/stop HVI depending on UI settings""" # get units units = self.get_pxi_config_from_ui() n_awg = len([x for x in units if x == 1]) n_dig = len([x for x in units if x == 2]) # if no units in use, just stop if (n_awg + n_dig) == 0: self.HVI.stop() return # check if unit configuration changed, if so reload HVI if units != self.units: # stop current HVI, may not even be running self.HVI.stop() self.HVI.close() self.units = units # we need at least one AWG if n_awg == 0: raise Error('This driver requires at least one AWG.') # currently only support 1 digitizer if n_dig > 1: raise Error('This driver only supports one digitizer.') # get HVI name and open hvi_name = 'InternalTrigger_%d_%d.HVI' % (n_awg, n_dig) dir_path = os.path.dirname(os.path.realpath(__file__)) self.HVI.open(os.path.join(dir_path, 'HVI', hvi_name)) # assign hardware awg_number = 0 dig_number = 0 for n, unit in enumerate(units): # if unit in use, assign to module if unit == 0: continue elif unit == 1: # AWG module_name = 'Module %d' % awg_number awg_number += 1 elif unit == 2: # digitizer module_name = 'DAQ %d' % dig_number dig_number += 1 r = self.HVI.assignHardwareWithUserNameAndSlot( module_name, self.chassis, n + 1) self.check_keysight_error(r) # clear old trig period to force update self.old_trig_period = 0.0 # only update trig period if necessary, takes time to re-compile if self.getValue('Trig period') != self.old_trig_period: self.old_trig_period = self.getValue('Trig period') # update trig period, include 460 ns delay in HVI wait = round(self.getValue('Trig period') / 10E-9) - 46 # special case if only one module: add 240 ns extra delay if (n_awg + n_dig) == 1: wait += 24 r = self.HVI.writeIntegerConstantWithIndex(0, 'Wait time', wait) self.check_keysight_error(r) # need to recompile after setting wait time, not sure why self.check_keysight_error(self.HVI.compile()) self.check_keysight_error(self.HVI.load()) # start or stop the HVI, depending on output state if self.getValue('Output'): self.check_keysight_error(self.HVI.start()) else: self.HVI.stop()