05-17-2011 06:15 PM
Greetings message board gurus,
I have a data synchronization puzzle that involves the data acquisition (DAQmx) from PSD detectors (indicated as trap0 Vx and Vy on attached JPG) and the voltage output to an EOD (used to move laser traps for optical tweezers). We need to know exactly when the command for the change in output voltage occurs compared to the input voltages, in order to properly reduce the data we acquire.
There are two parallel loops: one does the data acquisition from our PSDs, set to run continuously through EOD changes; it has it's own loop iteration period of ~100ms, sampling at 20kHz. The other switching the ao0 voltage to the EOD through two states based on a difference from an initial value, and it's period will always be different from the data acquisition loop.
Our setup is:
Labview 2010 SP1 on WinXP SP3
the inputs and output run off of the BNC-2090 connected to the PXI-6251 in the PXI-1036, linked by the PXI 8336 (MXI-4) via fibre optic to the PC.
We have tried to simply run the analog output associated with our EOD output voltage as an additional input to be recorded with the PSD voltages, but this of course ties up the output channel so that it cannot be changed by the other loop. (this seemed like a hack to me anyway) I'm also getting a DAQmx read error 200983, which doesn't turn up anything directly helpful in a google search.
The solution to this synchronization seems like it must be something like maybe: a timestamp with our PSD voltages, or sampling a local variable like DAQmx acquisition at 20kHz, or maybe a triggering on the loops, or maybe some other method of synchronisation?
05-18-2011 10:48 PM
Hi zapmx
First to comment on the error 200983, this is common if the property is unavailable for the board or inappropriate for the task. For instance, it does not make sense to read the Available Samples Per Channel on a task that writes data. Instead there are Write Status attributes like Space Available on the Buffer. Looking at your code, I only see one task which seems to be your read task, so that should be fine. It's not immediately clear why you are getting this error, but it should not be necessary to use this property for synchronizing your tasks. This property is more appropriate in situations where the timing durring buffering is a significant factor.
For synchronizing your Analog Input and Analog Output, there are a few different options, as you noted. For instance, you can setup one task as the master and one as the slave, exporting the master's clock and start trigger to the slave. Then, both tasks would start at the same time and process data in synch. I recommend looking at one of the NI Shipping Examples in our Example Finder to see how this can be done (see the example Multi-Function-Synch AI-AO.vi).
Looking at your code, it wasn't obvious where the Analog Output task was, so I wondered if maybe it is abstracted away in one of the subVIs and not easy or possible to configure in synch with the Analog Input. In this case, the Analog Output is just running with any availablity for modification or association between clocks. If this is the case, I would export the Analog Output to a common line, one of the PXI backplane Trigger lines, and input this signal to be a analog trigger on the Analog Input. You can set the voltage levels that you want to look for so when you see some expected noticeable change, the Analog Input begins, and you know that the beginning of the Analog Iinput corresponds to a certain point in your generation.
The above approaches are signal-based synchronization. Timestamping, or time-based synchronization, is certainly possible, but is typically more appropriate when the tasks are not physically close to each other so that transfering signals is unavailable. But, time-based synchronization can be used between tasks on the same board, and you may still want to use this approach. You can read a little more about time-based synchronization considerations here.
05-31-2011 08:06 PM
Hi Eric,
I've removed the abstraction of the subVIs, and have placed the create and stop tasks for the DAQmx analog out outside of the while loop. I've attached a screenshot to this post. I think this change will got rid of the inconsistent loop timing i had observed.
I've also tried a few different changes with the VI, but the 200983 error persists. I'm not sure why we originally wrote in this code for a scan backlog in our VIs, but it seems your right, because i think the scan backlog will indicate 0 while the VI is running.
Looking at the example you mentioned,
Program Files\National Instruments\LabVIEW 2010\examples\DAQmx\Synchronization\Multi-Function.llb\Multi-Function-Synch AI-AO.vi
it seems like in our case i would have to put the trigger in one of the while loops. The reason i'm thinking that i because we want our data acquisition to be continous while we flip our trap position to different settings - but we want to track that outputted voltage to do calculations on the measured voltages in the next frame of the stacked sequence.
Could one do a triggering like this with parallel loops? or is this a mistaken approach?
With the example you've pointed to, i better understand what you mean by,
"I would export the Analog Output to a common line, one of the PXI backplane Trigger lines, and input this signal to be a analog trigger on the Analog Input."
but the common line, PXI backplane trigger line, i'm not sure what this refers to.
thanks for your insight! I'm going to check out the timing link.
06-01-2011 05:02 PM
Scan backlog is often used to monitor speed of a the DAQ acquisition in order to match processing and acquisition. This is generally only useful in rare cases that push performance of a device. If you are using it to manage basic acquisition and processing tasks, you generally can accomplish it with a proper selection of sampling rate, and the number of samples to read from the board at a time.
I don't think you want to use triggering in parallel loops. It sounds like basic continuous acquisition can meet your needs. If you configre the task to perform continuous acquisition and you set the number of samples to read to -1, then it will read everything available at that time, or you can select a specific size for the chunk of data, as long as it is not mismatched from the buffer operation (overflow and underflow conditions can occur). If you want to move that data outside of the loop for processing, there are a couple of options, all of them involve setting up a shared resource. A basic one would be to use property nodes to access the value of an indicator, but this can cause complications when you try to do sophisticated operations (like making sure you only process each acquisition once, or making sure you don't write to the value while you are reading it). It takes a little more setup, but it sounds like you could use a queue with a basic producer/consumer paradigm. You can see an example of that if you create a new VI using that template (New»From Template»Frameworks»Design Patterns»Producer/Consumer Design Pattern(Data)).
The PXI devices are modules inside a PXI chassis. This chassis requires one controller, which commonly can be an embedded controller or an external controller through a connection like MXI (or fiber) to a card in an ordinary PC. This second configuration extends the PCI bus to include the devices in the chassis as if they were directly attached to your motherboard. In addition to connecting to the PCI bus, the PXI bus has specialized lines that basically encapsulate the RTSI lines on PCI boards. These special lines allow boards and the controller to route triggers and clocks so that different devices can operate synchronously. In a basic synchronous configuration, you can setup one device as a 'master' to output it's internal clock and internal start trigger onto the PXI bus, and then other devices can be setup to use an external clock and trigger, and route the clocks and triggers from PXI bus.
06-02-2011 02:55 PM
Hi Eric,
For the scan backlog it seems you should be correct, since the acquisition card (pxi-6251) will do 1.25MS/s, with a 10MHz clock rate; our acquisition of 4 channels at 20kHz would top out usage at 0.1MS/s at most, or 10% of the available bandwidth, roughly speaking. We've pushed our acquisition to 6 channels at 50kHz, which seems to translate to ~30% of the bandwidth. (is this realistic?)
You're right that we only need a basic continuous acquisition.
I've been looking at the Producer/Consumer design pattern, and it seems useful for a different part of our VI, where we reduce our data (passing the arrays from the DAQmx acquisition into Enqueue Element, and reading them from Dequeue Element).
But it appears that i would require a 3rd parallel loop to switch the output voltage over DAQmx output. Or would you suggest putting our output voltage loop as the producer, and the data acquisition as the consumer loop?
many thanks.
06-03-2011 03:13 PM
Glad to hear my posts have been so useful .
I think you are looking at just under 25%ish of the bandwidth limit, but yeah, you should be good, and still plenty of room for expansion too.
You can have the control of output voltage in the same loop as the data acquisition if it is a non-blocking call. So, for instance, if you have a GUI control inside the loop, the call to load the value from the GUI control does not cause any waiting to occur. A short file operation could work with a longer DAQ acquire. If you do file operations that are lengthy, they can cause the loop to wait too long for each iteration to handle the data coming from the DAQ. Basically, when you tunnel a value from something either you want that operation to be effectively instantaneous or you want to have a 3rd loop and use a shared resource structure. I might use something more like a notifier architecture in your case, so that the DAQ loop runs with the latest value in the notifier but isn't blocked if data acquisition is faster than updating the new output voltages, but I'm not sure what you want to use to control the output voltage. The notifier setup with a shared resource is an architecture that's appropriate for slow file systems like pulling values from networked variables or something like that, but that depends alot on the (often small) application details.
06-07-2011 01:38 PM
Hi Eric,
I've put something together following the Producer/consumer design pattern that uses a queue. I've attached an image of my two parallel loops. These two loops are within a stacked sequence structure that is basically two frames, the other frame containing a file saving snippet.
I have a few questions about a queue setup like this:
Our lab computer is an Intel core2 duo running on a Intel P965 chipset, with a firmware RAID-0 of two hard drives, so i think our file recording could be fast, but McAfee AV corporate is running on our machine, which might cause problems for continuous file writes?
thanks for any other tips.
06-08-2011 01:00 PM
I left two mistakes in my previous VI post: '-1' for the continuous samples buffer, and no indexing on my lower while loop.
But i'm not sure what a good value would be for a buffer size in continuous acquisition. any suggestions?
thanks again.
06-08-2011 04:42 PM
"the time out terminals on the Enqueue and Dequeue, should these be left unwired in a setup like this?"
Since you have -1 written to the timeout input terminal, the timedout? terminal is unused. This can be a problem if the queue fills up faster than the consumer can process, because that will basically block the loop until the queue empties. You can leave this in so that the loop breaks if you ever have data overflow, or you could include a case that handles what to do if the queue is too full (skip the newest data, or dequeue an element until there is room so you have the most recent).
"I've use a small frame in the top while loop to try to make the output voltage change the last operation of the while loop iteration. Should i use an error wire instead?"
I'm not sure which frame you are mentioning, but I always use wires instead of framed sequences. Framed sequences provide almost no advantage over basic data flow, and they are much more confusing. I make exceptions for single frames that group a few VIs together, but I never used multiple frames because it increases the time to develop, troubleshoot and maintain my code.
"I presume the continuous acquisition on the DAQmx reading path will be independent of the timer delay i've placed in the True/false structure"
The DAQ read will be independent of the wait, but the DAQ is forced to be after the timer delay, and it is not clear that it serves any purpose. If you want to have it run after the DAQ read, use data flow with an error wire or some other wire. If you are trying to ensure that the loop takes at least a certain amount of time to execute, it does not neet to be in the data flow to the DAQ write.
"I guess the compilier will make the data acquisition continuous within the loop?"
That's more or less correct. The data acquisition is dictated by processing that is performed on the board, not the computer, but configuring that is all you need to do.
"I presume i have nothing like a blocking call in these loops..."
The blocking calls in your producer loop would be the wait, and the enqueue element. Both of these are fine, and may serve necessary purposes (I like having an enqueue element as a blocking call because it shows me when the data throughput is not what I expect), although the purpose of your wait is not clear to me.
"Does this look like a notifier would be better?"
No, this looks like a better case for a queue. A notifier would squash the old data (although the notifier wouldn't block because it doesn't get full).
"thanks for any other tips."
One thing I recommend is some code clean up. Once a project becomes more complex like yours, I prefer to use a project to manage multiple VIs, and then make excessive use of subVIs. One of the most important performance measures for me is intuition. Code that can easily be understood means that it will do what you expect it to without you needing to keep track of many silly details. SubVIs can encapsulate logical chunks so that the logical unit takes up less space on the block diagram, and even if it's not reused it can help visualize the essential elements of logic (although this requires more time spent making pretty icons so the VI is sensible, and displaying labels of the VIs). I also recommend using data flow instead of sequences whether you can. Stacked sequences with heavy nesting combined with multiple nested loops means that I have no idea what my code will actually do until I run it in highlight execution. Although the graphics of highlight execution are pretty, I don't trust code that depends on that for understanding.
06-16-2011 01:31 PM
Hi Eric,
thanks for your reply, I tried using a frame over the time delay to force the output voltage to execute last, after measuring that position for the desired period of time.
However, i found that the output suggested that either the output voltage flipped at the beginning of the loop or somehow the wrong value for the output voltage was going into the queue bundle.
I then tried two different things: simply removing the frame around the Wait (ms) VI and taking the error wire from the continuous acquisition into the output voltage VI to make it execute last. This seems to correct the output:
(what that looks like in the VI:)
and
I was initially baffled by the difference between the difference produced by removing this frame. Now my guess is that it is the autostrart boolean on the analog out that is executed differently by not having that frame over the Wait (ms) vi.
For that matter, would there be any difference observed if I included start and stop task VIs outside of the while loop on the continous acquisition, or if i removed the autostart boolean and used start/stop inside the loop on the output voltage?
turning to your answers from your previous post,
"Since you have -1 written to the timeout input terminal, the timedout? terminal is unused. This can be a problem if the queue fills up faster than the consumer can process, because that will basically block the loop until the queue empties. You can leave this in so that the loop breaks if you ever have data overflow, or you could include a case that handles what to do if the queue is too full (skip the newest data, or dequeue an element until there is room so you have the most recent). "
I think that might be the case in terms of what i'm seeing in the output. The flat zeroed lines in my graphs come from the error frame of the consumer loop. I understand that the initial zeroed arrays would be because the queue is initially empty, but the subsequent error states come as a surprise to me. Not only that, i have a termination boolean in the eror frame of the consumer loop that should terminate both producer and consumer loops.
Another problem i've found is that in the second and subsequent runs of these loops, the consumer loop does not appear to be running, i.e. the producer and consumer loops are in a sequence that is in a while loop; the next frame being a file saving. Thus the second execution of the overall sequence produces no data and empty files. Is a queue cleanup needed on the consumer loop? Or a time delay on the consumer loop?
"One thing I recommend is some code clean up. Once a project becomes more complex like yours, I prefer to use a project to manage multiple VIs, and then make excessive use of subVIs. One of the most important performance measures for me is intuition. Code that can easily be understood means that it will do what you expect it to without you needing to keep track of many silly details. SubVIs can encapsulate logical chunks so that the logical unit takes up less space on the block diagram, and even if it's not reused it can help visualize the essential elements of logic (although this requires more time spent making pretty icons so the VI is sensible, and displaying labels of the VIs). I also recommend using data flow instead of sequences whether you can. Stacked sequences with heavy nesting combined with multiple nested loops means that I have no idea what my code will actually do until I run it in highlight execution."
I honestly could not agree more. Unfortunately, much of our code is inherited, from labs that i understand usually had graduate students out of physics coding in labview 6, which means that they might not have education in object oriented programming or understand what good programming style and pseudocode are. I have a difficult time convincing the other grad students and post-docs in my group that the quick and dirty approach (to have the instrument working ASAP) simply creates more work in the long run.
thanks again.