Measurement Studio for VC++

cancel
Showing results for 
Search instead for 
Did you mean: 

Synchronize input and output tasks to start at the same sample point [C++ NI_DAQmx Base]

I'm trying to initiate the analog input and output streams to start reliably at the same sample. I've tried triggering the output from the start of the input using the following code [NI-DAQmx Base 2.1 under Mac OS X with an M-Series multifunction board]. It compiles and runs, but gives an error message at the call to "DAQmxBaseCfgDigEdgeStartTrig". Any suggestions about synchronized I/O on this platform?

......................................................................................


#include "NIDAQmxBase.h"
#include
#include
#include

#define DAQmxErrorCheck( functionCall ) { if ( DAQmxFailed( error=( functionCall ) ) ) { goto Error; } }

int main( int argc, char *argv[] )
{
// Task parameters
int32 error = 0;
TaskHandle inputTaskHandle = 0;
TaskHandle outputTaskHandle = 0;
char errorString[ 2048 ] = {'\0'};
int32 i;
time_t startTime;

// input channel parameters
char inputChannelList[] = "Dev1/ai0, Dev1/ai1";
float64 inputVoltageRangeMinimum = -10.0;
float64 inputVoltageRangeMaximum = 10.0;

// output channel parameters
char outputChannelList[] = "Dev1/ao0, Dev1/ao1";
char outputTrigger[] = "Dev1/ai/StartTrigger";
float64 outputVoltageRangeMinimum = -10.0;
float64 outputVoltageRangeMaximum = 10.0;

// Timing parameters
char clockSource[] = "OnboardClock";
uInt64 samplesPerChannel = 100000;
float64 sampleRate = 10000.0;

// Input data parameters
static const uInt32 inputBufferSize = 100;
int16 inputData[ inputBufferSize * 2 ];
int32 pointsToRead = inputBufferSize;
int32 pointsRead;
float64 timeout = 10.0;
int32 totalRead = 0;

// Output data parameters
static const uInt32 outputBufferSize = 1000;
float64 outputData[ outputBufferSize * 2 ];
int32 pointsToWrite = outputBufferSize;
int32 pointsWritten;

for( int i = 0; i < outputBufferSize; i++ )
{
outputData[ 2 * i ] = 9.95 * sin( 2.0 * 3.14159 * i / outputBufferSize );
outputData[ 2 * i + 1 ] = -9.95 * sin( 2.0 * 3.14159 * i / outputBufferSize );
}

// ------------------- configure input task -----------------------
DAQmxErrorCheck ( DAQmxBaseCreateTask( "", &inputTaskHandle ) );
printf( "Created input task\n" );

DAQmxErrorCheck ( DAQmxBaseCreateAIVoltageChan( inputTaskHandle, inputChannelList, "", DAQmx_Val_RSE, inputVoltageRangeMinimum, inputVoltageRangeMaximum, DAQmx_Val_Volts, NULL ) );
printf( "Created AI Voltage Chan\n" );

DAQmxErrorCheck ( DAQmxBaseCfgSampClkTiming( inputTaskHandle, clockSource, sampleRate, DAQmx_Val_Rising, DAQmx_Val_ContSamps, samplesPerChannel ) );
printf( "Set sample rate\n" );

// ------------------- configure output task -----------------------
DAQmxErrorCheck ( DAQmxBaseCreateTask( "", &outputTaskHandle ) );
printf( "Created output task\n" );

DAQmxErrorCheck ( DAQmxBaseCreateAOVoltageChan( outputTaskHandle, outputChannelList, "", outputVoltageRangeMinimum, outputVoltageRangeMaximum, DAQmx_Val_Volts, NULL ) );
printf( "Created AO Voltage Chan OK\n" );

DAQmxErrorCheck ( DAQmxBaseCfgSampClkTiming( outputTaskHandle, clockSource, sampleRate, DAQmx_Val_Rising, DAQmx_Val_ContSamps, samplesPerChannel ) );
printf( "Set sample rate\n" );

// trigger output when input starts
DAQmxErrorCheck ( DAQmxBaseCfgDigEdgeStartTrig( outputTaskHandle, outputTrigger, DAQmx_Val_Rising ) );
printf( "Set output trigger\n" );


// ------------------- configuration -----------------------
// write output signal
DAQmxErrorCheck ( DAQmxBaseWriteAnalogF64( outputTaskHandle, pointsToWrite, 0, timeout, DAQmx_Val_GroupByScanNumber, outputData, &pointsWritten, NULL ) );
printf( "Write output signal\n" );

// set up input buffer
DAQmxErrorCheck ( DAQmxBaseCfgInputBuffer( inputTaskHandle, 200000 ) ); // use a 100,000 sample DMA buffer

// initiate acquisition - must start output task first
DAQmxErrorCheck ( DAQmxBaseStartTask( outputTaskHandle ) );
DAQmxErrorCheck ( DAQmxBaseStartTask( inputTaskHandle ) );

// The loop will quit after 10 seconds
.
.
.
Dr John Clements
Lead Programmer
AxoGraph Scientific
0 Kudos
Message 1 of 12
(11,674 Views)
Hi John,

It looks like you have looked into the situation, but i think you are looking at the way that trigger is used incorrectly.  I looked for the outputtrigger you defined for the trigger source, but looking at your code I am not seeing that.  I would recommend configuring both your tasks to trigger off the same source.  such as PFI0.  This digital line would then be where you could supply a digital ttl pulse to start your acquisition and output.  I think it would be helpful to look at the NI-DAQmxBase 2.x C Function Reference Help.

Looking under Triggering»StartTrigger»DAQmxCFGDIGEdgeStartTrig, you can see an example of using the function and a description of each parameter.

Let me know if you need further help with getting this function or application in general.

Have a great day,

Michael D
Applications Engineering
National Instruments
0 Kudos
Message 2 of 12
(11,643 Views)
Hi Michael,

I'm trying to trigger the output using a 'virtual' source named "Dev1/ai/StartTrigger". This should 'fire' when the input is input is started "DAQmxBaseStartTask( inputTaskHandle )". I took this from example code found on the NI site for NI-DAQmx (full), so perhaps this functionality is not supported by NI-DAQmx Base? If that's true it's a real shame. Or perhaps its not supported by the M-Series card that I'm using?

Internally synchronized output and input is a very common recording mode - the dominant mode in my scientific field (neuroscience) where you typically want to electrically stimulate the experimental preparation and record its response. Other digitizers that my program, AxoGraph X, supports synchronize their input/output by default (digitizers from Instrutech and MDC). Frankly, I'm amazed that it is proving so hard to do this with NI digitizers.

I'd really appreciate some more help on this. If I can't get synchronized I/O going I will have to drop support for NI digitizers, which will cost your company ten's or hundred's of lost sale opportunities in neuroscience labs around the world.

John.

Dr John Clements
Lead Programmer
AxoGraph Scientific

axographx.com
Dr John Clements
Lead Programmer
AxoGraph Scientific
0 Kudos
Message 3 of 12
(11,640 Views)
Hi John,

I understand you want internally synchronized output and I am sure we can get this working with DAQmx base.  I have a piece of LabVIEW code that does everything you described.  One thing that could help us in figuring out why you are having this issue is an actual error code you got and any description that is given.  The DAQmx base description is often "at undefined location", but the code that comes up will be useful.  Can you also let me know specifically wich M series device you are using?  The final thing I would like to see know is the location you pulled the code from originally.  I can find it by searching now that I know it was originally DAQmx code, but I will be able to help you more quickly if you can let me know the page you downloaded it from.  I believe the main issue we will have is making these outputs synched on a software trigger.  Is the trigger necessary, or do you just need to know that the measurements are running on the same clock?  Please get back to me and I will continue looking into software triggering methods.

Have a great day,

Michael D
Applications Engineering
National Instruments


0 Kudos
Message 4 of 12
(11,619 Views)
Hi Michael,

First of all, thanks very much for taking the time to investigate this problem! Much appreciated.

You asked for "an actual error code you got and any description that is given". The full output from the program that I posted earlier in this thread is appended to the end of this message. In summary, following the call to...

DAQmxErrorCheck ( DAQmxBaseCfgDigEdgeStartTrig( outputTaskHandle, outputTrigger, DAQmx_Val_Rising ) );

... with ...

char outputTrigger[] = "Dev1/ai/StartTrigger";

...the error message is ...

DAQmxBase Error: Specified route cannot be satisfied, because the hardware does not support it.

You asked "specifically which M series device you are using"? It is the PCIe 6251 (with BNC 2111 connector block). I'm testing and developing on an Intel Mac Pro (dual boot OS X and Windows XP).

You asked for "the location you pulled the code from". Here it is...

http://zone.ni.com/devzone/cda/epd/p/id/879

...specifically from the file "Multi-Function-Synch AI-AO_Fn.c".

I adapted the NI-DAQmx calls to their NI-DAQmx Base equivalents.

Finally, you asked "Is the trigger necessary, or do you just need to know that the measurements are running on the same clock?". I believe that some kind of sychronized trigger is necessary in my situation (correct me if I'm wrong). Timing is crucial. Say I initiate an analog output stream that delivers a voltage command step 5 ms from the onset. I need to record the response (analog input stream) so that its onset is accurately aligned (synchronized) at 5 ms. A typical recording situation would stimulate and record a short data 'sweep', then wait for the (biological) system to recover, then stimulate and record another short sweep, and repeat. I need all the recorded sweeps to align accurately so that they can be averaged and analyzed conveniently.

I definitely do not want my customers to rely on an expensive external TTL pulse generator to initiate and synchronize each 'sweep'. That would effectively eliminate the cost advantage of an NI board, as well as adding unnecessary complexity in setup and use. It would be a show-stopper for me.

It seems perverse, but would it be possible to use a digital output channel connected directly to a digital input chanel to trigger the input and output streams?

Regards,

John.

........................................

Full output from test program. Compiled with gcc 4 under OS X...

[Session started at 2007-05-23 14:17:01 +1000.]
LoadRuntime: MainBundle
CFBundle 0x303cc0 (executable, loaded)
_CompatibleWithLabVIEWVersion: linkedAgainst: 08208002
_CompatibleWithLabVIEWVersion: result= false, mgErr= 1, theActualVersion= 00000000
_CompatibleWithLabVIEWVersion: linkedAgainst: deadbeef
_CompatibleWithLabVIEWVersion: Reseting Linked Against
_CompatibleWithLabVIEWVersion: linkedAgainst: 08208002
_CompatibleWithLabVIEWVersion: result= true, mgErr= 0, theActualVersion= 00000000
_CompatibleWithLabVIEWVersion: linkedAgainst: 08208002
_CompatibleWithLabVIEWVersion: result= true, mgErr= 0, theActualVersion= 00000000
com.ni.LabVIEW.dll.nidaqmxbaselv
CFBundle 0x313760 (framework, loaded)
{type = 15, string = file://localhost/Library/Frameworks/nidaqmxbaselv.framework/, base = (null)}
Amethyst:Library:Frameworks:nidaqmxbaselv.framework
2007-05-23 14:17:02.248 test-ni[4445] CFLog (21): Error loading /Library/Frameworks/LabVIEW 8.2 Runtime.framework/resource/nitaglv.framework/nitaglv: error code 4, error number 0 (no suitable image found. Did find:
/Library/Frameworks/LabVIEW 8.2 Runtime.framework/resource/nitaglv.framework/nitaglv: mach-o, but wrong architecture)
CFBundle 0x1751fdc0
(framework, not loaded)
Created input task
Created AI Voltage Chan
Set sample rate
Created output task
Created AO Voltage Chan OK
Set sample rate
DAQmxBase Error: Specified route cannot be satisfied, because the hardware does not support it.

test-ni has exited with status 0.
Dr John Clements
Lead Programmer
AxoGraph Scientific
0 Kudos
Message 5 of 12
(11,615 Views)
Hi Michael,

I'm relieved to inform you that the test program I included at the start of this thread now runs following a minor modification.

NI Australia technical support finally got back to me (I sat in their in-box for over a week), and worked with me to solve this issue. It was a ridiculously simple kludge in the end, as I'll explain in a moment.

First I want to say how disappointed I am in the quality of NI's C language support. NI-DAQmx Base really should ship with a C-language example showing how to do synchronized input and output (feel free to use my testbed code). And another example app should show how to synchronize digital and analog outputs. It's wasted several human days (both mine and NI-support) to get a really basic data acquisition task up and running.

OK, so what fixed the problem? Adding a forward slash at the start of each channel name. I changed...

char inputChannelList[] = "Dev1/ai0, Dev1/ai1";
char outputChannelList[] = "Dev1/ao0, Dev1/ao1";
char outputTrigger[] = "Dev1/ai/StartTrigger";

...became...

char inputChannelList[] = "/Dev1/ai0, /Dev1/ai1";
char outputChannelList[] = "/Dev1/ao0, /Dev1/ao1";
char outputTrigger[] = "/Dev1/ai/StartTrigger";

I was able to perform unsynchronized data acquisition using channel names without a forward slash at the start (following a C example application), but could not get the synchronizing 'StartTrigger' to work. After adding the forward slash to all names, it worked.

I CONSIDER THIS A BUG in NI-DAQmx Base! (version 2.1 under Mac OS X). Do you agree? Is there any way to officially file a bug report?

John.

...........

Attached is source code for the de-bugged test application.
Dr John Clements
Lead Programmer
AxoGraph Scientific
0 Kudos
Message 6 of 12
(11,606 Views)
Hi John,

I am glad that things got working on your end and am looking into whether this is expected behavior and should be better documented or actually a bug.  Either way it will become a corrective action request of some sort.  I will let you know what I find at the end of looking into this.  I will also take your testbed code and comment it to be used as an example program.  I want to thank you for taking the time to get this setup in NI-DAQmx Base and hopefully I can get enough content created to make this a much easier issue for future NI-DAQmx Base users.  I will post again with the CAR and let you know if R&D and I decided it was a documentation CAR or actually a problem in the NI-DAQmx Base driver.  Thanks again for all your efforts and I will let you know what the final decision about this issue is.  I have reproduced this behavior and non of the other examples have the extra forward slash, so I am inclinced to think it will be a CAR of the driver.

Have a great day,

Michael D
Applications Engineering
National Instruments
0 Kudos
Message 7 of 12
(11,595 Views)

Michael,

 

Was this ever fixed?  You said in your last note you would tell us what the final decision was.

 

I just got bit by what appears to be the same issue.  I changed a parameter from "/ctr0" to "dev1/ctr0" in order to make it more readable, and broke my code. Because the code looks so emminently reasonable and correct,  I found the problem only after extensive effort.  Wasted time, wasted time.  I can imagine thousands of hours of programmers' lives being wasted by this. 

 

This thread is four years old.  I can't believe it's still an issue in NIDAQ 9.1.  Can't your driver parse these strings more intelligently? 

 

Or, is there a good reason why "/dev1/ctr0" and "dev1/ctr0" and "/ctr0" and "ctr0" should not all have the same effect?

 

Van

 

0 Kudos
Message 8 of 12
(9,124 Views)

Hi Van,

 

I want to make sure we answer your question correctly. You list NIDAQ 9.1. This implies to me you are using NI DAQmx and not NI DAQmx Base. They are seperate drivers and we will need to make sure we answer your question for the appropriate driver. I will have to check what the resolution was from the other issue, but that was regarding DAQmx Base so I am not sure that will have any impact on your current request. Thank you for seeking help on the forums. Please verify you are using DAQmx and not DAQmx Base. We may need to make another product suggestion.

 

Michael

0 Kudos
Message 9 of 12
(9,108 Views)

Van,

 

Thanks for posting.  When you say you "changed a parameter from '/ctr0' to 'dev1/ctr0' " and your code broke, I am not quite sure what behavior you were experiencing.  Perhaps you could post a screenshot of the problem.  Were you using the correct device name as listed in Measurement & Automation Explorer?

 

The correct syntax for identifying physical channels can be found in the DAQmx help.  In addition, physical channel controls and constants will auto-populate with the available channels on the device.  The behavior I experience is that there are no errors when "Dev1/ctr0" or "/Dev1/ctr0" are used, but "Error -200220: Device Identifier is invalid" is thrown when "ctr0" or "/ctr0" are used.  Does this help?

 

Regards,

Joe S.

0 Kudos
Message 10 of 12
(9,104 Views)