Recent

Author Topic: FreePascal bindings for National Instrument oscilloscope/digitizer cards  (Read 19588 times)

mig-31

  • Sr. Member
  • ****
  • Posts: 305
Re: FreePascal bindings for National Instrument oscilloscope/digitizer cards
« Reply #15 on: December 19, 2014, 01:23:52 pm »
Hi,

I found bugs in niscope_win32 bindging and correted it. Please find update in the attachment.

But I how troubles with testing it on NI-PXI5122 card.
Same code works on console application, but crashed in GUI on function CheckError(niScope_AutoSetup(vi),vi); with External:SIGSEGV error.
Now I don't know the way how to solve it.
Thanks for showing the way where is a bug (FPC 2.6.0 Lazarus 1.1 win32).

Console application code:
Code: [Select]
program test;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes, niscope, visatype,sysutils
  { you can add units after this };
var
  resourceName:string = 'Dev3';
  vi:ViSession = VI_NULL;
  actualRecordLength:ViInt32;
  actualSampleRate:viReal64;
  data:array of viReal64;
  channel:string='0';
  wfmInfo:niScope_wfmInfo;
  i:word;

procedure CheckError(AErrorCode: ViStatus; Avi: viSession);
var
  errorMessage:string;
begin
  SetLength(errorMessage,MAX_ERROR_DESCRIPTION);
  if (AErrorCode<>VI_SUCCESS) then begin
    niScope_GetErrorMessage(Avi,AErrorCode,Length(errorMessage),@errorMessage[1]);
    writeln(errorMessage);
    if Avi<>VI_NULL then begin
       niScope_close(Avi);
    end;
    Halt;
  end;
 end;
begin
  //Init Session
  CheckError(niScope_init(@resourceName[1],NISCOPE_VAL_FALSE,NISCOPE_VAL_FALSE,@vi),vi);
  writeln('Pointer value:' ,vi);
  //auto setup measurement
  CheckError(niScope_AutoSetup(vi),vi);
  showMessage(vi);
  //read the actual record length and sample rate setuped by autosetup
  CheckError(niScope_ActualRecordLength(vi,@actualRecordLength),vi);
  CheckError(niScope_SampleRate(vi,@actualSampleRate),vi);
  writeln('Record lenght: ',actualRecordLength,'. Sample Rate: ',actualSampleRate);
  //set buffer length(array) and read data
  SetLength(data,actualRecordLength);
  CheckError(niScope_Read(vi,@channel[1],5.0,ActualRecordLength,@data[1],@wfmInfo),vi);
  //shows first 1000 samples
  writeln('Pointer value:' ,vi);
  for i:=1 to 10 do writeln('sample[',i,'] = ',data[i]:4:2);
  //close Session
  if vi<>VI_NULL then niScope_close(vi)
end.

GUI application onButtonClick code

Code: [Select]
procedure TForm1.StartBtnClick(Sender: TObject);
var
  resourceName:string;
  vi:ViSession;
  actualRecordLength:ViInt32;
  actualSampleRate:viReal64;
  data:array of viReal64;
  channel:string;
  wfmInfo:niScope_wfmInfo;
  procedure CheckError(AErrorCode: ViStatus; Avi: viSession);
var
  errorMessage:string;
begin
  SetLength(errorMessage,MAX_ERROR_DESCRIPTION);
  if (AErrorCode<>VI_SUCCESS) then begin
    niScope_GetErrorMessage(Avi,AErrorCode,Length(errorMessage),@errorMessage[1]);
    ShowMessage(errorMessage);
    if Avi<>VI_NULL then begin
       niScope_close(Avi);
    end;
    Halt;
  end;
 end;
begin
  //Setup hardware
  vi:=VI_NULL;
  resourceName:='Dev3';
  channel:='0';
  //Init Session
  CheckError(niScope_init(Pchar(@resourceName[1]),NISCOPE_VAL_FALSE,NISCOPE_VAL_FALSE,@vi),vi);
  //auto setup measurement
  CheckError(niScope_AutoSetup(vi),vi);

  CheckError(niScope_ActualRecordLength(vi,@actualRecordLength),vi);
   //read the actual record length and sample rate setuped by autosetup
  CheckError(niScope_ActualRecordLength(vi,@actualRecordLength),vi);
  CheckError(niScope_SampleRate(vi,@actualSampleRate),vi);
  //DataMemo.Append('Record lenght: '+IntToStr(actualRecordLength));
  //DataMemo.Append('Sample Rate: '+FloatToStrF(actualSampleRate,ffFixed,6,2));

  //set buffer length(array) and read data
  if  actualRecordLength >1 then begin
    SetLength(data,actualRecordLength);
    niScope_Read(vi,@channel[1],5.0,ActualRecordLength,@data[1],@wfmInfo);
    //shows first 1000 samples
    //for i:=1 to 1000 do DataMemo.Append('Sample['+IntToStr(i)+'] = '+FloatToStrF(data[i],ffFixed,4,2));
  end;

  //close Session
  if vi<>VI_NULL then niScope_close(vi)
