Programming interface

Overview

This section defines terminology used in this documentation and provides an overview of how signals can be generated with the Pulse Streamer API.

Pulse pattern

The pulse pattern is a sequence of levels, defining the signal to generate. It is defined as an array of (duration, level) tuples, in other words, using Run-Length Encoding (RLE). In contrast to defining pulse patterns as an array of values with equal time durations, the RLE encoded pattern is more memory efficient, especially for patterns that consist of levels of both short and long durations.

The duration is always specified in nanoseconds. The level is either 0 or 1 for digital output or a real number between -1 and +1 in Volt for analog outputs. See the Hardware section for more details on the electrical properties of the generated signals.

../_images/pulsepattern.png

The following code shows how to define a pulse pattern similar to the one shown in the image above. In addition, it shows an example of an analog pattern definition.

pulse_patt = [(100, 0), (200, 1), (80, 0), (300, 1), (60, 0)]
analog_patt = [( 50, 0), (100, 0.5), (200, 0.3), (50, -0.1), (10, 0)]

Creating sequences

Before a pattern can be sent for streaming to the Pulse Streamer outputs, they have to be mapped to the output channels. All these steps are performed with the Sequence object, which is created with PulseStreamer.createSequence(). The digital and analog channel assignment is done with the setDigital() and setAnalog() methods, respectively.

../_images/sequence-pattern-assignment.png
from pulsestreamer import PulseStreamer

ps = PulseStreamer('pulsestreamer')

seq = ps.createSequence()
seq.setDigital(0, pulse_patt)
seq.setDigital(2, pulse_patt)
seq.setAnalog(0, analog_patt)

Sequence transformation

Sequence transformation methods enable the creation of complex sequences from simpler sub-sequences. The sequence data can be repeated or combined with another sequence. These operations, while inherently simple, have a few edge cases that are important to know. Concatenation and repetition operations are non-destructive. In other words, they preserve original sequence objects (immutability). The result is stored in a newly created sequence object. Internally, the sequence stores a map of the channel number and the pattern data with the pattern data unmodified. In general, this results in a sequence that consists of patterns having different durations. On concatenation or repetitions, however, it is intuitively expected that a sequence is treated as a solid unit with every pattern of the same duration. Therefore, before concatenating the sequence data, the pattern durations are padded to the common duration.

When two sub-sequences being concatenated have a different set of mapped channels, the resulting sequence will include them all. This is explained with the following example. Let’s assume we have two sequences, seq1 and seq2, which we want to concatenate. The seq1 has patterns mapped to channels (0,2), and seq2 has channels (0,1), as shown in the code below.

seq1 = ps.createSequence()
seq1.setDigital(0, patt1)
seq1.setDigital(2, patt2)

seq2 = ps.createSequence()
seq2.setDigital(0, patt3)
seq2.setDigital(1, patt4)

seq3 = seq1 + seq2   # concatenation

During the concatenation, the channel lists of the two sequences are compared and the output sequence seq3 will include them all (0,1,2). As a first step, a new sequence object seq3 will be created as a copy of seq1, and an empty pattern will be assigned to the channel 1. Next, all patterns in seq3 will be padded to the duration of the longest one, which is essentially the sequence duration. Finally, the pattern data from seq2 will be appended to the corresponding patterns of the seq3.

The duration padding is always performed with the value of the last element in the pattern. When there is no previous element, the default value is used. The repetition process behaves similarly and can be qualitatively understood as multiple concatenations of the object with itself.

Streaming

Now, any of the sequence objects created before can be sent for streaming by calling the PulseStreamer.stream() method, as shown in the following example for the sequence seq.

ps.stream(seq)
../_images/sequence-data.png

On streaming, the sequence object is converted to a hardware-specific run-length encoded data block, which can be understood as an array of Sequence steps. Every step defines the state of all channels and the duration to hold the state. Sequences support a number of useful methods, like repetition, concatenation, preview plotting, etc. With this basic set of methods, complex sequences can be built from smaller and simpler sub-sequences.

