Instrument

Contains modules for controlling the PSLab’s built-in instruments.

The built-in instruments are LogicAnalyzer, Multimeter, Oscilloscope, PowerSupply, PWMGenerator, and WaveformGenerator.

LogicAnalyzer

class pslab.instrument.logic_analyzer.LogicAnalyzer(device: Optional[pslab.serial_handler.SerialHandler] = None)[source]

Bases: pslab.serial_handler.ADCBufferMixin

Investigate digital signals on up to four channels simultaneously.

Parameters

device (SerialHandler, optional) – Serial connection to PSLab device. If not provided, a new one will be created.

trigger_channel

See configure_trigger().

Type

str

trigger_mode

See configure_trigger().

Type

str

capture(channels: Union[int, str, List[str]], events: int = 2500, timeout: float = 1, modes: List[str] = ('any', 'any', 'any', 'any'), e2e_time: Optional[float] = None, block: bool = True) → Optional[List[numpy.ndarray]][source]

Capture logic events.

This method cannot be used at the same time as the oscilloscope.

Parameters
  • channels ({1, 2, 3, 4} or str or list of str) – Number of channels to capture events on. Events will be captured on LA1, LA2, LA3, and LA4, in that order. Alternatively, the name of of a single digital input, or a list of two names of digital inputs can be provided. In that case, events will be captured only on that or those specific channels.

  • events (int, optional) – Number of logic events to capture on each channel. The default and maximum value is 2500.

  • timeout (float, optional) – Timeout in seconds before cancelling measurement in blocking mode. If the timeout is reached, the events captured up to that point will be returned. The default value is 1 second.

  • modes (List[str], optional) – List of strings specifying the type of logic level change to capture on each channel. See DigitalInput for available modes. The default value is (“any”, “any”, “any”, “any”).

  • e2e_time (float, optional) – The maximum time between events in seconds. This is only required in three and four channel mode, which uses 16-bit counters as opposed to 32-bit counters which are used in one and two channel mode. The 16-bit counter normally rolls over after 1024 µs, so if the time between events is greater than that the timestamp calculations will be incorrect. By setting this to a value greater than 1024 µs, the counter will be slowed down by a prescaler, which can extend the maximum allowed event-to-event time gap to up to 262 ms. If the time gap is greater than that, three and four channel mode cannot be used. One and two channel mode supports timegaps up to 67 seconds.

  • block (bool, optional) – Whether to block while waiting for events to be captured. If this is False, this method will return None immediately and the captured events must be manually fetched by calling fetch_data(). The default value is True.

Returns

events – List of numpy.ndarrays containing timestamps in microseconds when logic events were detected, or None if block is False. The length of the list is equal to the number of channels that were used to capture events, and each list element corresponds to a channel.

Return type

list of numpy.ndarray or None

Raises
  • ValueError if too many events are requested, or

  • ValueError if too many channels are selected.

configure_trigger(trigger_channel: str, trigger_mode: str)[source]

Set up trigger channel and trigger condition.

Parameters
  • trigger_channel ({"LA1", "LA2", "LA3", "LA4"}) – The digital input on which to trigger.

  • trigger_mode ({"disabled", "falling", "rising"}) – The type of logic level change on which to trigger.

count_pulses(channel: str = 'FRQ', interval: float = 1, block: bool = True) → Optional[int][source]

Count pulses on a digital input.

The counter is 16 bits, so it will roll over after 65535 pulses. This method can be used at the same time as the oscilloscope.

Parameters
  • channel ({"LA1", "LA2", "LA3", "LA4", "FRQ"}, optional) – Digital input on which to count pulses. The default value is “FRQ”.

  • interval (float, optional) – Time in seconds during which to count pulses. The default value is 1 second.

  • block (bool, optional) – Whether to block while counting pulses or not. If False, this method will return None, and the pulses must be manually fetched using fetch_pulse_count(). Additionally, the interval argument has no meaning if block is False; the counter will keep counting even after the interval time has expired. The default value is True.

