Multifunction DAQ

cancel
Showing results for 
Search instead for 
Did you mean: 

DAQmx read buffer as is?

Here's a funny question to type out: Is there a way to read the circular buffer from beginning to end?

 

That is, in a ten position read buffer that's addressed from 0 to 9 before looping back to 0, is there a way to get all ten values of the buffer in order, starting from that 0, regardless of how many times it's looped during continuous sampling?

 

RelativeTo doesn't make it straight forward: First Sample will be long gone after the first loop, Current Read Position might not be set to the first address, and we can't offset on Most Recent Sample since it has a 9/10 chance of not being aligned correctly.

 

In our use case we're sampling a periodic event at ten times per cycle, and we need the most recent samples at all ten times, but it doesn't matter if 0-3 are from this cycle while 4-9 are the most recent from the previous cycle. Also, I'm using the number 10 to simplify; we're actually doing thousands.

 

The best I can tell, the only way to do this is to make sure all reads actually are 10 samples long to maintain the read alignment. It would be nice not to have that limitation, though.

 

Thanks for any hints.

 

0 Kudos
Message 1 of 11
(4,592 Views)

I'm a bit confused at the question.  It sounds like you're asking for 2 different things that are at cross-purposes.   First you ask about reading the buffer from a fixed starting point (index 0).  Later you say you want the most recent samples, presumably 1 cycle worth of the periodic waveform.  You also don't want to restrict the # of samples to read.  It's hard to figure out what you really need here.

 

The best way I can think to force a read to start at index 0 is to first query the property "TotalSamplesAcquired", and use that value to select an appropriate offset to go with "RelativeTo" == First Sample.   However, when you get close to buffer wraparound, you'll be at risk of calculating a value that'll be stale by the time you can use it, leading to a DAQmx error.  Also, reads that start at index 0 each time, will usually *NOT* be the most recent data.

 

I don't understand the problem with using "RelativeTo" == Most Recent Sample.  Why don't you want to read exactly "10" samples each time?  What else is involved here?

 

 

-Kevin P

CAUTION! New LabVIEW adopters -- it's too late for me, but you *can* save yourself. The new subscription policy for LabVIEW puts NI's hand in your wallet for the rest of your working life. Are you sure you're *that* dedicated to LabVIEW? (Summary of my reasons in this post, part of a voluminous thread of mostly complaints starting here).
0 Kudos
Message 2 of 11
(4,519 Views)

To clarify, I mean for each buffer location, the most recent sample that has been recorded to that location. Location 0 will have more recent data than location 8 as it circles back, and that's ok.

 

To put it a different way, in our application we need an array of samples taken in order with the first sample happening aligned with the trigger. DAQmx has exactly that array in its circular buffer, and it'd be nice to grab the buffer as is instead of having to do math to recreate it.

 

 

I suppose I could read the whole buffer (10 before Most Recent would do it) and rotate it back into alignment if there's no way to get the buffer already aligned, but how do I know the exact sample number of the Most Recent Sample at the time of read?

 

Also, I don't suppose the raw 1D read function returns the buffer starting at 0, does it? The documentation mentions no reordering...

0 Kudos
Message 3 of 11
(4,512 Views)

I'm still missing something.  If you're using a Start Trigger, the sample at location 0 already *is* the one right after the trigger event.  The buffer doesn't start filling until *after* the trigger occurs.

 

If you're using a Reference Trigger, the default behavior is that DAQmx acquires into the circular buffer in the background while monitoring for a trigger event.  When the trigger occurs, DAQmx remembers the corresponding location in the buffer.  Then the default Read behavior is to do the alignment I *think* you're after.  If you wanted 4 pre-trigger samples, you'll get an array of data where the first 4 array values returned are extracted from the 4 positions in the buffer just before the trigger location.  That's already the normal behavior.

 