Note

Internally, the Pulse Streamer hardware is always splitting the sequence data into 8 nanosecond long chunks. When a sequence is shorter than 8 ns or its length is not an exact multiple of 8 ns the extra time will be padded to complete the last chunk. You can observe the effects of such padding if you try to stream a short pulse repetitively.

Example 1. Your sequence consists of 3 ns high-level and 2 ns low-level and you stream it with infinite repetitions, the resulting signal will have 3 ns high-level but 5 ns low-level. Therefore, the actual pulse frequency will be 125 MHz instead of 200 MHz. For continuous periodic signals, you can solve this problem by creating a sequence of repetitive pulses that has a duration which is multiple of 8 ns. One easy way to guarantee that sequence duration is a multiple of 8 ns is to repeat it 8 times using Sequence.repeat() method, which will repeat the sequence data in PC memory before sending it to the Pulse Streamer hardware.

Example 2. You want to stream a sequence that is 12345 ns long and you want to repeat it infinitely by setting n_runs=-1. Since this sequence duration is not a multiple of 8 ns (12345 ns / 8 ns = 1543.125) the Pulse Streamer will allocate 1544 chunks, and the actual sequence duration will be 1544 * 8ns = 12352 ns, or 7 ns longer.

Sequence step

The sequence step is the smallest element of a sequence that contains information on the state of every output of the Pulse Streamer and the duration to hold this state. The image below explains the relation between Sequence step, Sequence, and OutputState objects.

../_images/sequence-and-sequence-step.png

Warning

In a typical use of the client API, the user does not have to care about how to create or operate on the sequence data directly. All necessary functionality is enclosed within the API presented in this article. The description of the Sequence data corresponds to the RAW data as it is required by the hardware. The internal API data structures are implemented slightly differently for each programming language, aiming at the optimization of the client performance. Furthermore, the RAW sequence data format is hardware-dependent and future Pulse Streamer models are likely to use a different format. See also: Low-level RPC interface.

Module level functions

findPulseStreamers(search_serial='')
Parameters

search_serial (str) – Pulse Streamer serial number as a string.

Returns

List of DeviceInfo objects.

This function searches and returns basic information about discovered Pulse Streamers. If non-empty search_serial string is provided, then information is returned only for a specific Pulse Streamer unit.

The returned value is a list of DeviceInfo objects containing the IP address and basic information.

class DeviceInfo

This class contains read-only information about the discovered Pulse Streamer.

Property name

Example data

Description

ip

“192.168.0.2”

Device IP address

serial

“00:26:32:f0:3b:1b”

Device serial number

hostname

“pulsestreamer”

Pulse Streamer hostname

model

“Pulse Streamer 8/2”

Pulse Streamer model name

fpgaid

“123456789ABCD”

FPGA ID number

firmware

“1.2.0”

Firmware version

hardware

“1.3”

Hardware version

The discovery algorithm sends Pulse Streamer specific query packets overall available and active network interfaces and listens for responses from the connected Pulse Streamers.

Note

The findPulseStreamers() is capable of finding the devices and reporting their IP addresses even in the networks without dynamic IP assignment by a DHCP server or an improper IP address configuration. Therefore, it might happen that the reported Pulse Streamer IP is not accessible from your network. For example, when the reported IP is 169.254.8.2 (static fallback) and your PC is configured as 192.168.1.2, you will not be able to connect to the Pulse Streamer. This is because of the way how IP networks operate. However, you will still be able to discover this Pulse Streamer and learn its IP, which is very helpful for identifying network connection problems.

PulseStreamer

The PulseStreamer class is a wrapper for the RPC interface provided by the Pulse Streamer hardware. It handles the connection to the hardware and exposes all available methods. This class is implemented in various supported programming languages with consistently named methods. However, in some languages, additional functionality common to that language is also implemented, like callback functions in MATLAB.