Returns

Number of pulses counted during the interval, or None if block is False.

Return type

Union[int, None]

fetch_data() → List[numpy.ndarray][source]

Collect captured logic events.

It is possible to call fetch_data while the capture routine is still running. Doing so will not interrupt the capture process. In multi-channel mode, the number of timestamps may differ between channels when fetch_data is called before the capture is complete.

Returns

data – List of numpy.ndarrays holding timestamps in microseconds when logic events were detected. The length of the list is equal to the number of channels that were used to capture events, and each list element corresponds to a channel.

Return type

list of numpy.ndarray

fetch_pulse_count() → int[source]

Return the number of pulses counted since calling count_pulses().

Returns

Number of pulses counted since calling count_pulses().

Return type

int

get_initial_states() → Dict[str, bool][source]

Return the initial state of each digital input at the beginning of capture.

Returns

dict of four str – Dictionary containing pairs of channel names and the corresponding initial state, e.g. {‘LA1’: True, ‘LA2’: True, ‘LA3’: True, ‘LA4’: False}. True means HIGH, False means LOW.

Return type

bool pairs

get_progress() → int[source]

Return the number of captured events per channel held in the buffer.

Returns

progress – Number of events held in buffer. If multiple channels have events in buffer, the lowest value will be returned.

Return type

int

get_states() → Dict[str, bool][source]

Return the current state of the digital inputs.

Returns

dict of four str – Dictionary containing pairs of channel names and the corresponding current state, e.g. {‘LA1’: True, ‘LA2’: True, ‘LA3’: True, ‘LA4’: False}. True means HIGH, False means LOW.

Return type

bool pairs

get_xy(timestamps: List[numpy.ndarray], initial_states: Optional[Dict[str, bool]] = None) → List[numpy.ndarray][source]

Turn timestamps into plottable data.

Parameters
  • timestamps (list of numpy.ndarray) – List of timestamps as returned by capture() or fetch_data().

  • initial_states (dict of str, bool) – Initial states of digital inputs at beginning of capture, as returned by get_initial_states(). If no additional capture calls have been issued before calling get_xy(), this can be omitted.

Returns

List of x, y pairs suitable for plotting using, for example, matplotlib.pyplot.plot. One pair of x and y values is returned for each list of timestamps given as input.

Return type

list of numpy.ndarray

measure_duty_cycle(channel: str, timeout: float = 1) → Tuple[float][source]

Measure duty cycle and wavelength.

This method cannot be used at the same time as the oscilloscope.

Parameters
  • channel ({"LA1", "LA2", "LA3", "LA4"}) – Digital input on which to measure.

  • timeout (float, optional) – Timeout in seconds before cancelling measurement. The default value is 1 second.

Returns

  • wavelength (float) – Wavelength in microseconds.

  • duty_cycle (float) – Duty cycle as a value between 0 - 1.

measure_frequency(channel: str, simultaneous_oscilloscope: bool = False, timeout: float = 1) → float[source]

Measure the frequency of a signal.

Parameters
  • channel ({"LA1", "LA2", "LA3", "LA4"}) – Name of the digital input channel in which to measure the frequency.

  • simultaneous_oscilloscope (bool, optional) – Set this to True if you need to use the oscilloscope at the same time. Uses firmware instead of software to measure the frequency, which may fail and return 0. Will not give accurate results above 10 MHz. The default value is False.

  • timeout (float, optional) – Timeout in seconds before cancelling measurement. The default value is 1 second.

Returns

frequency – The signal’s frequency in Hz.

Return type

float

measure_interval(channels: List[str], modes: List[str], timeout: float = 1) → float[source]

Measure the time between two events.

This method cannot be used at the same time as the oscilloscope.

