Skip to main content
Skip table of contents

eStreamStart [LJM User's Guide]

Initializes a stream object and begins streaming. This function creates a buffer in memory that holds data from the device, so that higher data throughput can be achieved. Also clears any preexisting stream data.

Syntax

LJM_ERROR_RETURN LJM_eStreamStart(
int Handle,
int ScansPerRead,
int NumAddresses,
const int * aScanList,
double * ScanRate)

Parameters

Handle [in]

A device handle. The handle is a connection ID for an active device. Generate a handle with LJM_Open or LJM_OpenS.

ScansPerRead [in]

The number of scans returned by each call to the LJM_eStreamRead function.

High-throughput applications should use a large ScansPerRead to get more data per call of LJM_eStreamRead. A typical value would be equal to ScanRate or ScanRate/2, which results in a read once or twice per second, respectively.

Low-latency applications should use a smaller ScansPerRead to get less data per call of LJM_eStreamRead. This will cause eStreamRead to be called more frequently.

The frequency LJM_eStreamRead returns data at is:

eStreamRead frequency = ScanRate / ScansPerRead

NumAddresses [in]

The number of addresses in aScanList. The size of the aScanList array.

aScanList [in]

An array of addresses to stream. The scan list is the list of data addresses that are buffered by LJM and returned with LJM_eStreamRead and/or the STREAM_OUT# addresses for stream-out. Find addresses in the Modbus Map. For example, to stream AIN3, add the address 6 to the scan list.

ScanRate [in/out]

A pointer that sets the desired number of scans per second. Upon successful return of this function, ScanRate will be updated to the actual scan rate that the device will use. Keep in mind that data rate limits are specified in Samples/Second which is equal to NumAddresses * Scans/Second or NumAddresses * ScanRate

Returns

LJM errorcodes or 0 for no error.

Remarks

To read data from stream, use LJM_eStreamRead. LJM_eStreamRead reads ScansPerRead * NumAddresses samples. It waits for data to become available, if necessary.

To trigger a callback in a library thread when ScansPerRead * NumAddresses samples are available, use LJM_SetStreamCallback.

To stop stream, use LJM_eStreamStop.

Configuration

Channel configuration such as range and resolution must be handled elsewhere. Check the device datasheet for details about analog inputs and which connection types are capable of stream.

This function writes to the following registers, which cannot be set manually when using LJM_eStreamStart:

  • STREAM_SCANRATE_HZ

  • STREAM_NUM_ADDRESSES

  • STREAM_SAMPLES_PER_PACKET

  • STREAM_AUTO_TARGET

  • STREAM_SCANLIST_ADDRESS#(0:127)

  • STREAM_ENABLE

Note that there are other T-series stream configurations.

How samples are moved from the device to your application

When stream is running, there are two sample buffers to consider: the device buffer and the LJM buffer. After starting stream:

  1. The device acquires a scan of stream data at every scan interval according to its own clock and puts that data into the device buffer.

  2. At the same time, LJM is running a background thread that moves data from the device buffer to the LJM buffer.

  3. At the same time, the user application needs to move data from the LJM buffer to the user application—this is done using LJM_eStreamRead or LJM_eStreamBurst.

Both the device buffer and LJM buffer are first in, first out (FIFO) buffers.

Maximum Samples Per Packet

Each connection type has a maximum number of bytes per Modbus TCP packet sent between the device and host. That determines the maximum number of samples (given as Max Samples Per Packet below). A stream data packet has 16 bytes of overhead and each sample needs 2 bytes. See the appropriate hardware sections of your device to find the maximum samples per packet for your desired communication method. For example, see the Ethernet Section of the T-series Datasheet for Ethernet packet sizes.

Actual Samples Per Packet

The actual samples per USB or TCP packet is configured by LJM. This will be the smaller of either ScansPerRead * NumAddresses (the parameters of eStreamStart) or the Max Samples Per Packet.

Samples Per Transfer

The samples per transfer is the number of samples transferred per read call. This is done automatically in the LJM background thread. This sets the interval for how often LJM does a read. An LJM read might consist of multiple USB or TCP packets.

The samples per transfer is limited by ScansPerRead * NumAddresses and the Max Samples Per Packet.

Normally, ScansPerRead is set to a higher value because it is more efficient to move bigger chunks of data less often, but if you want minimum latency between input/output you can set ScansPerRead as low as 1.

STREAM_IS_ACTIVE

Some operations are invalid when stream is already running. Mainly this means that the error STREAM_IS_ACTIVE occurs when certain registers are written to when stream is already running. These registers include:

  • STREAM_CLOCK_SOURCE (it's illegal to configure the clock source during stream),

  • AIN registers (the AIN system is unable to be read during stream),

  • and others.

LJM automatically commands the LabJack to stop streaming when it closes the device, if needed. However, sometimes the process crashes before LJM is able to send the command to stop stream, and sometimes the command does not reach the device before the send/receive timeout is reached.

To work around this if it happens, you can power-cycle the device. Alternately, eStreamStop stops stream.

Externally Clocked Stream

Externally clocked stream (supported for the T7) is when a signal is received on CIO3, for which each rising edge a single scan is triggered. This allows for variable scan rates and synchronized streaming.

When using externally clocked stream, the ScanRate passed to LJM_eStreamStart should be set to the fastest scan rate that the externally clocked stream will occur at. This ensures that LJM will collect data fast enough, without making LJM perform busy waits for stream data.  This passed ScanRate has no effect on hardware in this case, but rather is just useful for LJM so it has an idea of expected data rate.

If externally clocked stream occurs at a variable scan rate or does not occur immediately after calling LJM_eStreamStart, you'll probably need to use an LJM configuration to prevent LJM from throwing an error when scans are not received by LJM within the expected timeframe since LJM normally expects a constant scan rate, and it will throw an error if it does not receive data from the device fast enough. Here are the options for setting LJM's configs for variable speed and/or delayed start streaming, which should be set using LJM_WriteLibraryConfigS:

- Set LJM_STREAM_SCANS_RETURN to LJM_STREAM_SCANS_RETURN_ALL_OR_NONE. This will cause LJM_eStreamRead to return immediately with the error code LJME_NO_SCANS_RETURNED if LJM has not received ScansPerRead scans worth of data. LJM wrappers like LJM Python that throw exceptions instead of returning error codes will throw their language-specific version of LJME_NO_SCANS_RETURNED.

- Set LJM_STREAM_RECEIVE_TIMEOUT_MS to a safe timeout for your expected stream speeds. This should be the longest expected timeout, plus a small amount extra. If you're not sure how long of a timeout you need, 0 is the infinite timeout and will never time out.

The configs mentioned above are defined in LabJackM.h.

Triggered Stream Start

Hardware-Triggered

Hardware-triggered stream (supported for the T7) could also be called "delayed start stream". It is when LJM_eStreamStart has been called but stream does not start until a signal is received on FIO0 or FIO1, after which stream continues to collect data as normal.

To use triggered stream with LJM, you must enable some LJM configurations and also some T-series configurations.

LJM triggered stream configurations that should be set using LJM_WriteLibraryConfigS:

- Set LJM_STREAM_SCANS_RETURN to LJM_STREAM_SCANS_RETURN_ALL_OR_NONE. This will cause LJM_eStreamRead to return immediately with the error code LJME_NO_SCANS_RETURNED if LJM has not received ScansPerRead scans worth of data. LJM wrappers like LJM Python that throw exceptions instead of returning error codes will throw their language-specific version of LJME_NO_SCANS_RETURNED.

- Set LJM_STREAM_RECEIVE_TIMEOUT_MS to a safe timeout for how long you expect it to take before stream starts. If you're not sure how long of a timeout you need, 0 is the infinite timeout and will never time out.

The configs mentioned above are defined in LabJackM.h.

Software-Triggered - For collecting pre-trigger data

Software-triggered stream is appropriate when pre-trigger data is required.

Software-triggered stream is when LJM_eStreamStart is called and stream immediately begins collecting data. The "trigger" point is then defined by software. Since this depends on the application, you would essentially iterate through the aData of LJM_eStreamRead until the desired trigger criteria is met. Data before that point is considered pre-trigger data.

Burst Stream

Burst stream is when a limited number of scans are collected by the device, after which the device stops streaming. LJM_StreamBurst may be used to automatically perform a burst stream or burst stream may be performed manually using the following steps:

  • Write the desired number of scans to collect to STREAM_NUM_SCANS.

  • Call LJM_eStreamStart

  • Call LJM_eStreamRead until all STREAM_NUM_SCANS have been read or until the return code is STREAM_BURST_COMPLETE (2944)

  • Call LJM_eStreamStop (to clean up memory)

Auto-Recovery Precaution

Auto-recovery mode is entered when the stream buffer on the LabJack device is too full. At that point, the device will discard further scans but will keep track of how many scans have been discarded. Once stream read has caught up and emptied the device stream buffer sufficiently, the device will stop discarding scans, and will send a data packet that reports the number of discarded scans and also has a scan of all 0xFFFF that denotes the border between pre and post auto-recovery data. In the LJM stream buffer that is read by the user's application, LJM inserts the proper number of dummy scans, filled with all -9999, to maintain proper timing of stream data through auto-recovery.

If auto-recovery is enabled (the default), and there is any risk of auto-recovery happening (typically happens at the highest data rates only), the first channel in the stream scan list should always be an analog input (AIN#). If you only want to stream other channels besides analog inputs and cannot add a dummy analog input because you need maximum speed, then you must either ensure that the first channel in the scan list will never return 0xFFFF as a normal valid reading and enable LJM_STREAM_DIGITAL_AUTO_RECOVERY_ERROR_DETECTION_DISABLED or see the Stream Speed section of this page to reduce the chances of auto-recovery.

LJM actually looks at the first value in each scan to detect the border scan. Once auto-recovery starts, LJM watches the first channel in a scan and if it sees 0xFFFF it assumes that is the border scan that marks the start of post auto-recovery data. 0xFFFF is a special reserved value that analog inputs never return under normal circumstances.

Low-Latency

Low-latency stream is for when you need to move data from the device to the host application as soon as possible after acquisition (i.e. with minimum latency). For example, you might be trying to correlate stream data with the host's clock, or perhaps are doing feedback control.

To do low-latency stream, set the ScansPerRead parameter (of LJM_eStreamStart) to a small number. This will cause LJM_eStreamRead or the callback of LJM_SetStreamCallback to read smaller amounts more often. LJM will also attempt to transfer data from the device according to how small ScansPerRead is:

  • If ScansPerRead is large, LJM may transfer many samples at once. This is efficient.

  • If ScansPerRead is 1 (the minimum), LJM will transfer one sample at a time from the device. This is less efficient, but allows for minimal latency.

Low-latency stream is typically limited to a scan rate of few kscans/second or less due to the increased overhead per scan. This increased overhead can cause the symptoms mentioned in the Stream Speed section of this page.

To get stream latency as low as possible, avoid doing unnecessary work in either the thread that calls LJM_eStreamRead or in the callback set by LJM_SetStreamCallback. File I/O, hardware I/O, and screen updates can all take substantial time.

A real-world example

During one test, we achieved reliable low-latency stream with:

  • Up to 7250 Hz ScanRate

  • 1 to 4 AIN channels

  • 1 ScansPerRead

  • USB connection

  • LJM 1.2100

  • macOS 10.13.6

  • Processor: 2.7 GHz Intel Core i7

  • Memory: 16 GB 1600 MHz DDR3

Stream Speed

To ensure the host application is able to stream quickly, see our troubleshooting guide for the following situations:

  • If LJM_eStreamRead is returning error 1301 (LJME_LJM_BUFFER_FULL)

  • If LJM_eStreamRead gives many -9999 values in aData

LJM 1.2100 and later automatically sets a higher priority. It sets higher thread and process priority on Windows, and sets higher thread priority only on Linux. LJM's priority configuration is controlled by the following:

Improving Stream Startup Speed

As of LJM 1.1405: If stream has been successfully started for a given device handle without returning a LJME_USING_DEFAULT_CALIBRATION warning, the next stream start for that device handle will not load the stream calibration. This has been shown to reduce the stream startup time by about 6 milliseconds via USB or 10 or more milliseconds via TCP.

Stream Out

Stream Out allows irregular or looping waveforms to be output via the digital I/O (DIO) or a DAC. See the Stream-Out section of the T-series datasheet for details.

When performing stream out with no stream in, ScansPerRead input parameter to LJM_eStreamStart is ignored.

Binary Stream Data

By default, LJM streams binary AIN data from the device and calibrates it to a floating point value before returning it using eStreamRead.

To read binary AIN stream data, enable the LJM configuration LJM_STREAM_AIN_BINARY by setting it to 1.

Example

Start stream with a scan rate of 10 kHz and 2 channels.

C
char ErrorString[LJM_MAX_NAME_SIZE];
int LJMError;
const int numAddresses = 2;
const int aScanList[] = {0, 2}; // AIN0 and AIN1
const int initScanRate = 10000;
double scanRate = initScanRate;
const int scansPerRead = initScanRate / 2;
double aData[numAddresses * scansPerRead];
int DeviceScanBacklog;
int LJMScanBacklog;
int iteration = 0;
// Start stream
// handle from LJM_Open() or LJM_OpenS()
LJMError = LJM_eStreamStart(handle, scansPerRead, numAddresses, aScanList, &scanRate);
if (LJMError != 0) {
    LJM_ErrorToString(LJMError, ErrorString);
    printf("LJM_eStreamStart error: %s\n", ErrorString);
}
else {
    printf("Stream started at %f Hz\n", scanRate);
}
// Read stream
for (iteration = 0; iteration < 10; iteration++) {
    LJMError = LJM_eStreamRead(handle, aData, &DeviceScanBacklog, &LJMScanBacklog);
    if (LJMError != 0) {
        LJM_ErrorToString(LJMError, ErrorString);
        printf("LJM_eStreamRead error: %s\n", ErrorString);
    }

    // Process the stream data in whatever way suits your application.
    // aData[0] contains the first AIN0 sample
    // aData[1] contains the first AIN1 sample
    // aData[2] contains the second AIN0 sample
    // aData[3] contains the second AIN1 sample
    // ...and so on
    MyDataProcessingFunction(aData, DeviceScanBacklog, LJMScanBacklog);
}
// Stop stream
LJMError = LJM_eStreamStop(handle);
if (LJMError != 0) {
    LJM_ErrorToString(LJMError, ErrorString);
    printf("LJM_eStreamStop error: %s\n", ErrorString);
}
JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.