class PulseStreamer
PulseStreamer(ip)

The class constructor accepts a single string argument, which can be either the IP address or a hostname by which the Pulse Streamer can be reached in the network. The constructor fails when the ip has an incorrect value or the device is not reachable. The Pulse Streamer hardware has a static fallback address “169.254.8.2”. This allows operation when the Pulse Streamer is directly connected to a PC network card and without requiring any additional configuration.

Parameters

ip (str) – IP address or hostname of the Pulse Streamer.

reset()

Reset the Pulse Streamer device to the default state. All outputs are set to 0V, and all functional configurations are set to default. The automatic rearm functionality is enabled, and the clock source is the internal clock of the device. No specific trigger functionality is enabled, which means that each sequence is streamed immediately when its upload is completed.

reboot()

Perform a soft reboot of the device without power-cycling.

createSequence()

Use this method to create a new hardware-specific Sequence object. A hardware-specific sequence object has the same functionality as Sequence and implements early checks for hardware limits. For example, an attempt to assign a pattern to a non-existing channel or setting analog voltage outside the DAC range will result in an error. The generic Sequence object’s normal behavior is to check hardware limits only on the call to PulseStreamer.stream() method.

Returns

Hardware-specific Sequence object.

Setting constant output state

PulseStreamer.constant(state=OutputState.ZERO)

Set the outputs to a constant state. Calling the method without a parameter will result in the default output state with all output set to 0V. If you set the device to a constant output, an eventually currently streamed sequence is stopped. It is not possible to retrigger the last streamed sequence after setting the Pulse Streamer constant. OutputState.ZERO is a constant equal to OutputState([],0,0).

Alternatively, the state parameter can be specified as a tuple consisting of three elements ([],0,0).

ps.constant(OutputState([1, 2, 5], 0, 0))
# or
ps.constant(([1, 2, 5], 0, 0))
Parameters

state (OutputState) – OutputState object that defines the state of outputs or a tuple.

Running pulse sequences

This section describes methods that allow to run sequences and configure trigger properties. You can repeat a pulse sequence infinitely or an integer number of times. If you want to stop a running sequence and force it to the final, specified in the function call, you can do this by calling the method forceFinal().

PulseStreamer.stream(sequence[, n_runs=PulseStreamer.REPEAT_INFINITELY[, final=OutputState.ZERO]])

Sends the sequence to the Pulse Streamer. After the sequence has been repeated the given n_runs, the constant state final will be reached. All parameters except sequence have default values and can be omitted. By default, the sequence is started immediately. Alternatively, you can select when the output of the sequence starts with setTrigger() to a software-based trigger started with startNow() or to an external hardware trigger.

If the sequence is empty, then the final state will set immediately.

The sequence parameter of the stream() method also accepts an RLE sequence defined as a list of 4 element tuples of the following format: (duration_ns, [channels_to_set_HIGH], analogV_0, analogV_1)

ps.stream([(100, [1, 2], 0, 0), (10, [2], 0, 0), (5, [], 0, 0)])
Parameters
  • sequence (Sequence) – Sequence object or a list of tuples.

  • n_runs (int) – Number of times to repeat the sequence. Infinite repetitions if n_runs<0. There is also a symbolic constant REPEAT_INFINITELY=-1

  • final (OutputState) – OutputState object, which defines the constant output after the sequence has finished.

PulseStreamer.setTrigger(start[, rearm=TriggerRearm.AUTO])

Defines how the uploaded sequence is triggered.

If you want to trigger the Pulse Streamer by using the external trigger input of the device, you have to set the start parameter to one of the following values: start=TriggerStart.HARDWARE_RISING (rising edge on the trigger input), start=TriggerStart.HARDWARE_FALLING (falling edge on the trigger input) or start=TriggerStart.HARDWARE_RISING_AND_FALLING (both edges are active).