For example, let's suppose you configure for 4 pre-trigger samples and have a total buffer size of 1000 (locations #0-999).  You start the task and DAQmx keeps rotating through the buffer while waiting for the trigger.  When the trigger comes, it's just after DAQmx had written to location 1.  You perform DAQmx Read and ask for 8 samples.  The relationship between the array you get back and the internal circular buffer location #'s would be:

array[0] = location[998]

array[1] = location[999]

array[2] = location[0]

array[3] = location[1]   // these are your 4 pre-trigger samples

array[4] = location[2]

array[5] = location[3]

array[6] = location[4]

array[7] = location[5]

 

The Raw 1D read is meant to let you read integer A/D values directly, before the board's calibration curve is applied.  There's other functions to query the parameters for the cal curve.  I've mainly known it to be used for data storage efficiency -- 2 bytes per sample rather than 4 (float) or 8 (double).  It doesn't relate to the ordering of samples within the acquisition buffer.

 

 

-Kevin P

CAUTION! New LabVIEW adopters -- it's too late for me, but you *can* save yourself. The new subscription policy for LabVIEW puts NI's hand in your wallet for the rest of your working life. Are you sure you're *that* dedicated to LabVIEW? (Summary of my reasons in this post, part of a voluminous thread of mostly complaints starting here).
0 Kudos
Message 4 of 11
(4,500 Views)

It gets confusing because there's two separate numbers: sample number and buffer index number. Let me drop the buffer index number for the moment and only look at sample numbers, starting with 1 = first sample.

 

Right: There's a trigger, and the DAQ starts cycling through the buffer continually. The first time through the buffer contains samples 1,2,3,4,5,6,7,8,9,10. A little while later it will have samples 31,32,33,34,35,36,37,38,39,40. 

 

A complication comes when the buffer contains samples 31,32,33,24,25,26,27,28,29,30. I'm sure we're on the same page up to here. The quesiton is, How can I request the buffer and get it in that order?

 

For example, if I use RelativeTo First Sample + offset, I still need to know the sample number at the first location for the offset, which as you pointed out can get stale if it wraps while I'm calculating, causing the read to fail.

 

RelativeTo Most Recent Sample with offset -10 doesn't solve it because I'll get samples 24,25,26,27,28,29,30,31,32,33. The array won't be starting at 31 as it is in the buffer, and the readings will be shifted by 3. They'll be shifted randomly with each read operation.  No error, but not aligned.

 

I don't see a way around needing to know what number sample is at the head of the buffer, but I don't see a way to determine that safely at the instant of the read.

0 Kudos
Message 5 of 11
(4,493 Views)

The *what* is more clear now, but I can't for the life of me figure out *why* you'd prefer to retrieve your data in non-sequential order.

 

This may or may not be relevant, but I've had apps where I paired up a counter measurement task that was synced to the AI task with some shared timing signals.  The counter would do buffered edge counting.  Its sample clock would be some external "trigger" signal that pulsed at the moments of interest.  The edge counting source terminal was designated as the AI sample clock.  

 

The way it would work is that the count value increments on every AI sample, and those count values are buffered on every "trigger" event.   Meanwhile, *all* the AI samples are continuously collected and held for processing.  Each counter value (actually, count -1) is the index into the continuous AI array where the trigger event happened.  You can extract and manipulate in whatever way you'd like after that.

 

This kind of setup makes AI to be a master task so it's important to start the counter task before the AI task.  You may need to mess with "duplicate count prevention" to get it to work right.

 

 

-Kevin P

 

CAUTION! New LabVIEW adopters -- it's too late for me, but you *can* save yourself. The new subscription policy for LabVIEW puts NI's hand in your wallet for the rest of your working life. Are you sure you're *that* dedicated to LabVIEW? (Summary of my reasons in this post, part of a voluminous thread of mostly complaints starting here).
0 Kudos
Message 6 of 11
(4,476 Views)

Well, it is sequential. The hangup is figuring out where the sequence begins in a circular buffer.

 

