def converge_scalar_tolerance(dmm, funcgen, set_point, tolerance, limit_amplitude=None, settle_number=1, attempts=5): p = LinearScalarControl(funcgen.amplitude_ch1) p.set_point(set_point) settle_cnt = 0 for _ in range(attempts): test_val = dmm.measurement() if test_val > 1e10: raise InstrumentError( "Measurement outside the specified dmm range") # Test in range if set_point * (1 - tolerance) <= test_val <= set_point * (1 + tolerance): settle_cnt += 1 else: settle_cnt = 0 if settle_cnt >= settle_number: return # Get the control value from controller contr_val = p.update(test_val) # Put the control value into the kwargs for the control function if contr_val >= limit_amplitude: raise InstrumentError( "Converge function value {}Vpp exceeded the limit set {}Vpp". format(contr_val, limit_amplitude)) funcgen.amplitude_ch1 = contr_val raise InstrumentError("Could not converge in {} attempts".format(attempts))
def get_cbus_pins(self): try: self.cmd_status.value = ftdI2xx.FT_SetBitMode( self.handle, UCHAR(0), BIT_MODE.FT_BITMODE_CBUS_BITBANG) if self.cmd_status.value != FT_STATUS.FT_OK.value: raise InstrumentError("FT_SetBitMode failed") data_bus = UCHAR() self.cmd_status.value = ftdI2xx.FT_GetBitMode( self.handle, ctypes.byref(data_bus)) if self.cmd_status.value != FT_STATUS.FT_OK.value: raise InstrumentError("FT_GetBitMode failed") finally: self.cmd_status.value = ftdI2xx.FT_SetBitMode( self.handle, UCHAR(self.pin_value_mask), self.bit_mode) return data_bus.value
def measurements(self): if not self.mode: raise InstrumentError( "Please set DMM mode before taking a measurement!") with self.lock: return self._read_measurements()
def write(self, data): """ The task should be in stopped state when calling write, it automatically starts the task through the DAQmxWriteDigitalLines call. When write is finished it is back in a stopped state :param data: :return: """ self.init() try: if len(data) % self.io_length: raise ValueError("data must be a length divisible by {}".format(self.io_length)) except TypeError as e: raise ValueError("data must be in an list divisible by {}".format(self.io_length)) from e if len(data) == self.io_length: # Sample clock only works for more than one sample so duplicate the sample data = list(data) data.extend(data) DAQmxCfgSampClkTiming(self.task, None, float64(self.frequency), DAQmx_Val_Rising, DAQmx_Val_FiniteSamps, uInt64(int(len(data) // self.io_length))) try: data_arr = numpy.zeros((len(data)), uInt8) data_arr[:] = data written = int32() DAQmxWriteDigitalLines(self.task, int(len(data) // self.io_length), 1, -1, DAQmx_Val_GroupByScanNumber, data_arr, written, None) self.task_state = "running" DAQmxWaitUntilTaskDone(self.task, -1) if written.value != len(data) // self.io_length: raise InstrumentError("Values not written correctly") finally: self.stop()
def _is_error(self, silent=False): """ Creates list of errors raise: InstrumentError if silent false return: list of errors """ errors = [] while True: code, msg = self._check_errors() if code != 0: errors.append((code, msg)) else: break if errors: if silent: return errors else: raise InstrumentError( "Error(s) Returned from DMM\n" + "\n".join( [ "Code: {}\nMessage:{}".format(code, msg) for code, msg in errors ] ) )
def trigger(self): if self._trigger_thread: self.clear() self._trigger_thread.join(10) if self._trigger_thread.is_alive(): raise InstrumentError("Existing Trigger Event in Progress") self._trigger_thread = ExcThread(target=self._read) self._trigger_thread.start()
def _is_error(self, silent=False): code, msg = self._check_errors() if code != 0: if silent: return [(code, msg)] else: raise InstrumentError("Error Returned from PPS\n" + "Code: {}\nMessage:{}".format(code, msg))
def _read(self): try: return self.instrument.read() except VisaIOError as e: # TODO Write to log if e.error_code == VI_ERROR_TMO: raise InstrumentTimeOut("Instrument Timed out on read operation") from e raise InstrumentError("Unknown IO error from read operation") from e
def retrieve_waveform_data(self): self.instrument.write(":WAV:DATA?") time.sleep(0.2) data = self.read_raw()[:-1] # Strip \n if data[0:1] != '#'.encode(): raise InstrumentError("Pound Character missing in waveform data response") valid_bytes = data[int(data[1:2]) + 2:] # data[1] denotes length value digits values = struct.unpack("%dB" % len(valid_bytes), valid_bytes) return values
def self_test(self): timeout = self.instrument.timeout try: self.instrument.timeout = 20000 resp = self.instrument.query("*TST?") if "0" not in resp: raise InstrumentError("Failed Self Test") finally: self.instrument.timeout = timeout
def baud_rate(self, rate): try: self.cmd_status.value = ftdI2xx.FT_SetBaudRate( self.handle, DWORD(rate)) if self.cmd_status.value != FT_STATUS.FT_OK.value: raise InstrumentError("FT_SetBaudRate failed") self._baud_rate = rate except: self._baud_rate = None raise
def _raise_if_error(self): errors = [] while True: code, msg = self._check_errors() if code != 0: errors.append((code, msg)) else: break if errors: raise InstrumentError("Error(s) Returned from DSO\n" + "\n".join(["Code: {}\nMessage:{}".format(code, msg) for code, msg in errors]))
def write_bit_mode(self, mask, validate=False): """ handle; gained from device info mask; value to write for the mask for BIT_MODE.FT_BITMODE_CBUS_BITBANG upper nibble is input (0) output (1) lower nibble is pin value low (0) high (1) bit_mode; Type BIT_MODE """ self.cmd_status.value = ftdI2xx.FT_SetBitMode(self.handle, UCHAR(mask), self.bit_mode) if self.cmd_status.value != FT_STATUS.FT_OK.value: raise InstrumentError("FT_SetBitMode failed") data_bus = UCHAR() if validate: self.cmd_status.value = ftdI2xx.FT_GetBitMode( self.handle, ctypes.byref(data_bus)) if self.cmd_status.value != FT_STATUS.FT_OK.value: raise InstrumentError("FT_GetBitMode failed") return data_bus.value & self.pin_value_mask == mask & self.pin_value_mask
def _read(self): if not self._data_characteristics_set: self._set_data_characteristics() amount_in_rx_queue = DWORD() amount_in_tx_queue = DWORD() status = DWORD() self.cmd_status.value = ftdI2xx.FT_GetStatus( self.handle, ctypes.byref(amount_in_rx_queue), ctypes.byref(amount_in_tx_queue), ctypes.byref(status)) if self.cmd_status.value != FT_STATUS.FT_OK.value: raise InstrumentError("FT_GetStatus failed") buffer = ctypes.create_string_buffer(amount_in_rx_queue.value) bytes_read = DWORD() self.cmd_status.value = ftdI2xx.FT_Read(self.handle, ctypes.byref(buffer), amount_in_rx_queue, ctypes.byref(bytes_read)) if self.cmd_status.value != FT_STATUS.FT_OK.value: raise InstrumentError("FT_Read failed") return buffer
def read(self): self._trigger_thread.join(self._thread_timeout) if self._trigger_thread.is_alive(): raise InstrumentError("Trigger thread failed to terminate") try: err = self._error_queue.get_nowait() except Empty: # no error in queue pass else: raise err return self._data
def init(self): if self.task_state == "": self.task = TaskHandle() DAQmxCreateTask(b"", byref(self.task)) DAQmxCreateCITwoEdgeSepChan(self.task, "{}/{}".format(self.device_name, self.counter_chan).encode(), b"", float64(self.min_val), float64(self.max_val), DAQmx_Val_Seconds, self.first_edge_type, self.second_edge_type, b"") if self.source_terminal: tmp_data = c_char_p(self.source_terminal.encode()) DAQmxSetCITwoEdgeSepFirstTerm(self.task, "{}/{}".format(self.device_name, self.counter_chan).encode(), tmp_data) if self.validate_terminals: tmp_data = c_char_p("".encode()) DAQmxGetCITwoEdgeSepFirstTerm(self.task, "{}/{}".format(self.device_name, self.counter_chan).encode(), tmp_data, uInt32(16)) if self.destination_terminal not in tmp_data.value.decode('utf-8'): raise InstrumentError( "Destination terminal is set to {}, should be /{}/{}".format(tmp_data.value.decode('utf-8'), self.device_name, self.destination_terminal)) if self.destination_terminal: tmp_data = c_char_p(self.destination_terminal.encode()) DAQmxSetCITwoEdgeSepSecondTerm(self.task, "{}/{}".format(self.device_name, self.counter_chan).encode(), tmp_data) if self.validate_terminals: tmp_data = c_char_p("".encode()) DAQmxGetCITwoEdgeSepSecondTerm(self.task, "{}/{}".format(self.device_name, self.counter_chan).encode(), tmp_data, uInt32(16)) if self.destination_terminal not in tmp_data.value.decode('utf-8'): raise InstrumentError( "Destination terminal is set to {}, should be /{}/{}".format(tmp_data.value.decode('utf-8'), self.device_name, self.destination_terminal)) self.task_state = "init"
def _set_data_characteristics(self): if not [ x for x in [self.word_length, self.stop_bits, self.parity] if x is None ]: self.cmd_status.value = ftdI2xx.FT_SetDataCharacteristics( self.handle, self.word_length, self.stop_bits, self.parity) if self.cmd_status.value != FT_STATUS.FT_OK.value: raise InstrumentError("FT_SetDatCharacteristics failed") self._data_characteristics_set = True return raise ValueError( "Please ensure that word length, stop bits and parity are set")
def write(self, data, size=None): if not self._data_characteristics_set: self._set_data_characteristics() if size is None: size = len(data) buffer = ctypes.create_string_buffer(bytes(data), size) bytes_written = DWORD() self.cmd_status.value = ftdI2xx.FT_Write(self.handle, buffer, ctypes.sizeof(buffer), ctypes.byref(bytes_written)) if self.cmd_status.value != FT_STATUS.FT_OK.value: raise InstrumentError("FT_Write failed")
def read(self): self._trigger_thread.join(self._thread_timeout) if self._trigger_thread.is_alive(): raise InstrumentError("Trigger thread failed to terminate") try: err = self._error_queue.get_nowait() except Empty: # no error in queue pass else: raise err # TODO: consider making this return self._data.value. We should return a python # float object, not a ctypes.c_double return self._data
def _write(self, data): try: if data: if isinstance(data, str): self.instrument.write(data) time.sleep(self.write_delay) else: for itm in data: self.instrument.write(itm) time.sleep(self.write_delay) # self._is_error() else: raise ParameterError("Missing data in instrument write") except VisaIOError as e: # TODO Write to log raise InstrumentError("Unknown IO error from read operation") from e
def _is_error(self, silent=False): errors = [] while True: code, msg = self._check_errors() if code != 0: errors.append((code, msg)) else: break if errors: if silent: return errors else: raise InstrumentError( "Error(s) Returned from FuncGen\n" + "\n".join([ "Code: {}\nMessage:{}".format(code, msg) for code, msg in errors ]))
def converge_amplitude_scalar(dmm, funcgen_or_ps, set_point, cur_amplitude, mode=None, frequency=None, offset=0, retries=None, timeout=None, settle_min=None, settle_max=None, settle_number=1, limit_amplitude=None): """ :param dmm: the instantiated class used as a dmm. The dmm should have the measurement type already set up :param funcgen_or_ps: the instantited class used as the control This can be a function generator or a programmable power supply :param set_point: The desired final resting value as measured through the dmm :param cur_amplitude: The current amplitude sent to the function generator or programmable power supply :param mode: Used for function generator and indicates the waveform eg. 'sin' or 'square' :param frequency: Used for function generator and indicates the waveform frequency eg. 1000 :param offset: Used for function generator and indicates the waveform offset eg. 0.0 :param retries: The amount of times the control is updated if it is not successful in converging first. Function will return True if all retries are attempted and settle parameters are not set Function will return False if all retries are attempted and settle parameters are set :param timeout: The amount of time the control will be updated if it is not successful in converging first or all retries attempted. Function will return True if timeout is reached and retries and settle parameters are not set Function will return False if timeout occurs and retries are set and settle parameters are set :param feedback_return_index: This is the index used from the return value. eg. dmm.measure()[0] would have feedback_return_index = 0 eg. dmm.measure()["data"] would have feedback_return_index = 'data' :param settle_min: The minimum value that is considered to be at an acceptable settle point :param settle_max: The maximum value that is considered to be at an acceptable settle point :param settle_number: The number of consecutive measurements in a that are within settle_min <= value <= settle_max before the function returns True :return: True if converged False if not completed before retries or timeout Parameter error if parsed parameters are incompatible Calling functions exceptions are not handled """ # Check if function gen # if issubclass(FuncGen, funcgen_or_ps): # if any(x is None for x in [mode, frequency, offset]): # raise MissingParameters("Need frequency and offset for function generator") # TODO Setup Timeout p = LinearScalarControl(cur_amplitude) p.set_point(set_point) settle_cnt = 0 new_kwargs = {"frequency": frequency, "offset": offset} for _ in _range_gen(retries): test_val = dmm.measure() try: test_val = test_val[0] except TypeError: pass if test_val > 1e10: raise InstrumentError( "Measurement outside the specified dmm range") # Test in range if settle_min is not None and settle_max is not None: if settle_min <= test_val <= settle_max: settle_cnt += 1 else: settle_cnt = 0 if settle_cnt >= settle_number: return True # Get the control value from controller contr_val = p.update(test_val) # Put the control value into the kwargs for the control function if limit_amplitude and contr_val >= limit_amplitude: raise InstrumentError( "Converge function value {}Vpp exceeded the limit set {}Vpp". format(contr_val, limit_amplitude)) new_kwargs.update(dict([('amplitude', contr_val)])) funcgen_or_ps.function(mode, **new_kwargs) if not (contr_val * 0.95 < funcgen_or_ps.amplitude_ch1 <= contr_val * 1.05): raise InstrumentError( "Amplitude {} set outside of instrument range {}".format( contr_val, funcgen_or_ps.amplitude_ch1))
def _connect(self): self.cmd_status.value = ftdI2xx.FT_OpenEx( ctypes.c_char_p(self.ftdi_description), FLAGS.FT_OPEN_BY_DESCRIPTION, ctypes.byref(self.handle)) if self.cmd_status.value != FT_STATUS.FT_OK.value: raise InstrumentError("FT_OpenEx failed")
def close(self): self.cmd_status.value = ftdI2xx.FT_Close(self.handle) if self.cmd_status.value != FT_STATUS.FT_OK.value: raise InstrumentError("FT_Close failed {}".format( self.cmd_status.value))
def _is_error(self): self.instrument.write("SYST:ERR?") time.sleep(self.write_delay) err_resp = self._read() if "no error" not in err_resp.lower(): raise InstrumentError(err_resp)