Parameters
  • channels (List[str]) – A pair of digital inputs, LA1, LA2, LA3, or LA4. Both can be the same.

  • modes (List[str]) – Type of logic event to listen for on each channel. See DigitalInput for available modes.

  • timeout (float, optional) – Timeout in seconds before cancelling measurement. The default value is 1 second.

Returns

interval – Time between events in microseconds. A negative value means that the event on the second channel happend first.

Return type

float

stop()[source]

Stop a running capture() function.

Multimeter

class pslab.instrument.multimeter.Multimeter(device: Optional[pslab.serial_handler.SerialHandler] = None)[source]

Bases: pslab.instrument.oscilloscope.Oscilloscope

Measure voltage, resistance and capacitance.

Parameters

device (Handler) – Serial interface for communicating with the PSLab device. If not provided, a new one will be created.

calibrate_capacitance()[source]

Calibrate stray capacitance.

Correctly calibrated stray capacitance is important when measuring small capacitors (picofarad range).

Stray capacitace should be recalibrated if external wiring is connected to the CAP pin.

measure_capacitance() → float[source]

Measure the capacitance of a capacitor connected between CAP and GND.

Returns

capacitance – Capacitance in Farad.

Return type

float

measure_resistance() → float[source]

Measure the resistance of a resistor connected between RES and GND.

Returns

resistance – Resistance in ohm.

Return type

float

measure_voltage(channel: str = 'VOL') → float[source]

Measure the voltage on the selected channel.

Parameters

channel ({"CH1", "CH2", "CH3", "MIC", "CAP", "RES", "VOL"}, optional) – The name of the analog input on which to measure the voltage. The default channel is VOL.

Returns

voltage – Voltage in volts.

Return type

float

Oscilloscope

class pslab.instrument.oscilloscope.Oscilloscope(device: Optional[pslab.serial_handler.SerialHandler] = None)[source]

Bases: pslab.serial_handler.ADCBufferMixin

Capture varying voltage signals on up to four channels simultaneously.

Parameters

device (SerialHandler, optional) – Serial interface for communicating with the PSLab device. If not provided, a new one will be created.

capture(channels: int, samples: int, timegap: float, trigger: Optional[Union[float, bool]] = None, trigger_channel: Optional[str] = None, block: bool = True) → List[numpy.ndarray][source]

Capture an oscilloscope trace from the specified input channels.

Parameters
  • channels (str or {1, 2, 3, 4}) – Number of channels to sample from simultaneously, or the name (CH1, CH2, CH3, MIC, CAP, RES, VOL) of a single channel to sample from. If channel is an integer, the oscilloscope will sample the first one, two, three, or four channels in the aforementioned list.

  • samples (int) – Number of samples to fetch. Maximum 10000 divided by number of channels.

  • timegap (float) –

    Time gap between samples in microseconds. Will be rounded to the closest 1 / 8 µs. The minimum time gap depends on the type of measurement:

    Simultaneous channels

    No trigger (10-bit)

    Trigger (10-bit)

    No trigger (12-bit)

    1

    0.5 µs

    0.75 µs

    1 µs

    2

    0.875 µs

    0.875 µs

    N/A

    4

    1.75 µs

    1.75 µs

    N/A

    Sample resolution is set automatically based on the above limitations; i.e. to get 12-bit samples only one channel may be sampled, there must be no active trigger, and the time gap must be 1 µs or greater.

  • trigger (float or bool, optional) – Voltage at which to trigger sampling. Triggering is disabled by default. Trigger settings persist between calls; disable by setting trigger=False.

  • trigger_channel (str, optional) – Wait for the voltage level on this channel to cross the trigger value before sampling. Same as the first sampled channel by default.

  • block (bool, optional) – Whether or not to block while sampling. If False, return timestamps immediately without waiting for corresponding voltages. User is responsible for waiting an appropriate amount of time before collecting samples with fetch_data(). True by default.

Example

>>> from pslab import Oscilloscope
>>> scope = Oscilloscope()
>>> x, y = scope.capture(1, 3200, 1)
Returns

