LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

How to collect data for n seconds after pressing button

Solved!
Go to solution

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

Message 11 of 21
(2,873 Views)

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.

 

Producer Consumer.JPG

0 Kudos
Message 12 of 21
(2,861 Views)

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:

  • Producer Loop -- put your new Get File Name VI as the first item on the Producer Error Line.
  • Create Queue -- move Create Queue into the Producer "stream" (i.e. on to the Producer's Error Line), before you start the Producer.  You want to have the Queue up and running ...
  • You don't need two Error Lines in the Producer -- put Enqueue following the DAQmx Read (you'll have to "detour" the Channel I/O line above/around the Enqueue).
  • Just as you branched the Queue to both Producer and Consumer, branch the Error Line the same way.
  • You should now have two Error Lines -- one in the Producer, one in the Consumer.  Use Merge Errors to join them when both Loops end and finish things off with the Simple Error Handler (you don't need an Error Out on your Main VI).

Bob Schor

Message 13 of 21
(2,844 Views)

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!Producer Consumer.JPG

0 Kudos
Message 14 of 21
(2,832 Views)

Some comments (in random order):

  • Get the (slow) Waveform Chart out of the Producer -- it belongs in the Consumer (as you don't want to put any "time-pressure" on data acquisition).
  • Tell the DAQmx Read exactly how many points to read (don't use -1!).  You've now converted the Producer Loop into a "clock", a Good Thing.
  • Before doing anything else, you want to get the File Save Path.  You have it as an Indicator -- are you planning to "load" it before you run the Program?  Or do you want to (at run time) ask "Where should I save the Data?".  This is an excellent place for a sub-VI that runs, maybe even first, and asks this question, saving the desired File Save Path as an indicator that you simply wire into the Consumer Loop.
  • I notice that in the Consumer Loop, you took the Waveform from the Queue and made an Array out of it so you could wire it to the Export Waveforms To Spreadsheet File function.  Do this experiment -- delete the Build Array, and simply wire the Waveform to Export Waveforms.  What happens?  It works!  Now look at the name of the function -- it has changed to "Export Waveform" (singular).  This is an example of a polymorphic VI, one that changes to adapt to its input (a Waveform or an Array of Waveforms).
  • As a general rule, you should avoid Property Nodes for Front Panel Controls if possible (they can slow things down and have other "problems").  Here you could have just used the Control, itself (what is it doing in the Producer Loop?  Nothing!).  Plus, as I've noted above, it might not be needed here (hide it in a sub-VI).

Bob Schor

Message 15 of 21
(2,818 Views)

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.

Producer Consumer.JPG

0 Kudos
Message 16 of 21
(2,809 Views)
Solution
Accepted by topic author Big_Blue

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 DemoBlock Diagram of P-C Demo

  • Notice that this VI has a (simple) Icon.  This takes less than a minute to make, and really helps when you use them.
  • All of the Controls and Indicators on the Front Panel are no longer shown as Icons (right-click, turn off the check mark on "View as Icon").  This makes the Block Diagram neater and more compact.
  • I got rid of all of the DAQmx stuff and replaced it with a Waveform Generator that generates a sinusoid.  This lets me test the code without worrying about hardware.
  • I set Sampling Rate at 1000 (Hz), and Collection Time at 5 (s).  The Producer Loop generates 1000 points at a time, so it "ticks" at 1 Hz which I simulated by putting a 1000ms Wait inside the Producer Loop.  Note that the Consumer Loop runs "full speed".
  • There are two little pink boxes, one going into the Obtain Queue function, one going into the final Enqueue function.  This is a Waveform Constant, which is a special kind of Cluster.  Go ahead and right-click the edge of this symbol (but step back ...) -- a LabVIEW Feature allows Cluster Constants (which can take a lot of Real Estate on the Block Diagram) to be replaced with an Icon.
  • The three Inputs (Collection Time, Sampling Rate, and File Save Path) are connected by wires to the place they are used.  It is much easier to follow a wire (and see its branches) than to see a Local Variable, a Global Variable, or a Value Property and have to ask "What is this, where did it come from, has it been modified?".
  • There is one (1) Error Line that is branched in two for the Two Loops, then brought back together with Merge Errors.
  • The Producer Loop has a Waveform (Palette) "Waveform Generator", generating a sinusoid.  The cluster of inputs wired into the bottom of the Generator contains Fs (Frequency for samples) and #s (# of samples).  I wired Sampling Rate into both, which gives me (1000 (samples)/1 kHz) * 1000 ms/s , which results in 1000 ms that I wired (without "showing the math) into the Wait (ms) function.  This "times" the Producer, making it generate 1000 points at a time, running 1/sec.
  • The Cluster is put on the Producer, and then the Producer is done!  It has Produced, and that's all it is supposed to do.
  • Just for fun, I put an indicator on the For Loop index so you can want it "count to 4" (why 4?).
  • When the Producer exits, I put a Sentinel, a "default" Waveform constant (which has an Empty Array, a unique signal that you don't have to worry about) on the Queue. So the Producer has put 5 "Waveforms with 1000-point arrays" and one (Sentinel) Waveform with an Empty array, and now is really done.
  • Now the Consumer.  When we dequeue, we can use a function on the Waveform Palette to "get Components", the most important of which is Y, the Data array.  We test for an Empty Array -- if True, it's the Sentinel.  So the False branch (shown) has the data.
  • So what do we do?  We plot it on a Chart, and send it to an Export Waveform to Spreadsheet File function.  Note I did not pass this Waveform through Build Array, even though the default form for this function wants an array of Waveforms (appropriate if you have multiple channels of data).  You obviously didn't do the experiment I suggested of removing the Build Array and watching the (polymorphic) Function "adapt" to its input.
  • Use the Help function to read how this function works.  I'll admit the Help isn't the clearest I've ever seen (I got the input wrong the first time I tried it, and it is, in fact, "backwards", but too late to fix now ...).  You want to wire a False when the Loop fires the first time (True means "Append to File", which means False means "Open and write to file").  How do you get True every time except the first time?  See the Shift Register?
  • There's another Index for the Consumer.  Why does the Consumer Loop run one more time than the Producer Loop?
  • When the Consumer exit, it can safely release the Queue, and both loops are done.

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

Message 17 of 21
(2,801 Views)

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.

0 Kudos
Message 18 of 21
(2,784 Views)

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.

0 Kudos
Message 19 of 21
(2,777 Views)

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.)

Simulated_working.JPG

 

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:

Analog_not working.JPG

 

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:

 

Simple Analog Input.JPG

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!

0 Kudos
Message 20 of 21
(2,763 Views)