class Driver: """Driver class for NI PXI 654x HSDIO card """ def __init__(self, dev_name_str, logger=None): # # Define internal variables # self.log = LogHandler(logger=logger) self.map_dict = dict() self.writn_wfm_set = set() # # "Load" niHSDIO DLL # try: self.dll = ctypes.WinDLL(NI_HSDIO_DLL_PATH) except OSError: msg_str = 'DLL loading failed. \n' \ 'Ensure that niHSDIO DLL path is correct: \n' \ 'it should be specified in pylabnet.hardware.p_gen.ni_hsdio.__init__.py \n' \ 'Current value is: \n' \ '"{}"'.format(NI_HSDIO_DLL_PATH) self.log.error(msg_str=msg_str) raise PGenError(msg_str) # Build C-prototypes (in particular, specify the return # types such that Python reads results correctly) build_c_func_prototypes(self.dll) # # Connect to device # # Create blank session handle self._handle = NITypes.ViSession() # Convert dev_name to the DLL-readable format self._dev_name = NITypes.ViRsrc(dev_name_str.encode('ascii')) self._er_chk( self.dll.niHSDIO_InitGenerationSession( self._dev_name, # ViRsrc resourceName NIConst.VI_TRUE, # ViBoolean IDQuery NIConst.VI_TRUE, # ViBoolean resetDevice NIConst. VI_NULL, # ViConstString optionString - not used, set to VI_NULL ctypes.byref(self._handle) # ViSession * session_handle )) # Log info message serial_str = self._get_attr_str(NIConst.NIHSDIO_ATTR_SERIAL_NUMBER) self.log.info( 'Connected to NI HSDIO card {0}. Serial number: {1}. Session handle: {2}' ''.format(dev_name_str, serial_str, self._handle)) def reset(self): self.writn_wfm_set = set() self.map_dict = dict() return self._er_chk(self.dll.niHSDIO_reset(self._handle)) def start(self): return self._er_chk(self.dll.niHSDIO_Initiate(self._handle)) def stop(self): return self._er_chk(self.dll.niHSDIO_Abort(self._handle)) def disconnect(self): self.reset() op_status = self._er_chk(self.dll.niHSDIO_close(self._handle)) return op_status # ================================================================ # Hardware settings # ================================================================ def get_samp_rate(self): return self._get_attr_real64(NIConst.NIHSDIO_ATTR_SAMPLE_CLOCK_RATE) def set_samp_rate(self, samp_rate): # Sanity check if not samp_rate <= self.constraints['samp_rate']['max']: self.log.warn( 'set_samp_rate({0} MHz): the requested value exceeds hardware constraint max={1} MHz.\n' 'The max possible value will be set instead.' ''.format(samp_rate / 1e6, self.constraints['samp_rate']['max'] / 1e6)) samp_rate = self.constraints['samp_rate']['max'] elif not self.constraints['samp_rate']['min'] <= samp_rate: self.log.warn( 'set_samp_rate({0} Hz): the requested value is below the hardware constraint min={1} Hz.\n' 'The min possible value will be set instead.' ''.format(samp_rate, self.constraints['samp_rate']['min'])) samp_rate = self.constraints['samp_rate']['min'] # Call DLL function # Currently, the onboard clock is used as the sample clock source self._er_chk( self.dll.niHSDIO_ConfigureSampleClock( self._handle, # ViSession vi NIConst. NIHSDIO_VAL_ON_BOARD_CLOCK_STR, # ViConstString clockSource NITypes.ViReal64(samp_rate) # ViReal64 clockRate )) # Return the actual final sample rate return self.get_samp_rate() def get_active_chs(self): return self._get_attr_str(NIConst.NIHSDIO_ATTR_DYNAMIC_CHANNELS) def set_active_chs(self, chs_str=None): if chs_str is None: # un-assign all channels chs_str = 'none' chs_str = NITypes.ViString(chs_str.encode('ascii')) self._er_chk( self.dll.niHSDIO_AssignDynamicChannels( self._handle, # ViSession vi, chs_str # ViConstString channelList )) return self.get_active_chs() def get_mode(self): """ :return: (str) "W" - Waveform, "S" - Scripted """ mode_id = self._get_attr_int32( attr_id=NIConst.NIHSDIO_ATTR_GENERATION_MODE) if mode_id == NIConst.NIHSDIO_VAL_WAVEFORM.value: return 'W' elif mode_id == NIConst.NIHSDIO_VAL_SCRIPTED.value: return 'S' else: msg_str = 'get_mode(): self._get_attr_int32(NIHSDIO_ATTR_GENERATION_MODE) ' \ 'returned unknown mode_id = {0}'.format(mode_id) self.log.error(msg_str) raise PGenError(msg_str) def set_mode(self, mode_string): """ :param mode_string: (str) "W" - Waveform, "S" - Scripted :return: actual run mode string ("W"/"S") """ if mode_string == 'W': run_mode = NIConst.NIHSDIO_VAL_WAVEFORM elif mode_string == 'S': run_mode = NIConst.NIHSDIO_VAL_SCRIPTED else: msg_str = 'set_mode({0}): invalid argument. Valid values: "W" - Waveform, "S" - scripted. \n' \ 'Run mode was not changed. Actual run mode string was returned.'.format(mode_string) self.log.error(msg_str=msg_str) raise PGenError(msg_str) # Call DLL function self._er_chk( self.dll.niHSDIO_ConfigureGenerationMode( self._handle, # ViSession vi run_mode # ViInt32 generationMode )) # Return actual run mode return self.get_mode() @property def constraints(self): # Total memory size # [in samples; one sample contains 32 bits and covers all channels] max_wfm_len = self._get_attr_int32( attr_id=NIConst.NIHSDIO_ATTR_TOTAL_GENERATION_MEMORY_SIZE) constr_dict = dict(samp_rate=dict(min=48, max=100e6), wfm_len=dict(min=2, step=2, max=max_wfm_len)) return constr_dict def get_status(self): try: # Record current samp_rate to restore it later current_rate = self.get_samp_rate() rate_lims = self.constraints['samp_rate'] test_rate = (rate_lims['min'] + rate_lims['max']) / 2 # Try changing samp_rate op_status = self.dll.niHSDIO_ConfigureSampleClock( self._handle, # ViSession vi NIConst. NIHSDIO_VAL_ON_BOARD_CLOCK_STR, # ViConstString clockSource NITypes.ViReal64(test_rate) # ViReal64 clockRate ) # If device is idle, operation should be successful: # op_status = 0. # Restore original samp rate and return 0 - "idle" if op_status == 0: self.set_samp_rate(samp_rate=current_rate) return 0 # If device is running, attempt to change samp_rate should return # the following error code: # -1074118617 # "Specified property cannot be set while the session is running. # Set the property prior to initiating the session, # or abort the session prior to setting the property." elif op_status == -1074118617: # Device is running return 1 # This method cannot interpret any other error/warning code and has # to raise an exception else: raise PGenError( 'get_status(): the attempt to test-change samp_rate returned unknown error code {}' ''.format(op_status)) # If connection to the device is lost # or any operation fails, raise an exception. except Exception as exc_obj: self.log.exception( 'get_status(): an exception was produced. \n' 'This might mean that connection to the device is lost ' 'or there is some bug in the get_status() method. \n') raise exc_obj # ================================================================ # Waveform Generation # ================================================================ def write_wfm(self, pb_obj, len_adj=True): # # Sanity checks # # Only data_width=32 write is currently implemented # (DLL function niHSDIO_WriteNamedWaveformU32) hrdw_data_width = 8 * self._get_attr_int32( NIConst.NIHSDIO_ATTR_DATA_WIDTH) if hrdw_data_width != 32: msg_txt = 'write_wfm(): the card you use has data_width = {0} bits. \n' \ 'The method was written assuming 32-bit width and have to be modified for your card. \n' \ 'Rewrite bit_ar construction part and use niHSDIO_WriteNamedWaveformU{1}() DLL function' \ ''.format(hrdw_data_width, hrdw_data_width) self.log.error(msg_txt) raise PGenError(msg_txt) # # Sample PulseBlock # # Map user-friendly names onto physical channel numbers pb_obj = copy.deepcopy(pb_obj) pb_obj.ch_map(map_dict=self.map_dict) # Sample pulse block samp_rate = self.get_samp_rate() samp_dict, n_pts, add_pts = pb_sample( pb_obj=pb_obj, samp_rate=samp_rate, len_min=self.constraints['wfm_len']['min'], len_max=self.constraints['wfm_len']['max'], len_step=self.constraints['wfm_len']['step'], len_adj=len_adj) wfm_name = pb_obj.name del pb_obj self.log.info( 'write_wfm(): sampled PulseBlock "{}". \n' 'Sample array has {} points. {} samples were added to match hardware wfm len step' ''.format(wfm_name, n_pts, add_pts)) # # Pack samp_dict into bit_ar # # Create a blank bit_ar - all elements zero (all channels off) bit_ar = np.zeros(shape=n_pts, dtype=np.uint32) # Iterate through each channel and set corresponding bit to '1' # if value is True for ch_num in samp_dict.keys(): # This number in uint32 representation has all zeros and # exactly one '1' at the ch_num-th bit from the LSB ch_bit_one = 2**ch_num for idx, val in enumerate(samp_dict[ch_num]): if val: bit_ar[idx] += ch_bit_one # TODO: consider making very fast with numpy: # ch_bit_ar = samp_dict[ch_num].astype(int) * 2**ch_num # bit_ar = np.add(bit_ar, ch_bit_ar) # # Load bit_ar to memory # # Delete waveform with the same name, # if it is already present in the memory if wfm_name in self.writn_wfm_set: self.del_wfm(wfm_name=wfm_name) # Create C-pointer to bit_ar using numpy.ndarray.ctypes attribute bit_ar_ptr = bit_ar.ctypes.data_as(ctypes.POINTER(NITypes.ViUInt32)) # Call DLL function self._er_chk( self.dll.niHSDIO_WriteNamedWaveformU32( self._handle, # ViSession vi NITypes.ViConstString( wfm_name.encode('ascii')), # ViConstString waveformName NITypes.ViInt32(n_pts), # ViInt32 samplesToWrite bit_ar_ptr # ViUInt32 data[] )) self.writn_wfm_set.add(wfm_name) return 0 def del_wfm(self, wfm_name): self._er_chk( self.dll.niHSDIO_DeleteNamedWaveform( self._handle, # ViSession vi NITypes.ViConstString( wfm_name.encode('ascii')) # ViConstString waveformName )) self.writn_wfm_set.remove(wfm_name) return 0 def clr_mem(self): wfm_set = copy.deepcopy(self.writn_wfm_set) for wfm_name in wfm_set: self.del_wfm(wfm_name=wfm_name) return 0 def get_rep(self): """Returns number of repetitions in Waveform generation mode. On the hardware level, it is just a pair of attributes NIHSDIO_ATTR_REPEAT_MODE and NIHSDIO_ATTR_REPEAT_COUNT which are not bound to any specific waveform. :return: (int) repeat mode + number of repetitions: 0 - repeat infinitely positive integer - finite, number of repetitions PGenError exception - error """ rep_mode = self._get_attr_int32(NIConst.NIHSDIO_ATTR_REPEAT_MODE) if rep_mode == NIConst.NIHSDIO_VAL_CONTINUOUS.value: rep_num = 0 elif rep_mode == NIConst.NIHSDIO_VAL_FINITE.value: rep_num = self._get_attr_int32(NIConst.NIHSDIO_ATTR_REPEAT_COUNT) else: msg_str = 'get_rep(): DLL call returned unknown repetition mode code {}'.format( rep_mode) self.log.error(msg_str=msg_str) raise PGenError(msg_str) return rep_num def set_rep(self, rep_num): """Set repeat mode and number of repetitions :param rep_num: (int) repeat mode + number of repetitions: 0 - repeat infinitely positive integer - finite, number of repetitions :return: (int) actual repeat mode + number of repetitions: 0 - repeat infinitely positive integer - finite, number of repetitions PGenError exception - error """ if rep_num == 0: rep_mode = NIConst.NIHSDIO_VAL_CONTINUOUS rep_num = NIConst.VI_NULL elif rep_num > 0: rep_mode = NIConst.NIHSDIO_VAL_FINITE rep_num = NITypes.ViInt32(rep_num) else: msg_str = 'set_rep() invalid argument {} \n' \ 'Valid values: 0 - infinite, positive integer - finite' \ ''.format(rep_num) self.log.error(msg_str=msg_str) raise PGenError(msg_str) self._er_chk( self.dll.niHSDIO_ConfigureGenerationRepeat( self._handle, # ViSession vi rep_mode, # ViInt32 repeatMode rep_num # ViInt32 repeatCount )) return self.get_rep() def get_wfm_to_gen(self): return self._get_attr_str(NIConst.NIHSDIO_ATTR_WAVEFORM_TO_GENERATE) def set_wfm_to_gen(self, wfm_name): self._er_chk( self.dll.niHSDIO_ConfigureWaveformToGenerate( self._handle, # ViSession vi NITypes.ViConstString( wfm_name.encode('ascii')) # ViConstString waveformName )) return self.get_wfm_to_gen() # ================================================================ # Script Generation # ================================================================ def write_script(self, script_str): # Sanity check: script_str is a string if not isinstance(script_str, str): msg_str = 'write_script(): passed argument is not a string' self.log.error(msg_str=msg_str) raise PGenError(msg_str) plain_script_str = script_str.replace('\n', ' ') # Convert into C-string c_script_str = NITypes.ViConstString(plain_script_str.encode('ascii')) op_status = self._er_chk( self.dll.niHSDIO_WriteScript( self._handle, # ViSession vi c_script_str # ViConstString script )) return op_status def get_scr_to_gen(self): return self._get_attr_str(NIConst.NIHSDIO_ATTR_SCRIPT_TO_GENERATE) def set_scr_to_gen(self, script_name): # Convert script_name into C-string script_name = NITypes.ViConstString(script_name.encode('ascii')) self._er_chk( self.dll.niHSDIO_ConfigureScriptToGenerate( self._handle, # ViSession vi script_name # ViConstString scriptName )) return self.get_scr_to_gen() # ================================================================ # Wrappers for C DLL helper functions # ================================================================ def _er_chk(self, error_code): # C:\Program Files\National Instruments\Shared\Errors\English\IVI-errors.txt if error_code == 0: # Success return 0 else: # Warning or Error # Create buffer for DLL function to output the error message string msg_buf = ctypes.create_string_buffer(256) # Call DLL function to generate readable error message self.dll.niHSDIO_error_message( self._handle, # ViSession vi NITypes.ViStatus(error_code), # ViStatus errorCode msg_buf # ViChar errorMessage[256] ) msg_str = msg_buf.value.decode('ascii') if error_code > 0: # Warning self.log.warn(msg_str=msg_str) return error_code else: # Error self.log.error(msg_str=msg_str) raise PGenError(msg_str) def _get_attr_int32(self, attr_id, ch=None): """ :param attr_id: :param ch: (int) :return: (int) obtained value in the case of success PGenError exception is produced in the case of error """ # Create buffer where niHSDIO_GetAttribute will store the result buf = NITypes.ViInt32() # Convert channel number into C-string # (used to request channel-agnostic/-specific attributes) if ch is None: ch_str = NIConst.VI_NULL else: ch_str = str(ch) # Convert into C-string ch_str = ctypes.c_char_p(ch_str.encode('ascii')) # Call DLL function try: self._er_chk( self.dll.niHSDIO_GetAttributeViInt32( self._handle, # ViSession vi ch_str, # ViConstString channelName attr_id, # ViAttr attribute ctypes.byref(buf) # ViInt32 *value )) return buf.value except OSError: # DLL normally handles all "moderate" errors and returns error code, # which is being analyzed by self._er_chk. # "try" handles OSError when the DLL function fails completely # and isn't able to handle the error by itself msg_str = '_get_attr_int32(): OSError, DLL function call failed' self.log.error(msg_str=msg_str) raise PGenError(msg_str) def _get_attr_str(self, attr_id, ch=None): """ :param attr_id: :param ch: (int) :return: (str) obtained value in the case of success Exception is produced in the case of error """ # Create buffer where niHSDIO_GetAttribute will store the result buf_size = 1024 # Buffer size of 1024 was chosen for no specific reason. Increase if needed. buf = ctypes.create_string_buffer(buf_size) # Convert channel number into C-string # (used to request channel-agnostic/-specific attributes) if ch is None: ch_str = NIConst.VI_NULL else: ch_str = str(ch) # Convert into C-string ch_str = ctypes.c_char_p(ch_str.encode('ascii')) # Call DLL function try: self._er_chk( self.dll.niHSDIO_GetAttributeViString( self._handle, # ViSession vi ch_str, # ViConstString channelName attr_id, # ViAttr attribute NITypes.ViInt32(buf_size), # ViInt32 bufSize buf # ViChar value[] )) return buf.value.decode('ascii') except OSError: # DLL normally handles all "moderate" errors and returns error code, # which is being analyzed by self._er_chk. # "try" handles OSError when the DLL function fails completely # and isn't able to handle the error by itself msg_str = '_get_attr_str(): OSError, DLL function call failed' self.log.error(msg_str=msg_str) raise PGenError(msg_str) def _get_attr_real64(self, attr_id, ch=None): """ :param attr_id: :param ch: (int) :return: (float) obtained value in the case of success Exception is produced in the case of error """ # Create buffer where niHSDIO_GetAttribute will store the result buf = NITypes.ViReal64() # Convert channel number into C-string # (used to request channel-agnostic/-specific attributes) if ch is None: ch_str = NIConst.VI_NULL else: ch_str = str(ch) # Convert into C-string ch_str = ctypes.c_char_p(ch_str.encode('ascii')) # Call DLL function try: self._er_chk( self.dll.niHSDIO_GetAttributeViReal64( self._handle, # ViSession vi ch_str, # ViConstString channelName attr_id, # ViAttr attribute ctypes.byref(buf) # ViReal64 *value )) return buf.value except OSError: # DLL normally handles all "moderate" errors and returns error code, # which is being analyzed by self._er_chk. # "try" handles OSError when the DLL function fails completely # and isn't able to handle the error by itself msg_str = '_get_attr_real64(): OSError, DLL function call failed' self.log.error(msg_str=msg_str) raise PGenError(msg_str)
class Driver(MWSrcInterface): """Adapted from Qudi <https://github.com/Ulm-IQO/qudi/> """ def __init__(self, addr_str, logger=None): self.log = LogHandler(logger=logger) # Connect to the device self.rm = visa.ResourceManager() try: self._dev = self.rm.open_resource(addr_str) except Exception as exc_obj: self.log.exception( msg_str='Could not connect to the address >>{}<<. \n'.format( addr_str)) raise exc_obj # Log confirmation info message id_str = self._dev.query('*IDN?').replace(',', ' ') id_str = id_str.strip('\n') self.log.info(msg_str='{} initialised and connected.'.format(id_str)) # Reset device self.reset() # Error check self._er_chk() def reset(self): # Reset self._cmd_wait('*RST') # Clear status register self._cmd_wait('*CLS') return 0 def activate_interface(self): # Store hardware settings which are not controlled by logic, # to restore them after reset() # [logic does not know anything about this params, so it should not # introduce any changes to them by calling activate_interface()]. tmp_trig_dict = self.get_trig() # Reset device self.reset() # Restore hardware settings which are not controlled by logic # but were changed by self._dev.reset() self.set_trig(src_str=tmp_trig_dict['src_str'], slope_str=tmp_trig_dict['slope_str']) return 0 # Output control def on(self): if self.get_mode() == 'sweep': self.reset_swp_pos() return self._cmd_wait(cmd_str=':OUTP:STAT ON') def off(self): return self._cmd_wait(cmd_str=':OUTP:STAT OFF') def get_status(self): status = int(self._dev.query('OUTP:STAT?')) return status # Power def get_pwr(self): return float(self._dev.query(':POW?')) def set_pwr(self, pwr): self._cmd_wait(':POW {0:f}'.format(pwr)) return self.get_pwr() # Frequency def get_freq(self): mode = self.get_mode() if mode == 'cw': ret_val = float(self._dev.query(':FREQ?')) elif mode == 'sweep': start = float(self._dev.query(':FREQ:STAR?')) stop = float(self._dev.query(':FREQ:STOP?')) step = float(self._dev.query(':SWE:STEP?')) n_pts = int((stop - start) / step) + 2 ret_val = dict(start=start, stop=stop, n_pts=n_pts) else: raise MWSrcError('get_freq(): got unknown mode {}'.format(mode)) return ret_val def set_freq(self, freq): if self.get_status() == 1: self.off() # Activate CW mode self._cmd_wait(':FREQ:MODE CW') # Set CW frequency self._cmd_wait(':FREQ {0:f}'.format(freq)) return self.get_freq() def set_freq_swp(self, start, stop, n_pts): if self.get_status() == 1: self.off() # Set mode to Sweep self._cmd_wait(':FREQ:MODE SWEEP') # Set frequency sweep step = (stop - start) / (n_pts - 1) self._cmd_wait(':SWE:MODE STEP') self._cmd_wait(':SWE:SPAC LIN') self._cmd_wait(':FREQ:START {0:f}'.format(start)) self._cmd_wait(':FREQ:STOP {0:f}'.format(stop)) self._cmd_wait(':SWE:STEP:LIN {0:f}'.format(step)) return self.get_freq() def reset_swp_pos(self): """Reset of MW sweep mode position to start (start frequency) @return int: error code (0:OK, -1:error) """ self._cmd_wait(':ABOR:SWE') return 0 def get_mode(self): mode_str = self._dev.query(':FREQ:MODE?').strip('\n').lower() if 'cw' in mode_str: return 'cw' elif 'swe' in mode_str: return 'sweep' else: msg_str = 'get_mode(): unknown mode string {} was returned' self.log.error(msg_str=msg_str) raise MWSrcError(msg_str) # Technical methods def _cmd_wait(self, cmd_str): """Writes the command in command_str via resource manager and waits until the device has finished processing it. @param cmd_str: The command to be written """ self._dev.write(cmd_str) # Block command queue until cmd_str execution is complete self._dev.write('*WAI') # Block Python process until cmd_str execution is complete self._dev.query('*OPC?') # Error check self._er_chk() return 0 def _er_chk(self): # Block command queue until all previous commands are complete self._dev.write('*WAI') # Block Python process until all previous commands are complete self._dev.query('*OPC?') # Read all messages out_str = self._dev.query(':SYSTem:ERRor:ALL?') out_str += ',' out_str += self._dev.query('SYST:SERR?') out_str = out_str.replace('\n', '') out_str = out_str.replace('\r', '') out_str = out_str.replace('"', '') out_list = out_str.split(',') # Collect all warns and errors er_list = [] warn_list = [] msg_n = int(len(out_list) / 2) for idx in range(msg_n): msg_code = int(out_list[2 * idx]) if msg_code == 0: # No error continue elif msg_code > 0: # Warning warn_list.append(out_list[2 * idx + 1]) else: # Error er_list.append(out_list[2 * idx + 1]) # Construct Warn message string if len(warn_list) > 0: warn_str = '' for warn in warn_list: warn_str += (warn + ' \n') warn_str = warn_str.rstrip('\n') self.log.warn(msg_str=warn_str) # Construct Error message string if len(er_list) > 0: er_str = '' for er in er_list: er_str += (er + ' \n') er_str = er_str.rstrip('\n') self.log.error(msg_str=er_str) raise MWSrcError(er_str) return 0 def get_trig(self): # # Get trigger source # src_str = self._dev.query('TRIG:FSW:SOUR?') if 'EXT' in src_str: src_str = 'ext' elif 'AUTO' in src_str: src_str = 'int' else: msg_str = 'get_trig(): unknown trigger source was returned "{}" \n' \ ''.format(src_str) self.log.error(msg_str=msg_str) raise MWSrcError(msg_str) # # Get edge slope # slope_str = self._dev.query(':TRIG1:SLOP?') if 'POS' in slope_str: slope_str = 'r' elif 'NEG' in slope_str: slope_str = 'f' else: msg_str = 'get_trig(): unknown slope was returned "{}" \n' \ ''.format(slope_str) self.log.error(msg_str=msg_str) raise MWSrcError(msg_str) return dict(src_str=src_str, slope_str=slope_str) def set_trig(self, src_str='ext', slope_str='r'): if self.get_status() == 1: self.off() # # Set trigger source # if src_str == 'ext': src_str = 'EXT' elif src_str == 'int': src_str = 'AUTO' else: msg_str = 'set_trig(): unknown trigger source "{}" \n' \ 'Valid values are "ext" - external, "int" - internal' \ ''.format(src_str) self.log.error(msg_str=msg_str) raise MWSrcError(msg_str) self._cmd_wait('TRIG:FSW:SOUR {}'.format(src_str)) # # Set trigger edge # if slope_str == 'r': edge = 'POS' elif slope_str == 'f': edge = 'NEG' else: msg_str = 'set_trig(): invalid argument slope_str={} \n' \ 'Valid values are: "r" - raising, "f" - falling' \ ''.format(slope_str) self.log.error(msg_str=msg_str) raise ValueError(msg_str) self._cmd_wait(':TRIG1:SLOP {0}'.format(edge)) return self.get_trig() def force_trig(self): """ Trigger the next element in the list or sweep mode programmatically. @return int: error code (0:OK, -1:error) Ensure that the Frequency was set AFTER the function returns, or give the function at least a save waiting time. """ self._cmd_wait('*TRG') return 0