List of numpy arrays holding timestamps and corresponding voltages. In non-blocking mode, only timestamps are returned; voltages must be fetched using fetch_data().

Return type

list of numpy.ndarray

Raises

ValueError – If :channels: is not 1, 2, 3, 4, or one of CH1, CH2, CH3, MIC, CAP, RES, VOL, or :samples: > 10000 / :channels:, or :timegap: is too low.

configure_trigger(channel: Optional[str] = None, voltage: float = 0, prescaler: int = 0, enable: bool = True)[source]

Configure trigger parameters for 10-bit capture routines.

The capture routines will wait until a rising edge of the input signal crosses the specified level. The trigger will timeout within 8 ms, and capture will start regardless.

To disable the trigger after configuration, set the trigger_enabled attribute of the Oscilloscope instance to False.

Parameters
  • channel ({'CH1', 'CH2', 'CH3', 'MIC', 'CAP', 'RES', 'VOL'}, optional) – The name of the trigger channel. First sampled channel by default.

  • voltage (float, optional) – The trigger voltage in volts. The default value is 0.

  • prescaler (int, optional) – The default value is 0.

  • enable_trigger (bool, optional) – Set this to False to disable the trigger. True by default.

Examples

>>> from pslab import Oscilloscope
>>> scope = Oscilloscope()
>>> scope.configure_trigger(channel='CH1', voltage=1.1)
>>> xy = scope.capture(channels=1, samples=800, timegap=2)
>>> diff = abs(xy[1, 0] - 1.1)  # Should be small unless a timeout occurred.
Raises

TypeError – If the trigger channel is set to a channel which cannot be sampled.

fetch_data() → List[numpy.ndarray][source]

Fetch captured samples.

Example

>>> from pslab import Oscilloscope
>>> scope = Oscilloscope()
>>> scope.capture_nonblocking(channels=2, samples=1600, timegap=1)
>>> y1, y2 = scope.fetch_data()
Returns

List of numpy arrays holding sampled voltages.

Return type

list of numpy.ndarray

progress() → Tuple[bool, int][source]

Return the status of a capture call.

Returns

A boolean indicating whether the capture is complete, followed by the number of samples currently held in the buffer.

Return type

bool, int

select_range(channel: str, voltage_range: Union[int, float])[source]

Set appropriate gain automatically.

Setting the right voltage range will result in better resolution.

Parameters
  • channel ({'CH1', 'CH2'}) – Channel on which to apply gain.

  • voltage_range ({16, 8, 4, 3, 2, 1.5, 1, 5}) –

Examples

Set 2x gain on CH1. Voltage range ±8 V:

>>> from pslab import Oscilloscope
>>> scope = Oscilloscope()
>>> scope.select_range('CH1', 8)
property trigger_channel

Name of channel to trigger on.

Type

str

property trigger_enabled

Wait for trigger condition before capture start.

Type

bool

property trigger_voltage

Trigger when voltage crosses this value.

Type

float

PowerSupply

class pslab.instrument.power_supply.PowerSupply(device: Optional[pslab.serial_handler.SerialHandler] = None)[source]

Bases: object

Control the PSLab’s programmable voltage and current sources.

An instance of PowerSupply controls three programmable voltage sources on pins PV1, PV2, and PV3, as well as a programmable current source on pin PCS. The voltage/current on each source can be set via the voltage/current properties of each source.

Parameters

device (SerialHandler) – Serial connection with which to communicate with the device. A new instance will be created automatically if not specified.

property pcs

Current on PCS; range [0, 3.3e-3] A.

Notes

The maximum available current that can be output by the current source is dependent on load resistance:

I_max = 3.3 V / (1 kΩ + R_load)

For example, the maximum current that can be driven across a 100 Ω load is 3.3 V / 1.1 kΩ = 3 mA. If the load is 10 kΩ, the maximum current is only 3.3 V / 11 kΩ = 300 µA.

