04-23-2019 07:33 AM
What DAQ device(s) are you using? Does it natively support retriggerable AI?
It sounds like you're collecting almost-but-not-quite continuous data. What makes it necessary to "re-sync" to these nested trigger signals? What's generating those external trigger signals -- are they independent of one another? What about your sample clock -- is it internally generated on your board or are you following an external signal?
Adding retriggering config to the AI task in the code I posted should be fairly easy to try. You'd need to combine that change with an L-iteration For loop around DAQmx Read. Then you could accumulate an LxMxN array on the output tunnel. You'd be vulnerable to the timing issue I described back in msg #2.
Depending on what DAQ device(s) you have, you might be able to get fancy with 2 separate finite retriggerable counter pulse trains. The way to make the "M" counter run only when the "L" counter allows it would be for the L counter to generate the timebase used by the M counter. You would need to make sure that L generates *exactly* the right # of timebase pulses to let the M counter generate its M pulses at your sample rate.
-Kevin P
04-23-2019 08:00 AM - edited 04-23-2019 08:02 AM
@Kevin_Price wrote:
What DAQ device(s) are you using? Does it natively support retriggerable AI?
It sounds like you're collecting almost-but-not-quite continuous data. What makes it necessary to "re-sync" to these nested trigger signals? What's generating those external trigger signals -- are they independent of one another? What about your sample clock -- is it internally generated on your board or are you following an external signal?
Adding retriggering config to the AI task in the code I posted should be fairly easy to try. You'd need to combine that change with an L-iteration For loop around DAQmx Read. Then you could accumulate an LxMxN array on the output tunnel. You'd be vulnerable to the timing issue I described back in msg #2.
Depending on what DAQ device(s) you have, you might be able to get fancy with 2 separate finite retriggerable counter pulse trains. The way to make the "M" counter run only when the "L" counter allows it would be for the L counter to generate the timebase used by the M counter. You would need to make sure that L generates *exactly* the right # of timebase pulses to let the M counter generate its M pulses at your sample rate.
-Kevin P
Thanks for replying.
I'm using USB-6363, which has on-board counters. I'm using a counter to generate N pulses triggered by external 4KHz.
This is the sampling clock of AI, which is triggered by external 4/M KHz, and fetch finite L*M*N samples (L-burst).
The 4KHz is from laser clock, the 4/M KHz is from chopper, both trigger rise at the same time. Each one of M is different, so a 4/M KHz trigger is necessary.
Then I reshape the array to get L/M/N dimensions. The reason I don't want to put for loop around DAQmx read is that it cannot keep up with 4/M KHz, which defeats the purpose of "burst".
Are you proposing that I use another counter to generate M*N pulses and put AI in continuous mode (in continuous mode, burst is no longer needed so there is no L) ? In that case, when I call DAQmx read, it could start reading from anywhere within M*N and give me junk data. I need to read from the beginning of M*N.
04-23-2019 08:55 AM
@Kevin_Price wrote:
What DAQ device(s) are you using? Does it natively support retriggerable AI?
It sounds like you're collecting almost-but-not-quite continuous data. What makes it necessary to "re-sync" to these nested trigger signals? What's generating those external trigger signals -- are they independent of one another? What about your sample clock -- is it internally generated on your board or are you following an external signal?
Adding retriggering config to the AI task in the code I posted should be fairly easy to try. You'd need to combine that change with an L-iteration For loop around DAQmx Read. Then you could accumulate an LxMxN array on the output tunnel. You'd be vulnerable to the timing issue I described back in msg #2.
Depending on what DAQ device(s) you have, you might be able to get fancy with 2 separate finite retriggerable counter pulse trains. The way to make the "M" counter run only when the "L" counter allows it would be for the L counter to generate the timebase used by the M counter. You would need to make sure that L generates *exactly* the right # of timebase pulses to let the M counter generate its M pulses at your sample rate.
-Kevin P
Thanks for replying.
I use USB-6363, which has onboard counters. I use a counter triggered by 4KHz to generate finite N pulses as the time base for AI.
The AI is triggered by 4/M KHz and fetch finite L*M*N samples.
The triggers are phase-synced to each other and rise at the same time. Each sample of N, and each N samples of M are different, so two triggers are needed.
The reason I don't want to put DAQmx read in a loop is that it cannot keep up with 4/M KHz. Instead, I use a burst of L to reduce the loss of samples.
Are you suggesting running an other counter with M*N pulses and put AI in continuous mode ? (in continuous mode, burst is no longer needed, so no L)
In that case, when I call DAQmx read, it could read from anywhere within M*N, which gives me junk data. The read must start at the beginning of M*N.
04-23-2019 09:02 AM
Before this gets too confusing, could you provide a timing diagram of exactly what you want to happen and label L, M, and N for reference?
04-23-2019 12:19 PM
Yes, a timing diagram would be really helpful. I also have questions about the *criticality* of never missing an M or L trigger. It's a pretty steep road to get this all set up perfectly with hardware timing and multiple retriggerable finite tasks, so I want to understand whether it's *truly* needed.
I suspect yes here, but I've been in a number of threads over the years where a thread suddenly turns from some initial pretty tricky timing requirements, through many back and forth rounds of ironing out fine details toward a solution, ending when the OP mentions declares the problem "solved" via a method that's simple, crude, and doesn't actually meet most of the requirements we'd been chasing.
Consider:
- What if your combo of N and sample rate is longer than the interval (250 microsec) between M triggers?
- With M at 4 kHz and L defined as 4/M kHz, there would seem to be *no gap* between the end of 1 MxN data set and the start of the next. I'm not so sure finite retriggering is the right method to handle that.
-Kevin P
03-23-2021 09:02 AM
Hi,
Thanks everyone for the useful discussions.
I am trying to acquire continuous voltage signals starting on an external trigger edge and have configured a counter for sample clock of AI. The instrument I am reading the signals off of outputs the signal continuously together with a trigger at the start of each signal. I am basically using the shipping example "Multi-Function-Ctr Retrigg Pulse Train Generation for AI Sample Clock" to which I have added a producer consumer (that might be irrelevant here). However by looking at how often the voltage signal graph gets updated, it seems like every other signal is missing! (~50% loss)
I have tried acquiring more than one signal upon receiving one trigger and then dicing up the data afterwards (MxN discussions above). That works to some extent, but still it seems like the timing won't be exactly right and a few of the signals will be missed (~10% loss), I guess because of data becoming larger and transferring takes time.? I would like to be able to acquire all the data that comes off of the instrument continuously. This is critical since I am trying to characterize the stability of the output of the instrument.
My hardware is a cDAQ 9172 Chassis, a 9215 AI module, and a 9401 DOI module (on slot 6). My external trigger comes in at 10Hz and I am acquiring the voltage signals at 100kHz. So for example over 1 minute (timed by my watch lets say), I expect 10*60=600 records of the signal whereas I only get about 300.
Am I missing something in terms of configuring the tasks? or is my hardware limited? or something else?
Unfortunately my LV is rather old (2010) and I was not able to open the example that Kevin Price had uploaded.
Your suggestions/help is very much appreciated.
03-23-2021 10:08 AM
I can't do much to try to troubleshoot your code unless you post it. The one guess I'll make is that you might have your finite retriggering task configured such that it's not completed (maybe almost but not quite) before the next triggering signal arrives. Did you configure the counter pulse train for 10k pulses at 100 kHz to completely fill an entire 0.1 second interval?
Meanwhile, I've attached that old example of mine after back-saving to LV 2010. (At least I *think* it's back-saved. I noticed that when I re-opened it in LV 2018 and closed it again, I didn't get the usual prompt to re-save due to converting from the old version. So I back-saved again and saw the same behavior again.)
-Kevin P
03-23-2021 05:22 PM
Hi,
Thanks a lot Kevin for the quick reply and providing the LV2010 version of your code.
I can open your code now and see the differences compared to mine.
My program is attached together with a snapshot of how I have configured the trigger/timing. It is basically the same as the shipping example for re-triggerable AI.
I think I have configured it to cover the entire 0.1 s (# of samples =10,000 with 100kHz rate). I also tried acquiring less samples (e.g. 9,000) in case there are small timing differences between the 10Hz external trigger and my cDAQ but that didn't seem to help.
Thanks.
03-24-2021 08:11 AM - edited 03-24-2021 08:12 AM
Your code could use some tidying, but I didn't notice anything *essentially* wrong. I'd have thought the only problem was the one I guessed -- that your counter hadn't finished its 10000 pulses in time to rearm for the next trigger. What exactly happened when you configured the counter to do 9000 pulses per trigger instead of 10000? It seems to me that it should have worked out ok for you.
One small potential problem is that you don't specify a # samples to read in the DAQmx Read call. So it'll use the default value -1 which means "read all available samples". It's conceivable that you might read too many samples that way -- all the samples from 1 triggering event plus some from the next event.
Instead, wire up the # samples you really want based on how many pulses the counter generates on each external trigger.
Another subtle thing is that if your upper producer loop terminates due to an error, that error will be passed along and prevent you from sending the dt=NaN value that signals the lower consumer loop to end. I'd recommend you don't wire the input error for that Enqueue function.
-Kevin P
03-26-2021 12:59 PM
Hi,
Thanks Kevin for the insights. The unspecified number of samples to read was definitely the issue.
When I ran with number of samples to read=9000, the shipping example works as expected (i.e approximately 600 acquisitions per minute, with my 10Hz trigger) as long as the the number of samples to read is specified for DAQmx Read (as you suggested). If the number of samples is unspecified, the shipping example stopped sporadically and was not reliable.
I also noticed that my trigger is not exactly 10Hz, since I was seeing slightly more than 600 acquisitions per minute. So I added an initial stage to the code to measure the "actual" trigger frequency and from there calculate the number of samples to read as: [sampling rate/trigger frequency], where square brackets means rounding down. Just for being sure, I subtracted 10 from that in case trigger frequency drifts slightly during runtime. With this everything works nicely: For example, with a measured trigger frequency of 10.172568727 Hz and a sampling rate of 100kHz, choosing a number of samples of 9830 captures all the data whereas with 9831 every other record of the data is missed (as expected).
Thanks for your insightful comments. I have attached the basic version of the code if anyone might find it useful. I removed the condition that checks the number of available samples before the DAQmx Read happens since the number of data is specified now. I suspect that condition was causing some issues too (at least in my application). Also the program/system was capable of doing the simple processing I needed and plotting all in the DAQmx Read loop without the need to have a Producer/Consumer loop. But that may not be reliable and could result in buffer overflows in applications where processing is heavy or the number of data is large.