Multifunction DAQ

cancel
Showing results for 
Search instead for 
Did you mean: 

Where are nidaqmx low level functions?

> PS, if you are using the Unscaled (e.g. BinaryI16) read calls with M-Series, be sure you correctly apply the scaling coefficients for the device.  M-Series uses a polynomial scaling function, not linear like E-Series.  The polynomial scaling coefficients are a channel property, and you can read them back. 
PS, if you are using the Unscaled (e.g. BinaryI16) read calls with M-Series, be sure you correctly apply the scaling coefficients for the device.  M-Series uses a polynomial scaling function, not linear like E-Series.  The polynomial scaling coefficients are a channel property, and you can read them back. 
 
 
What scaling? We read just voltages but they are inaccurate.
Can you tell me what channel properties give us the polynomial scaling coefficients so we can use the analog binary reading functions?
 
Thanks,
Lavio
 
0 Kudos
Message 41 of 62
(2,795 Views)
Lavio,

This thread is growing very long and I wonder if it might be better to address individual concerns (in separate threads) rather than everything at once.  

This being said I will try to make some comments on your concerns:

1.   Direct Access to the buffer

We understand your request for direct access into the buffer, however, for the reasons stated by Jonathan earlier in this thread it is unlike to happen.  Is the biggest concern that you will have to re-architect your application without such access or is it a concern over performance?  If it is a concern over performance can you provide specific benchmarking code that you have found to be less efficient in DAQmx than Traditional DAQ?  Any such benchmarking code is welcome and we will be happy to look into it.

2.  Scaling M-Series Data

When you read Unscaled data or Raw data it hands you the data as if it came directly from the ADC.  No modification, no adaption, no scaling.  M-Series employs a technology called MCal.  MCal is software calibration based on a third-order polynomial that is created when the board is calibrated.  It allows for very linear measurements across the entire range.  E-Series employed a hardware calibration and raw data returned from the ADC was therefore precalibrated.  However, since hardware calibration is significantly more complicated, the calibration algorithm (albeit an analog calibration algorithm) only adjusted for a single offset and single gain error.  Calibrated M-Series data ends up being far more accurate since it can adjust for none linear gain error.  For more information on this take a look at:

New NI-MCal Calibration Methodology Improves Measurement Accuracy (http://zone.ni.com/devzone/cda/tut/p/id/3688)

By reading the coefficients from the driver you can post-scale your data with these third-order polynomial coefficents and gain this accuracy.  If you don't apply these coefficents I imagine your data can be much less accurate that raw E-Series data.

Usually in situations where a customer is streaming to disk they save the prescaled data along with the polynomial coefficients.  Since the polynomial coefficients don't change during the acquisition the file size overhead is very small.  These coefficients can be pulled out later to scale the data.

3.  Why NI-DAQmx?

First let me say for the record that NI-DAQmx was not a ploy by National Instruments to try to get more people to use LabVIEW.  While Traditional DAQ (Legacy) was a world class driver for its time, when it was originally designed we did not know how many products would eventually be supported.  As devices were added and the API grew more complicated it became clear that a new generation of driver would be needed to address the growing needs of our customers while still being able to continually release world class products such as M-Series, Compact DAQ, and others.  

At that point we decided to tackle the driver from the ground up and we did a lot of thinking.  A lot of thinking...  In fact development of DAQmx was a very, very large undertaking and our developers (some of which have contributed on this very thread) took it very seriously.  It was not trivial to design or produce a driver that would meet the needs of our very diverse customer base that ranges from the very expert software developer such as yourself all the way to the first year engineering student.

Obviously, the fact that we have a very diverse customer base doesn't help you in your application and I understand your specific concerns.  We do hear these concerns and continually look for ways to improve our driver (as noted by Jonathan's earlier comments).

I also understand that the transition from Traditional DAQ to NI-DAQmx in our customer's code is often non-trivial and often requires a major rethinking and overhaul of how things are done.  Such changes are never convenient, but in most cases the end result is much better with more maintainable, easier to understand code and higher performance even in C or C++.

Change is often difficult, but progress is rarely achievable without change (Sorry to wax a bit philosophical)...

Let me know if I have misinterpreted any of your concerns and let us know if you have any additional questions (although you may want to start a new thread for them).

Regards,

Neil S.
Multifunction DAQ PSE
National Instruments
Message 42 of 62
(2,767 Views)
I forgot to mention the name of the function to get the coefficients:

I described this in a different post

http://forums.ni.com/ni/board/message?board.id=250&message.id=35991#M35991

0 Kudos
Message 43 of 62
(2,748 Views)
> I forgot to mention the name of the function to get the coefficients:I forgot to mention the name of the function to get the coefficients:
 
 
Ok, I got this:
 
