Multifunction DAQ

cancel
Showing results for 
Search instead for 
Did you mean: 

NIDAQmx Counter innacurracies using the C-API in Linux

I'm experiencing small inacurracies in my counters and I'm struggling to figure out why.

 

I currently have two signals feeding into my M-Series devices (6254). One signal is a 10KHz square wave and the other signal is a 2 Hz square wave.

 

The fast signal is going into the device counter input and the slow signal is going into a digital-in line.  Whenever the slow signal switches from low to high it triggers a callback and I acquire the count from the counter.

 

Because I know the frequencies of the two signals I can predict how many fast oscillations will transpire for every slow oscillation.  On the rising edge of each slow wave  I should be able to predict  what the count should be at the next rising edge of the slow wave. What I have found is that my predictions are inaccurate a substantial fraction of the time.

 

The two signals are being generated by an M-Series device:

 

DAQmxCreateCOPulseChanFreq(th1, "Dev1/ctr0", "", DAQmx_Val_Hz,DAQmx_Val_Low, 0.0, 2, 0.50);
DAQmxCreateCOPulseChanFreq(th2, "Dev1/freqout", "", DAQmx_Val_Hz, DAQmx_Val_Low, 0.0, 1000, 0.50);

I've examined the generated signals on my oscilloscope and they don't appear to have any jitter, leading me to believe that the signals are in fact stationary.

 

 

I'm trying to locate the source of the inaccuracies, but have been unable to do so.

 

What is really confusing is that i'm observing both positive and negative errors, which leads me to believe that my problem is not simply the OS not executing code fast enough (if this were the case I would only see negative errors). 

 

I tried attaching the source code but was unable so i've pasted it below

 


 

#include <NIDAQmx.h>
#include <iostream>
#include "nichk.cpp"

static TaskHandle t1,t2; uInt32 prev=0, curr=0, next=0, diff=0; int32 err=0; int n=0; int32 CVICALLBACK ChangeDetectionCallback(TaskHandle t1, int32 signalID, void *callbackData); void Cleanup (void); int main(void){ int32 error=0; char errBuff[2048]={'\0'}; //prepare the digital trigger task ECmx (DAQmxCreateTask("",&t1)); ECmx (DAQmxCreateDIChan(t1,"Dev1/port0/line0:7","",DAQmx_Val_ChanPerLine)); ECmx (DAQmxCfgChangeDetectionTiming(t1,"Dev1/port0/line0:7",NULL,DAQmx_Val_ContSamps,1)); ECmx (DAQmxRegisterSignalEvent(t1,DAQmx_Val_ChangeDetectionEvent,0,ChangeDetectionCallback,NULL)); ECmx (DAQmxStartTask(t1)); //prepare the counter task ECmx (DAQmxCreateTask("",&t2)); ECmx (DAQmxCreateCICountEdgesChan(t2,"Dev1/ctr1","",DAQmx_Val_Rising,0,DAQmx_Val_CountUp)); ECmx (DAQmxStartTask(t2)); //wait until the user kills the program getchar(); Cleanup(); } int32 CVICALLBACK ChangeDetectionCallback(TaskHandle t1, int32 signalID, void *callbackData){ n++; uInt32 count; ECmx (DAQmxReadCounterScalarU32(t2,10.0,&count,NULL)); prev = curr; curr = count;
//diff = curr-prev; // Calculate the diff
  diff = 5000; //Use hardcoded diff

 err = next-curr; next = curr+diff; if (n<5) return 0; if (err!=0){ std::cout<<"\tActual: "<<curr; std::cout<<"\tErr: "<<err; std::cout<<"\tLoop Number:"<<n<<std::endl; std::cout<<"Prediction: "<<next; } return 0; } void Cleanup (void){ if( t1!=0 ) { DAQmxStopTask(t1); DAQmxClearTask(t1); DAQmxStopTask(t2); DAQmxClearTask(t2); t1 = 0; t2 = 0; } }

 

 

Here is the code for nichk.cpp

 

 

#include <nichk.h>
#include <iostream>

void ECmx(int32 error){
  char errBuff[2048];
  if( DAQmxFailed(error) ){
    std::cout << "Caught a DAQmx error..." << std::endl;
    DAQmxGetExtendedErrorInfo(errBuff,2048);
    std::cout << "Caught the daqmx error num: " << error << " with message: " << errBuff << 
std::endl;
  }
}

 

 

Note: I'm running these on Linux and compliling using g++

 

 

 

0 Kudos
Message 1 of 10
(3,701 Views)

Neurostu,

 

Do you know what version of DAQ drivers you have installed on your machine? I see that you are using DAQmx function calls, so are you using the 8.0 version for Linux?

 

At first glance, it sounds like there is a timing issue with when the callback function is being made and when the actual measurement is being taken.

Have you considered using a gated counter measurement for this that is gated with the 2 Hz square wave as opposed to using the callback function?

 

Also, what exactly are you trying to do with these two signals? Are you simply trying to count the ticks in the 10kHz square wave when the 2 Hz wave is high? What is our end goal?

Finally, are the errors in the measurement you are seeing simply that it is reading more/less counts than expected, and are those values even within a reasonable range of the expected numbers?

 

Timothy

Timothy S.
Senior Technical Support Engineer
Message 2 of 10
(3,657 Views)

Timothy, 

 

Thanks for your reply.  

 

I have the 8.0.1 drivers installed

 

