High-Speed Digitizers

cancel
Showing results for 
Search instead for 
Did you mean: 

Why niScope_FetchBinary16 is slow for some sampling rates

I was trying to profile application fetching data from NI 5124 PXI digitizer.

Starting from example code in BinaryAcquisition and using VisualStudio 2008 C++ Net Express edition I made test application

and measured the time spent in niScope_FetchBinary16 called in loop.

 

The result is strange to me. I have summarized it in table.

 

Sample rate (Sr) || Namber of point to fetch (N) || Averaged time spent in function (T), ms || theoretical time of scan = N/Sr, ms

200 Ms/s  ||  2000  ||  0.22 ms     ||  0.01ms

  20 Ms/s  ||  2000  ||  0.22 ms     ||  0.1ms

    2 Ms/s  ||  2000  ||  8.9 ms       ||  1 ms

200 Ks/s   ||  2000  || 19.1 ms      ||  10 ms

  20 Ks/s   ||  2000  ||  109.78 ms || 100ms

 

It looks to me that difference of two times ( T - N/Sr ) should be approximately same for all sampling rates because number of points N is constant.

That contradicts with measured results.

Can some body reproduce these measurements / explain results to me?

 

The following is code I have used:

 

 

// test.cpp : main project file.

#include "stdafx.h"
#include <string>
#include "niScope.h"

#define MAX_STRING_SIZE 100

using namespace System;

ViStatus _VI_FUNC niScope_GenericBinaryAcquisition (void);

int main(array<System::String ^> ^ /*args*/)
{
    Console::WriteLine(L"Start Test niScope_GenericBinaryAcquisition");

    System::Diagnostics::Stopwatch^ stopwatch = gcnew System::Diagnostics::Stopwatch();
    stopwatch->Stop();
    stopwatch->Reset();
    stopwatch->Start();

    ViSession vi;

    // Variables used to get values from the GUI
    ViChar resourceName[MAX_STRING_SIZE] = "Dev1";
    ViChar channelName[MAX_STRING_SIZE] = "0";
    ViChar ansiOptionString[MAX_STRING_SIZE] = "Simulate=0,RangeCheck=1,QueryInstrStatus=1,Cache=1";
    ViReal64 verticalRange = 10.0;
    ViReal64 verticalOffset = 0.0;
    ViReal64 minSampleRate = 200000000.0;
    ViInt32 minRecordLength = 2000;
    ViInt32 binaryDataType = 2; // bin16
    ViReal64 refPosition = 50.0;
    ViReal64 timeout = 15.0; // seconds

    ViInt32 numWaveform;
    ViInt32 actualRecordLength;
    ViReal64 actualSampleRate;

    // Waveforms
    struct niScope_wfmInfo * wfmInfoPtr = NULL;
    ViReal64 * scaledWfmPtr = NULL;
    void * binaryWfmPtr = NULL;

    // Open the NI-SCOPE instrument handle
    if( VI_SUCCESS != niScope_InitWithOptions( resourceName, NISCOPE_VAL_FALSE, NISCOPE_VAL_FALSE, ansiOptionString, &vi ))
        return 0;

    // Configure the vertical parameters
    if( VI_SUCCESS != niScope_ConfigureVertical( vi, channelName, verticalRange, verticalOffset, NISCOPE_VAL_DC, 1.0, NISCOPE_VAL_TRUE ) )
        return 0;

    // loop over sampling rate
    for(int i = 0; i < 10; i++)
    {
        Int64 mss1;
        Int64 tiks1;
        Int64 mss2;
        Int64 tiks2;
        Int64 dmss = 0;
        Int64 dtiks = 0;
        String^ mess;
        // Configure the horizontal parameters
        if( VI_SUCCESS != niScope_ConfigureHorizontalTiming( vi, minSampleRate, minRecordLength, refPosition, 1, VI_TRUE ) )
            return 0;

        // Configure the trigger
        if( VI_SUCCESS != niScope_ConfigureTriggerImmediate( vi ) )
            return 0;

        // Find out the number of waveforms, based on # of channels and records
        if( VI_SUCCESS != niScope_ActualNumWfms( vi, channelName, &numWaveform ) )
            return 0;

        // Query the coerced record length
        if( VI_SUCCESS != niScope_ActualRecordLength( vi, &actualRecordLength ) )
            return 0;

        // Query the coerced record length
        if( VI_SUCCESS != niScope_SampleRate( vi, &actualSampleRate ) )
            return 0;

        // Allocate space for the waveform ,waveform info, and binary waveform 
        // according to the record length and number of waveforms
        wfmInfoPtr = (struct niScope_wfmInfo*) 
            malloc (sizeof (struct niScope_wfmInfo) * numWaveform);
        binaryWfmPtr = (void*) malloc (binaryDataType * actualRecordLength * numWaveform);
        scaledWfmPtr = (ViReal64*) malloc (sizeof (ViReal64) * actualRecordLength * numWaveform);

        // Check is memory allocations succeeded.
        if ( scaledWfmPtr == NULL || wfmInfoPtr == NULL || binaryWfmPtr == NULL )
            return 0;

        mess = L"S/s: " + actualSampleRate.ToString() + L"; N: " + actualRecordLength.ToString();
        Console::WriteLine( mess );

        // 50 repetitions of calls to niScope_FetchBinary16 function
        for(int j=0; j<50; j++)
        {

            // Initiate the acquisition
            if( VI_SUCCESS != niScope_InitiateAcquisition( vi ) )
                return 0;

            mss1 = stopwatch->ElapsedMilliseconds;
            tiks1 = stopwatch->ElapsedTicks;
            mess = L"\t" + mss1.ToString() + L"ms" + L" (" + tiks1.ToString() + L")";
            //Console::WriteLine( mess );

            // Fetch the data according to the obtained data type
            // Use the 16 bit fetching function
            if( VI_SUCCESS != niScope_FetchBinary16( vi, channelName, timeout, actualRecordLength, (ViInt16*) binaryWfmPtr, wfmInfoPtr ) )
                return 0;

            mss2 = stopwatch->ElapsedMilliseconds;
            tiks2 = stopwatch->ElapsedTicks;
            mess = "\t" + mss2.ToString() + L"ms" + L" (" + tiks2 + L")";
            //Console::WriteLine( mess );

            dmss = dmss + mss2 - mss1;
            dtiks = dtiks + tiks2 - tiks1;
        }
        mess = "            Delta: " + dmss/50.0+ L"ms" + L" (" + dtiks/50.0 + L")";
        Console::WriteLine( mess );

        minSampleRate = minSampleRate / 10;
    }

    // Close the session
    if (vi)
        niScope_close (vi);

    return 0;
}

 

 