If the automatic rearm functionality is enabled (rearm=TriggerRearm.AUTO), which is the default power-on state, you can re-trigger a sequence which is finished, by providing an appropriate trigger signal, depending on start argument. You can disable the automatic rearm by passing rearm=TriggerRearm.MANUAL.

If the automatic rearm functionality is disabled, you can manually rearm the Pulse Streamer by calling the method rearm()

Parameters
  • start (TriggerStart) – Defines the source of the trigger signal

  • rearm (TriggerRearm) – Enables or disables trigger automatic rearm.

PulseStreamer.getTriggerStart()

Queries the hardware for the currently active trigger start condition.

Returns

Returns TriggerStart value.

PulseStreamer.getTriggerRearm()

Queries the hardware for the currently active rearming method.

Returns

Returns TriggerRearm value.

PulseStreamer.startNow()

Starts streaming the sequence present in the Pulse Streamer’s memory. The behavior of this method depends on the trigger configuration performed with setTrigger() method.

If the start is TriggerStart.IMMEDIATE, n_runs>=0, and the sequence has finished, then startNow() will trigger the sequence again.

If the start is TriggerStart.SOFTWARE then the sequence is started every time the startNow() is called. In case of the rearm=TriggerRearm.MANUAL the method startNow() will trigger the sequence only once. Call rearm() to manually rearm the trigger.

If the start is set to one of the hardware sources, then this method does nothing.

PulseStreamer.rearm()

Rearm the trigger in case the Pulse Streamer has reached the final state of the current sequence and the trigger rearm method was set to TriggerRearm.MANUAL. Returns True on success.

Returns

bool True or False

PulseStreamer.forceFinal()

Interrupts the sequence and sets the final state. This method does not modify the output state if the sequence has already finished and the Pulse Streamer is in the final state.

In case the Pulse Streamer is streaming a sequence and has not reached the final state, the upload speed of a new sequence with stream() is reduced by about 20%. This can be avoided by calling forceFinal() before uploading the sequence.

If no final state was declared in the current sequence, the output of Pulse Streamer will change to (or stay in) the last known constant state.

The recommended way to stop the Pulse Streamer streaming is to set its output to a constant value via the method constant(), described above.

PulseStreamer.setCallbackFinished(callback_func) (MATLAB only)

Allows to set up a callback function, which will be called after the sequence streaming has finished. The callback function will be called with the following signature callbackFunction(pulseStreamer_obj). An example of such a function is shown below.

function callbackFunction(pulseStreamer)
    % this is an example of a callback function

    disp('hasFinishedCallback - Pulse Streamer finished.');

end

Requesting the streaming state

The following methods allow you to request whether the Pulse Streamer has a sequence in memory, whether a sequence is currently being streamed or if it has already finished.

PulseStreamer.hasSequence()

Returns True if the Pulse Streamer memory contains a sequence.

Returns

True or False.

PulseStreamer.isStreaming()

Returns True if the Pulse Streamer is currently streaming a sequence. When the sequence has finished and the device remains in the final state, this method returns False again.

Returns

True or False.

PulseStreamer.hasFinished()

Returns True if the Pulse Streamer remains in the final state after having finished a sequence.

Returns

True or False.

Using an external clock

The Pulse Streamer can be clocked from three different clock sources. By default, the internal clock of the device is used. It is also possible use an external clock of 125MHz (sampling clock) or an external 10MHz reference signal. You can choose the clock source via selectClock(). For more information on the required electrical parameters of an external clock signal, please see the section Hardware.

PulseStreamer.selectClock(source)

Set the hardware clock source.

Parameters

source (ClockSource) – Specifies the clock source for the Pulse Streamer hardware.

PulseStreamer.getClock()

Returns a ClockSource element with the clock source currently used by the Pulse Streamer.

Returns

ClockSource Current clock source