I haven't considered using a gated counter measurment, probably because I'm not sure what that is, although I'm guessing it won't work for what I need.  This leads into your next question which is what exactly am I trying to accomplish, the code I've attached above was simply meant to be a proof of concept of the problem I'm observing. The actual problem I'm trying to solve is a bit more complex.

 

I'm trying to synchronize the counters across multiple different computers (1 master and several slaves). Each computer has three inputs:

 

  1. 10 KHz square wave (its the counted signal, and the count represents the timestamp)
  2. 2 Hz signal that is used to fire a call back
  3. 2 Hz signal that is used to arm the counter
The master computer generates the two square wave signals and on each rising edge of signal 2 (the slow 2hz signal) it broadcasts a UDP packet predicting what the count will be on the next rising edge of the slow signal. 
Any slave computer that is looking to join the enseble waits for a packet. Upon receipt of a packet it sets its initial count value to the predicted count in the packet and then arms it counter to start on the rising edge.  

 

 

This protocol assumes that the two signals are stationary and that there are always the same number of 10KHz cycles that occur during one cycle of the slow (2hz) signal. 

 

When I first saw that number of fast cycles that occured for every slow cycle was not constant I initially assumed that there was a variable lag associated with the digital edge detection callback. However, this doesn't explain the compensatory nature of the errors. For example on slow cycle N  there could be m+1 fast cycles (when m is the predicted value). On the next slow cycle (or maybe a one or two later) it is common it observe m-1 fast cycles.  In fact I ran the test over the weekend and kept a running total of errors. After 7500 errors the sum of the errors was floating around 0 implying that for every positive error there is also a negative error.    If OS related latency issues where the only factor at play then you would expect to only see positive errors and no negative errors.

 

Our end goal is to acquire high frequency signals across many computers (that are all synchronized) and an error of 100 useconds is not within the permissable range.

 

 

 

 

0 Kudos
Message 3 of 10
(3,649 Views)

Thank you for the information.

 

After looking in to things further on my end, it is still my opinion that the small errors you are seeing are to be expected based on the non-deterministic nature of your program.

 

The timing for your system there is based on the 10kHz signal, the 2kHz trigger signal, the time it takes to register the trigger on the digital line, and most especially the callback function involved with any OS and computer-based timing. You are allowing that portion of the measurement to be software timed as to when it occurs based on using the callback to register it.  This brings in any system clock jitter that could be in your computer itself, and the fact the errors are roughly averaging out to zero seems to support that.  With the non-deterministic nature of that code, it isn't unreasonable to see a small shift in the timing for when the call is made and registered.  I'm honestly surprised that you're not seeing more swing errors in the measurement than you already are.  It seems to me that based on how you are approaching this, it may simply be a limitation you are going to run in to.  We do have some more specific timing based cards (usually based around IEEE 1588 or GPS timing) that might be better suited for this kind of application than a multifunction DAQ card.

 

Timothy

Timothy S.
Senior Technical Support Engineer
0 Kudos
Message 4 of 10
(3,632 Views)

Timothy,

 

Thanks for your reply.

 

Do you have IEEE 1588 cards that are supported under linux?

 

 

0 Kudos
Message 5 of 10
(3,626 Views)

Neurostu,

 

That was actually an oversight on my part, and I'm sorry for not checking this before, but the 1588 uses the NI SYNC device drivers which are not currently supported for Linux. My apologies for the miscommunication there.

 

Timothy

Timothy S.
Senior Technical Support Engineer
0 Kudos
Message 6 of 10
(3,608 Views)

Hi neurostu,

 

Sounds interesting.  The gated counter measurement that Timothy mentioned goes something like this (from the M Series User Manual😞

 

2011-02-10_141835.png

 

So, you can deterministically sample the counter off of the 2 Hz signal in hardware.  What you're currently seeing is jitter due to relying on non-deterministic software calls to sample the count register.

 

 

I'm not 100% clear on the requirements of your application, but it seems like this is what you are looking for.  You would need to call DAQmxCfgSampClkTiming to configure the external clock to be used for your counter measurement.  The change detection then becomes unnecessary, you can use DAQmxRegisterEveryNSamplesEvent to generate a software event whenever a new sample is available in the buffer.

 

 

Best Regards,

John Passiak
0 Kudos
Message 7 of 10
(3,593 Views)

John P,

 

Wow thanks for the explanation. Would this still allow me to query the counter value at any other arbitrary time, or can counter values only be obtained on the 2hz wave?

 

 

0 Kudos
Message 8 of 10
(3,589 Views)

Hi neurostu,

 

My first thought was to suggest querying the count register with DAQmxGetCICount, but this won't work because the value is only updated on the actual edge of the sample clock.  In fact, for M Series (and probably TIO as well) it looks like the value is only updated on every other sample clock edge due to the 2-sample FIFO used on the hardware.

 

So, on your M Series the only way that I can think of to read the count independently of the sample clock would be to use the second counter (assuming it's available) in a software-timed task that is armed off of the same signal as the first counter.

 

For what it's worth I also tested X Series boards and the DAQmxGetCICount is updated on these boards independently of the sample clock.  Unfortunately, there is no current Linux support for X Series so this doesn't really help your current situation.

 

 

Best Regards,

John Passiak
Message 9 of 10
(3,582 Views)
I'll try implementing this and let you know if it works. Thanks!
0 Kudos
Message 10 of 10
(3,570 Views)