end;                   
« Last Edit: December 19, 2014, 01:46:28 pm by mig-31 »
Lazarus 2.2.6 - OpenSuse Leap 15.4, Mageia 8, CentOS 7

mig-31

  • Sr. Member
  • ****
  • Posts: 305
Problem solved!

Thank guys, who found my stupid mistake with cdecl and stdcall convetion error! :)
There is first small worked example with autosetuped card.
To your own project simply add niscope and visatype to USES.
Code: [Select]
program test;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes, niscope, visatype,sysutils
  { you can add units after this };
var
  resourceName:string='Dev3';
  vi:ViSession=VI_NULL;
  wfmNum:ViInt32;
  actualRecordLength:dword;
  actualSampleRate:viReal64;
  data:array of viReal64;
  channel:string = '0';
  wfmInfo:array of niScope_wfmInfo;
  i:longint;
procedure CheckError(AErrorCode: ViStatus; Avi: viSession);
var
  errorMessage:string;
  bufferSize:ViStatus;
begin
  SetLength(errorMessage,MAX_ERROR_DESCRIPTION);
  if (AErrorCode<>VI_SUCCESS) then begin
    //should call twice because we don't know errorMessage length
    //get string length
    bufferSize:=niScope_GetErrorMessage(Avi,AErrorCode,0,@errorMessage[1]);
   //get Error message
    SetLength(errorMessage,bufferSize);
    niScope_GetErrorMessage(Avi,AErrorCode,Length(errorMessage),@errorMessage[1]);
    writeln(errorMessage);
    if Avi<>VI_NULL then begin
       niScope_close(Avi);
    end;
    Halt;
  end;
 end;
begin
  //Init Session
  CheckError(niScope_init(PChar(@resourceName[1]),NISCOPE_VAL_FALSE,NISCOPE_VAL_FALSE,@vi),vi);
  writeln('Vi handler value (should be 1):' ,vi);
  //auto setup measurement
  CheckError(niScope_AutoSetup(vi),vi);
  //read the actual record length and number of waveforms
  //setuped by autosetup
  CheckError(niScope_ActualRecordLength(vi,@actualRecordLength),vi);
  CheckError(niScope_SampleRate(vi,@actualSampleRate),vi);
  CheckError(niScope_ActualNumWfms(vi,PChar(@channel[1]),@wfmNum),vi);

  //set buffer for data (lenght of data array)
  SetLength(data,actualRecordLength*wfmNum);
  SetLength(wfmInfo,wfmNum);

  CheckError(niScope_Read(vi,PChar(@channel[1]),5.0,actualRecordLength,@data[0],@wfmInfo[0]),vi);
  //shows first 10 samples
  writeln('----first ten samples of record------------------');
  for i:=Low(data) to Low(data)+9 do writeln('sample[',i,'] = ',data[i]:4:2);
  //show Info
  writeln('---------------------------------------------------');
  writeln('Record lenght: ',actualRecordLength);
  writeln('Sample rate: ',actualSampleRate);
  writeln('Number of waveforms: ',wfmNum);
  //show Info from wfmInfo record
  writeln('Actual samples: ',wfmInfo[0].actualSamples);
  writeln('X Increament: ',wfmInfo[0].xIncrement);
  writeln('Gain: ',wfmInfo[0].Gain);
  //close Session
  if vi<>VI_NULL then niScope_close(vi)
end.                         
Find corrected bindings in the attachment
« Last Edit: January 16, 2015, 02:05:38 pm by mig-31 »
Lazarus 2.2.6 - OpenSuse Leap 15.4, Mageia 8, CentOS 7

mig-31

  • Sr. Member
  • ****
  • Posts: 305
There is next example based on binary acquisition example from NI-Scope driver.
Lazarus 2.2.6 - OpenSuse Leap 15.4, Mageia 8, CentOS 7

mig-31

  • Sr. Member
  • ****
  • Posts: 305
Hi,

