Multifunction DAQ

cancel
Showing results for 
Search instead for 
Did you mean: 

reading samples: critical section atomicity?

Hello all,

I am using NIDAQmx with VC++ and I have the following situation:
- one thread (T1) is constantly reading samples from a NI USB 9229 in a local buffer and copying them an array A (44100Hz)
- another thread (T2) is waiting for a command and subsequently copying a batch of 44100 samples from A to a local array variable and doing some computation with them
- array A is protected in a critical section (T1 and T2 enter the critical section when working with A directly and leave it afterwards)

How "atomic" is the reading of samples from the device in my array A? I.e., is it guaranteed that the critical section protection on A means that by the time T2 accesses A, T1 has finished writing 44100 samples in it?
Or do I have to register with DAQmxRegisterEveryNSamplesEvent() a callback which 1) reads 44100 samples, 2) notifies T2 that the batch is complete and available?

Thank you in advance!
0 Kudos
Message 1 of 6
(4,239 Views)

acgrama,

 

DAQmx read will not return until it has copied all of the requested data into your 'A' array (unless an error occurs).  So as long as you are waiting for the call to DAQmx Read to return inside your critical section, the data will be copied to A before T2 accesses it.

 

Hope that helps,
Dan

Message 2 of 6
(4,223 Views)

Additionally, I'm guessing that you're trying to accomplish asynchronous execution of your computational code while reading from the DAQmx device, but you might not need to make the application so optimized.

 

What I mean is:

The DAQmx Read already copies data to an intermediate buffer.  Actually, what happens is that the onboard memory for the device is constantly being transferred (typically through DMA) to a reserved user-kernel shared buffer on the host PC.  When you call DAQmx Read, it copies a chunk of data from that circular user-kernel buffer into your application memory.  Therefore, essentially, what you are trying to do is already actually done internally by DAQmx.  There could be some value in adding another buffer in some cases, but in most cases, this approach would be taxing memory bandwidth for little or no benefit.  Additionally, consider the maintainability and ease of such a design - that is, maintaining a synchronization object and ensuring that a race condition can't occur (for instance, consumer tries to access buffer twice in a row before producer gets to execute twice - however unlikely this could be).

 

In summary, I would consider whether this design is really necessary at 44 kHz.  It might be easier and more maintainable to simply do the processing "in the loop"; that is, either call DAQmx Read and do processing in the same loop iteration, or register for an Every N Event and do the processing in the callback.

Thanks,

Andy McRorie
NI R&D
Message 3 of 6
(4,212 Views)
Thank you Dan and Andy for your answers, they are spot on and I learned a lot!

Andy's guess was right -- indeed, I'm doing some asynchronous execution in parallel with reading samples.
My app listens for commands to process signal on a particular channel. By "processing" I mean storing all samples until a "stop" command comes, at which point the samples will be saved to disk as an audio snippet in .mp3 format. During this processing, other commands are expected, for instance "is audio?" which involves detecting the presence of audio signal on the channel, defined by certain limits of frequency and amplitude (so I have to do FFT on the samples from that channel).

A typical usage of the app would be:
- start processing on channel ai0
- start processing on channel ai3
- is audio on ai0?
- ....
- stop processing on channel ai0
- start processing on channel ai1
- stop processing on channel ai3
- .... and so on.

This is the reason why it was designed to have a thread process samples for each channel. (As I understood from another question I posted, the threading model of NIDAQmx does not allow me to have a separate task behaving like a thread for working with each of the four channels in a device (I hope I did not misunderstand that bit!)).

Unfortunately I am just beginning to learn working with the NIDAQmx, so maybe there is a better solution? Perhaps I just cannot see the forest for the trees 🙂 Thank you for your help!
0 Kudos
Message 4 of 6
(4,194 Views)

That seems like a reasonable design considering your application.

 

You are correct in that a DAQmx task will reserve a DAQ card such that no other task can use it while in use.

 

That being said, there is a feature that you might find helpful for your application.  You can filter the read call to specific channels.  There's a DAQmx Read property for "Channels to Read".  In the C API, the function call would be DAQmxSetReadChannelsToRead(...).  If you're using the C++ API, there's a"set_ChannelsToRead" method on the stream.

Thanks,

Andy McRorie
NI R&D
0 Kudos
Message 5 of 6
(4,180 Views)

I did not know the feature you mention, it will be very useful to me!

Thank you so much for the feedback 🙂

0 Kudos
Message 6 of 6
(4,166 Views)