VISA XOP ReadBinary DataFormat
Dear Igor Community,
currently I'm working on a data acquisition program, which reads from a Keyside 2985 electromter using the VISA XOP. To accomplish a high data transfer rate, I would like to read the data in binary format with the VISAReadBinaryWave operation. But unfortunately, in my case, the operation does not work as expected. Instead of the measured values, the VISAReadBinaryWave operation fills the wave with random numbers. Additionally, I get a the following VISA library error:
"(3fff0006) The number of bytes transferred is equal to the requested input count. More data might be available."
My code works fine if I set the Instrument to ASCII output and recieve the dataset via the VISAReadWave operation. I have also disabled terminator character detection as suggested in the VISA XOP manual as you can see in the reduced example below.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
Function KeysightReadBinaryData()
// Initialization request
String K2980Init = "*CLS;:SYSTem:PRESet;"
K2980Init += ":FUNCtion:ON 'CURRent';"
K2980Init += ":FORMat:ELEMents:SENSe CURRent,TIME;" // VOLTage|CURRent|CHARge|RESistance|TIME|STATus|SOURce|TEMPerature|HUMidity
K2980Init += ":FORMat REAL,32;" // Set Format to 32 bit float
K2980Init += ":CURRent:NPLCycles:AUTO OFF;"
K2980Init += ":CURRent:NPLCycles 1;"
K2980Init += ":CURRent:RANGe 2e-12;"
K2980Init += ":CURRent:AVERage:MOVing OFF;"
K2980Init += ":INPut ON;" // Enables Current or Charge measurements
K2980Init += ":SYSTem:TIME:TIMer:COUNt:RESet;"
K2980Init += ":TRIGger:ACQuire:COUNt INF;"
K2980Init += ":TRIGger:ACQuire:SOURce TIMer;"
K2980Init += ":TRIGger:ACQuire:TIMer 25E-3\n"
// Connect
Variable defaultRM, InstrumentID
viOpenDefaultRM(defaultRM)
viOpen(defaultRM, "GPIB0::23::INSTR", 0, 0, InstrumentID)
viClear(InstrumentID)
viSetAttribute(InstrumentID, VI_ATTR_TERMCHAR_EN, 0) // Disable terminator character detection
// Send Initialization
VISAWrite InstrumentID, K2980Init
// Get Errors
String Error = ""
int i = 0
do
VISAWrite InstrumentID, ":SYSTem:ERRor?\n"
VISARead/T="\n" InstrumentID, Error
if (!StringMatch(Error, "*No error*"))
Print Error
endif
i++
while (!StringMatch(Error, "*No error*") && i<20)
VISAWrite InstrumentID, ":INITiate:ACQuire\n"
Sleep/S 10
// Create Waves
Make/D/O/N=40 Currentwave, TimeWave
// Request 40 Datapoints
VISAWrite InstrumentID, ":DATA? CURR,40\n"
// Read binary data
VisaReadBinaryWave/B/TYPE=2 InstrumentID, Currentwave, TimeWave // Type: 2 -> 32-bit float, 4 -> 64-bit float
// Print Error
Print ReportVISAError("VISAReadBinary", InstrumentID, V_status)
viClose(InstrumentID)
return 0
End
Is it possible, that the dataformat of the Instrument is not compatible?
The instruments manual gives the following information:
"IEEE-754 single precision format, set by :FORMat[:DATA] REAL,32
4-byte definite length block data, #<number of digits for byte length><byte length><byte>...<byte><terminator>. For example, two data elements are sent by a data block which consists of a header (3 bytes, #18), two 4-byte data, and a terminator (1 byte). A 4-byte data is used for each data element. Each element consists of a fraction (bits 0 (LSB) to 22), exponent (bits 23 to 30), and sign (bit 31).
Order of bytes set by :FORMat:BORDer NORMal (default): byte 1 to 4
Order of bytes set by :FORMat:BORDer SWAPped: byte 4 to 1
NaN indicates “not a number.”
+infinity indicates positive infinity.
-infinity indicates negative infinity.
IEEE-754 double precision format, set by :FORMat[:DATA] REAL,64
8-byte definite length block data, #<number of digits for byte length><byte length><byte>...<byte><terminator>. For example, one data element is sent by a data block which consists of a header (3 bytes, #18), one 8-byte data, and a terminator (1 byte). An 8-byte data is used for each data element. Each element consists of a fraction (bits 0 (LSB) to 51), exponent (bits 52 to 62), and sign (bit 63).
Order of bytes set by :FORMat:BORDer NORMal (default): byte 1 to 8
Order of bytes set by :FORMat:BORDer SWAPped: byte 8 to 1
NaN indicates “not a number.”
+infinity indicates positive infinity.
-infinity indicates negative infinity."
I tested both, single and double precision, I also tried to change the byte-order. But no approach was succesful. I would be very happy, if anybody has an idea what is going wrong, or what else I could try. Thanks in advance.
I don't completely understand this description of the format but...
Only the data after the byte length and before the terminator is "IEEE-754 single precision format" (FP32 for short). So use VISAReadBinary to read the bytes before the FP32 data, then use VISAReadBinaryWave to read only the FP32 data, then use VISAReadBinary to read the terminator.
Before calling VISAReadBinaryWave, set the number of points in the wave to the number of FP32 data elements, such as (if you know that there are 40 FP32 data elements):
Make/O/N=40 Currentwave, TimeWave
VISAReadBinaryWave reads one element (FP32 in this case) for each element of the wave. (Remove the /D in your current Make statement as shown above.)
I don't know what byte order the instrument uses and it is not clear from the description above. You may need the /B flag or you may need to remove it.
March 9, 2024 at 10:38 am - Permalink
Thanks a lot for your comment, which helped me to solve the problem.
Here comes my solution in case anybody has a similar problem:
With the format setting REAL,32 the Keysight 2985 transmits the binary data as follows:
-> The first byte is 35 in decimal (which corresponds to the #-singn in ASCII code)
-> The second byte represents the number of the following header-bytes in ASCII code (1-9 in ASCII, this is represented by the decimal numbers 49-57)
-> The following header bytes describe the number of databytes in ASCII code
As suggested, the header bytes are read first by VisaReadBinary. As VisaReadBinaryWave fills two waves subsequently, in my example the data are read in only one wave, which is subsequently split up.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
Function KeysightReadBinaryData()
// Initialization request
String K2980Init = "*CLS;:SYSTem:PRESet;"
K2980Init += ":FUNCtion:ON 'CURRent';"
K2980Init += ":FORMat:ELEMents:SENSe CURRent, TIME;" // VOLTage|CURRent|CHARge|RESistance|TIME|STATus|SOURce|TEMPerature|HUMidity
K2980Init += ":FORMat REAL,32;" // Set Format to 32 bit float
K2980Init += ":CURRent:NPLCycles:AUTO OFF;"
K2980Init += ":CURRent:NPLCycles 1;"
K2980Init += ":CURRent:RANGe 2e-12;"
K2980Init += ":CURRent:AVERage:MOVing OFF;"
K2980Init += ":INPut ON;" // Enables Current or Charge measurements
K2980Init += ":SYSTem:TIME:TIMer:COUNt:RESet;"
K2980Init += ":TRIGger:ACQuire:COUNt INF;"
K2980Init += ":TRIGger:ACQuire:SOURce TIMer;"
K2980Init += ":TRIGger:ACQuire:TIMer 25E-3\n"
// Connect
Variable defaultRM, InstrumentID
viOpenDefaultRM(defaultRM)
viOpen(defaultRM, "GPIB0::23::INSTR", 0, 0, InstrumentID)
viClear(InstrumentID)
viSetAttribute(InstrumentID, VI_ATTR_TERMCHAR_EN, 0) // Disable terminator character detection
// Send Initialization
VISAWrite InstrumentID, K2980Init
// Get Errors
String Error = ""
int i = 0
do
VISAWrite InstrumentID, ":SYSTem:ERRor?\n"
VISARead/T="\n" InstrumentID, Error
if (!StringMatch(Error, "*No error*"))
Print Error
endif
i++
while (!StringMatch(Error, "*No error*") && i<20)
VISAWrite InstrumentID, ":INITiate:ACQuire\n"
Sleep/S 5
// Request 40 Datapoints
VISAWrite InstrumentID, ":DATA? CURR,40\n"
// Read Header
Variable Prefix, HeaderLineNum
VisaReadBinary/TYPE=8 InstrumentID, Prefix, HeaderLineNum
String ByteNumStr = ""
VisaReadBinary/S=(ASCIIToDecimal(HeaderLineNum)) InstrumentID, ByteNumStr
Variable ByteNum = str2num(ByteNumStr)
// Create Waves
Make/O/N=(ByteNum/8) Currentwave, TimeWave
Make/O/N=(ByteNum/4) TransferWave
// Read binary data
VisaReadBinaryWave/TYPE=2 InstrumentID, TransferWave // Type: 2 -> 32-bit float, 4 -> 64-bit float
CurrentWave = TransferWave[2*p]
TimeWave = TransferWave[2*p+1]
// Read Terminator
Variable Terminator
VisaReadBinary/TYPE=8 InstrumentID, Terminator
// Print Error
Print ReportVISAError("VISAReadBinary", InstrumentID, V_status)
viClose(InstrumentID)
return 0
End
// ASCII conversion
Function ASCIIToDecimal(Input)
Variable Input
Switch (Input)
case 48:
return 0
case 49:
return 1
break
case 50:
return 2
break
case 51:
return 3
break
case 52:
return 4
break
case 53:
return 5
break
case 54:
return 6
break
case 55:
return 7
break
case 56:
return 8
break
case 57:
return 9
break
default:
return NaN
break
Endswitch
End
March 16, 2024 at 04:54 am - Permalink