there is corrected (bugs in visatype.pas missing of some pointers definitions) NI-Scope pascal bindings for NI-Scope 3.1 for Linux(to correct installation of NI-Scope install NIDAQmx 8.0.2 for Linux after NI-Scope).

Tested under Scientific Linux 6.4 on National Instruments PXI system:
1. NI PXIe-1078 chassic.
2. NI PXIe-8101 controller.
3. 2x NI PXI-5122 (NI-Scope 3.1 for Linux driver needed).

Autosetup code example

Code: [Select]
program test;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes, niscope, visatype,sysutils
  { you can add units after this };
var
  resourceName:string='Dev1';
  vi:ViSession=VI_NULL;
  wfmNum:ViInt32;
  actualRecordLength:dword;
  actualSampleRate:viReal64;
  data:array of viReal64;
  channel:string = '0';
  wfmInfo:array of niScope_wfmInfo;
  i:longint;
procedure CheckError(AErrorCode: ViStatus; Avi: viSession);
var
  errorMessage:string='';
  bufferSize:ViStatus;
begin
  SetLength(errorMessage,MAX_ERROR_DESCRIPTION);
  if (AErrorCode<>VI_SUCCESS) then begin
    //due on unkown string length niScope_GetErrorMessage should call twice
    //get buffer size
    bufferSize:=niScope_GetError(Avi,@AErrorCode,0,@errorMessage[1]);
    //read error message
    SetLength(errorMessage,bufferSize);
    niScope_GetError(Avi,@AErrorCode,Length(errorMessage),@errorMessage[1]);
    writeln(errorMessage);
    if Avi<>VI_NULL then begin
       niScope_close(Avi);
    end;
    Halt;
  end;
 end;
begin
  //Init Session
  CheckError(niScope_init(PChar(@resourceName[1]),NISCOPE_VAL_FALSE,NISCOPE_VAL_FALSE,@vi),vi);
  writeln('Vi handler value (should be 1):' ,vi);
  //auto setup measurement
  CheckError(niScope_AutoSetup(vi),vi);
  //read the actual record length and number of waveforms
  //setuped by autosetup
  CheckError(niScope_ActualRecordLength(vi,@actualRecordLength),vi);
  CheckError(niScope_SampleRate(vi,@actualSampleRate),vi);
  CheckError(niScope_ActualNumWfms(vi,PChar(@channel[1]),@wfmNum),vi);

  //set buffer for data (lenght of data array)
  SetLength(data,actualRecordLength*wfmNum);
  SetLength(wfmInfo,wfmNum);

  CheckError(niScope_Read(vi,PChar(@channel[1]),5.0,actualRecordLength,@data[0],@wfmInfo[0]),vi);
  //shows first 10 samples
  writeln('----first ten samples of record------------------');
  for i:=Low(data) to Low(data)+9 do writeln('sample[',i,'] = ',data[i]:4:2);
  //show Info
  writeln('---------------------------------------------------');
  writeln('Record lenght: ',actualRecordLength);
  writeln('Sample rate: ',actualSampleRate);
  writeln('Number of waveforms: ',wfmNum);
  //show Info from wfmInfo record
  writeln('Actual samples: ',wfmInfo[0].actualSamples);
  writeln('X Increament: ',wfmInfo[0].xIncrement);
  writeln('Gain: ',wfmInfo[0].Gain);
  //close Session
  if vi<>VI_NULL then niScope_close(vi)
end.

Binary acquisition example

Code: [Select]
program binaryacq;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Classes,niscope,visatype
  { you can add units after this };
 var
   error:ViStatus = VI_SUCCESS;
   vi:ViSession;
   //Variables setuped hardware
   resourceName:string = 'Dev1';
   channelName:string = '0,1';  //nastavit merici kanal
   verticalRange:ViReal64 = 10;//rozsah -5 - 5 V
   verticalOffset:ViReal64 = 0.0;
   minSampleRate:ViReal64 = 2e5;
   minRecordLength:ViInt32 = 1000;
   binaryDataType:ViInt32;
   numWaveform:ViInt32;
   actualRecordLength:ViInt32;
   //Default values
   triggerType:ViInt32 = 0; //Immediate trigger
   triggerCoupling:ViInt32 = NISCOPE_VAL_DC;
   triggerSlope:ViInt32 = NISCOPE_VAL_POSITIVE;
   triggerLevel:ViReal64 = 0.0;
   triggerHoldoff:ViReal64 = 0.0;
   triggerDelay:ViReal64 = 0.0;
   triggerSource:string = '0';//nastavit kanal pro trigger
   refPosition:ViReal64 = 50.0;
   timeout:ViReal64 = 5.0;
   stop:ViInt32;
   i,j:ViInt32;

   wfmInfo:array of niScope_wfmInfo;
   scaledWfm:array of ViReal64;
   //NI-5122 is 14-bit digitizer
   binaryWfm:array of ViInt16;