Also, you can apply a continuous square wave of 125MHz to the dedicated Pulse Streamer output channels as an external clock signal for other devices to be synchronized with the Pulse Streamer.

PulseStreamer.setSquareWave125MHz(channels=[])

Set a persistent square wave with a frequency of 125 MHz to the selected digital outputs. The 125 MHz will remain and will not be affected by any other settings applied to this channel unless the corresponding channel is deselected via setSquareWave125MHz() or the method reset() is called. A call to this method without a parameter will disable the 125 MHz signal on all channels it was enabled before.

ps.setSquareWave125MHz(channels=[1, 2, 5])
Parameters

channels (list) – defines to which channels the 125 MHz square wave should be applied.

Hardware identification

PulseStreamer.getSerial()
Returns

String containing the serial number which is the same as MAC address of the Ethernet interface.

PulseStreamer.getFPGAID()
Returns

String containing FPGA ID number.

PulseStreamer.getFirmwareVersion()
Returns

String containing the firmware version number of the connected Pulse Streamer.

PulseStreamer.getHardwareVersion()
Returns

String containing the hardware revision number of the connected Pulse Streamer.

PulseStreamer.setHostname(hostname)
Parameters

hostname (str) – Hostname string to set for the connected Pulse Streamer.

Set hostname of the connected Pulse Streamer.

Note

Depending on your network environment, this setting may not affect how your Pulse Streamer is identified in the network. However, the stored hostname will be returned when you call getHostname().

PulseStreamer.getHostname()
Returns

String containing the hostname of the connected Pulse Streamer.

Calibrating the analog outputs

Pulse Streamer 8/2 devices shipped with firmware version v1.3.0 or later (published in July 2020) already include calibration data for analog outputs, and no further user action is required. Devices shipped with older firmware require analog output calibration in order to achieve specified accuracy, see Hardware. You can perform this calibration yourself by following the steps described below. The calibration requires a sufficiently accurate multimeter (not an oscilloscope) connected to the analog outputs (can be done one channel at a time).

Calibration procedure

  1. Connect the multimeter to the analog output of the Pulse Streamer. The measurement has to be performed at 50 Ohm load, so you will need to attach 50 Ohm termination.

  2. Using the Pulse Streamer API, set the analog output to several values and record multimeter readings. This has to be done at least for -0.9 and +0.9 output values.

  3. Calculate the slope

    slope = \frac{voltage_{+0.9V} - voltage_{-0.9V}}{1.8}

  4. Calculate the offset

    offset = voltage_{+0.9V} - slope*0.9

  5. Perform the steps 1 to 4 for each analog output.

  6. Call the setAnalogCalibration() and specify both offsets and slopes.

  7. Reboot the Pulse Streamer to apply the new calibration data (from firmware v1.5.0 on, the device is rebooted automatically).

Warning

If you perform repeated calibration, you have to reset the slope and offset to values 1 and 0, respectively. Failing to do so will lead to invalid calibration data.

PulseStreamer.setAnalogCalibration(dc_offset_a0=0, dc_offset_a1=0, slope_a0=1, slope_a1=1)

Sends the DC-offset and slope of each analog channel to the Pulse Streamer and stores it to internal memory. These values will be applied after reboot. With firmware version v1.5.0 or later, this is done automatically. If you use a former firmware of the Pulse Streamer 8/2, you will have to power cycle your device.

Parameters
  • dc_offset_a0 – The DC offset of analog channel 0

  • dc_offset_a1 – The DC offset of analog channel 1

  • slope_a0 – The gradient of the transfer function of analog channel 0

  • slope_a1 – The gradient of the transfer function of analog channel 1

If you need help during the calibration procedure, please contact support@swabianinstruments.com.

PulseStreamer.getAnalogCalibration()

Returns the stored calibration values of your Pulse Streamer. These values will be rounded according to the DAC resolution. If you call this method immediately after setAnalogCalibration(), the returned data will not reflect the actual calibration state.