float64 coefficients[100];
unsigned int arraySizeInSamps = 1000;
DAQmxGetAIScalingCoeff( taskHandle, "DevX/ai0", coefficients, arraySizeInSamps );
 
But now I am confused... because this function is not in my nidaqmx documentation (I installed when installing the software-CD that comes with the M-boards).
 
In my documentation, the closer I have is (on channel properties):
 
int32 __CFUNC DAQmxGetAIChanCalPolyForwardCoeff(TaskHandle taskHandle, const char channel[], float64 *data, uInt32 arraySizeInSamples);

... without any explanation on how it works.

On online documentation, I found:

"NI-MCal has the unique ability to return calibrated data from every channel in a scan, even if they are at different input ranges. This is because NI-MCal determines, saves, and applies correction polynomials for every input range on the device."

How one polynomial from the functon you mentioned can be used for every input range on the device?
Then does it mean we need to use the function mentioned to get the coefficients for each gain (input range) defined with different input range channel configurations?

Sorry for the insistent questions, but the absolute lack of explanation in the documentation does not give me other way to understand this stuff.

In some place I read the polynomials are three order. Also each board and each task can use a different number of channels. Then why these numbers:
float64 coefficients[100];
unsigned int arraySizeInSamps = 1000;
???
I would like to find an explanation on how these function work.

Thanks, Lavio

 

0 Kudos
Message 44 of 62
(2,740 Views)
> > DAQmxGetAIScalingCoeff
>
> But now I am confused... because this function is not in my
> nidaqmx documentation (I installed when installing the
> software-CD that comes with the M-boards).

Lavio,

I believe the function you are looking for is called DAQmxGetAIDevScalingCoeff.

Hope this helps.
LDP
0 Kudos
Message 45 of 62
(2,720 Views)
Lavio,

Yeah... I'm not sure what I was thinking in that code.  I believe you really only have to allocate an array of three parameters (unless you have multiple channels with different gains and want to store all coefficients in the same array).  And I don't know why I created an array of 100 and then said it was 1000...  I guess I was just tired or something....Smiley Sad  Either way, I do agree that we need some better documentation on this.  We have pretty good documentation on the 90% use case, but the 10% use case definitely needs some work.  It is also where people usually need more documentation.  Several other customers have been successful using this scaling method, but it never comes before some discussion either on the forums, or more often over email or phone.  Either way, I'll work on writing a little example I can post here that I have tried out using an M-Series board and verified it worked, rather than just throwing some code up.  I'll try to get something by tomorrow morning.

Thanks for your patience...

Regards,

Neil S.
0 Kudos
Message 46 of 62
(2,699 Views)
> Several other customers have been successful using this scaling method, but it never comes before some discussion either on the forums, or more often over email or phone. Either way, I'll work on writing a little example I can post here that I have tried out using an M-Series board and verified it worked, rather than just throwing some code up.
 
 
Thanks Neils, it will help very much !!!!
We already sold and installed a 16 bits system using the nidaqmx and it is working fine, except for the reading innacurracy, we applied a crap and fast temporary solution. As soon as I am sure how to get and use the polynomial coefficients, the problem will be solved: we will keep using binary numbers in the input and scale them only when needed, not inside the callback.
 
I guess I know how the coefficients work, but of course I need to be 100% sure before making more changes in our software code.
 
🙂
 
And by the way... an old problem I always struggled... because we use continuous acquisition, when we need to change gain (or input range) the nightmare starts... we need to stop the aquisition, stop or kill the task, recreate or use another task already created for the specific channels/gains new configuration choosed by the user in real time and restart the aquisition. Not very useful. Is there another way to change gains/input ranges without the need to stop and restart everything?
 
LMP
 
0 Kudos
Message 47 of 62
(2,684 Views)
LMP,

Here you are.  I finished a simple example.  The example acquires a finite number of samples from two channels that have different gain settings (and therefore different gain settings).  It acquires using ReadUnscaledI16 and then scales and prints out the data.  The two most important parts I have included below to let other forum readers easily see what is happening without downloading the entire file:

This part is where you obtain the coefficients.  This only needs to be done once and can be done before the task starts:

    // Obtain the number of coefficients.  All channels in a single task will have the same number of
    // coefficients so we will just look at the first channel.
    // Passing a NULL and a 0 as the array and number of samples respectively
    // causes the function to return the number of samples instead of an error.
    // Positive number errors are not considered errors and will not halt execution
    DAQmxErrChk (numberOfCoefficients=DAQmxGetAIDevScalingCoeff(taskHandle, channels[0], NULL, 0));

    // Allocate Memory for coefficient array
    coefficients=(float64*)(malloc(64*numberOfCoefficients*numberOfChannels));
    // Check for allocation error
    if (coefficients==NULL) goto Error; else
   
    // Get Coefficients for each channel
    for (uInt32 i=0; i < numberOfChannels; i++) {
        DAQmxErrChk (DAQmxGetAIDevScalingCoeff(taskHandle, channels[i],
               coefficients+(numberOfCoefficients*i), numberOfCoefficients));
    }


