Programming interface

Overview

This section defines terminology used in this documentation and provides overview of how signals are defined with the Pulse Streamer API.

Pulse pattern

The pulse pattern is a sequence of levels that make up the signal to be produced. 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 and 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 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)]
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};
../_images/example-pattern-1.vi.png

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)
import PulseStreamer.PulseStreamer;

ps = PulseStreamer('pulsestreamer');

seq = ps.createSequence();
seq.setDigital(0, pulse_patt);
seq.setDigital(2, pulse_patt);
seq.setAnalog(0, analog_patt);
../_images/example-creating-sequences.vi.png

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
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
../_images/example-seq-transformation.vi.png

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 a 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)
ps.stream(seq);
../_images/example-PulseStreamer.stream.vi.png
../_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.

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 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.

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 incorrect value or 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, 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.

createSequence()

Use this method to create new hardware specific Sequence object. A hardware specific sequence object has exactly 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. While normal behavior of generic Sequence object 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 re-trigger 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))
ps.constant(OutputState([1, 2, 5], 0, 0));
% or
ps.constant({[1, 2, 5], 0, 0});
../_images/example-PulseStreamer.constant.vi.png
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:

ps.stream([(100, [1, 2], 0, 0), (10, [2], 0, 0), (5, [], 0, 0)])
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=HARDWARE_RISING (rising edge on the trigger input), HARDWARE_FALLING (falling edge on the trigger input) or HARDWARE_RISING_AND_FALLING (both edges are active).

If the automatic rearm functionality is enabled (rearm=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=MANUAL.

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

TriggerStart (enumeration)
  • 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 falling edge.

  • HARDWARE_RISING_AND_FALLING: External trigger on rising and falling edges.

TriggerRearm (enumeration)
  • AUTO: Trigger is rearmed automatically. (default)

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

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

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

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 IMMEDIATE, n_runs>=0 and the sequence has finished then startNow() will trigger the sequence again.

If the start is 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 clock source is the internal clock of the device. It is also possible to feed in the system by 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.

ClockSource (enumeration)
  • INTERNAL: Use internal clock generator (default)

  • EXT_125MHZ: Use external 125 MHz clock signal

  • EXT_10MHZ: Derive clock from external 10 MHz reference signal

Parameters

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

Hardware identification

PulseStreamer.getSerial()
Returns

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

PulseStreamer.getFirmwareVersion()
Returns

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

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, however successive mappings to the same channel will simply 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 (int_array) – Digital channel number(s) as labelled on the Pulse Streamer connectors panel.

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

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 (int_array) – Analog channel number(s) as labelled on the Pulse Streamer connectors panel.

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

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 a OuputState 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
seq1 = repeat(seq, 5)
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, this object sequence1 and sequence2 objects 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 concatenation operator. This allows for transparent use of any function of these languages that implicitly uses concatenation. See the example code for each language.

seq3 = seq1 + seq2
seq3 = [seq1, seq2];
../_images/example-seq-concatenate-1.vi.png
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).