Returns

structure of the four calibration values rounded to the DAC resolution.

Modify the network configuration

By default, the Pulse Streamer 8/2 will attempt to acquire an IP address via DHCP. If you want to assign a specific IP address to your Pulse Streamer, you can disable DHCP and configure a static IP instead. We recommend using our graphical user interface (Windows only) to modify the Pulse Streamer 8/2 network configuration. For more information, please have a look at Network Connection. Alternatively, the following methods allow you to configure the device’s network settings via our client software interfaces.

PulseStreamer.setNetworkConfiguration(dhcp, ip='', netmask='', gateway='', testmode=True)

Enables DHCP or sets static IP address, Netmask and Standard Gateway. If testmode=True, the configuration is applied temporarily. Power-cycling will restore the former configuration. If testmode=False, the configuration will be applied permanently and the device is rebooted.

Parameters
  • dhcp (bool) – DHCP enable/disable True/False

  • ip (str) – static IP address (If dhcp=True, this value is ignored)

  • netmask (str) – Netmask for static IP address configuration (If dhcp=True, this value is ignored)

  • gateway (str) – Standard gateway for static IP address configuration (If dhcp=True, this value is ignored)

  • testmode (bool) – If True, the configuration is applied temporarily. Power-cycling will restore the former configuration. If False, the configuration will be applied permanently and the device is rebooted.

PulseStreamer.getNetworkConfiguration(permanent=False)
Parameters

permanent (bool) – If True, the method returns network settings stored in the device’s configuration file. If False, the method returns the current network settings of the device.

Returns

structure of the current or stored network settings

PulseStreamer.applyNetworkConfiguration()

Applies current (and successfully tested) network configuration permanently and the device is rebooted.

Enumerations

class ClockSource(enumeration)

This enumeration describes the selectable clock sources of the Pulse Streamer

INTERNAL

Use internal clock generator (default)

EXT_125MHZ

Use external 125 MHz clock signal

EXT_10MHZ

Derive clock from external 10 MHz reference signal

class TriggerStart(enumeration)

This enumeration describes the selectable start modes of the Pulse Streamer

IMMEDIATE

Trigger immediately after a sequence is uploaded. (default)

SOFTWARE

Trigger by calling startNow() method.

HARDWARE_RISING

External trigger on the rising edge.

HARDWARE_FALLING

External trigger on the falling edge.

HARDWARE_RISING_AND_FALLING

External trigger on rising and falling edges.

class TriggerRearm(enumeration)

This enumeration describes the rearm functionality of the Pulse Streamer

AUTO

Trigger immediately after a sequence is uploaded. (default)

MANUAL

Trigger once only and do not rearm automatically. Rearm manually via the rearm() method.

Sequence

The Sequence contains information about the patterns and channel assignments. It also handles the mapping of patterns (see Pulse pattern) to output channels and has a number of built-in methods that operate on the whole sequence, like concatenation, repetitions, visualization, etc.

Warning

While the same pattern can be mapped to one or more channels, successive mappings to the same channel will replace the previous mapping.

class Sequence
Sequence()

Class constructor. The Sequence is a generic class without early hardware limit checks. Since this class is not aware of hardware-specific limitations, like available channels or analog range, the validation will be performed only during an attempt to stream this Sequence object.

Note

If you want to have early limit checks, channel number validation, please create a hardware-specific Sequence object with PulseStreamer.createSequence() method. This, however, requires an active connection to the hardware.

setDigital(channels, pattern)

Assign a pattern to a digital output. The same pattern can be assigned to one or more channels simultaneously.

sequence.setDigital(0, patt1)
sequence.setDigital([1,2,6], patt2)
Parameters
  • channels (list[int]) – Digital channel number(s) as labeled on the Pulse Streamer connectors panel.

  • pattern (list) – A pattern to be assigned to the channel.

invertDigital(channels)

