02-11-2019 10:34 PM
There are some strange things with your code. You appear to be collecting data at 2 Hz and collecting for 5 seconds, for a total of 10 samples. Sounds to me like you want to think in a slightly different (truly serial, not parallel) manner. One thing isn't clear in your design -- do you want to acquire data (and basically ignore it) until you say "Go", then acquire and save the data?
For such a small data sample, the simplest thing to do is to set up your DAQmx code to acquire 10 samples at 2 Hz when you push a "Start" button (learn about Event Loops to know how to "do something when I push this button"). You can set up the DAQ code (even using the DDA!) to also automatically save these data to a file.
What I thought you were doing was to collect data at some reasonably high sampling rate (say 1kHz) for some reasonable amount of time (say 5 minutes) where the amount of data might get to be a problem to hold all in memory. [For the time being, I'm not thinking about starting at the push of a button].
Suppose you have a DAQmx loop that samples 1000 points at 1kHz and simply throws the data away. How fast does this loop run? Why, precisely once a second, as the time to collect 1000 points at 1000 points/sec is 1 second. Suppose you want to collect for N seconds (still throwing away all the data). Simply execute the loop N times (a For loop might be good for that, if you know N in advance).
But now you want to collect and save the data. OK, your once-a-second loop is the Producer, which every second puts 1000 points on a Queue (as an Array of points), and spends 99.99% of its time "waiting" for the DAQmx hardware to finish. When the Consumer gets the Array, it has all that "free time" to get the data saved to disk, again in a loop. Note that the Consumer loop probably wants to open the output file before the loop starts, save the data inside the loop (with the file reference carried in a Shift Register), then close the file when the Consumer exits.
One more detail -- how do you stop multiple loops? Here's a trick -- "information" flows from Producer to Consumer. The Producer "knows" when the process ends (in your case, after N seconds). It needs to send a "Done" signal to the Consumer. You might think "get the Producer to Release the Queue, which will cause the Consumer to show an Error when it tries to Dequeue", but in the Worst of All Possible Worlds (i.e. "most of the time"), you'll miss some data. Much better is to get the Producer to send a special signal, a "sentinel", that says "No more data are coming from me, so you, the Consumer, can safely exit when you see this signal". If your Queue is an Array of data (such as 1000 points), get the Producer to send an empty array when it exits, and get the Consumer to test for an empty array when the data are dequeued. If the array is not empty, then save the data, otherwise if the array is empty, the Producer is done, so the Consumer can exit and the Consumer can safely release the Queue.
Bob Schor
02-12-2019 12:59 PM
Thank you, Bob_Schor, for taking the time to assist me. The 2Hz collection is to keep things simple while I test the vi. The end goal is two channels measuring at 500kHz for 5 seconds with synced time. I attempted to add a sentinel based on other examples I have found, and I scrapped the data collection timer (since I know how many N data points I want, as you pointed out). I have also removed the "Collect Data" button for now to keep things simple.
Upon starting, my vi instantly prompts to save, and then saves an empty file, which is a problem. I am not sure how to use a "For Loop" with waveform-type data to collect N points. I am also unsure what the purpose of the yellow Error wires is.
My vi is shown below and also attached. Any help is greatly appreciated.
02-12-2019 04:22 PM
The Error Wires serve two helpful (and vital!) purposes. First, they inform you when you do something "wrong" (like try to write without opening a file), and second, help organize your code by providing a visual indication of steps (VIs) that should be undertaken in a serial order. If you attempt to keep your Error lines straight (so Errors don't "leak out" through the sharp corners), you will also generally have a neater, more readable (and hence more debuggable) VI.
Do you know how to write a sub-VI? Write one called "Get File Name", give it the "standard" 4-2-2-4 Connector Pane (which you'll get with a New VI), put Error In and Error Out on the lower corners, and inside, asks for (and export through an Upper Right indicator) the File Name.
Now, arrange your two Loops and their Error Lines as follows:
Bob Schor
02-13-2019 09:36 AM - edited 02-13-2019 09:40 AM
Thank you again for your help, Bob_Schor. I re-wired the error wires as straight as possible and hopefully correctly. I must not be defining the file name correctly in the producer, because the sub-VI does not have error inputs/outputs. When run, the vi immediately prompts a save folder dialog despite defining it in the "File Save Path" Control, and a text file with one comma is saved rather than data. If the initial read waveform happens to be 0, does the Consumer interpret this as the Sentinel, thereby immediately terminating the vi? Maybe this is the issue.
My vi is shown below and attached. Any help is greatly appreciated!
02-13-2019 12:22 PM
Some comments (in random order):
Bob Schor
02-13-2019 07:38 PM - edited 02-13-2019 07:39 PM
Bob_Schor, your help and tips are fantastic. I am keeping the Build Array before Export Waveforms because I plan on adding a second waveform once this vi works. I see the use of the error wires now; I receive an error immediately after the For Loop finishes. The good news is that the array indicator after DAQmx Read shows the correct sampling time and rate occurring. There is something wrong with the Sentinel, I believe, because the error complains about an incorrect input parameter. I am using a timestamp from the year 1600 as the signal for the Consumer. I also do not think the vi gets to the Consumer because the Waveform Chart remains blank. I'm not confident that I have initialized the save file either. Any help is greatly appreciated! Thank you
My vi is shown below and attached.
02-13-2019 09:40 PM
OK, there are multiple lessons here, and the easiest way to do this is to make a simple Demo that illustrates the major points. Here's a Picture (this one is not a Snippet, so I'm attaching the VI) and a bunch of points. Please pay attention to each one (unlike your last attempt, where you followed some of them and ignore the others).
Block Diagram of P-C Demo
Try this out. I put a constant name in for the File Name -- that kind of makes this a "run one time" VI, which is why I suggested you make a sub-VI that asks the User for a File Name, makes sure the Name isn't already holding data, and then lets the Program proceed. Note you want to do this before the Producer starts.
Play with this. Make sure you understand the parts. This simple(r) VI requires no hardware, you can play and change things to see how all the parts work.
Oh, yes, one more thing -- I changed the Graph to show "Relative Time" (meaning "Time from beginning of recording"), since having the date and time of day on the graph isn't particularly useful. I also changed the format to HH:MM:SS so I could see the 5 seconds of data (otherwise the time labels would be 00:00, 00:00, 00:00, boring).
Bob Schor
02-14-2019 10:01 AM
Thank you for help and patience, Bob_Schor. I have read through your comments several times (including those on other posts), and I am learning a great deal. I am unable to get the "Producer-Consumer Demo.vi" you sent to work. The only thing that changes is the Producer Index counting up correctly. The Consumer index remains at 0, and the Waveform Chart remains blank. I have tried defining the File Save Path before and after hitting Run, but this makes no difference. Does this Demo work for anyone else? Perhaps there is an issue with my version or installation.
02-14-2019 11:35 AM
I found my error and the vi is working for me now. I'll keep playing around with this and see how far I get when applying it to my individual project.
02-14-2019 08:07 PM
After playing around with the demo, I was able to make a producer-consumer vi with 2 simulated square waves as inputs. The user is able to have the signals running and then collect N seconds of data multiple times, all while the signal is un-interrupted. I output the square signal as Analog Output to my NI-DAQ and confirmed this with an oscilloscope. Both signals are saved to a single csv file in 3 columns (time, Y1, Y2). This working vi is shown below first.
(I used the same producer for both because the queued signals will always be equal lengths with equal dt values. To collect the correct N number of points, I define the "Number of Samples" and then loop just once through the "For Loop" to collect all points. I tried keeping the "For Loop" as a second clock and looping through multiple times (once per second), but then the timestamp of the recorded data reset to 0 each loop. This was because the signals "Samples Per Second" must equal the "Number of Samples" for the time axis to be correct and at 1 Hz.)
However when using DAQmx analog voltage waveform as input, the signal is not read irregardless of the Sampling Frequency. This non-working vi is shown next:
To check my sanity, I made a simple vi with the identical analog voltage waveform input, and it worked perfectly. This vi is shown below:
Why does the vi work with simulated square signals but not with DAQmx analog voltage inputs? I'm puzzled by this since everything else is identical. All 3 vi's are attached. Any help is greatly appreciated!