Like I said, we're measuring something periodic. It's easy to align the start of the buffer with the period using a trigger, and to cycle through the buffer, filling it in sync with the oscillation.

 

The problem here is getting the sequence out of the buffer. It doesn't sound like there's an easy way to do it.

0 Kudos
Message 7 of 11
(4,471 Views)

I guess I'd agree, there's no easy way to do exactly what you want the way you want to do it.  I sitll think the main problem to be solved is the want.

 

You have a fairly crucial assumption that you can fit exactly 1 cycle of your periodic waveform into your buffer.  That won't generally be *exactly* true, depending on the available quantized sample clock rates and the actual period of the waveform in question.  Quantization will usually cause you to be off by some fraction of a sample period.  It also depends on the timing source for that waveform not to have any skew or drift relative to your DAQ board.  Unless you're generating it yourself on the same DAQ board, it's kinda unlikely you'll get the exactness that your assumptions demand.

 

For some reason I don't currently understand, you want to trigger the acquisition, wait some unknown or arbitrary amount of time, then read a variable # of samples, all while being assured that the samples you receive will be arranged such that the first one represents the same phase within the waveform as what was present at the original start trigger, even if that sample was taken later than some of the others you receive.

 

I'm almost certain that the only way you're gonna get something like that behavior is by reading *all* AI samples continuously, move them into a software buffer of your own, and manage all the counting, indexing, aligning business with your own app-level processing.  As a starting point, you can always read samples in multiples of "10" (or whatever # samples represents the real waveform period, keeping in mind the prior caution that the truth often isn't *exactly* an integer)

 

 

-Kevin P

CAUTION! New LabVIEW adopters -- it's too late for me, but you *can* save yourself. The new subscription policy for LabVIEW puts NI's hand in your wallet for the rest of your working life. Are you sure you're *that* dedicated to LabVIEW? (Summary of my reasons in this post, part of a voluminous thread of mostly complaints starting here).
0 Kudos
Message 8 of 11
(4,460 Views)

I just didn't want to complicate with more background. The AI is synched with an AO based on the same clock, so it's guaranteed to be kept in sync forever.

 

To correct one thing you said, I don't want a variable number of samples. I always need one buffer full since that's a complete sample. At arbitrary times (when the user or another process requests it) I need the last cycle of samples out of the buffer, so it will always be 10 samples, for example 21,22,23,14,15,16,17,18,19,20.

 

The AO has to keep running, not be started and stopped, and the AI will be read many times, whenever users or programs need the last set of samples. That's why it's compelling to find a way to just grab the buffer that's already there: it has exactly what I need, with the right samples in the right order aligned correctly. 

0 Kudos
Message 9 of 11
(4,455 Views)

I think I'd make the AI buffer be an integer multiple of the # samples per waveform, choosing a multiple to let the buffer represent a substantial duration of maybe 1-5 seconds.  At the moment of a request, query "TotalSamplesAcquired".    That sets your buffer marker.  Next you'll do some modulo math to calculate the next lowest index within the buffer where the waveform was in the correct phase.  Then you can use that as the Offset value while setting "RelativeTo" == FirstSample.  Then you can perform the Read.  As long as the buffer represents enough duration, you should be able to avoid the buffer wraparound error I mentioned before.

 

Example pseudocode:

Tot = TotalSamplesAcquired

Offset = Tot - (Tot MOD WfmSize)

Set DAQmx RelativeTo and Offset values

Perform DAQmx Read with # samples == WfmSize

 

 

-Kevin P

CAUTION! New LabVIEW adopters -- it's too late for me, but you *can* save yourself. The new subscription policy for LabVIEW puts NI's hand in your wallet for the rest of your working life. Are you sure you're *that* dedicated to LabVIEW? (Summary of my reasons in this post, part of a voluminous thread of mostly complaints starting here).
Message 10 of 11
(4,448 Views)