procedure CheckError(AErrorCode: ViStatus; Avi: viSession);
var
  errorMessage:string='';
  bufferSize:ViStatus;
begin
  SetLength(errorMessage,MAX_ERROR_DESCRIPTION);
  if (AErrorCode<>VI_SUCCESS) then begin
    //due on unkown string length niScope_GetErrorMessage should call twice
    //get buffer size
    bufferSize:=niScope_GetError(Avi,@AErrorCode,0,@errorMessage[1]);
    //read error message
    SetLength(errorMessage,bufferSize);
    niScope_GetError(Avi,@AErrorCode,Length(errorMessage),@errorMessage[1]);
    writeln(errorMessage);
    if Avi<>VI_NULL then begin
       niScope_close(Avi);
    end;
    Halt;
  end;
 end;
begin
   //Open the NI_SCOPE instrument handler
   CheckError(niScope_init(Pchar(@resourceName[1]),NISCOPE_VAL_FALSE,NISCOPE_VAL_FALSE,@vi),vi);

   //Configure the vertical parameters
   CheckError(niScope_ConfigureVertical(vi,PChar(@channelName[1]),verticalRange,verticalOffset,NISCOPE_VAL_DC,1.0,NISCOPE_VAL_TRUE),vi);
   //Configure the horizontal parameters
   //one record

   CheckError(niScope_ConfigureHorizontalTiming(vi,minSampleRate,minRecordLength,refPosition,1,VI_TRUE),vi);
   //Configure the trigger

   if triggerType<>0  //Edge
     then CheckError(niScope_ConfigureTriggerEdge(vi,PChar(@triggerSource[1]),triggerLevel,triggerSlope,triggerCoupling,triggerHoldoff,triggerDelay),vi)
     else CheckError(niScope_ConfigureTriggerImmediate(vi),vi);

   //Get number of waveforms
   CheckError(niScope_ActualNumWfms(vi,PChar(@channelName[1]),@numWaveform),vi);

   //Get record length
   CheckError(niScope_ActualRecordLength(vi,@ActualRecordLength),vi);
   writeln('Record length: ',ActualRecordLength);

   //Set buffer for data and waveform Info
   SetLength(binaryWfm,ActualRecordLength*numWaveform);
   SetLength(scaledWfm,ActualRecordLength*numWaveform);
   SetLength(wfmInfo,numWaveform);

   //signal acquisition  1 record
   stop:=NISCOPE_VAL_FALSE;
   while stop<>NISCOPE_VAL_TRUE do begin
     CheckError(niScope_InitiateAcquisition(vi),vi);
     CheckError(niScope_FetchBinary16(vi,PChar(@channelName[1]),timeout,actualRecordLength,@binaryWfm[0],@wfmInfo[0]),vi);
     stop:=NISCOPE_VAL_TRUE;
   end;
   niScope_Close(vi);
   //calculate scaled data from binary data
   for i:=0 to NumWaveform-1 do
     for j:=0 to ActualRecordLength-1 do scaledWfm[j+(i*ActualRecordLength-1)]:=binaryWfm[j+(i*ActualRecordLength-1)]*wfmInfo[i].Gain+wfmInfo[i].offset;

   for i:=0 to NumWaveform-1 do begin
     writeln('   Waveform channel   ',i+1);
     writeln('   First 10 samples ');
     writeln('----------------------');
     for j:=0 to 10 do writeln('value[',j,'] = ',scaledWfm[j+(i*ActualRecordLength-1)]:4:2);
     writeln('----------------------');
  end;
   writeln('program stoped');
end.                                       
« Last Edit: March 18, 2015, 02:03:20 pm by mig-31 »
Lazarus 2.2.6 - OpenSuse Leap 15.4, Mageia 8, CentOS 7

mig-31

  • Sr. Member
  • ****
  • Posts: 305
Hi, guys

I have created the wiki page for NI-Scope Pascal bindings http://wiki.freepascal.org/NI-Scope_examples.

I hope it would be usefull for others.

I invite you to check, improve, correct and add information.

Thanks.
Lazarus 2.2.6 - OpenSuse Leap 15.4, Mageia 8, CentOS 7

 

TinyPortal © 2005-2018