Using a T7-pro, I am collecting three channels of analog data at resolution 8 and a sample rate of 1-2 kHz using a C program with LJM running on a RPi. I need to synchronize this data with data collected by other instruments running on different RPi's (other labjacks and also some other instruments). Is there a way to do this? I also would like to know the time delay between the measurements of my three channels. The data sheet says these are collected "as fast as possible" which isn't very informative. So ideally I would be able to time stamp every measurement with a meaningful collection time based on the clock on the RPi, which is synchronized with the other RPi units. Is this possible to do somehow?
I also would like to know the time delay between the measurements of my three channels. The data sheet says these are collected "as fast as possible" which isn't very informative.
See "Interchannel Delay" in Table A1.5:
https://labjack.com/support/datasheets/t7/appendix-a-1
So ideally I would be able to time stamp every measurement with a meaningful collection time based on the clock on the RPi, which is synchronized with the other RPi units. Is this possible to do somehow?
At your scan rates of 1k-2k you could try low latency stream techniques. Basically you tweak some settings that makes the T7 and LJM do minimal (or no) buffering and you read 1 scan at a time. Thus you get each scan in pretty much real time which will typically be less than 1 ms delay. Your data rates are at the upper end of streaming 1 at a time, though, so you might instead do a technique where you read every 100 ms and set buffer sizes so there is no leftover data each time you read. This way you know the last scan has minimal delay and can timestamp the other scans based on the specified scan rate (which is very accurate). If you want to go down this route we can help further.
The scan clock on the T7 is quite accurate, so the typical techniques for synchronizing are addressing a couple things:
1. Unknown about the delay between software telling stream to start and the first scan happening.
2. Clock difference between the T7 and the host over longer periods of time. The T7 is 20 ppm at room temp (Table A5-6).
Some techniques:
A. External scan clock. Provide an external scan clock to the T7 so you are controlling when each scan happens.
B. Common signal. Add another channel to the stream and include some other signal that can be used for synchronizing. For example, perhaps 1 RPi produces a 1 pps pulse and this pulse goes to everything else so they can use it to synchronize in software.
I'm a bit puzzled by table A1.5, if it's supposed to apply to this case. It states that the delay is 1335 microsec for resolution 8, but if I'm doing 1000 scans per second and each scan is 3 channels, then I need the delay between channels to be less than 1/3 ms.
I initially collected data using resolution 9 and reading one at a time. The time spacing I got when I did this exhibited a peculiar square wave shape, and the measured data seemed to be correlated with this shape as well. This nonuniform spacing complicates frequency analysis that we want to do, and we really originally wanted higher sampling rate than this strategy provides, so I dropped down to resolution 8 in order to use streaming, and found I seem to be able to get somewhere between 1k and 2k. If I apply the "low latency stream techniques" is this similar to what I did already, reading data one value at a time with LJM_eReadName()? I'm wondering if I'd still have to deal with nonuniformity in the time spacing.
What are the relative merits between methods A and B? Does A require another piece of hardware to produce the clock, or can it come from the Pi?
The way we have things set up we were expecting to synchronize data collected on different Pi's in software after the data has been logged using recorded time stamps. We were thinking that would be easier than trying to start the logging on all the Pi's simultaneously with time synchronization. We do have the need to keep synchronization with another instrument that will be communicating on a serial input.
the delay is 1335 microsec for resolution 8, but if I'm doing 1000 scans per second and each scan is 3 channels, then I need the delay between channels to be less than 1/3 ms.
The InterchannelDelay of 0.001335s suggests a max possible sample rate of 749 Hz. Looking above at A.1.4 we specify a max sample rate of 630 Hz, so considering a little extra overhead and margin these numbers agree. If you are scanning 3 channels at 1000 scans/second that is a sample rate of 3000 samples/second and definitely should not work. Make sure you are setting STREAM_RESOLUTION_INDEX, not AIN#_RESOLUTION_INDEX or AIN_ALL_RESOLUTION_INDEX which only affect command-response mode. If you collect readings from a channel jumpered to GND you can compare to the noise levels from Table A.1.5 or A.3.1.1 to tell whether you are setting res index or not.
To further complicate, I just tested (LJM 1.1402 and T7 firmware 1.0208) and with ResIndex=8 it does not stream well at all. I can get only get it to start around 30 samples/second or less and even then it does not run long before it has a problem. We are looking into that.
I initially collected data using resolution 9 and reading one at a time. The time spacing I got when I did this exhibited a peculiar square wave shape,
In command-response the time spacing is dictated by the AIN Sample Time, your operating system, and your software. From Table A.3.1.1 it takes about 3.5 ms/sample at ResIndex=9, so I could see writing software that holds a 5ms or 10ms loop pretty solid.
If I apply the "low latency stream techniques" is this similar to what I did already, reading data one value at a time with LJM_eReadName()?
Likely the stream data is being transferred (T7 buffer to LJM buffer) in large chunks in the background, even though your software is reading (from the LJM stream buffer) only 1 scan at a time. With low-latency techniques you tweak advanced settings so data is transferred 1 scan at a time from device to host. You can't transfer 100 ksamples/second this way, but in my experience you can do perhaps a few k.
I'm wondering if I'd still have to deal with nonuniformity in the time spacing.
This is just affecting the timing of when the data is transferred from device to LJM (and then you control when data is transferred from LJM to your software). The actual acquisition is clocked in hardware by the stream clock and happens exactly at the known scan rate or does not happen at all.
What are the relative merits between methods A and B? Does A require another piece of hardware to produce the clock, or can it come from the Pi?
Both methods require a signal from something besides the T7. What might work depends on what signals you have available or can produce. I'm really not sure what sort of signal you can produce from a Pi itself.
The way we have things set up we were expecting to synchronize data collected on different Pi's in software after the data has been logged using recorded time stamps.
Time stamps from where? I'm not quite following but if you can clear me up a little I'll likely have more ideas.
I feel like I'm not being very clear. So I'm going to try to summarize what I've done so far, and what I observed, and what we're trying to do from the beginning.
We have a device that produces 3 channels analog data with +/- 10V range---it's actually vector valued data. There seems to be some uncertainty about the required precision we really need. Initially I was told we needed super high precision, with precision more important than a high sample rate, but a target sample rate of 1 kHz. So we'd take 40 Hz with high precision over 1000 Hz with low precision. The reason wanting 1 kHz is to be able to better characterize noise or interference. Ultimately we're going to time average because our actual signal is DC. I was actually time averaging 15 s long periods. But the noise/interference is expected to be time varying. For combining the three channels to form the vector value we need to know the time offset between channels. It would be really nice if we could get our sample rate above 120 Hz so we could at least resolve 60 Hz interference. But if we can't it's not a deal breaker. Whenever I refer to sample rate I mean the number of scans of all three channels per second.
My initial attempt was at resolution 9 reading single measurements using LJM_eReadString and cycling through the three channels. My observed sample rate is variable, but is somewhere around 35 Hz. I didn't try to control the sample rate from my program---this was what I got letting it run as fast as possible. It's not clear to me how reliably I can control timing from the Pi. This situation was considered tolerable, but then we noticed that the variability in the sample spacing was correlated with the data values. That seemed really strange. The time spacing follows a square wave pattern and the data values also exhibit a square wave pattern that is aligned with the square wave pattern of the time sampling. I thought the time sample pattern had something to do with variability on the Pi, so I was surprised to see this pattern. Note that the data collecting program is very simple: it loads data into shared memory and sends a signal when the buffer is full so other programs know the data is ready and can look at it.
So you said above that "likely stream data is being transferred in the background..." Does that refer to this situation? In command and response mode does the labjack stream in the background? That doesn't make sense to me.
We decided to try the streaming. You are correct that I did not set the resolution correctly. I tried to figure out what the default resolution is and it says "0" on the modbus. Is that the same as resolution 1? I'm a little puzzled now because the manual suggests I should be able to stream much faster if I'm at resolution 1. When I push up above 2315 Hz scan rate I get an error about overlap and my program fails. (Sorry to be vague. I'm not with the instrument right now.) I think I would like to consider streaming at a resolution of maybe 6 to try to get close to the initial target sample rate. It sounds to me like the low latency stream method may be the best way to achieve the desired time synchronization. Unless there's a way to get a time sync signal out of the Pi I don't think we have a good way to provide that, and because this project is in a proof-of-concept phase, we're short on time, so we don't want to introduce additional hardware and address the issue of synchronizing the Pi with the additional hardware.
We want the time stamps to come from the clock on the Pi. The various Pi's in the project are synchronized using ntp, which it appears can give 1 ms time synchronization. If we use low latency streaming does that mean that data will get to the Pi at a time close to when it was collected, so I could use that as a way of applying time synchronization? Do I understand correctly that with low latency streaming the data collection occurs at fixed intervals chosen by the labjack and I fetch data one scan at a time? If the labjack's sample spacing is reliable, may not be worth the trouble...unless it's the only way to determine when the first data value was collected. 'm OK with limiting the duration of the data collection to some duration longer than a minute, so I could start with a time stamp from the Pi's system clock and assume the labjack's specified sample rate from there to apply time stamps going forward. It seems like for this to work I just need to be able to relate the stream start time to the Pi's clock to within a millisecond.
I measured the time that the call to LJM_eReadString requires when resolution=9 and it varies from 6.65 ms to 10.3 ms. If I assume that the time to associate with the data is the time when the call returns (which is what I did assume) then I can't get uniform sampling this way because the call duration is not predictable. Note also that this is a lot more than the 3.5 ms/sample quoted on table A1.3.
Whenever I refer to sample rate I mean the number of scans of all three channels per second.
We would call that scan rate. We define sample rate and scan rate in Appendix A-1:
https://labjack.com/support/datasheets/t7/appendix-a-1
Address - You can usually also call this a "channel". An address usually returns the value of 1 channel.
Sample - A reading from one address.
Scan - One reading from a list of addresses (scan list).
The scan rate, by definition, is a fraction of the sample rate where the fraction is the inverse of the number of channels being read in a single scan. The scan rate is defined as:
ScanRate = SampleRate / NumAddresses
SampleRate = ScanRate * NumAddresses
My observed sample rate is variable, but is somewhere around 35 Hz.
From Table A.1.3 we expect a command-response reading to take about 3.5 ms/sample at Res=9. If using eReadName (I don't see how eReadString applies) you are reading 1 at a time so add overhead from Table A.1.2 to each read.
I didn't try to control the sample rate from my program---this was what I got letting it run as fast as possible. It's not clear to me how reliably I can control timing from the Pi.
C should have the ability to implement an interval timer in software that is based off the system clock of the computer. Someone else here can follow up here if needed, but I would think we have some looping C examples that implement an interval timer (as opposed to a simple delay). Basically you look at how long it takes for your execute and set the interval to something safely longer than that.
So you said above that "likely stream data is being transferred in the background..." Does that refer to this situation? In command and response mode does the labjack stream in the background? That doesn't make sense to me.
No, does not apply. When you do a command-response read there is really no buffering to think about. You send the command and the device responds as soon as it can.
We decided to try the streaming. You are correct that I did not set the resolution correctly. I tried to figure out what the default resolution is and it says "0" on the modbus. Is that the same as resolution 1?
The default ResolutionIndex=0 equates to:
T7 = 8 in command-response and 1 in stream.
T7-Pro = 9 in command-response and 1 in stream.
I'm a little puzzled now because the manual suggests I should be able to stream much faster if I'm at resolution 1. When I push up above 2315 Hz scan rate I get an error about overlap and my program fails.
If you set Res=0 and every channel in the scan list is set to Range=10 you should be able to get a sample rate of 100k or more. Also assumes STREAM_SETTLING_US=0.
Add a write of AIN_ALL_RANGE=10 (or =0) before you start the stream to make sure all channels are using the +/-10V range.
If we use low latency streaming does that mean that data will get to the Pi at a time close to when it was collected, so I could use that as a way of applying time synchronization?
Exactly. In my experience the time between acquisition and getting the scan in software roughly half a millisecond. That is what we mean by low-latency.
Do I understand correctly that with low latency streaming the data collection occurs at fixed intervals chosen by the labjack and I fetch data one scan at a time?
The former is always true in stream. The latter, host app reads 1 scan at a time, is a typical part of low-latency streaming technique. You can read 1 scan at a time with any stream, but the detail is that without doing some advanced stream configuration the data will be moved device to LJM in large chunks in the background:
https://labjack.com/support/software/api/ljm/function-reference/ljmestre...
If the labjack's sample spacing is reliable, may not be worth the trouble...unless it's the only way to determine when the first data value was collected.
The spacing is very reliable, per the LabJack's clock, then you are exactly right that the main issue is knowing the exact time the stream starts in relation to the host's clock.
limiting the duration of the data collection to some duration longer than a minute, so I could start with a time stamp from the Pi's system clock and assume the labjack's specified sample rate from there to apply time stamps going forward.
Right again. If the T7 clock is 20 ppm fast, and the host clock is 20 ppm slow, that means they differ by 40 ppm which is 40us per second, or 1 millisecond every 25 seconds.
It seems like for this to work I just need to be able to relate the stream start time to the Pi's clock to within a millisecond.
Yes, you've got it.
I have a new idea that should work within 1 ms using normal easy stream.
1. We will add a new register that records the value of CORE_TIMER at the start of the first scan of stream. From the JSON:
{"address":61520, "name":"CORE_TIMER", "type":"UINT32", "devices":[{"device":"T7", "fwmin":1.0071}], "readwrite":"R", "tags":["CONFIG"], "streamable":true, "description":"Internal 32-bit system timer running at 1/2 core speed, thus normally 80M/2 => 40 MHz."},
Note from Section 3.2 that right now you can actually add a couple channels to your stream and record CORE_TIMER with your stream data. This is really overkill, though, as you just need to know the time of 1 scan as all others will have a relative time of exactly ScanNumber/ScanRate.
2. Now you just need to relate CORE_TIMER to the time on your host. You should be able to do this within +/-1ms at any time by doing a command-response read of CORE_TIMER while at the same time noting the host's system clock. Per the rough clock drift math above I would do this once per minute.
I measured the time that the call to LJM_eReadString requires when resolution=9 and it varies from 6.65 ms to 10.3 ms. If I assume that the time to associate with the data is the time when the call returns (which is what I did assume) then I can't get uniform sampling this way because the call duration is not predictable. Note also that this is a lot more than the 3.5 ms/sample quoted on table A1.3.
If those times are accurate for your situation, I would suggest a software timed loop interval of 20 ms so you know the work can be safely done each time through the loop and you will get regular timing.
Still not sure what you are doing with eReadString. I would expect eReadName, or for best speed eReadAddress. And if doing multiple channels you would use eReadNames or eReadAddresses so you only have 1 overhead from Table A.1.2.
In LabVIEW I have an example "C-R Speed Test.vi" to check command-response speeds. Do you see something similar in our C examples?
I tested over USB with #Iterations=128, Range=10, and ResIndex=9:
1 Channel = 4.3 ms/iteration
2 Channels = 7.8 ms/iteration
3 Channels = 11.4 ms/iteration
So I have an overhead of about 0.8 ms plus about 3.5 ms/sample.
I don't understand how a software timed loop will help. You are right, I'm using eReadName. The test code looks something like:
start=get_time();
err = LJM_eReadName(...);
end = get_time();
and the time difference is over 6.6 ms. I'm using USB. I have a vague recollection of using eReadNames and it wasn't faster, but I'll revisit that.
The CORE_TIMER method seems promising. I added it to my stream and was able to read out times (but with a reduced scan rate). Note that CORE_TIMER doesn't seem to be documented. I recall seeing a reference to it, but then when I looked at modbus map it didn't seem to be there.
Are you saying that you could make a modification where I'd be able to read a register that has the start time of the first scan? Reading this register would require exiting streaming mode, right? So it would be an after-the-fact operation?
I set my resolution to 6 and am getting behavior that seems reasonable, though my data standard deviation doesn't seem to have improved much from the default of resolution 0. I was able to set resolution to 8 and collect at 160 Hz scan rate (without clock data).
I don't understand how a software timed loop will help.
The software timed loop is useful if you use command-response mode and want regular scan intervals. You could make a loop that executes every 20 ms and each time scans your 3 channels.
I have a vague recollection of using eReadNames and it wasn't faster, but I'll revisit that.
In the test I ran it was doing 128 calls to eReadAddresses where each call read 3 channels, and this took about 0.8 + 3*3.5 ms. If instead I made 3 calls to eReadAddress each time it would take about 3 * (0.8 + 3.5) ms.
If I used eReadName or Names intead, the 3.5 would be a little longer but not much.
You are right, I'm using eReadName. The test code looks something like:
It is tough to measure down to a few milliseconds with the normal windows timers. Instead you should do a bunch of iterations so it takes perhaps a second. For testing to determine the time per iteration I suggest code like:
start=get_time();
For 512 iterations;
err = LJM_eReadNames(...);
end = get_time();
timeperiteration = (end-start)/512
Or to test top performance do:
NamesToAddresses()
start=get_time();
For 512 iterations;
err = LJM_eReadAddresses(...);
end = get_time();
timeperiteration = (end-start)/512
The CORE_TIMER method seems promising. I added it to my stream and was able to read out times (but with a reduced scan rate). Note that CORE_TIMER doesn't seem to be documented. I recall seeing a reference to it, but then when I looked at modbus map it didn't seem to be there.
It is not mentioned much. I will look for some sort of General section to add it and some other general config registers.
It is mentioned as something you can stream here:
https://labjack.com/support/datasheets/t7/communication/stream-mode
And it is in the modbus map. In the search box that is in the modbus map itself type "core_" and it will be the only result left. Or if you set Tags = CONFIG you will be left with 38 registers tagged as config registers, including core-timer.
https://labjack.com/support/datasheets/t7/communication/modbus-map
Are you saying that you could make a modification where I'd be able to read a register that has the start time of the first scan? Reading this register would require exiting streaming mode, right? So it would be an after-the-fact operation?
Correct. Not correct. You can read it with a command-response read while the stream is going on. About the only thing you can't read via command-response while a stream is going is analog inputs. I propose during a long term stream you could read the core-timer once per second via command-response to maintain a relationship between the T7 core-timer and the host clock.
I set my resolution to 6 and am getting behavior that seems reasonable, though my data standard deviation doesn't seem to have improved much from the default of resolution 0. I was able to set resolution to 8 and collect at 160 Hz scan rate (without clock data).
Hard to say what sort of noise might be on your signal, so first test with a channel jumpered to GND (or AIN15). When I watch the readings from AIN15 with Range = 10 and Res = 0 or 1, I see 5 different values: -0.000027, 0.000289, 0.000604, 0.000920, and 0.001235. When I change Res = 3 I pretty much just see 2 values. And Res = 5+ it pretty much just sits on 1 value.
To add, in our C/C++ examples there is a testing/c-r_speed_test.c example for testing command-response speeds on your system. This is basically the C/C++ version of the "C-R Speed Test.vi" example we mentioned earlier.
On the modbus map page I didn't even see the "search" box. I just saw the tags box. If I choose "CORE" there the CORE_TIMER doesn't show up, because it doesn't appear to have that tag. (Its only tag is "CONFIG".) It wasn't obvious to me what was going on here, how the tags were chosen, that a register with "core" in its name might not have the "core" tag....
For timing I'm using the linux call clock_gettime which returns in nanoseconds. I don't know what its actual precision really is, but I think it might be microseconds. If I average over a bunch of calls then I don't know that my data is being irregularly sampled. Or at least it's going to tend to average out. I know that my average scan rate is something around 35 Hz, so about 30 ms, for averaging over about 2 second packets. (I tried to set the packet size based on the specs that say I should have something like 90 Hz and I don't get nearly that speed.) I am not getting the speed that you note. Is LJM_eReadName much slower than LJM_eReadAddress? There's no mention in the manual that this is the case (e.g. use eReadAddress if you care about speed.)
The software timed loop is only useful if I'm doing the timing incorrectly. I'm setting a time stamp for a sample when the call to LJM_eReadName returns. Is this wrong? Should I set my time stamp before making this call? Since the time to call LJM_eReadName is varying from 6 to 10 ms that blows away the regularity created by my software timed loop, introducing a 4ms variation. Note that when I tested using a signal generator as input it supported the correctness of my time stamps in that the sine wave looked correct only when I used my non-uniform time data.
My recollection with using LJM_eReadNames was that the improvement in timing was negligible, but I lost the ability to time stamp each channel. I can also revisit this, but I do need a way to know the delay between each channel. Since my observations seem to routinely deviate from the specifications (e.g. it's not taking (3.5 + 0.8 ) ms to take a sample, it's not clear I can rely on the specs to determine the time between samples
Regarding noise level, I recall that when I measured GND as you suggest I got sigma=26 microvolts. I think there was also a bias that was bigger than the signal, but I don't recall now what it was. In the case at hand, I have multiple sources of noise, there's measurement noise in the labjack, there's measurement noise in the rest of my instrument, and there's environmental noise. At res=9 I observe a certain noise level and get a certain data std deviation. If I change to res=1 that data std deviation rises by about a factor of 5. In this case, since the only thing that changed is the labjack, I know it's all measurement noise. When I changed to res=6 my data std deviation didn't drop very much compared to res=1, which I thought was strange, but I didn't pursue it.
Are all the GND terminals internally connected together? (As in, do I need to separately connect them if I'm using more than one of the 4 terminal blocks?)
Without the new register that records the scan time start am I right that the only way to get the time sync I'm looking for is to stream CORE_TIMER? I was wondering about whether I could do it with just the lower 16 bits if I check the full CORE_TIMER just before or after starting the stream, so it's guaranteed to be less than 65 ms before the scan starts. After I call LJM_eStartScan does the scan start in less than 65ms? I thought I read somewhere that while streaming you couldn't do anything else because the labjack was very busy. So this is not the case?
How long would it take for you to add the new register? (Is that a library change? Firmware on the labjack? Both?) We're on a kind of crazy timeline, so we may not have time to go this route. I may just have to make do with the existing capability, even though it means wasting time unnecessarily streaming the time channel---hence my thinking about cutting out the high order 16 bits.
Is LJM_eReadName much slower than LJM_eReadAddress?
Not too much. Maybe 10%.
The software timed loop is only useful if I'm doing the timing incorrectly. I'm setting a time stamp for a sample when the call to LJM_eReadName returns.
That all sounds correct, but without an interval loop you wind up with scans that are not evenly spaced. They have the correct timestamp but are not evenly spaced. That is what you could fix by adding an interval timer. If you know the scans typically take 10-15 ms to complete, then put them in a loop that executes every 20 ms and you will get a scan every 20 ms.
Are all the GND terminals internally connected together?
Yes. All GND terminals are the same thing.
Without the new register that records the scan time start am I right that the only way to get the time sync I'm looking for is to stream CORE_TIMER?
Another possibility might be to read core-timer right before you start the stream, and then add a constant that reflects the typical time from when you call startstream to when the first scan actually starts. I just checked this on my machine (LJM 1.1402 and firmware 1.0208) and the time was 43-46 ms. Beyond the 1 ms variability you want, but still pretty consistent.
I was wondering about whether I could do it with just the lower 16 bits if I check the full CORE_TIMER just before or after starting the stream, so it's guaranteed to be less than 65 ms before the scan starts.
Since the core-timer is 40 MHz it will roll the lower 16-bits every 1.6 ms, so it will be tough to use just the lower 16 bits.
I thought I read somewhere that while streaming you couldn't do anything else because the labjack was very busy. So this is not the case?
That is true for the U12, but all our other devices can do other things during stream. The device is busy while streaming and stream can impact other things, but in general you can do about anything via command-response except analog input reads.
How long would it take for you to add the new register? (Is that a library change? Firmware on the labjack? Both?)
It is just a firmware change to add the new register. You could then read it by address. To read it by name you would need a new JSON that defines the new name for LJM. Usually we can get something like this done very quick, day or 2, but we are focused on a different firmware project at the moment and might not be able to jump on this very quickly.
If I measure time after the LJ_eReadName call returns, and this call takes a variable amount of time, then there's no way to achieve uniformity. I can make the calls start at a uniform time but I can't make them finish at a uniform time. It sounds like you're saying that this call will run in a uniform amount of time if I call it less frequently, which doesn't match up with my idea of how command and response works. I thought you issue command and get response, and so there should be no advantage in waiting between commands, because the labjack is done with the last command and is idle at that point. Do I have a misconception here?
There is the other question of why my data rate is much lower than the spec when I run this way.
I thought CORE_TIMER was reporting in microseconds. I was trying to infer its units from my measurements, though, since I couldn't find any documentation, so I must have gotten confused. If it's 40 MHz that's obviously going to make the higher 16 bits more important.
If I measure time after the LJ_eReadName call returns, and this call takes a variable amount of time, then there's no way to achieve uniformity. I can make the calls start at a uniform time but I can't make them finish at a uniform time. It sounds like you're saying that this call will run in a uniform amount of time if I call it less frequently, which doesn't match up with my idea of how command and response works. I thought you issue command and get response, and so there should be no advantage in waiting between commands, because the labjack is done with the last command and is idle at that point. Do I have a misconception here?
If you use an interval timed loop the average time per loop will be spot on your specified interval, but you are correct that and variability in the execution time of each command will be jitter.
I added an interval timing feature to measure the execution time of each command in my eReadNames C-R speed test program. Resolution is only 1 ms but works decent here. I did 1000 iterations of 3 channels at Range=10 and ResIndex=9. The average time per iteration was 12.4 ms. One iteration took 17 ms, 2 took 16 ms, 2 took 15 ms, and the rest took 10-14 ms.
My implementation then would be to make a 20 ms interval loop that calls this eReadNames command. For my timestamp I would read the system timer at the beginning of each loop so the timestamp always increments by 20 ms. Thus it is a timestamp of when made each call. The variability of the time per call means that there will be perhaps +/-3.5 ms jitter in the actual time of each call, but over the course of 10 seconds I will get 500 calls.
There is the other question of why my data rate is much lower than the spec when I run this way.
Start by running the test programs mentioned in post #12 to see what results you get. I would have to ask someone else here if a Pi typically hits the same speeds we see on full machines.
I thought CORE_TIMER was reporting in microseconds. I was trying to infer its units from my measurements, though, since I couldn't find any documentation, so I must have gotten confused. If it's 40 MHz that's obviously going to make the higher 16 bits more important.
Here is the description from the JSON:
"Internal 32-bit system timer running at 1/2 core speed, thus normally 80M/2 => 40 MHz."
It has been decided that we are going to use the approach of checking the Pi clock, starting streaming, and using an estimated delay duration to apply time stamps going forward. I've been trying to estimate this delay value using code that looks like
LJM_eStreamStart(...);
LJM_eReadAddress( ... CORE_TIMER ...);
get_system_clock();
When I do this at a scan rate of 160 Hz (with 5 channels, my 3 data channels and 2 clock channels) this delay is typically around 4 ms. When I do it at a faster sample rate the value is sometimes negative. Is there an expected relationship here? (Is the first streamed sample delayed by the sample rate?)
Another problem I've been having as I've been stopping and starting the program is that sometimes my program hangs indefinitely at the StartStream call and I have to kill it and restart. Then it usually works. This seems to be more likely when I choose a faster scan rate. Another thing is that I don't seem to be able to get the scan rates I expect based on table A1.4. For example, it says 100kHz at resolution 1, so I selected 19kHz scan rate with 5 channels and I get various errors such as INCORRECT_NUM_RESPONSE_BYTES_RECEIVED. Once I get this error it seems like I have to unplug the labjack to get it working again. (Is there a better way?) It seemed like It seems to be working at 10kHz scan rate, but with the occasional hanging at StartScan as described above. At res=4 the advertised sample rate is 11 kHz. I seem to be able to run at 3.5 kHz but at 3.6 kHz I get STREAM_SCAN_OVERLAP error. So any advise on how to determine the maximum reasonable streaming rate? And how to avoid hanging and errors? Ultimately I'll take the timing channels out and be back at 3 channels, so what I'll need to choose is maximum scan rates with 3 channels.
Another question: when streaming what is the expected delay between channels of the same scan? Should it be the value from the last column of table A.1.3?
I've been trying to estimate this delay value using code that looks like:
LJM_eStreamStart(...);
LJM_eReadAddress( ... CORE_TIMER ...);
get_system_clock();
When I do this at a scan rate of 160 Hz (with 5 channels, my 3 data channels and 2 clock channels) this delay is typically around 4 ms. When I do it at a faster sample rate the value is sometimes negative.
I'm not quite following. You call streamstart, read the core-timer, and then note your system clock. What delay is 4ms or negative?
any advise on how to determine the maximum reasonable streaming rate? And how to avoid hanging and errors?
The hanging should not happen, but could be related to your scan rate problems so lets tackle that.
- Make sure you are running the latest LJM and firmware and confirm to us what you are running. Someone here can test the same using our Python stream example on a Pi. We are running LJM 1.1402 and T7 firmware 1.0208 ourselves, and on my Windows machine I did notice a problem with ResIndex>1 streams (not going nearly as fast as they should) but at ResIndex=1 I can go 3x35k over USB and even faster over Ethernet.
- In addition to your write of STREAM_RESOLUTION_INDEX=n, add a couple writes before your streamstart to do AIN_ALL_RANGE=10 and STREAM_SETTLING_US=0 so we know for sure what range and settling are set to.
Another question: when streaming what is the expected delay between channels of the same scan? Should it be the value from the last column of table A.1.3?
Those would give a rough guess, but the actual number you want for stream mode is the "Interchannel Channel" delay specified in the last column of Table A.1.5.
https://labjack.com/support/datasheets/t7/appendix-a-1
My firmware is 1.0188, which looks like the latest non-beta. Looks like LJM is 1.0703 for ARMv7, which appears to be the latest. I was already setting AIN_ALL_RANGE to 10. I added STREAM_SETTLING_US set to 0. I'm still getting the hanging behavior. I tried to stream 3 channels at 35 kHz scan rate and resolution 1 and got the same error about incorrect response.
Regarding my timing method, I neglected to note that the difference is between the first sample of the stream (which is CORE_TIMER) and the value returned by the call immediately after starting the stream. So to summarize, I start the stream, I call eReadAddress to get CORE_TIMER. I fetch the stream data, which includes CORE_TIMER and look at the first two channels. That difference is between the two times reported by labjack, one in the stream and one right after starting the stream. This difference is about 4 ms when my sample rate is 160---the stream starts 4 ms after eStartStream returns. At faster sample rates it's a negative number (the stream starts before I get my time report back from eReadAddress), for example -3ms. I don't think there's anything wrong with it being negative.
Regarding max. stream speeds tests on our Raspberry Pi 3 using LJM 1.1200 (latest beta armhf build) and T7 firmware 1.0208 (latest beta):
With 5 channels with resolution index 1, around 13 to 14K I saw reliable stream scan rates speeds. I tested in C since that is what you are using and used a USB connection. Note that by "reliable" I mean there were no -9999.0 samples which are skipped samples caused by a stream buffer overflow error on the T7. I could get up to around 23K but with skipped samples, then eventually run into STREAM_SCAN_RATE_OVERLAP and STREAM_SCAN_RATE_INVALID errors.
With 3 channels and resolution index 1, reliable scan rate speeds are around 19 K. Over 19K to 24K, after starting stream mode there is an initial stream buffer overflow (-9990.0 samples), but after this initial overflow the stream was running fine until the next stream start.
Try the latest T7 beta firmware and see if that helps the INCORRECT_NUM_RESPONSE_BYTES_RECEIVED errors. If not, then we may have resolved that further with a newer LJM version, and would need to look into updating the ARMv7 build. STREAM_SCAN_RATE_OVERLAP and STREAM_SCAN_RATE_INVALID errors will indicate that the T7 hardware cannot stream faster with current stream configuration.
I updated the firmware to 1.0208 and am not noticing a difference. With 3 channels at 20 kHz I get the INCORRECT_NUM_RESPONSE_BYTES error. At 19 kHz it looks like it works, but I ran it four times and two of the times it hung on eStartStream. Note that I have not noticed invalid data like -9999, just the various errors and hangs. Also i just noticed the reported backlog values are large, which is a bit strange. As I try to investigate what's going on whith that I get a new error: "STREAM_IS_ACTIVE, 4010". The only resolution seems to be to unplug the labjack.
Actually now I am now getting the -9999. It doesn't happen at the beginning of the first frame (which is all I was looking at because I was trying to study the delay for starting up the first frame). I'm getting device backlog of ~300 and scan backlog of ~70000 for a few packets and then it fails and I get -9999s. I don't understand why I have a device_backlog when I have just *started* the stream. My interpretation was that device_backlog meant I had that many packets to retrieve from the stream waiting for me. I have it set so I get 2 packets per second, and yet I'm starting with 150s of data already waiting??? It doesn't seem to make sense. (And there's no timing gap between the time stamp on the first stream and when I call eStartStream.) So I must have misunderstood. What does device_backlog actually mean?
I just ran it for a while and the device_backlog stayed around 200. The scan backlog started at 10k and then rose gradually to 200k whereupon I got LJME_LJM_BUFFER_FULL. I reduced scan rate to 10kHz and the device backlog is now around 80 and the scan backlog seems to be stable around 4500.
Another thing that has me puzzled is that I set resolution to 4. At this resolution it was previously working at a scan rate of 3 kHz, but not any more. I'm getting the incorrect_num_response_bytes error.
I'm finding that even at scan rate 1000 Hz, resolution 4, with 5 channels, it works for a short time, like maybe 5 stream reads, and then I get the INCORRECT_NUM_RESPONSE_BYTES_RECEIVED error. The device backlog stays at zero.
Relating to maximum speeds and trying to improve performance, try starting your application's process with a higher CPU priority and see if that helps software stream performance. This might help with the -9999.0 samples and LJME_LJM_BUFFER_FULL errors at faster stream speeds. You can use the "nice" command to start an application and specify its priority. I will point out that it seems like you are using an older Raspberry Pi with a slower processor, so in your tests you might not be able to reach the stable/reliable stream speeds I was seeing. What Raspberry Pi model are you using?
With the STREAM_IS_ACTIVE error, that indicates that stream mode was started but not stopped. In this case power cycling the T7 will stop stream mode, or you can detect this error, stop stream mode and then start stream mode again.
We are looking further into the other issues.
I have Pi3 Model B rev 1.2. I think this may be the latest. Is it really beyond such hardware to transfer at 10 kHz? I assume all the hardware has to do is read data arriving by USB.
I did another test: I connected the same Pi to a labjack that I did not upgrade, so it's running Firmware 1.0188. This is behaving better. I am able to run resolution 4 up to about 3200 Hz scan rate with 5 channels. I get device backlogs as high as 26, and I didn't look at all the data so there could be hidden issues. But with the upgraded firmware I cannot even run at 1000 Hz without getting the INCORRECT_NUM_RESPONSE error.
Again using the old firmware I tried pushing the rate at resolution 0, but even at 15 kHz with 5 channels i have problems. I got STREAM_NOT_RUNNING after it fetched two packets with high backlog (200). Even at 10 kHz the scan backlog is rising so I assume it's going to fail eventually....yes, just got BUFFER_FULL. I have the same behavior at 8 kHz. I see that top reports one cpu at 95% and another at 58%. I tried renicing to -19 and it didn't seem to have any effect. (Nothing else is running on the Pi, so we're only competing with the OS, and the other two cores are idle.) At 6 kHz it finally seems to be stable.
I haven't heard anything for a couple days. Do you have any insight into the INCORRECT_NUM_RESPONSE error? Should I back out to the old 188 firmware, since it seems to work better? Or will there be a new LJM library that works properly with the new firmware?
We are working on new firmware to solve this issue and add the new register that will report the stream_start time stamp. Please send an email to [email protected] so that we can send you the new firmware when it is ready.
I installed the new 214 firmware and have done some testing. I still have the problem that about 50% of the time it hangs on stream start.
At resolution 4 I seem to be getting better performance. I actually had it working above spec with 3000 kHz scan rate and 5 channels. When I go to resolution 1, the behavior appears to be the same. I got the INCORRECT_NUM_RESPONSE error at 19 kHz and I had to lower the rate to 6 kHz in order to get robust performance. I'm more interested in higher resolution, slower sample rates, so I'm probably not going to pursue this end of things.
The new timing register is working great. I did notice that there's a 133 clock tick discrepancy. So if R is the sample rate, and SS is the start scan time returned from the new register, and FS is the first scan returned when I stream CORE_TIMER, then I found that FS = SS + 40e6/R + 133. It doesn't make a difference to me, but I thought it was curious, and it always seemed to be exactly that value.
An observation is that whereas before the labjack would hang when I tried to read from the stream now it hangs when I call eStartStream. As before, about half the time. I'm not sure if this is because I'm making more calls before I call eStartStream. But if you have any ideas it would be nice if I didn't have to deal with this.
For testing, power cycle your T7, then in your application open the T7 and then do eStreamStart with minimal calls in between. Is there a hang? To double check, by "hang" do you mean that the eStreamStart call stalls indefinitely and does not return?
If you find that certain calls/configuration before eStreamStart are causing issues, please provide the calls.
I'm not sure what you're suggesting for the test. Call eStreamStart then eStreamStop and then eStreamStart again?
By "hang" I mean that the call stalls indefinitely and does not return. It doesn't appear that the problem is specifically eStreamStart, because that call used to succeed until I inserted before it a call to eReadAddress to get timing data. The earlier version of my code would stall on the next call, when I tried to read the stream.
For your test I meant remove the calls before eStreamStart and see if you still have the problem. This is to see if a certain configuration or set of calls are causing an issue, or if stream mode in general is having the issue. Also, you could try our stream_example.c example in the C/C++ examples download and see if you get hangs.
If you find a set of calls that can cause the issue, please provide that chunk of code so we can see the registers you are reading/writing and how your eStream start is configured so we can try to reproduce the issue.
I have attempted to reduce my example to something small that exhibits the behavior. It appears that it suffices to open the labjack and start the stream and then close it. If I run this program again, it hangs when I start the stream. I commented out my resolution setting code and the code that actually reads data and I still get the hang. It seems to happen reliably every second time I run it (running on the raspberry pi with the 214 firmware you sent).
I've attached my reduced program that exhibits this behavior. Note that with the data reading commented out it doesn't actually display any valid data. What happens is that I run it and get a bunch of bogus data (zeros). Then I run it again, the labjack lights flicker, then it hangs on "Starting stream...." and never gets past that point.
I wasn't able to reproduce the hang on LJM_eStreamStart yet. I will continue looking into this tomorrow and I will update this thread tomorrow as well.
I tried again to reproduce the LJM_eStreamStart hang, but was unable to. I compiled and ran your program many times on a Pi 3 rev 4. I tried altering your program:
I tried with LJM 1.1403 firmware 1.0214 via USB connection.
Some ideas:
Then, let us know what the output of `thread apply all bt` is. The LJM .so is stripped (of debug symbols), but in my quick test I was able to see some LJM functions in the backtrace.
What's the longest you've left it to hang for? (Tens of seconds / minutes / tens of minutes?)
I tried running my program under gdb. It says
Thread 1 "labjacks" receivd signal SIGSEGV, Segmentation fault.
0x76b4fa58 in ?? () from /usr/lib/libusb-1.0.so.0
Note that it didn't hang. I don't get a segfault when I run normally. I typed the command you requested and got this:
(gdb) thread apply all bt
Thread 3 (Thread 0x76017450 (LWP 8528)):
#0 0x76b73b10 in [email protected]@GLIBC_2.4 ()
from /usr/lib/libpthread.so.0
#1 0x76b73578 in [email protected]@GLIBC_2.4 () from /usr/lib/libpthread.so.0
#2 0x76016a78 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
Thread 2 (Thread 0x76986450 (LWP 8527)):
#0 0x76c7fa34 in poll () from /usr/lib/libc.so.6
#1 0x76b50ec0 in ?? () from /usr/lib/libusb-1.0.so.0
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
Thread 1 (Thread 0x76988000 (LWP 8524)):
#0 0x76b4fa58 in ?? () from /usr/lib/libusb-1.0.so.0
#1 0x00000000 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
I'm confused about the LJM version. Am I supposed to be using armhf? The version I'm using is: LabJackM-1.0703-Angstrom-Linux-ARMv7
I just let it hang for 12 minutes. That's my longest test.
12 minutes is long enough to indicate that there's almost definitely a deadlock, rather than any sort of delay. Try the armhf version. It's for ARMv7 with hard floats. (We probably need to rename our release types in the future to help avoid confusion.) The recent 1.1403 release might fix some old bug that causes a deadlock. Let us know if it doesn't work, of course.
I tried upgrading to the 1403 release. I noticed that the performance seems to be much better. My deadlock values are lower on the settings I was using. (I didn't try to see if this resolves the inability to achieve 100 kHz at resolution 1, but it made me wonder.)
The hanging behavior has changed. The way I wrote my code I have an infinite loop that collects data, and I use signals to close the stream and the labjack when the user hits ^C. Now when I hit ^C, it hangs indefinitely (>10 minutes) trying to close the stream. To get out of my program I have to kill it from another terminal.
If I alter my program to just collect a few times and then quit and run streamStop then everything seems to work without any problems. I ran this code about 10 times in a row without any hangs. Any thoughts on why I have problems stopping from a signal handler? Does the LJM library use signals in some way that could play a role?
LJM does handle signals (by default). If you are currently handling signals, I recommend either not handling them or calling the LJM handler from your handler as described here:
https://labjack.com/support/software/api/ljm/does-ljm-handle-signals
I removed my signal handler and now everything is working as desired.
Also note that with the new LJM I'm able to get faster data rates. If I set resolution 1 I can get a scan rate of 25kHz, whereas before it was a struggle to get 6 kHz. (When I tried a higher scan rate, though, I got the error about wrong number of response bytes after a short while.)
One odd thing I noticed: I often get the first packet of data is bad, full of -99990 values. This wasn't happening before with the other LJM.
I've replicated the -9999 issue. I'm not sure what's causing it, but I'll keep you updated. One thing that I noticed is that the initial -9999 error never happened for the first stream after the T7 was unplugged and replugged. Alternately, I was able prevent the initial -9999 by rebooting the device by writing 0x4C4A0000 to SYSTEM_REBOOT after stream. See the attached stream_example.c.
I'll update this thread when I have more information.