0 Kudos
Message 1 of 2
(5,692 Views)

I have recompiled the test application as win32 console application using VisualStudio 2008 C++ Express edition.

 

The results are same.

The spent in fetching function time smaller by order for 20 Ms/s sampling rate than for 2 Ms/s.

If some body has ideas why is that share it with me please.

 

Sample rate (Sr) || Number of points to fetch (N) || Averaged time spent in function (T), ms || theoretical time of scan = N/Sr, ms

200 Ms/s  ||  2000  ||  0.25 ms     ||  0.01ms

  20 Ms/s  ||  2000  ||  0.22 ms     ||  0.1ms

    2 Ms/s  ||  2000  ||  9.95 ms     ||  1 ms

200 Ks/s   ||  2000  || 20.02 ms    ||  10 ms

  20 Ks/s   ||  2000  ||  110.92 ms || 100ms

 

The code of test is following (this forum rejects cpp code to be attached).

 

 

// ltdtestwin32.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include "niScope.h"
#include "Windows.h"

#define MAX_STRING_SIZE 100

int _tmain(int argc, _TCHAR* argv[])
{
std::cout << "Start Test niScope" << std::endl;

ViSession vi;

// Variables used to get values from the GUI
ViChar resourceName[MAX_STRING_SIZE] = "Dev1";
ViChar channelName[MAX_STRING_SIZE] = "0";
//ViChar ansiOptionString[MAX_STRING_SIZE] = "Simulate=1,DriverSetup=Noise:4.0;Model:5124;BoardType:PXI;";
ViChar ansiOptionString[MAX_STRING_SIZE] = "Simulate=0,RangeCheck=1,QueryInstrStatus=1,Cache=1";
ViReal64 verticalRange = 10.0;
ViReal64 verticalOffset = 0.0;
ViReal64 minSampleRate = 200000000.0;
ViInt32 minRecordLength = 2000;
ViInt32 binaryDataType = 2; // bin16
ViReal64 refPosition = 50.0;
ViReal64 timeout = 15.0; // seconds

ViInt32 numWaveform;
ViInt32 actualRecordLength;
ViReal64 actualSampleRate;

// Waveforms
struct niScope_wfmInfo * wfmInfoPtr = NULL;
ViReal64 * scaledWfmPtr = NULL;
void * binaryWfmPtr = NULL;

// Open the NI-SCOPE instrument handle
// niScope_init( resourceName, NISCOPE_VAL_FALSE, NISCOPE_VAL_FALSE, &vi );
if( VI_SUCCESS != niScope_InitWithOptions( resourceName, NISCOPE_VAL_FALSE, NISCOPE_VAL_FALSE, ansiOptionString, &vi ))
return 0;

// Configure the vertical parameters
if( VI_SUCCESS != niScope_ConfigureVertical( vi, channelName, verticalRange, verticalOffset, NISCOPE_VAL_DC, 1.0, NISCOPE_VAL_TRUE ) )
return 0;

LARGE_INTEGER tiks1;
LARGE_INTEGER tiks2;
LARGE_INTEGER dtiks;

//calibrate QueryPerformanceCounter()
int num_of_ms = 1000;
QueryPerformanceCounter( &tiks1 );
Sleep( num_of_ms );
QueryPerformanceCounter( &tiks2 );
dtiks.QuadPart = tiks2.QuadPart - tiks1.QuadPart;
double tiks_per_ms = 1.0 * dtiks.QuadPart / num_of_ms;

int num_of_rep = 100;

for(int i = 0; i < 10; i++)
{
// Configure the horizontal parameters
if( VI_SUCCESS != niScope_ConfigureHorizontalTiming( vi, minSampleRate, minRecordLength, refPosition, 1, VI_TRUE ) )
return 0;

// Configure the trigger
if( VI_SUCCESS != niScope_ConfigureTriggerImmediate( vi ) )
return 0;

// Find out the number of waveforms, based on # of channels and records
if( VI_SUCCESS != niScope_ActualNumWfms( vi, channelName, &numWaveform ) )
return 0;

// Query the coerced record length
if( VI_SUCCESS != niScope_ActualRecordLength( vi, &actualRecordLength ) )
return 0;

// Query the coerced record length
if( VI_SUCCESS != niScope_SampleRate( vi, &actualSampleRate ) )
return 0;

// Allocate space for the waveform ,waveform info, and binary waveform
// according to the record length and number of waveforms
wfmInfoPtr = (struct niScope_wfmInfo*)
malloc (sizeof (struct niScope_wfmInfo) * numWaveform);
binaryWfmPtr = (void*) malloc (binaryDataType * actualRecordLength * numWaveform);
scaledWfmPtr = (ViReal64*) malloc (sizeof (ViReal64) * actualRecordLength * numWaveform);

// Check is memory allocations succeeded.
if ( scaledWfmPtr == NULL || wfmInfoPtr == NULL || binaryWfmPtr == NULL )
return 0;

std::cout << "S/s: " << actualSampleRate << "; N: " << actualRecordLength << std::endl;

dtiks.QuadPart = 0;
for(int j=0; j<num_of_rep; j++)
{
// Initiate the acquisition
if( VI_SUCCESS != niScope_InitiateAcquisition( vi ) )
return 0;

QueryPerformanceCounter( &tiks1 );

// Fetch the data according to the obtained data type
// Use the 16 bit fetching function
if( VI_SUCCESS != niScope_FetchBinary16( vi, channelName, timeout, actualRecordLength, (ViInt16*) binaryWfmPtr, wfmInfoPtr ) )
return 0;

QueryPerformanceCounter( &tiks2 );

//std::cout << " t1: " << tiks1.QuadPart << " t2: " << tiks2.QuadPart << std::endl;

dtiks.QuadPart = dtiks.QuadPart + tiks2.QuadPart - tiks1.QuadPart;

}

std::cout << " Delta: " << dtiks.QuadPart / tiks_per_ms / num_of_rep << " ms" << std::endl;

minSampleRate = minSampleRate / 10;
}

// Close the session
if (vi)
niScope_close (vi);

return 0;
}

 

 

0 Kudos
Message 2 of 2
(5,679 Views)