9.2 - High Speed Acquisition - Streaming (Applies to UD-Series)
9.2.1 Introduction
When you want to do things faster than 100hz, its usually best to let the LabJack perform the timing. The LabJack can then send data back in blocks and relieve your CPU from constantly having to do things. The LabJacks support streaming of both digital and analog inputs as well as timers and counters. This section explains how to setup and performed streamed data acquisition.
9.2.2 Basic Streaming
When you want to read inputs at rates faster than 100hz, or at very precise intervals, it is usually best to use the LabJack's stream mode instead of having DAQFactory and the PC set the read rates. Anything above 100hz is difficult for a PC to perform since it has so many other tasks to do as well. To setup streaming in DAQFactory you will need to use a combination of Channels and sequence script. Streaming in DAQFactory is different from how the LabJack User Manual describes, as DAQFactory handles all the callback and data collection, putting the data into the appropriate channels. In this sample we'll stream 2 channels.
1) Start DAQFactory up with a new document.
2) Click on CHANNELS: in the Workspace to go to the channel table.
3) Add two new channels, ChannelA and ChannelB, both Device Type = LabJack, D# = 0, I/O Type = A to D, and Channel numbers 2 and 3. Set the Timing for both channels to 0, and the History: set to 36000.
3) Click on Apply to save your changes.
4) Right click on SEQUENCES: in the Workspace and select Add Sequence. Call the new sequence StartStream
5) In the sequence editor window that appears, enter the following script:
// standard initialization: using("device.labjack")
include("c:\program files\LabJack\Drivers\labjackud.h")
// setup stream:// set scan rate:
AddRequest(0, LJ_ioPUT_CONFIG, LJ_chSTREAM_SCAN_FREQUENCY, 1000, 0, 0)
// setup channels to stream:AddRequest(0, LJ_ioCLEAR_STREAM_CHANNELS, 0, 0, 0, 0)
AddRequest(0, LJ_ioADD_STREAM_CHANNEL, 2, 0, 0, 0)
AddRequest(0, LJ_ioADD_STREAM_CHANNEL, 3, 0, 0, 0)
GoOne(0)
// start the stream:
global scanrate = 0
eGet(0,LJ_ioSTART_STREAM, 0, @scanrate, 0)
// scanrate now has the actual scanrate, which you can display on the screen if you want.
6) Click on Apply and Compile to save your script.
7) Right click on SEQUENCES: in the Workspace and select Add Sequence. Call the new sequence StopStream and
enter the following script:
ePut(0,LJ_ioSTOP_STREAM, 0, 0, 0)
8) Click on Apply and Compile to save your script.
9) Click on Page_0 under PAGES: in the workspace to display a blank page.
10) On that page, right click and select Graphs - 2D Graph.
11) While holding down the Ctrl key, click and drag the graph to move it to the top left corner of the page, then click and drag the bottom right corner of the graph to expand it to take up about 3/4 of the screen.
12) Right click on the graph and select Properties.... For the Y Expression put ChannelA. Click on New Trace and for
the Y Expression put ChannelB. Click OK to close the properties window.
13) Right click somewhere on the empty part of the page and select Buttons - Button. Right click on the new button
and select Properties....
14) For Caption, put Start, then go to the Action tab and select Start/Stop Sequence, then select your StartStreamsequence.
15) Repeat steps 13 and 14, but put Stop for the caption and StopStream for the sequence.
That is it. You should be able to click on the Start button and have streaming on channels 2 and 3 start up and be graphed. It is possible that the values will be off the scale of the graph, so you may want to click on the graph, then right click on the graph and select AutoScale - AutoScale Y.
One important point if you start tweaking this sample: the Channels that you created must have the same D# and channel number as the one you specified in the LJ_ioADD_STREAM_CHANNEL request. The I/O Type must be "A to D" as well, even if you are streaming digital inputs, timers or counters. If not then DAQFactory won't know where to put the data that is streaming in.
Note: make sure you configure your inputs before starting the stream. For the U3, this means you have to set the pins to analog input as shown in the example file.
Note: you should not change the LJ_chSTREAM_WAIT_MODE, as all waiting is handled internally. If you change this, you will most likely cause streaming to stop functioning
Sample file: LJGuideSamples\BasicStream.ctl
9.2.3 Streaming Other Inputs
You can stream other inputs besides the analog inputs of your LabJack. This is done by specifying special channel numbers when doing LJ_ioADD_STREAM_CHANNEL. The important part here is that even though the LabJack is actually streaming something other than an analog input, you MUST specify A to D for the I/O Type when creating your DAQFactory Channels to receive the data.
The available channel numbers are slightly different for each LabJack and listed here for your reference:
U3:
193 EIO_FIO
200 Timer0
201 Timer1
210 Counter0
211 Counter1
224 TC_Capture0
225 TC_Capture1
226 TC_Capture2
227 TC_Capture3
UE9:
193 EIO_FIO
194 MIO_CIO
200 Timer0
201 Timer1
202 Timer2
203 Timer3
204 Timer4
205 Timer5
210 Counter0
224 TC_Capture0
225 TC_Capture1
226 TC_Capture2
227 TC_Capture3
227 TC_Capture4
227 TC_Capture5
227 TC_Capture6
You may notice that in DAQFactory, we have multiple TC_Capture channel numbers, where the LabJack documentation only lists one. This is to allow you to stream the high order word of multiple timers and counters and keep the data separate in separate channels. Internally, they are exactly the same, so you have to specify the appropriate TC_Capture immediately following its Timer or Counter channel #. In other words, to read the entire 32 bits of Timers 0 and 1, you'd do:
AddRequest(0, LJ_ioADD_STREAM_CHANNEL, 200, 0, 0, 0)
AddRequest(0, LJ_ioADD_STREAM_CHANNEL, 224, 0, 0, 0)
AddRequest(0, LJ_ioADD_STREAM_CHANNEL, 201, 0, 0, 0)
AddRequest(0, LJ_ioADD_STREAM_CHANNEL, 225, 0, 0, 0)
9.2.4 Triggered
Triggered streaming is currently only supported by the UE9 and UE9-Pro.
Triggered streaming is similar to regular streaming except instead of using the internal LabJack clock to determine when a scan of the stream channels occurs, an external pulse triggers the scan. The interval between external pulses must be less than the maximum stream rate for the current input resolution. The external pulses do not need to occur at a constant interval. To enable external triggering, just add the following line of script before adding your stream channels using LJ_ioADD_STREAM_CHANNEL:
AddRequest(ID, LJ_ioPUT_CONFIG, LJ_chSTREAM_EXTERNAL_TRIGGER, 1, 0, 0)
The trigger input will be the first available FIO pin based on which timers and counters you have enabled.
The only problem with triggered streaming is that the time of each data point will be off. This is because the LabJack buffers the scans and DAQFactory doesn't actually get the data until a full packet occurs. DAQFactory doesn't realize this and assigns times based on an assumed interval. If you have the bandwidth, i.e. your pulses are slow enough that you aren't close to the stream interval limit, you can use the system timer mode of the timers to retrieve exact relative times of your scans. To do this, you need to setup a timer for system timer in, and then add the timer to the list of channels to stream. Depending on how long your experiment runs, you may be able to get away with only SYSTIMERLOW. For the UE9 at 750khz, the low timer will roll over every 5726 seconds. Here's how to do it:
1) Enable two Timers:
AddRequest(ID, LJ_ioPUT_CONFIG, LJ_chNUMBER_TIMERS_ENABLED, 2, 0, 0)
2) Set the mode:
AddRequest(ID, LJ_ioPUT_TIMER_MODE, 0, LJ_tmSYSTIMERLOW, 0, 0)
AddRequest(ID, LJ_ioPUT_TIMER_MODE, 1, LJ_tmSYSTIMERHIGH, 0, 0)
GoOne(ID)
3) Set up the stream to stream analog input 2 and 3 in external trigger mode:
AddRequest(ID, LJ_ioPUT_CONFIG, LJ_chSTREAM_EXTERNAL_TRIGGER, 1, 0, 0)
// setup channels to stream:
AddRequest(ID, LJ_ioCLEAR_STREAM_CHANNELS, 0, 0, 0, 0)
AddRequest(ID, LJ_ioADD_STREAM_CHANNEL, 3, 0, 0, 0)
AddRequest(ID, LJ_ioADD_STREAM_CHANNEL, 4, 0, 0, 0)
4) Now we need to add our timers to the list of channels to stream. Make sure you use the order indicated:
AddRequest(ID, LJ_ioADD_STREAM_CHANNEL, 200, 0, 0, 0)
AddRequest(ID, LJ_ioADD_STREAM_CHANNEL, 224, 0, 0, 0)
AddRequest(ID, LJ_ioADD_STREAM_CHANNEL, 201, 0, 0, 0)
AddRequest(ID, LJ_ioADD_STREAM_CHANNEL, 225, 0, 0, 0)
5) Now finish up the stream setup:
GoOne(ID)
// start the stream:
global scanrate = 0
eGet(ID,LJ_ioSTART_STREAM, 0, @scanrate, 0)
// scanrate now has the actual scanrate, which you can display on the screen if you want.
6) Create 4 channels to receive this timing data in addition to the 2 you created to receive the analog input. All 6
channels are I/O type: A to D, Timing = 0. Channel #'s will be 3, 4, 200, 201, 224, and 225.
That completes it. When you run your script, the stream will start, streaming both analog inputs 2 and 3 as well as the system timer. A scan will occur every time a trigger is detected on FIO2. FIO0 and FIO1 are used by the 2 timers. With each scan, your six channels will update. The time associated with these channels will be incorrect, but channels 200, 224, 201, and 225 will contain the 4 words that make up the 64 bit system timer value. While this is not absolute time, it will give you relative time between each triggered scan. Just use the difference in counts divided by the system clock speed of 750khz for the UE9 to determine the actual number of seconds between scans. The best way to do this is to create a calculated V channel:
7) Right click on CHANNELS: under V: in the Workspace. Note this is note the same CHANNELS: that we've been clicking before. Select Add V Channel
8) Call the new channel TheTime
9) Click on the new channel in the workspace. In the Expression window, put:
(TimerLowLow + TimerLowHigh*2^16 + TimerHighLow * 2^32 + TimerHighHigh * 2^48)
This assumes you named channel 200 TimerLowLow, 224 TimerLowHigh, 201 TimerHighLow, and 225 TimerHighHigh. You also may want to put a divisor at the end to convert to seconds:
UE9:
(TimerLowLow + TimerLowHigh*2^16 + TimerHighLow * 2^32 + TimerHighHigh * 2^48) / 750000
10) Click Apply.
At this point, you can reference this channel like you would any other, except putting V. in front of it. Instead of getting a channel reading, you'll get the result of the calculation. Since we didn't use any [0] notation, this is the entire array calculated from all the readings. If you want to graph your channels, you'd put:
V.TheTime
as the X Expression in place of Time. You'll need to change the bottom axis type to Lin, undo Use Time Width, and
adjust the Scale From and Scale To:
![](../__attachments/49580090/dfappguide-924-timeplot.png?inst-v=53c3afbe-b679-4ae4-ad8c-e895cddff14e)
All of this is shown, complete, in the sample file:
Sample file: LJGuideSamples\TriggeredStream.ctl
9.2.5 Error Handling for Steaming
Streaming from a LabJack is what is called an asynchronous action. This means that the LabJack does its own thing and every so often it tells DAQFactory that there is new data or there is an error. For this reason, you cannot simply look at the error code returned by LJ_ioSTART_STREAM to catch all stream errors. This command may return a stream configuration type error, so you'll want to check for it using the same methods as the low speed acquisition, but will not handle errors in the actual stream. For this you have two choices:
1) You can create a simple sequence to retrieve the last stream error continuously and do something if it returns an error. The function GetLastStreamError() will return the code for the last stream error. This is reset when you start the stream. This, however, is not the best way to do this and will waste processor power.
2) You can use the DAQFactory OnAlert event, which only gets called when an actual error occurs. Using the event as an error handler is described in the previous section on error handling, and catching stream errors would be done the same way. One common error you might want to catch is if your LabJack accidentally gets unplugged while streaming. If you wanted to automatically restart streaming when it is reconnected you could do this in your OnAlert sequence:
if ((left(strAlert,10) == "D0050:00:2001") && (Streaming)) // 2001 is LJE_RECONNECT StartStream()
endif
Now, this assumes that you have a sequence called StartStream that will reconfigure the LabJack and actually restart the stream. It also assumes that you've created a global variable called "Streaming" that you set to 1 in StartStream, and to 0 in StopStream. This is so an accident unplug when you aren't streaming doesn't spontaneously start the streaming process. Finally this assumes you are using device number 0 / first found. Please see the section on OnAlert if you are using multiple LabJacks.