Inverts level values in the stored pattern for the specified channel.

sequence.setDigital(1, [(10, 0), (20, 1), (80, 0)])
sequence.invertDigital(1)
# is equivalent to
sequence.setDigital(1, [(10, 1), (20, 0), (80, 1)])
Parameters

channel (int) – Digital channel number.

setAnalog(channels, pattern)

Assign a pattern to an analog output. The same pattern can be assigned to one or more channels simultaneously.

sequence.setAnalog([0,1], patt2)
Parameters
  • channels (list[int]) – Analog channel number(s) as labeled on the Pulse Streamer connectors panel.

  • pattern (list) – A pattern to be assigned to the channel.

invertAnalog(channel)

Inverts level values in the pattern for the specified channel.

sequence.setAnalog(0, [(100, -0.1), (200, 0), (800,  0.5)])
sequence.invertAnalog(0)
# is equivalent to
sequence.setAnalog(0, [(100,  0.1), (200, 0), (800, -0.5)])
Parameters

channel (int) – Analog channel number.

Properties

Sequence.isEmpty()
Sequence.isempty() (in MATLAB)

Returns True if the sequence is empty.

Sequence.getDuration()

Returns sequence duration in nanoseconds.

Sequence.getLastState()

Returns the last state in the sequence as an OutputState object.

Returns

OutputState Last state of the sequence.

Sequence.getData()

Returns the run-length encoded (RLE) sequence data. This method is called automatically by the PulseStreamer.stream() method.

Returns

Run-length encoded data.

Transformation

static Sequence.repeat(sequence, n_times)

Returns the sequence data duplicated n_times. The sequence data in the original object remains unmodified. In case the Sequence object consists of patterns with different durations, they will be padded to the longest one, which defines sequence duration as returned by Sequence.getDuration() method. In this context, a Sequence shall be understood as a set of patterns all of the same duration.

In Python, you can repeat sequences similarly to lists by multiplying it by a number.

# The following three lines are fully equivalent
seq1 = Sequence.repeat(seq, 5)
seq1 = seq * 5
seq1 = 5 * seq
Parameters
  • sequence (Sequence) – Sequence object to be repeated.

  • n_times (int) – Number of times the sequence is repeated.

Returns

Returns a new Sequence object with data duplicated n_times.

static Sequence.concatenate(sequence1, sequence2)

Creates a new Sequence object with a sequence of sequence2 object appended at the end of this sequence. Both object, sequence1 and sequence2, remain unmodified. In case the sequence1 sequence consists of patterns with different durations, they will be padded to the longest one, which defines sequence duration as returned by Sequence.getDuration() method. In this context, a Sequence shall be understood as a set of patterns all of the same duration.

This method is exposed directly in LabVIEW. However, in MATLAB and Python, it is exposed only as the override of the concatenation operator. This allows for transparent use of any function of these languages that implicitly use concatenation. See the example code for each language.

seq3 = seq1 + seq2
Returns

Returns a new Sequence object with concatenated data.

Visualization

Sequence.plotDigital()

Plots the sequence data for digital outputs. Plotting is done into the current axes. (Only in MATLAB and LabView)

Sequence.plotAnalog()

Plots sequence data for analog outputs. Plotting is done into the current axes. (Only in MATLAB and LabView)

Sequence.plot()

Calls plotDigital() and plotAnalog() and shows the results as subplots in a single figure. Plotting is done in the current figure.

An example of the plot() output is shown in the image below.

../_images/example-sequence-plot.png

OutputState

The OutputState is a simple value class that contains information on the state of every output of the Pulse Streamer.

class OutputState
OutputState(channels, A0, A1)

Class constructor. Input parameters specify the state of each output of the Pulse Streamer. Digital and analog output values are specified differently. In order to set a HIGH level at the digital channel, add the channel number to the channels list, for example channels=[0,2,3] will set HIGH level on the channels 0, 2 and 3. All other digital channels will be set to LOW. Output values at each of two analog outputs are specified with corresponding parameters A0 and A1.

