Lazarus
Announcements => Third party => Topic started by: mig-31 on December 03, 2014, 09:52:12 am
-
Hello all,
I opened this topic because there is no subforum for Automation and Measurement.
National Instruments provide ANSI C headers for library NI-Scope for working with their oscilloscope/digitazer cards, for example NI PCI-5122 http://sine.ni.com/nips/cds/view/p/lang/cs/nid/13309 (http://sine.ni.com/nips/cds/view/p/lang/cs/nid/13309).
NI-Scope for Windows http://www.ni.com/download/ni-scope-4.1.3/4598/en/ (http://www.ni.com/download/ni-scope-4.1.3/4598/en/)
NI-Scope for Linux http://www.ni.com/download/ni-scope-3.1/1633/en/ (http://www.ni.com/download/ni-scope-3.1/1633/en/)
I discovered C headers (approx. 5 files) and its have macros and some other definitions, which need deeper C knowledge.
From my side i can also propose bindings testing on real harware, because we will have two NI PXI-5122 card this year or at the start of New Year.
I think it will be good to create binding for FreePascal.
-
Well where are the C headers? Are they just a raw VISA register interface, or maybe an IVI driver interface?
If it's just the VISA(visa.h and visatype.h) headers plus a header for the register table in the specific card then you can find a conversion of the VISA headers here: https://github.com/Laksen/FP-VISA
-
It Ivi driver interface.
From my discovering it six C headers files:
ivi.h
IviScope.h
niScope.h
visa.h
visatype.h
vpptype.h
The most problem is how to convert macros:
its do something or not?
remove all of them?
terrible stringification? >:(
If it's just the VISA(visa.h and visatype.h) headers plus a header for the register table in the specific card then you can find a conversion of the VISA headers here: https://github.com/Laksen/FP-VISA
Two of them you have already translated.
Well where are the C headers? Are they just a raw VISA register interface, or maybe an IVI driver interface?
I don't know if I can upload C headers here, due to copyright restrictions?
-
IviScope sounds like it could be used from IVI-COM. The other headers are probably for a IVI-C interface
If you are on windows you should be able to generate that a header for IVI-COM with importtl. Just run it on the DLL that NI supplies and you should have an autogenerated IVI-COM wrapper right away without the need for the VISA header stuff
-
What is importtl?
I can't find this tool
-
It should come with fpc. At least it does in SVN trunk
-
Thanks, I will try it.
Did you convert visa.h and visatypes.h by partly using h2pas utility or just manually?
-
I used Chelper (http://wiki.lazarus.freepascal.org/Chelper) and some manual fiddling
-
I hope to add information from these messages to this page: http://controlpascal.com/ (http://controlpascal.com/)
The Comedi page (http://controlpascal.com/comedi.htm (http://controlpascal.com/comedi.htm)) has similar material.
-
It will be good, but now we only have binding for NI-VISA, not complete NI-Scope. Thanks for @Laksen.
And also comedy drivers are not supported NI oscilloscopes/digitazers. I hope, yet.
-
I have a problem to convert this part form ivi.h, because i don't what it is
#define IVI_VAL_NAN (*Ivi_GetPtrToSpecialViReal64Value(IVI_VAL_TYPE_NAN))
#define IVI_VAL_PINF (*Ivi_GetPtrToSpecialViReal64Value(IVI_VAL_TYPE_PINF))
#define IVI_VAL_NINF (*Ivi_GetPtrToSpecialViReal64Value(IVI_VAL_TYPE_NINF))
It looks like macro to pointer of function
-
Ivi_GetPtrToSpecialViReal64Value(IVI_VAL_TYPE_NAN)
run that macro or function, which returns a pointer.
The asterix then dereferences that pointer. (
So literally it is
{$define IVI_VAL_NAN := (Ivi_GetPtrToSpecialViReal64Value(IVI_VAL_TYPE_NAN)^)}
though while possible, making it a macro doesn't make much sense in Pascal.
-
Thank,
I think so.
{$define IVI_VAL_NAN := (Ivi_GetPtrToSpecialViReal64Value(IVI_VAL_TYPE_NAN)^)}
Ivi_GetPtrToSpecialViReal64Value - function, IVI_VAL_TYPE_NAN -parametr and defined in other header
I check it not used in other parts of header and other headers.
Only for understanding, can we rewrite it using constant variable like
var
IVI_VAL_NAN:Pointer=Ivi_GetPtrToSpecialViReal64Value(IVI_VAL_TYPE_NAN)^;
-
Still I don't know if I can upload here C headers to NI-Scope driver.
-
Hi, all
there are Pascal bindings for NI-Scope 14.0 driver for Win32 (probably works with Win64) and NI-Scope 3.1 fo Linux 32-bit.
Not tested on real hardware.
Simply add niscope.pas to your project to test it with your hardware. You can find information how to work with it in ANSI C examples, which are distributed with NI-Scope driver.
I hope, when I have real hardware, I will upload some basic examples in Pascal. Without real hardware, I think there is no sense to rewrite some ANSI C examples to Pascal.
There is a good guard to make first steps with NI-Scope on National Instruments web http://www.ni.com/white-paper/3382/en/ (http://www.ni.com/white-paper/3382/en/)
Linux version probably works only for linux kernel 2.6.x, which still supported https://www.kernel.org/ (https://www.kernel.org/) and used in RHEL5,6, Scientific Linux 5,6 (supported until 2017) and old distributions like Mandriva 2006 or Suse Linux.
Bindings are in the attachments.
-
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:
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
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;
-
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.
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
-
There is next example based on binary acquisition example from NI-Scope driver.
-
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
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
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.
-
Hi, guys
I have created the wiki page for NI-Scope Pascal bindings http://wiki.freepascal.org/NI-Scope_examples (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.