def set_monitor(self, monitor_ch, source): """ Configures the specified monitor channel to view the desired PID instrument signal. There are two 12-bit monitoring channels available, 'a' and 'b'; each of these can be assigned to source signals from any of the internal PID instrument monitoring points. Signals larger than 12-bits must be either truncated or clipped to the allowed size. The source is one of: - **adc1** : Channel 1 ADC input - **in1** : PID Channel 1 input (after mixing, offset and scaling) - **out1** : PID Channel 1 output - **adc2** : Channel 2 ADC Input - **in2** : PID Channel 2 input (after mixing, offset and scaling) - **out2** : PID Channel 2 output :type monitor_ch: str; {'a','b'} :param monitor_ch: Monitor channel :type source: str; {'adc1', 'in1', 'out1', 'adc2', 'in2', 'out2'} :param source: Signal to connect to the monitor channel """ _utils.check_parameter_valid('string', monitor_ch, desc="monitor channel") _utils.check_parameter_valid('string', source, desc="monitor signal") monitor_sources = { 'none': _PID_MON_NONE, 'adc1': _PID_MON_ADC1, 'in1': _PID_MON_IN1, 'out1': _PID_MON_OUT1, 'adc2': _PID_MON_ADC2, 'in2': _PID_MON_IN2, 'out2': _PID_MON_OUT2 } monitor_ch = monitor_ch.lower() source = source.lower() _utils.check_parameter_valid('set', monitor_ch, allowed=['a', 'b'], desc="monitor channel") _utils.check_parameter_valid( 'set', source, allowed=['none', 'adc1', 'in1', 'out1', 'adc2', 'in2', 'out2'], desc="monitor source") if monitor_ch == 'a': self.monitor_a = source self.mon1_source = monitor_sources[source] elif monitor_ch == 'b': self.monitor_b = source self.mon2_source = monitor_sources[source] else: raise ValueOutOfRangeException("Invalid channel %d", monitor_ch)
def set_monitor(self, monitor_ch, source): """ Select the point inside the lockin amplifier to monitor. There are two monitoring channels available, 'A' and 'B'; you can mux any of the internal monitoring points to either of these channels. The source is one of: - **none**: Disable monitor channel - **in1**, **in2**: Input Channel 1/2 - **main**: Lock-in output (Output Channel 1) - **aux**: Auxillary output (Output Channel 2) - **demod**: Demodulation signal input to mixer - **i**, **q**: Mixer I and Q channels respectively. :type monitor_ch: string; {'A','B'} :param monitor_ch: Monitor channel :type source: string; {'none','in1','in2','main','aux','demod','i','q'} :param source: Signal to monitor """ _utils.check_parameter_valid('string', monitor_ch, desc="monitor channel") _utils.check_parameter_valid('string', source, desc="monitor signal") monitor_ch = monitor_ch.lower() source = source.lower() _utils.check_parameter_valid('set', monitor_ch, allowed=['a', 'b'], desc="monitor channel") _utils.check_parameter_valid( 'set', source, allowed=['none', 'in1', 'in2', 'main', 'aux', 'demod', 'i', 'q'], desc="monitor source") monitor_sources = { 'none': _LIA_MON_NONE, 'in1': _LIA_MON_IN1, 'in2': _LIA_MON_IN2, 'main': _LIA_MON_OUT, 'aux': _LIA_MON_AUX, 'demod': _LIA_MON_DEMOD, 'i': _LIA_MON_I, 'q': _LIA_MON_Q, } if monitor_ch == 'a': self.monitor_a = source self.monitor_select0 = monitor_sources[source] elif monitor_ch == 'b': self.monitor_b = source self.monitor_select1 = monitor_sources[source] else: raise ValueOutOfRangeException("Invalid channel %d", monitor_ch)
def set_filter(self, ch, decimation_factor, filter_coefficients): """ Set FIR filter sample rate and kernel coefficients for the specified filter channel. This will enable the specified channel output. See class documentation for information on the `filter_coefficients` array formatting and how `decimation_factor` relates to instrument sample rate. :type ch: int; {1,2} :param ch: Filter channel. :type decimation_factor: int; [3,10] :param decimation_factor: the binary exponent *n* specifying the filter sample rate: :math:`Fs = 125 MHz / 2^n`. :type filter_coefficients: array(float) :param filter_coefficients: array of FIR filter coefficients. The length of the array must not exceed :math:`N = min(29*2^n, 14819)`. """ _utils.check_parameter_valid('set', ch, [1, 2], 'filter channel') _utils.check_parameter_valid('set', decimation_factor, range(3, 11), 'decimation factor') _utils.check_parameter_valid( 'range', len(filter_coefficients), [0, _FIR_NUM_BLOCKS * min(2**decimation_factor, 2**9 - 1)], 'filter coefficient array length') # Check that all coefficients are between -1.0 and 1.0 if not all(map(lambda x: abs(x) <= 1.0, filter_coefficients)): raise ValueOutOfRangeException("set_filter filter coefficients " "must be in the range " "[-1.0, 1.0].") """ IMPORTANT: The decimation filter samplerate must be set before the coefficients are written this is because the mmap access bit appropriately resets the other decimation blocks. """ self._set_samplerate(ch, 2.0**decimation_factor) self._write_coeffs(ch, filter_coefficients) if ch == 1: self.filter_en1 = True self.output_en1 = True self.input_en1 = True else: self.filter_en2 = True self.output_en2 = True self.input_en2 = True self._channel_reset(ch) # Manually commit all outstanding registers as this function does not # use needs_commit # This is because the mmap_access register muts be committed when # writing coefficients. self.commit()
def set_output(self, ch, amplitude, offset=0): """ Set the output sweep amplitude. .. note:: Ensure that the output amplitude is set so as to not saturate the inputs. Inputs are limited to 1.0Vpp with attenuation turned off. :type ch: int; {1, 2} :param ch: Output channel :type amplitude: float; [0.0, 2.0] Vpp :param amplitude: Sweep amplitude :type offset: float; [-1.0, 1.0] Volts :param offset: Sweep offset """ _utils.check_parameter_valid('set', ch, [1, 2], 'output channel') _utils.check_parameter_valid('range', amplitude, [0.001, 2.0], 'sweep amplitude', 'Vpp') _utils.check_parameter_valid('range', offset, [-1.0, 1.0], 'sweep offset', 'volts') # ensure combination of amplitude and offset doesn't cause output # clipping if ((amplitude / 2.0) + abs(offset)) > 1.0: raise ValueOutOfRangeException("Output sweep waveform must not " "exceed +/- 1.0 volts. Reduce " "output amplitude and/or offset.") # Set up the output scaling register but also save the voltage value # away for use in the state dictionary to scale incoming data if ch == 1: self.sweep_amplitude_ch1 = amplitude self.sweep_amp_volts_ch1 = amplitude self.sweep_offset_ch1 = offset self.channel1_en = amplitude > 0 elif ch == 2: self.sweep_amplitude_ch2 = amplitude self.sweep_amp_volts_ch2 = amplitude self.sweep_offset_ch2 = offset self.channel2_en = amplitude > 0
def set_filter(self, f_corner, order): """ Set the low-pass filter parameters. :type f_corner: float :param f_corner: Corner frequency of the low-pass filter (Hz) :type order: int; [0, 1, 2] :param order: filter order; 0 (bypass), first- or second-order. """ # Ensure the right parts of the filter are enabled self.lpf_den = 0 if order == 0: self.filt_bypass1 = True self.filt_bypass2 = True if order == 1: self.filt_bypass1 = False self.filt_bypass2 = True self.slope = 1 elif order == 2: self.filt_bypass1 = False self.filt_bypass2 = False self.slope = 2 else: raise ValueOutOfRangeException("Order must be 0 (bypass), 1 or 2;" " not %d" % order) self.input_gain = 1.0 gain_factor = 2.0**12 * self._adc_gains()[0] / \ (10.0 if self.get_frontend(1)[1] else 1.0) self.lpf_pidgain = gain_factor if order == 1 else \ math.sqrt(gain_factor) ifb = 1.0 - 2.0 * (math.pi * f_corner) / _LIA_CONTROL_FS self.lpf_int_ifb_gain = ifb self.lpf_int_i_gain = 1.0 - ifb
def set_output_range(self, ch, maximum, minimum): """ Set upper and lower bounds for the signal on each DAC channel. The auxilliary waveform is not restricted to these bounds when added to either DAC channel. :type ch : int; [1, 2] :param ch : 1 = Output 1, 2 = Output 2 :type maximum: float, [-1.0, 1.0] Volts; :param maximum: maximum value the output signal can be before clipping occurs. :type minimum: float, [-1.0, 1.0] Volts; :param maximum: maximum value the output signal can be before clipping occurs. """ _utils.check_parameter_valid('set', ch, [1, 2], 'output channel') _utils.check_parameter_valid('range', maximum, [-1.0, 1.0], desc='maximum', units='Volts') _utils.check_parameter_valid('range', minimum, [-1.0, 1.0], desc='minimum', units='Volts') if minimum > maximum: raise ValueOutOfRangeException( "Maximum range value must be greater than minimum.") if ch == 1: self.cliprange_upper_ch1 = maximum / self._dac_gains()[0] / 2.0**15 self.cliprange_lower_ch1 = minimum / self._dac_gains()[0] / 2.0**15 else: self.cliprange_upper_ch2 = maximum / self._dac_gains()[1] / 2.0**15 self.cliprange_lower_ch2 = minimum / self._dac_gains()[1] / 2.0**15
def set_monitor(self, monitor_ch, source): """ Select the point inside the laser lock box to monitor. There are two monitoring channels available, 'A' and 'B'; you can mux any of the internal monitoring points to either of these channels. The source is one of: - **error_signal**: error signal (after low-pass filter) - **pid_fast**: output of the fast pid - **pid_slow**: output of the slow pid - **offset_fast**: offset on the input to the fast pid - **in1**: input channel 1 - **in2**: input channel 2 - **out1**: output channel 1 - **out2**: output channel 2 - **scan**: scan signal - **lo**: local oscillator signal - **aux**: auxiliary sinewave signal :type monitor_ch: string; {'A','B'} :param monitor_ch: Monitor channel :type source: string; {'error', 'pid_fast', 'pid_slow', 'offset_fast', 'offset_slow', 'in1', 'in2', 'out1', 'out2', 'scan', 'lo', 'aux', 'slow_scan'} :param source: Signal to monitor """ _utils.check_parameter_valid('string', monitor_ch, desc="monitor channel") _utils.check_parameter_valid('string', source, desc="monitor signal") monitor_ch = monitor_ch.lower() source = source.lower() _utils.check_parameter_valid('set', monitor_ch, allowed=['a', 'b'], desc="monitor channel") _utils.check_parameter_valid('set', source, allowed=[ 'error', 'pid_fast', 'pid_slow', 'offset_fast', 'offset_slow', 'in1', 'in2', 'out1', 'out2', 'scan', 'lo', 'aux', 'slow_scan' ], desc="monitor source") monitor_sources = { 'error': _LLB_MON_ERROR, 'pid_fast': _LLB_MON_PID_FAST, 'pid_slow': _LLB_MON_PID_SLOW, 'offset_fast': _LLB_MON_OFFSET_FAST, 'in1': _LLB_MON_IN1, 'in2': _LLB_MON_IN2, 'out1': _LLB_MON_OUT1, 'out2': _LLB_MON_OUT2, 'scan': _LLB_MON_SCAN, 'lo': _LLB_MON_LO, 'aux': _LLB_MON_AUX } if monitor_ch == 'a': self.monitor_a = source self.monitor_select0 = monitor_sources[source] elif monitor_ch == 'b': self.monitor_b = source self.monitor_select1 = monitor_sources[source] else: raise ValueOutOfRangeException("Invalid channel %d", monitor_ch)
def set_waveform_trigger(self, ch, source, edge, level, minwidth=None, maxwidth=None, hysteresis=False): """ Specify what constitutes a trigger event for the given output channel. This takes effect only when the channel has triggered output mode enabled (see :any:`set_waveform_trigger_output <pymoku.instruments.ArbitraryWaveGen.set_waveform_trigger_output>`). :type ch: int; {1,2} :param ch: Output channel to set triggering on :type source: string, {'in1','in2','ext'} :param source: Trigger source. May be either input channel, or the external 'Trig' back-panel connector allowing triggering from an externally-generated digital [LV]TTL or CMOS signal. :type edge: string, {'rising','falling','both'} :param edge: Which edge to trigger on. In 'Pulse Width' mode this specifies whether the pulse is positive (rising) or negative (falling), with the 'both' option being invalid. :type level: float, [-5.0, 5.0] volts :param level: Trigger level. Ignored in 'ext' mode. :type minwidth: float, seconds :param minwidth: Minimum Pulse Width. 0 <= minwidth < (2^32/samplerate). Can't be used with maxwidth. :type maxwidth: float, seconds :param maxwidth: Maximum Pulse Width. 0 <= maxwidth < (2^32/samplerate). Can't be used with minwidth. :type hysteresis: bool :param hysteresis: Enable hysteresis around trigger point. """ valid('set', ch, [1, 2], 'channel') valid('set', source, ['in1', 'in2', 'ext'], 'trigger source') valid('set', edge, ['rising', 'falling', 'both']) valid('range', level, [_ARB_TRIG_LVL_MIN, _ARB_TRIG_LVL_MAX], 'trigger level', 'Volts') if not (maxwidth is None or minwidth is None): raise InvalidConfigurationException( "Can't set both 'minwidth' and 'maxwidth' for Pulse Width " "trigger mode. Choose one.") if (maxwidth or minwidth) and (edge == 'both'): raise InvalidConfigurationException( "Can't set trigger edge type 'both' in Pulse Width trigger " "mode. Choose one of {'rising','falling'}.") # External trigger source is only available on Moku 20 if (self._moku.get_hw_version() == 1.0) and source == 'ext': raise InvalidConfigurationException( "External trigger source is not available on your hardware.") if source == 'ext' and level: log.warning( "Trigger level ignored when triggering from source 'ext'.") # TODO: Add timer source _str_to_source = { 'in1': _ARB_TRIG_SRC_CH1, 'in2': _ARB_TRIG_SRC_CH2, 'ext': _ARB_TRIG_SRC_EXT } _str_to_edge = { 'rising': Trigger.EDGE_RISING, 'falling': Trigger.EDGE_FALLING, 'both': Trigger.EDGE_BOTH } source = str_to_val(_str_to_source, source, 'trigger source') edge = str_to_val(_str_to_edge, edge, 'edge type') if ch == 1: self.trig_source1 = source self.trig_level1 = level elif ch == 2: self.trig_source2 = source self.trig_level2 = level else: raise ValueOutOfRangeException("Incorrect channel number %d", ch) trig_channels = [self._trigger1, self._trigger2] # AKA: Normal trigger mode only (HG-2598) trig_channels[ch - 1].timer = 0.0 trig_channels[ch - 1].auto_holdoff = 0 trig_channels[ch - 1].edge = edge trig_channels[ch - 1].duration = minwidth or maxwidth or 0.0 # TODO: Enable setting hysteresis level. For now we use the iPad LSB # values for ON/OFF. trig_channels[ch - 1].hysteresis = 25 if hysteresis else 0 if maxwidth: trig_channels[ch - 1].trigtype = Trigger.TYPE_PULSE trig_channels[ch - 1].pulsetype = Trigger.PULSE_MAX elif minwidth: trig_channels[ch - 1].trigtype = Trigger.TYPE_PULSE trig_channels[ch - 1].pulsetype = Trigger.PULSE_MIN else: trig_channels[ch - 1].trigtype = Trigger.TYPE_EDGE
def gen_waveform(self, ch, period, amplitude, phase=0, offset=0, interpolation=True, dead_time=0, dead_voltage=0, en=True): """ Configure and enable the Arbitrary Waveform on the given output channel. The look-up table for this channel's output waveform should have been loaded beforehand using :any:`write_lut`. The Arbitrary Waveform Generator has the ability to insert a deadtime between cycles of the look-up table. This time is specified in cycles of the waveform. During this time, the output will be held at the given *dead_voltage*. This allows the user to, for example, generate infrequent pulses without using space in the LUT to specify the time between, keeping the full LUT size to provide a high-resolution pulse shape. Where the period and look-up table contents are set such that there isn't exactly one LUT point per output sample, the AWG instrument can optionally provide a linear interpolation between LUT points. This function enables the output channel by default. If you wish to enable the outputs simultaneously, you should set the `en` parameter to False and enable both when desired using :any:`enable_output`. :type ch: int; {1,2} :param ch: Channel on which to generate the wave :type period: float, [4e-9, 1]; :param period: period of the signal in seconds :type amplitude: float, [0.0,2.0] Vpp :param amplitude: Waveform peak-to-peak amplitude :type phase: float, [0-360] degrees :param phase: Phase offset of the wave :type offset: float, [-1.0,1.0] Volts :param offset: DC offset applied to the waveform :type interpolation: bool [True, False] :param interpolation: Enable linear interpolation of LUT entries :type dead_time: float [0, 2e18] cyc :param dead_time: number of cycles which show the dead voltage. Use 0 for no dead time :type dead_voltage: float [-2.0,2.0] V :param dead_voltage: signal level during dead time in Volts :type en: bool :param en: Enable output :raises ValueError: if the parameters is invalid :raises ValueOutOfRangeException: if wave parameters are out of range :raises InvalidParameterException: if the parameters are the wrong types """ valid('set', ch, [1, 2], desc='output channel') valid('range', period, [4e-9, 1000], desc='period of the signal') valid('range', amplitude, [0.0, 2.0], desc='peak to peak amplitude', units='volts') valid('bool', interpolation, desc='linear interpolation') valid('range', dead_time, [0.0, 2e18], desc='signal dead time', units='cycles') valid('range', dead_voltage, [-2.0, 2.0], desc='dead value', units='volts') valid('range', phase, [0, 360], desc='phase offset', units='degrees') valid('bool', en, 'output enable') upper_voltage = offset + (amplitude / 2.0) lower_voltage = offset - (amplitude / 2.0) if (upper_voltage > 1.0) or (lower_voltage < -1.0): raise ValueOutOfRangeException( "Waveform offset limited by amplitude " "(max output range 2.0Vpp).") # Ensure that dead voltage does not exceed the amplitude of the # waveform if dead_voltage > upper_voltage or dead_voltage < lower_voltage: raise ValueOutOfRangeException( "Dead voltage must not exceed custom waveform voltage range " "of [%.2f, %.2f] Volts.".format(lower_voltage, upper_voltage)) if ch == 1: freq = 1.0 / period self.interpolation1 = interpolation phase_modulo = (self.lut_length1 + 1) * \ _ARB_LUT_INTERPLOATION_LENGTH self._sweep1.step = freq / _ARB_SMPL_RATE * phase_modulo phase_modulo = phase_modulo * (1 + dead_time) self._sweep1.stop = phase_modulo self._sweep1.start = (phase / 360.0) * phase_modulo self.dead_value1 = 0.0 if not amplitude else 2.0 * \ (dead_voltage - lower_voltage) / \ (upper_voltage - lower_voltage) - 1.0 self.amplitude1 = amplitude self.offset1 = offset self.enable1 = en if ch == 2: freq = 1.0 / period self.interpolation2 = interpolation phase_modulo = (self.lut_length2 + 1) * \ _ARB_LUT_INTERPLOATION_LENGTH self._sweep2.step = freq / _ARB_SMPL_RATE * phase_modulo phase_modulo = phase_modulo * (1 + dead_time) self._sweep2.stop = phase_modulo self._sweep2.start = (phase / 360.0) * phase_modulo self.dead_value2 = 0.0 if not amplitude else 2.0 * \ (dead_voltage - lower_voltage) / \ (upper_voltage - lower_voltage) - 1.0 self.amplitude2 = amplitude self.offset2 = offset self.enable2 = en
def write_lut(self, ch, data, mode=None): """Writes the signal lookup table to memory in the Moku:Lab. You can also choose the output rate of the AWG, which influences the maximum length of the look-up table as follows: - 1000MSPS: 8192 points per channel - 500MSPS: 16384 points per channel - 250MSPS: 32768 points per channel - 125MSPS: 65536 points per channel If you don't specify a mode, the fastest output rate for the given data length will be automatically chosen. This is correct in almost all circumstances. If you specify a particular mode along with a data array too big for that mode, the behaviour is undefined. To avoid unexpected output signals during write, disable the outputs by using the :any:`enable_output` function. :type ch: int; {1,2} :param ch: Output channel to load the LUT to :type data: float array; :param data: Lookup table coefficients normalised to range [-1.0, 1.0]. :type mode: int; {125, 250, 500, 1000} MSmps :param mode: defines the output sample rate of the AWG. :raises ValueError: if the channel is invalid :raises ValueOutOfRangeException: if wave parameters are out of range """ valid('set', ch, [1, 2], 'output channel') valid('set', mode, [125, 250, 500, 1000], desc='output sample rate', units="MSmps", allow_none=True) # Check that all coefficients are between -1.0 and 1.0 if not all(map(lambda x: abs(x) <= 1.0, data)): raise ValueOutOfRangeException( "Lookup table coefficients must be in the range [-1.0, 1.0].") n_points = len(data) if n_points <= 2**13: max_lut_samplerate = 1000 elif n_points <= 2**14: max_lut_samplerate = 500 elif n_points <= 2**15: max_lut_samplerate = 250 elif n_points <= 2**16: max_lut_samplerate = 125 else: raise ValueOutOfRangeException( "Maximum data length is 65535 samples") if not mode: mode = max_lut_samplerate if mode > max_lut_samplerate: raise InvalidConfigurationException( "Maximum samplerate for {} lookup table coefficients " "is {}Msmps.".format(n_points, max_lut_samplerate)) _str_to_mode = { '1000': _ARB_MODE_1000, '500': _ARB_MODE_500, '250': _ARB_MODE_250, '125': _ARB_MODE_125 } mode = str_to_val(_str_to_mode, str(mode), "operating mode") self._set_mode(ch, mode, len(data)) self.commit() # picks the stepsize and the steps based in the mode steps, stepsize = [(8, 8192), (4, 8192 * 2), (2, 8192 * 4), (1, 8192 * 8)][mode] byte_data = bytearray() for step in range(steps): byte_data += bytearray(b''.join([ struct.pack('<hh', int(math.ceil((2.0**15 - 1) * d)), 0) for d in data ])) byte_data += bytearray(b'\0' * (stepsize * 4 - (len(data) * 4))) # Write the data to AWG memory map self._set_mmap_access(True) self._moku._send_file_bytes('j', '', byte_data, offset=_ARB_LUT_LENGTH * 8 * 4 * (ch - 1)) self._set_mmap_access(False) # Release the memory map "file" to other resources self._moku._fs_finalise('j', '', _ARB_LUT_LENGTH * 8 * 4 * 2)
def set_sweep(self, f_start=100, f_end=120e6, sweep_points=512, sweep_log=False, averaging_time=1e-3, settling_time=1e-3, averaging_cycles=1, settling_cycles=1): """ Set the output sweep parameters :type f_start: int; 1 <= f_start <= 120e6 Hz :param f_start: Sweep start frequency :type f_end: int; 1 <= f_end <= 120e6 Hz :param f_end: Sweep end frequency :type sweep_points: int; 32 <= sweep_points <= 512 :param sweep_points: Number of points in the sweep (rounded to nearest power of 2). :type sweep_log: bool :param sweep_log: Enable logarithmic frequency sweep scale. :type averaging_time: float; sec :param averaging_time: Minimum averaging time per sweep point. :type settling_time: float; sec :param settling_time: Minimum setting time per sweep point. :type averaging_cycles: int; cycles :param averaging_cycles: Minimum averaging cycles per sweep point. :type settling_cycles: int; cycles :param settling_cycles: Minimum settling cycles per sweep point. """ _utils.check_parameter_valid('range', f_start, [1, 120e6], 'sweep start frequency', 'Hz') _utils.check_parameter_valid('range', f_end, [1, 120e6], 'sweep end frequency', 'Hz') _utils.check_parameter_valid('range', sweep_points, [32, 512], 'sweep points') _utils.check_parameter_valid('bool', sweep_log, desc='sweep log scale enable') _utils.check_parameter_valid('range', averaging_time, [1e-6, 10], 'sweep averaging time', 'sec') _utils.check_parameter_valid('range', settling_time, [1e-6, 10], 'sweep settling time', 'sec') _utils.check_parameter_valid('range', averaging_cycles, [1, 2**20], 'sweep averaging cycles', 'cycles') _utils.check_parameter_valid('range', settling_cycles, [1, 2**20], 'sweep settling cycles', 'cycles') # Frequency span check if (f_end - f_start) == 0: raise ValueOutOfRangeException("Sweep frequency span must be " "non-zero: f_start/f_end/span " "- %.2f/%.2f/%.2f." % (f_start, f_end, f_end - f_start)) self.sweep_freq_min = f_start self.sweep_length = sweep_points self.log_en = sweep_log self.averaging_time = averaging_time self.averaging_cycles = averaging_cycles self.settling_time = settling_time self.sweep_freq_delta = self._calculate_sweep_delta( f_start, f_end, sweep_points, sweep_log) self.settling_cycles = settling_cycles
def get_stream_data(self, n=0, timeout=None): """ Get any new instrument samples that have arrived on the network. This returns a tuple containing two arrays (one per channel) of up to 'n' samples of instrument data. If a channel is disabled, the corresponding array is empty. If there were less than 'n' samples remaining for the session, then the arrays will contain this remaining amount of samples. :type n: int :param n: Number of samples to get off the network. Set this to '0' to get all currently available samples, or '-1' to wait on all samples of the currently running streaming session to be received. :type timeout: float :param timeout: Timeout in seconds :rtype: tuple :returns: ([CH1_DATA], [CH2_DATA]) :raises NoDataException: if the logging session has stopped :raises FrameTimeout: if the timeout expired :raises InvalidOperationException: if there is no streaming session running :raises ValueOutOfRangeException: invalid input parameters :raises DataIntegrityException: If the network layer detects dropped data """ if timeout and timeout <= 0: raise ValueOutOfRangeException( "Timeout must be positive or 'None'") if n < -1: raise ValueOutOfRangeException( "Invalid number of samples. Expected (n >= -1).") # If no network session exists, can't get samples if not self._stream_net_is_running(): raise InvalidOperationException( "No network streaming session is running.") if self._no_data: log.debug("No more samples to get.") return ([], []) if type(n) is not int: raise TypeError("Sample number 'n' must be an integer") # Check how many samples are already processed and waiting to be # read out processed_samples = self._stream_get_processed_samples() if n > 0: # Actual number of samples processed already num_processed_samples = [len(x) for x in processed_samples] else: # We don't need to track the number of processed samples # if n = [0,1] num_processed_samples = [-1, -1] # Only "get" samples off the network if we haven't already processed # enough to return 'n' for all enabled channels. while ((n == -1) or (self.ch1 and ((num_processed_samples[0] <= n) or (num_processed_samples[0] <= 0))) or (self.ch2 and ((num_processed_samples[1] <= n) or (num_processed_samples[1] <= 0)))): try: self._stream_receive_samples(timeout) except NoDataException: log.debug("No more data available for current stream.") self._no_data = True # Update our list of current processed samples processed_samples = self._stream_get_processed_samples() if n != -1: # Update the number of processed samples if we aren't asking # for 'all' of them num_processed_samples = [len(x) for x in processed_samples] # Check if the streaming session has completed if self._no_data: break active_channels = [self.ch1, self.ch2] to_return = min( [len(p) for c, p in zip(active_channels, processed_samples) if c]) if n > 0: to_return = min(n, to_return) dout_ch1 = processed_samples[0][0:to_return] if self.ch1 else [] dout_ch2 = processed_samples[1][0:to_return] if self.ch2 else [] self._stream_clear_processed_samples(to_return) return (dout_ch1, dout_ch2)
def set_demodulation(self, mode, frequency=1e6, phase=0, output_amplitude=0.5): """ Configure the demodulation stage. The mode is one of: - **internal** : for an internally set local oscillator - **external** : to directly use an external signal for demodulation (Note: Q is not selectable in this mode) - **external_pll** : to use an external signal for demodulation after running it through an internal PLL. .. note:: When 'external' is used (that is, without a PLL), the Lock-in Amplifier doesn't know the frequency and therefore can't form the quadrature for full I/Q demodulation. This in turn means it can't distinguish I from Q, X from Y, or form R/Theta. This limits the choices for signals that can be output on the Main and AUX channels to ones not formed from the quadrature signal. An exception will be raised if you attempt to set the demodulation to 'external' while viewing one of these signals. :type mode: string; {'internal', 'external', 'external_pll'} :param mode: Demodulation mode :type frequency: float; [0, 200e6] Hz :param frequency: Internal demodulation signal frequency (ignored for all 'external' modes) :type phase: float; [0, 360] deg :param phase: Internal demodulation signal phase (ignored in 'external' mode) :type output_amplitude: float; [0.0, 2.0] Vpp :param output_amplitude: Output amplitude of the demodulation signal when auxillary channel set to output `demod`. """ _utils.check_parameter_valid('range', frequency, allowed=[0, 200e6], desc="demodulation frequency", units="Hz") _utils.check_parameter_valid('range', phase, allowed=[0, 360], desc="demodulation phase", units="degrees") _utils.check_parameter_valid( 'set', mode, allowed=['internal', 'external', 'external_pll']) if mode == 'external' and not ( self.aux_source in _NON_PLL_ALLOWED_SIGS and (self.main_source in _NON_PLL_ALLOWED_SIGS)): raise InvalidConfigurationException("Can't use external" " demodulation source " "without a PLL with " "quadrature-related outputs." "Allowed outputs are " + (str(_NON_PLL_ALLOWED_SIGS))) self.autoacquire = 1 self.bandwidth = 0 self.lo_PLL_reset = 0 self.lo_reacquire = 0 # Store the desired output amplitude in the case that 'set_outputs' # is called with 'demod' for the auxillary channel output. We can't # set the register here because it is shared with the local oscillator # amplitude. It will be updated on commit. self._demod_amp = output_amplitude if mode == 'internal': self.ext_demod = 0 self.lo_PLL = 0 self.frequency_demod = frequency self.phase_demod = phase self.demod_mode = mode elif mode == 'external': self.ext_demod = 1 self.lo_PLL = 0 self.demod_mode = mode elif mode == 'external_pll': self.ext_demod = 0 self.lo_PLL = 1 self.lo_reacquire = 1 self.phase_demod = phase self.demod_mode = mode else: # Should not happen raise ValueOutOfRangeException( 'Demodulation mode must be one ' 'of "internal", "external" ' 'or "external_pll", not %s', mode)