LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Multithreading - thread-safe-queue timing

I'm wrting a large data-acquisition and processing program using LabWindows/CVI 2010, Windows XP.

In the data acquistion section, I have a DAQmx task (set up using MAX) that will acquire a number of points, typically 1000 @ 50 kHz, on receipt of a trigger.

 

After the task is loaded with

  DAQmxLoadTask ("task_name", &DAQTask_Handle);

A handler is installed with

    DAQmxErrChk(DAQmxRegisterEveryNSamplesEvent (DAQTask_Handle,
                                                 DAQmx_Val_Acquired_Into_Buffer,
                                                 PointsToAcq,
                                                 0, //non-synchronous event callback, called from DAQmx thread
                                                 CB_Level1_DataRdy, NULL));
The trigger frequency is supposed to be 30 Hz, so in order for things to happen quickly enough I've already determined that the callback event needs to be non-synchronous. This is what is turning this into a multi-threading exercise.

 

A threadsafe queue is also set up  with

        status = CmtNewTSQ (DataQueueListSize(), sizeof (float64), OPT_TSQ_DYNAMIC_SIZE, &DataQueueHandle);
A callback (from the receiving module, in another dll) is installed with

         status = CmtInstallTSQCallback (DataQueueHandle, EVENT_TSQ_ITEMS_IN_QUEUE, PointsToAcq, callback_func, NULL, CmtGetCurrentThreadID(), NULL); (where callback_func is the callback routine passed in through a subroutine call. )


 

The DAQ callback, then looks something like this:

int32 CVICALLBACK CB_Level1_DataRdy (TaskHandle taskHandle,
                                     int32 everyNsamplesEventType,
                                     uInt32 nSamples,
                                     void *callbackData) {


    //set priority
    status = CmtGetCurrentThreadPriority (&Priority);
    status = CmtSetCurrentThreadPriority (THREAD_PRIORITY_TIME_CRITICAL);

    // setup stuff here

    .......

    // read data from DAQmx
    DAQmxStatus = DAQmxReadAnalogF64 (taskHandle, DAQmx_Val_Auto, 0,
                                    DAQmx_Val_GroupByChannel,
                                    DataBuffer,   //float64 array large enough to receive the samples
                                    BufferSize, &samplesRead, 0);
    //do some processing here

    status = CmtWriteTSQData (DataQueueHandle,  DataBuffer, samplesRead, 0, NULL);

 

    //restore thread priority

    CmtSetCurrentThreadPriority (Priority);
}

 

So far, so good.Note that the callback above is called from the DAQmx thread; the queue is used to buffer the data with the display, which is accomplished in the main process thread. It was necessary to set the DAQmx thread priority to THREAD_PRIORITY_TIME_CRITICAL to ensure that the handler doesn't get interrupted by later events.

 

On the other end, the queue callback is defined as

void CVICALLBACK Receiver_DataAcqHandler (CmtTSQHandle queueHandle, unsigned int event, int value, void *callbackData) {

    num_data_read = CmtReadTSQData (queueHandle,
                                    ReceiverVoltsBuffer,
                                    samplesRead,
                                    TSQ_INFINITE_TIMEOUT, 0);

 

       //get rid of old handle

       if (g_iPlotHandleReceiver) {
           status = DeleteGraphPlot (MainWindowHandle, PANEL_GRAPH, g_iPlotHandleReceiver, VAL_DELAYED_DRAW);
           g_iPlotHandleReceiver = 0;
       }

       //plot data

       g_iPlotHandleReceiver = PlotWaveform (MainWindowHandle, PANEL_GRAPH, ReceiverVoltsBuffer,
                     num_data_read, VAL_DOUBLE, 1.0, 0.0, 0.0, 1.0,
                     VAL_THIN_LINE, VAL_NO_POINT, VAL_SOLID, 1, VAL_GREEN);
}

 

This version works nicely. I instrumented the program with some writes to the DAQ card's digital IO lines: the call to the DAQ callback taks about 250 us, being called immediately after the 1000 points have been acquired. No problem. The queue call takes about 5 milliseconds; what's interesting, though, is that you can watch the time-slicer working: the delay from the end of teh DAQ callback to the start of the queue callback varies from 0 to 30 ms, in a regular fashion. It starts out at 30, walks up to 0, then resets to 30 ms.

 

Question 1: is there a way to control this, so that the queue callback is called immediately? I've tried changing the thread priority, and it did nothing.

 

If you notice, using this scheme I have to double-buffer the data: DAQ->DataBuffer->queue->ReceiverVoltsBuffer->graph control. 

I tried to shorted things up a bit by using the facility to get a pointer into the thread-safe queue. In this case, the queue callback looked like this:

 

void CVICALLBACK Receiver_DataAcqHandler (CmtTSQHandle queueHandle, unsigned int event, int value, void *callbackData) {

     float64 *F64_data_p;

     status = CmtGetTSQReadPtr (queueHandle, &F64_data_p, &num_data_read);

       //get rid of old handle

       if (g_iPlotHandleReceiver) {
           status = DeleteGraphPlot (MainWindowHandle, PANEL_GRAPH, g_iPlotHandleReceiver, VAL_DELAYED_DRAW);
           g_iPlotHandleReceiver = 0;
       }

       //plot data

       g_iPlotHandleReceiver = PlotWaveform (MainWindowHandle, PANEL_GRAPH, F64_data_p,
                     num_data_read, VAL_DOUBLE, 1.0, 0.0, 0.0, 1.0,
                     VAL_THIN_LINE, VAL_NO_POINT, VAL_SOLID, 1, VAL_GREEN);

 

      //return pointer

     status = CmtReleaseTSQReadPtr (queueHandle, num_data_read);

}

 

This doesn't work well at all. In addition to the time-slicer delays, the actual routine can take up to 30 ms. I can only conclude that having the queue checked out for the duration of the PlotWaveform call messes things up somehow.

 

Question 2: can somebody help me under stand this timing issue? Something obscure is going on here.

0 Kudos
Message 1 of 4
(4,677 Views)

Hi pblase,

 

Can you clarify what the actual issue is here?  Are you getting error messages or losing data?  Since you are using a queue, the timing of the data collection and processing should be decoupled. 

Jayme W.
Applications Engineer
National Instruments
0 Kudos
Message 2 of 4
(4,634 Views)

Was this issue ever resolved? I am having a similiar problem using a PCI 6534 and TSQ's

0 Kudos
Message 3 of 4
(4,157 Views)

Hi bill.kim,

 

As the last poster commented, this issue is a little hard to understand.  Could you clarify what your exact problem is?  Is there an error message or lost data?

 

I would also strongly recommend starting a new thread for your issue, since this thread is relatively old and won't get as much attention.

 

 

Regards,

0 Kudos
Message 4 of 4
(4,138 Views)