Parameters
  • channels (list) – List of digital channels to be set HIGH.

  • A0 (float) – Analog output 0 voltage in Volt.

  • A1 (float) – Analog output 1 voltage in Volt.

ZERO

This is a helper constant equal to OutputState([], 0, 0).

Advanced (Beta) features

Synchronized Pulse Streamer (Python only)

With our programming examples in Python, we provide the class SyncPulseStreamer as a wrapper class for the client interface of the Pulse Streamer 8/2. It combines two Pulse Streamer 8/2 (requires firmware version v1.4.0 or later) and is currently only available for the Python client.

Synchronization concept and setup

One Pulse Streamer master generates the clock signal and trigger for one Pulse Streamer slave. The only necessary preparation is that digital channel 6 of the master must be connected to the external clock input of the slave as well as digital channel 7 of the master must be connected to the trigger input of the slave. To avoid race conditions between the trigger and trigger-sampling clock-edge, we recommend using cables of equal length.

The features of the resulting SyncPulseStreamer object can be described as follows:

  • 14 digital channels (6 of the master, 8 of the slave)

  • 4 analog channels (2 in each case master/slave)

  • The slave is delayed by a constant time offset of ~70ns (internal + cable)

  • Increased ({\sim}x*\sqrt{2}) RMS jitter of the 8 digital channels of the slave (< 75 ps)

  • In this configuration, devices with hardware Version 2.x (devices before 2021) have a 100mV ripple on the digital channels of the slave due to the external clock signal. The analog outputs are not affected.

Usage and sequence generation

The Pulse Streamer synchronization wrapper offers the same API-structure as the original Pulse Streamer client if possible. For a detailed description of the Pulse Streamer 8/2 API, please have a look at the Programming interface section and the provided Python example.

When it comes to sequence generation, unlike the original method createSequence(), the equivalent method of the sync-wrapper returns two sequence objects, one for the Pulse Streamer master and one for the Pulse Streamer slave. You can set the digital and analog channels of both sequences independently. Setting Channel 6 (clock signal) of the master will be ignored, and channel 7 (trigger) of the master will be overwritten by the stream method).

If you want to use a parameter n_runs>1 with the stream() function, you should ensure that the two sequences are of equal duration. Therefore the stream() prints a warning message if the duration of the two sequences differs.

To compensate for the delay of the Pulse Streamer slave, you can add an empty pulse as a first pulse to each channel of the master sequence, as it is shown in the Python code example. In that case, you have to take into account that if you want to use n_runs>1, you have to subtract the delay from the last pulses of the master-sequence, or you can accordingly pad the slave-sequence. The drawback is that this either requires the same state at the beginning and the end of the channel pattern, or you have to deal with the padding. Furthermore, you have to take into account that when a sequence is given to the method stream() of the Pulse Streamer or to the transformation-methods (see Transformation) of the Sequence class, all channels are padded with the last value to the longest channel duration. For further information, please have a look at the sections Creating sequences and Sequence.

Just to give you the complete information of the padding issue: Due to the internal design of the Pulse Streamer, the device itself will pad the last sequence step to a duration that the sequence duration as a whole is a multiple of 8ns (which means a prolongation between 0 and 7ns). This step is executed before the parameter n_runs is applied (see Streaming).

Further Limitations

  1. If you use the synchronized Pulse Streamer with a fast external trigger, it is possible that the Pulse Streamer master is ready for retriggering and the slave is not and vice versa. Therefore, you should always poll the method hasFinished() before retriggering the Pulse Streamer with the external trigger.

  2. If you stream a second pulse pattern into an already running sequence, you previously should set the Pulse Streamer to a constant state by using the methods constant() or reset(). Otherwise, the new sequence of the Pulse Streamer slave might already be triggered by the still running stream of the Pulse Streamer master.