Be careful to not set a current higher than available for a given load. If a current greater than the maximum for a certain load is requested, the actual current will instead be much smaller. For example, if a current of 3 mA is requested when connected to a 1 kΩ load, the actual current will be only a few hundred µA instead of the maximum available 1.65 mA.

Type

float

property pv1

Voltage on PV1; range [-5, 5] V.

Type

float

property pv2

Voltage on PV2; range [-3.3, 3.3] V.

Type

float

property pv3

Voltage on PV3; range [0, 3.3] V.

Type

float

PWMGenerator

class pslab.instrument.waveform_generator.PWMGenerator(device: Optional[pslab.serial_handler.SerialHandler] = None)[source]

Bases: object

Generate PWM signals on SQ1, SQ2, SQ3, and SQ4.

Parameters

device (SerialHandler) – Serial connection with which to communicate with the device. A new instance will be created automatically if not specified.

Examples

Output 40 kHz PWM signals on SQ1 and SQ3 phase shifted by 50%. Set the duty cycles to 75% and 33%, respectivelly:

>>> from pslab import PWMGenerator
>>> pwmgen = PWMGenerator()
>>> pwmgen.generate(["SQ1", "SQ2"], 4e4, [0.75, 0.33], 0.5)

Output a 32 MHz PWM signal on SQ4 with a duty cycle of 50%:

>>> pwmgen.map_reference_clock(["SQ4"], 2)

Set SQ2 high:

>>> pwmgen.set_states(sq2=True)
property frequency

Get the common frequency for all digital outputs in Hz.

generate(channels: Union[str, List[str]], frequency: float, duty_cycles: Union[float, List[float]], phases: Union[float, List[float]] = 0)[source]

Generate PWM signals on SQ1, SQ2, SQ3, and SQ4.

Parameters
  • channels ({1, 2, 3, 4} or {'SQ1', 'SQ2', 'SQ3', 'SQ4'} or list of the same) – Pin name or list of pin names on which to generate PWM signals. Pins which are not included in the argument will not be affected.

  • frequency (float) – Frequency in Hz. Shared by all outputs.

  • duty_cycles (float or list of floats) –

    Duty cycle between 0 and 1 as either a single value or a list of values.

    If ‘duty_cycles’ is a single value, it is applied to all channels given in ‘channels’.

    If ‘duty_cycles’ is a list, the values in the list will be applied to the corresponding channel in the ‘channels’ list. The lists must have the same length.

  • phases (float or list of floats) –

    Phase between 0 and 1 as either a single value or a list of values.

    If ‘phases’ is a single value, it will be the phase between each subsequent channel in ‘channels’. For example,

    >>> generate(['SQ1', 'SQ2', 'SQ3', 'SQ4'], 1000, 0.5, 0.1)
    

    SQ2 will be shifted relative to SQ1 by 10%, SQ3 will be shifted relative to SQ2 by 10% (i.e. 20% relative to SQ1), and SQ4 will be shifted relative to SQ3 by 10% (i.e. 30% relative to SQ1).

    If ‘phases’ is a list, the values in the list will be applied to the corresponding channel in the ‘channels’ list. The lists must have the same length.

map_reference_clock(channels: List[str], prescaler: int)[source]

Map the internal oscillator output to a digital output.

The duty cycle of the output is locked to 50%.

Parameters
  • channels ({'SQ1', 'SQ2', 'SQ3', 'SQ4'} or list of the same) – Digital output pin(s) to which to map the internal oscillator.

  • prescaler (int) – Prescaler value in interval [0, 15]. The output frequency is 128 / (1 << prescaler) MHz.

set_state(sq1: Optional[Union[bool, str]] = None, sq2: Optional[Union[bool, str]] = None, sq3: Optional[Union[bool, str]] = None, sq4: Optional[Union[bool, str]] = None)[source]

Set the digital outputs HIGH or LOW.