This is the scaling and output section:

    for ( uInt32 i=0; i<numberOfChannels; i++) {
        printf("%s Data: ",channels[i]);
        for ( uInt32 j=0; j<numberOfSamplesPerChannel; j++ ){

            // Set offset as initial value
            scaledData[i*numberOfSamplesPerChannel + j] = coefficients[i*numberOfCoefficients];

            // Calculate gain error with the remaining coefficients (for M Series
            // There are 3.  For E-Series there is only 1.
            for ( int32 k=1; k<numberOfCoefficients; k++){
                scaledData[i*numberOfSamplesPerChannel + j] +=
                        (data[i*numberOfSamplesPerChannel+j]^k)*coefficients[i*numberOfCoefficients+k];
            }
            printf("%f, ",scaledData[i*numberOfSamplesPerChannel+j]);
        }
        printf("\n");
    }

There is probably a more efficient way to do the scaling but I wanted to program for clarity rather than efficiency.  You can probably do some pointer magic and create a more efficient algorithm.

As far as changing gains while the task is running, currently this is not possible.  However, you can include the same channel in the same task twice with different gain settings and it will scan the channel twice with adjusted gain settings.  As long as your aggregate sampling rate (i.e. the number of channels added to the task * sampling per channel per second) doesn't exceed the maximum rate of the board you should be okay.

Regards,

Neil S.




Message Edited by Neil S. on 05-01-2008 05:09 PM
0 Kudos
Message 48 of 62
(2,661 Views)
I had someone else look over my code and I found a few things to improve.  Attached is the updated example:

Note that the following lines have changed from the code posted above:

// I was allocating more than I needed.  This is better:
    coefficients=(float64*)(malloc(sizeof(float64)*numberOfCoefficients*numberOfChannels));

// The else was unnecessary:
    if (coefficients==NULL) goto Error;

//  Too many programming languages! (^ is exclusive or in C not power...Smiley Sad)  Note that you have to include math.h
                scaledData[i*numberOfSamplesPerChannel + j] +=
                        coefficients[i*numberOfCoefficients+k]*pow(data[i*numberOfSamplesPerChannel+j],k+0.0);


Also note that this example may not work in the Compact DAQ case since two different modules in the Compact DAQ chassis can have a different number of coefficients.  The example could be modified to handle this case, but it would require a bit more thinking.


Message Edited by Neil S. on 05-02-2008 10:29 AM
0 Kudos
Message 49 of 62
(2,639 Views)
Regarding the scaling of M-Series data: "When you read Unscaled data or Raw data it hands you the data as if it came directly from the ADC. No modification, no adaption, no scaling."

Would it be useful to offer, in a future version of NIDAQmx, a function that reads *corrected* unscaled data i.e. as if you were reading raw data from highly linear ADCs? Or is there a specific reason why this wouldn't be a good idea? I for one would greatly appreciate having such a routine.

Assuming that I read (linearized) floats and do the conversion myself, the following code should work(?):

// MININT = Low(SmallInt) = -32768
// MAXINT = High(SmallInt) = 32767

float scalevolt = 1.0 / (MAXVOLT-MINVOLT) * (MAXINT-MININT);
short int iv;

// in loop
if (v >= MAXVOLT)
iv = MAXINT;
else {
vs = (v-MINVOLT) * scalevolt + MININT;
iv = (short int)(vs+0.5);
}

(Sorry if there are errors - I haven't coded in C for many years.)

Is the routine DAQmxSetReadReadAllAvailSamp relevant to what I am trying to do, or should I not be using an undocumented function?

Finally, it would be very useful to have sample code available which shows how to "fake" circular buffer access as previously done in NIDAQ. In other words, the user creates a buffer array, which is freely accessible in the user's address space, calls a routine (like SCAN_Start or DAQ_Start) which includes the address of this array as a parameter, and then is able to call another routine (DAQ_Check, say) which returns the number of data points collected so far. This would save people who are forced to migrate to NIDAQmx from having to reinvent the wheel, and would provide a quick way of getting up and running (though I realize it may not be the most run-time efficient way). I believe this would also assuage users who, like me, feel frustrated about having to jump through this particular hoop.
0 Kudos
Message 50 of 62
(2,594 Views)