Low-level LabOne API Commands

This section describes the API commands getSample(), subscribe and poll(). For continuous data acquisition, however, it is recommended to use the Data Acquisition Module in continuous mode.

The getSample command

For one-shot measurement demodulator data

The simplest function to obtain demodulator data is the getSample command. It returns a single sample from one demodulator channel, i.e., it returns the sample fields (not only the demodulator outputs X and Y) described in Demodulator Sample Data Structure at one timestamp. The getSample function returns the last sample for the specified demodulator that the Data Server has received from the instrument.

Please note, the getSample function only works with the demodulator data type. It does not work with other data types such as impedance or auxiliary input samples. For non-demodulator sample types the recommended way to get data is via the subscribe and poll commands.

The getSample command raises a ZIAPITimeoutException if no demodulator data is received from the device within 5 seconds. This is the case if the requested demodulator is not enabled. As getSample only returns data from a single demodulator, wildcard path specification (e.g., /devn/demods/*/sample) is not supported.

If multiple samples (even from one demodulator channel) are required, it is recommended to use either subscribe and poll (for high performance API applications) or Data Acquisition Module. Using getSample in anything other than low-speed loops data is not recommended.

The subscribe and poll commands

For high-performance continuous or block streaming data

The subscribe and poll functions are low-level commands that allow high-speed data transfer from the instrument to the API. The idea is as follows: The user may subscribe to one or more nodes in the API session by calling subscribe for each node. This tells the Data Server to create a buffer for each subscribed node and to start accumulating the data corresponding to the node that is streamed from the instrument (or instruments, as the case may be). The user can then call poll (in the same API session) to transfer the data from the Data Server’s buffers to the API’s client code. If poll is not called within 5 seconds of either subscribing, the last call to poll or calling the sync command (more information on sync below), the Data Server clears its buffers for the subscribed nodes and starts accumulating data again. This means that for continuous transfer of data, the user must regularly poll data from the Data Server to ensure that no data is lost in between polls.

Simple Example

For example, the following Python code subscribes to the first and fifth demodulator sample streaming nodes on a lock-in amplifier:

import zhinst.core

daq = zhinst.core.ziDAQServer('localhost', 8004, 6)
# Enable the demodulator output and set the transfer rate.
# This ensure the device actually pushes data to the Data Server.
daq.setInt('/dev2006/demods/0/enable', 1)
daq.setInt('/dev2006/demods/4/enable', 1)
# This value will be corrected to the nearest legal value by the instrument's FW.
daq.setDouble('/dev2006/demods/0/rate', 10e3)
daq.setDouble('/dev2006/demods/4/rate', 10e3)
daq.subscribe('/dev2006/demods/0/sample')
daq.subscribe('/dev2006/demods/4/sample')
time.sleep(1)  # Subscribed data is being accumulated by the Data Server.
data = daq.poll(0.020, 10, 0, True)
data.keys()
# Out: dict_keys(['/dev2006/demods/0/sample', '/dev2006/demods/4/sample'])
len(data['/dev2006/demods/0/sample']['timestamp'])
# Out: 13824
len(data['/dev2006/demods/4/sample']['timestamp'])
# Out: 13746

The subscribe and poll commands return data from streaming nodes as sent from the instrument’s firmware at the configured data rate. The data returned is the discrete device data as sampled or calculated by the digital signal processing algorithms on the instrument. As explained in Section Streaming Nodes the data from multiple nodes may not be aligned due to different sampling rates or different streaming node sources.

When you call subscribe on a node the Data Server starts accumulating data in a dedicated buffer (for that node and the API session from which subscribe was called). When the buffer becomes full, then the data is discarded by the Data Server (poll must be called frequently enough to ensure that data is not lost in between multiple poll calls). When you call poll, then all the data that has accumulated in the Data Server’s buffers (of subscribed nodes) is returned (it is transferred to the API client) and is deleted from the Data Server’s buffers. The Data Server continues to accumulate new data sent from the device for the subscribed nodes after polling and will continue to do so until unsubscribe is called for that node. It is good practice to call unsubscribe on the nodes when you no longer want to poll data from them so that the Data Server stops accumulating data and can free up the system’s memory.

If you would like to continue recording new data for a subscribed node, but discard older data, then either poll can be called (and the returned older data simply discarded) or the sync command can be used. Sync clears the Data Server’s buffers of subscribed data but has an additional function. Additionally, the sync command blocks (it is a synchronous command) until all set commands have been sent to the device and have taken effect. E.g., if you enable the instrument’s signal output and want to ensure that you only receive data from poll after the setting has taken effect the device, then sync should be called after calling set and before calling poll.

The first two mandatory parameters to poll are the "poll duration" and the "poll timeout" parameters: Poll is also a synchronous command and will block for the specified poll duration. It will return the data accumulated during the time in seconds specified by the poll duration, and as mentioned above, it will also return the data previously accumulated data before calling poll. If you would like to poll data continuously in a loop, then a very small poll duration should be used, e.g., 0.05 seconds, so that it only blocks for this time. The poll timeout parameter typically must not be set very carefully and a value of 100 ms is sufficient. It is indeed only relevant if set to a larger value than poll duration. In this case, if no data arrives from the instrument, poll will wait for poll timeout milliseconds before returning. If data does arrive after poll duration but before the poll timeout, poll returns immediately. It is recommended to simply use a poll timeout of 100 ms (or if poll_duration is smaller, to set it equal to the poll_duration). Unfortunately, the units of poll duration and poll timeout differ: The poll duration is in seconds, whereas poll timeout is in milliseconds.

Ensure synchronization of settings before streaming data (sync)

To ensure that any settings have taken effect on the instrument before streaming data is processed, a special sync command is provided which ensures that the API blocks during the full command execution of sending down a marker to the device and receiving it again over the API. During that time all buffers are cleaned. Therefore, after the sync command the newly recorded poll data will be later in time than the previous set commands. Be aware that this command is quite expensive ~100ms.

Asking poll to always return a value for a settings node (getAsEvent)

The poll command only returns value changes for subscribed nodes; no data will be returned for the node if it has not changed since subscribing to it. If poll should also return the value of a node that has not changed since subscribing, then getAsEvent may be used instead of or in addition to subscribe. This ensures that a settings node value is always pushed.

daq = zhinst.core.ziDAQServer('localhost', 8004, 6)
# Without getAsEvent no value would be returned by poll.
daq.getAsEvent('/dev2006/sigouts/0/amplitudes/3')
daq.subscribe('/dev2006/sigouts/0/amplitudes/3')
daq.poll(0.200, 10, 0, True)
# Out: {'/dev2006/sigouts/0/amplitudes/0': {
#   'timestamp': array([26980521883432], dtype=uint64),
#   'value': array([ 0.30001831])}}

If no data was stored in the Data Server’s data buffer after issuing a poll, the command will wait for the data until the timeout time. If the buffer is empty after the timeout time passed, poll will either simply return an empty data structure (for example, an empty dictionary in Python) or throw an error, depending which flags it have been provided.

Often one of the LabOne ziCore Modules provide an easier and more efficient choice for data acquisition than the comparably low-level poll command. Each ziCore Module is a software component that performs a specific high-level measurement task, for example, the Data Acquisition Module can be used to record bursts of data when a defined trigger condition is fulfilled or the Sweeper Module can be used to perform a frequency response analysis. See Section ziCore Modules for an overview of the available Modules.

Explanation of buffering in the Data Server and API Client

The following graphics illustrate how data are stored and transferred between the Instrument, the Data Server, and the API session in the case when the API session is the only client of the Data Server. Figure 1 shows the situation when the API session has subscribed to a node, but no poll command is being sent. Figure 2 corresponds to the situation when the poll command with a recording time of 0 is sent at regular intervals, and illustrates the moment just before the last poll command. Figure 3 then illustrates the moment just after the last poll command.

obtaining data never polled
Figure 1. Illustration of data storage and transfer: the API Session (no other Data Server clients) is subscribed to a node (blue bars representing data stream) but never issues a poll command. The data are stored in the Data Server’s buffer for a certain time and dumped afterwards.
obtaining data before poll
Figure 2. Illustration of data storage and transfer: the API Session is subscribed to a node and regularly issues a poll command. The Data Server holds only the data in the memory that were accumulated since the last poll command.
obtaining data after poll
Figure 3. Illustration of data storage and transfer: the API Session is subscribed to a node and regularly issues a poll command. Upon a new poll command, all data accumulated in the Data Server buffer are transferred to the API Session and subsequently cleared from the Data Server buffer.

In the following cases, the picture above needs to be modified:

  1. Multiple Data Server clients: in case multiple clients (API sessions, Web Server) are subscribed to the same node, the Data Server will keep the corresponding data in the buffer until all clients have polled the data (or until it’s older than the buffer length). This means different clients will not interfere with each other.

  2. LabVIEW, C, and .NET APIs: in these APIs (unlike in MATLAB and Python), it’s not guaranteed that a single poll command leads to the transfer of all data in the Data Server buffer because the block size of transferred data is limited. Nonetheless, by calling poll frequently enough, a gapless stream of data can be obtained.

  3. HF2 Series instruments: the buffer Data Server for HF2 Series instruments is defined by its memory size rather than by its length in units of time. This means that the duration for which the Data Server will store data depends on the sampling rate.