Parameters
  • sq1 ({True, False, None, 'HIGH', 'LOW', 'PWM'}, optional) – Set the state of SQ1. True or “HIGH” sets it HIGH, False or “LOW” sets it low, and None or “PWM” leaves it in its current state. The default value is None.

  • sq2 ({True, False, None, 'HIGH', 'LOW', 'PWM'}, optional) – See ‘sq1’.

  • sq3 ({True, False, None, 'HIGH', 'LOW', 'PWM'}, optional) – See ‘sq1’.

  • sq4 ({True, False, None, 'HIGH', 'LOW', 'PWM'}, optional) – See ‘sq1’.

WaveformGenerator

class pslab.instrument.waveform_generator.WaveformGenerator(device: Optional[pslab.serial_handler.SerialHandler] = None)[source]

Bases: object

Generate analog waveforms on SI1 or SI2.

Parameters

device (SerialHandler, optional) – Serial connection with which to communicate with the device. A new instance is created automatically if not specified.

Examples

Output the default function (3.3*sin(t)) on SI2 with frequency 2.5 kHz:

>>> from pslab import WaveformGenerator
>>> wavegen = WaveformGenerator()
>>> wavegen.generate("SI2", 2500)
[2500]

Output phase shifted sine waves on SI1 and SI2:

>>> wavegen.generate(["SI1", "SI2"], 1000, 90)
[1000, 1000]

Reduce the amplitude on SI1:

>>> import numpy as np
>>> wavegen.load_function("SI1", lambda x: 1.5*np.sin(x), [0, 2*np.pi])

Output two superimposed sine waves on SI2:

>>> wavegen.load_function("SI2", lambda x: 2*np.sin(x) + np.sin(5*x), [0, 2*np.pi])
generate(channels: Union[str, List[str]], frequency: Union[float, List[float]], phase: float = 0) → List[float][source]

Generate analog waveforms on SI1 or SI2.

The default waveform is a sine wave with amplitude 3.3 V. Other waveforms can be set using load_function() or load_table().

Parameters
  • channels ({1, 2} or {'SI1', 'SI2', ['SI1', 'SI2']}) – Pin(s) on which to generate a waveform.

  • frequency (float or list of floats) – Frequency in Hz. Can be a list containing two different values when ‘channel’ is [‘SI1’, ‘SI2’]. Must be greater than 0.1 Hz. For frequencies below 1 Hz the signal is noticably attenuated by AC coupling.

  • phase (float, optional) – Phase between waveforms when generating waveforms on both SI1 and SI2 in degrees. The default is 0.

Returns

frequency – The actual frequency may differ from the requested value due to the device-interal integer representation. The actual frequency is therefore returned as a list. The length of the list is equal to the number of channels used to generate waveforms.

Return type

List[float]

load_function(channel: str, function: Union[str, Callable], span: Optional[List[float]] = None)[source]

Load a custom waveform function.

Parameters
  • channel ({'SI1', 'SI2'}) – The output pin on which to generate the waveform.

  • function (Union[str, Callable]) –

    A callable function which takes a numpy.ndarray of x values as input and returns a corresponding numpy.ndarray of y values. The y-values should be voltages in V, and should lie between -3.3 V and 3.3 V. Values outside this range will be clipped.

    Alternatively, ‘function’ can be a string literal ‘sine’ or ‘tria’, for a sine wave or triangle wave with amplitude 3.3 V.

  • span (List[float], optional) – The minimum and maximum x values between which to evaluate ‘function’. Should typically correspond to one period. Omit if ‘function’ is ‘sine’ or ‘tria’.

load_table(channel: str, points: numpy.ndarray)[source]

Load a custom waveform as a table.

Parameters
  • channel ({'SI1', 'SI2'}) – The output pin on which to generate the waveform.

  • points (np.ndarray) – Array of voltage values which make up the waveform. Array length must be 512. Values outside the range -3.3 V to 3.3 V will be clipped.