LeCroy scope trace acquisition over IP with VISA
hiller
Synopsis:
LeCroyGetTrace(99, "C1", awave) // Scope has IP address of 192.168.1.99
Wave is redimensioned to number of points in the trace, so original size doesn't matter. Wave scaling is correct for both horizontal and vertical axes. Averaged traces are handled properly, using 2-byte transfer to maintain higher precision.
#pragma rtGlobals=1 // Use modern global access method.
// File LeCroyGetTrace.ipf
// Version 2009-02-19
//
// Read a trace from LeCroy digital oscilloscope over IP
// Assumes 'scope is on local LAN with fixed IP address of the form 192.168.1.x
// This allows using multiple scopes (with different IP addresses).
//
// Only tested on WinXP and LeCroy 104Xi
//
// Requires NI-VISA - ftp.ni.com/support/visa/drivers/win32/4.4/visa441full.exe
// Requires LeCroy VICP Passport - lecroy.com/tm/Library/Software/LabView/VICP.asp?menuid=8
// Requires VISA.xop - comes with Igor, put into C:\Program Files\Igor\Igor Extensions
//
// Synopsis:
//
// Make awave // Number of points doesn't matter, it will be redimensioned
// LeCroyGetTrace(99, "C1", awave) // Scope has IP address of 192.168.1.99
// Display awave
// print SelectString(WaveIsClipped(a), "Wave is not clipped", "Wave is clipped")
//
// Wave is redimensioned to number of points in the trace, so original size doesn't matter
// Wave scaling is correct for both horizontal and vertical axes.
//
// Averaged traces are handled properly, using 2-byte transfer to maintain higher precision
//
// Author: Robert Hiller bob AT roberthiller DOT com
//
// This function is copied from VISA.ipf (and renamed) to allow operation
// even if the VISA.ipf procedure file is not present.
Function LocalReportVISAError(name, session, status)
String name // VISA function name, e.g., viRead or other identifier
Variable session // Session ID obtained from viOpen
Variable status // Status code from VISA library
String desc
viStatusDesc(session, status, desc)
Printf "%s error (%x): %s\r", name, status, desc
End
Structure LeCroy_time // Time Stamp structure for trigger time from LeCroy scope
double seconds // seconds and fractional seconds
char minutes // (0-59)
char hours // (0-23)
char days // (0-31)
char months // (1-12)
int16 year // (0-16000)
int16 unused //
EndStructure
Structure WAVEDESC // Wave descriptor block for LeCroy scope
char DEF9header[16] // 'DESC,#9000000346' (Not part of template)
char DESCRIPTOR_NAME[16] // 'WAVEDESC '
char TEMPLATE_NAME[16] // 'LECROY_2_3 ' Name of the template
int16 COMM_TYPE // 0=byte, 1=word
int16 COMM_ORDER // 0=hifirst, 1=lofirst
int32 WAVE_DESCRIPTOR // length of block WAVEDESC
int32 USER_TEXT // length of block USERTEXT
int32 RES_DESC1 // unknown
int32 TRIGTIME_ARRAY // length of TRIGTIME array
int32 RIS_TIME_ARRAY // length of RIS_TIME array
int32 RES_ARRAY1 // (expansion entry)
int32 WAVE_ARRAY_1 // lengh of first data array
int32 WAVE_ARRAY_2 // length of second data array
int32 RES_ARRAY2 // (expansion entry)
int32 RES_ARRAY3 // (expansion entry)
char INSTRUMENT_NAME[16] // string name of instrument
int32 INSTRUMENT_NUMBER // serial number
char TRACE_LABEL[16] // identifies the waveform
int16 RESERVED1 // (expansion entry)
int16 RESERVED2 // (expansion entry)
int32 WAVE_ARRAY_COUNT // number of data points in data array
int32 PNTS_PER_SCREEN // nominal number of points on screen
int32 FIRST_VALID_PNT // number points to skip before first valid
int32 LAST_VALID_PNT // index of last good point
int32 FIRST_POINT // offset from beginning of trace buffer
int32 SPARSING_FACTOR // sparsing into the data block
int32 SEGMENT_INDEX // index of the transmitted segment
int32 SUBARRAY_COUNT // acquired segment count
int32 SWEEPS_PER_ACQ // for average or extrama
int16 POINTS_PER_PAIR // for peak detect
int16 PAIR_OFFSET // for peak detect, offset array2 to array1
float VERTICAL_GAIN // to get floating values from raw data:
float VERTICAL_OFFSET // value = VERTICAL_GAIN * data - VERTICAL_OFFSET
float MAX_VALUE // maximum allowed value, upper edge of the grid.
float MIN_VALUE // minimum allowed value, lower edge of the grid.
int16 NOMINAL_BITS // intrinsic precision: ADC data is 8 bit, averate 10-12
int16 NOM_SUBARRAY_COUNT // for Sequence, nominal segment count, else 1
float HORIZ_INTERVAL // sampling interval for time domain waveforms
double HORIZ_OFFSET // seconds between trigger and the first data point
double PIXEL_OFFSET // "needed to know how to display the waveform"
char VERTUNIT[48] // units of the vertical axis
char HORUNIT[48] // units of the horizontal axis
float HORIZ_UNCERTAINTY // uncertainty of the horizontal offset in seconds
STRUCT LeCroy_time TRIGGER_TIME // time of the trigger
float ACQ_DURATION // duration of the acquisition (in sec)
int16 RECORD_TYPE // 0=single_sweep, etc
int16 PROCESSING_DONE // 0=no_processing, etc
int16 RESERVED5 // (expansion entry)
int16 RIS_SWEEPS // for RIS, number of sweeps, else 1
int16 TIMEBASE // 0=1_ps/div, 1=2_ps/div, etc
int16 VERT_COUPLING // 0=DC_50_Ohms, 1=ground, 2=DC_1MOhm, 3=ground, 4=AC_1MOhm
float PROBE_ATT // probe attenuation
int16 FIXED_VERT_GAIN // 0=1_uV/div, 1=2_uv/div, etc
int16 BANDWIDTH_LIMIT // 0=off, 1=on
float VERTICAL_VERNIER // vertical gain fine adjust
float ACQ_VERT_OFFSET // unknown
int16 WAVE_SOURCE // 0=CHANNEL_1, 1=CHANNEL_2, 2=CHANNEL_3, 3=CHANNEL_4, 9=UNKNOWN
EndStructure
// Get waveform data from LeCroy scope into a wave using IP (ethernet)
// Scope is expected to be on local network with ip address 192.168.1.X
//
// The wave is properly scaled for time. Vertical scaling shows scope range.
// The wave is re-dimensioned to hold the number of points in the waveform.
// Averaged waves are handled properly, using 2-byte transfer to maintain higher precision
//
// This function requires: NIVisa, LeCroy VICP passport, and the Igor VISA xop.
//
Function LeCroyGetTrace(ip, trace, datawave)
Variable ip // Last number of IPv4 address: 192.168.1.ip
String trace // Trace to fetch, e.g. "C1" or "Z2" or "F3" or "M4"
Wave datawave // Wave to receive data. This wave is redimensioned to fit the data.
Variable defaultRM // Resource manager session ID
Variable instr // Instrument ID, used for subsequent read and write calls
Variable status // Status return from vi calls. Non-zero value indicates failure
String cmd
STRUCT WAVEDESC wd
String wdstring
Variable NominalBits, DataBits, DataOffset, Minimum, Maximum
// Create a Resource Manager for VISA calls. It will be closed at the end of the function.
status = viOpenDefaultRM(defaultRM)
if (status != 0)
LocalReportVISAError("viOpenDefaultRM", instr, status)
return status
endif
// Open a session with the scope. The variable instr is used for subsequent communication.
String resourceName
sprintf resourceName, "VICP::192.168.1.%d", ip // VICP requires LeCroy VICP passport plugin
status = viOpen(defaultRM, resourceName, 0, 0, instr)
if (status != 0)
viClose(defaultRM)
LocalReportVISAError("viOpen", instr, status)
return status
endif
// Turn off command headers for predictable response from scope
VISAwrite instr, "CHDR OFF" // Turn off command headers (echo) in return
if (V_flag == 0) // Problem with communication
LocalReportVISAError("VISAWrite", instr, V_status)
viClose(instr)
viClose(defaultRM)
return V_status
endif
// Non-averaged data has 8 bits of resolution. Averaged or calculated data can have more bits.
// The number of bits is interrogated first. If data has more than 8 bits of resolution
// then the data is transferred as words. This has to be resolved before the header is read.
// Bad return for NOMINAL_BITS means bad communication or bad trace identifier
sprintf cmd, "%s:INSPECT? 'NOMINAL_BITS'", trace // Get bit depth
VISAwrite instr, cmd
VISAread/T="\r\n" instr, cmd
Sscanf cmd, "\"NOMINAL_BITS : %d", NominalBits // This scan only works with CHDR OFF
if (NominalBits == 8)
VISAwrite instr, "CFMT DEF9, BYTE,BIN" // Use single byte transfer if bit depth is 8
elseif (NominalBits > 8)
VISAwrite instr, "CFMT DEF9, WORD,BIN" // Use 2 byte transfer if bit depth is greater
else
Print "Failure: bad return for NOMINAL_BITS:", NominalBits, " Trace requested:", trace
viClose(instr)
viClose(defaultRM)
Return NominalBits
endif
// Get the Wave Descriptor block, and parse into WAVEDESC structure
sprintf cmd, "%s:WF? DESC", trace // Request waveform descriptor
VISAwrite instr, cmd
VISAReadBinary/S=362 instr, wdstring // Header is 346 bytes, plus 16 byte IEEE488.2 DEF9 leadin
StructGet/S/b=3 wd, wdstring // String is parsed into header template. Little-endian byte order.
// Redimension wave to fit the data. Wave is made single precision (to save space).
Redimension/S/N=(wd.WAVE_ARRAY_COUNT) datawave
// Get the data array block. Bytes per datum (1 or 2) is set above, depending on nominal bits.
// Data is scaled on the fly with /Y option to VISAReadBinaryWave to true vertical scaling (volts)
sprintf cmd, "%s:WF? DAT1", trace // Request data array (bytes or words depending on CommType)
VISAwrite instr, cmd
DataOffset = -wd.VERTICAL_OFFSET / wd.VERTICAL_GAIN // This is needed for on-the-fly scaling in VISAReadBinary
VISAReadBinary/S=16 instr, cmd // Get past IEEE488 DEF9 leadin "DAT1,#9xxxxxxxx"
DataBits = (wd.COMM_TYPE + 1) * 8 // TYPE flag is 8 for byte, 16 for word
VISAReadBinaryWave/B/TYPE=(DataBits)/Y={DataOffset,wd.VERTICAL_GAIN} instr, datawave // Vertical scale data on the fly
// Set horizontal scaling for correct timing on each point
SetScale/P x, wd.HORIZ_OFFSET, wd.HORIZ_INTERVAL, "s", datawave // Change wave scaling for correct horizontal
// Set vertical scaling to represent absolute maximum and minimum.
// If data values exceed these it may be assumed to be clipped.
// Use functions FullScaleMax(w) and FullScaleMin(w) (in this .ipf file) to retrieve these values
Minimum = wd.MIN_VALUE*wd.VERTICAL_GAIN-wd.VERTICAL_OFFSET
Maximum = wd.MAX_VALUE*wd.VERTICAL_GAIN-wd.VERTICAL_OFFSET
SetScale d Minimum,Maximum, "V", datawave // Set vertical units to volts
viClose(instr)
viClose(defaultRM)
End
// Get scope channel trace by channel number. See LeCroyGetTrace for details.
Function LeCroyGetChannel(ip, channel, datawave)
Variable ip // Last number of IPv4 address: 192.168.1.ip
Variable channel // Channel to fetch, 1-4
Wave datawave // Wave to receive data. This wave is redimensioned to fit the data.
String trace
sprintf trace, "C%d", channel
LeCroyGetTrace(ip, trace, datawave)
End
// Generic command send and receive from the scope
// Example:
// print LeCroyQuery(99, "*IDN?")
Function/S LeCroyQuery(ip, cmd)
Variable ip
String cmd
Variable defaultRM, instr
Variable status
status = viOpenDefaultRM(defaultRM)
String resourceName
sprintf resourceName, "VICP::192.168.1.%d", ip
status = viOpen(defaultRM, resourceName, 0, 0, instr)
VISAwrite instr, cmd
VISAread/T="\r\n" instr, cmd
viClose(instr)
viClose(defaultRM)
Return cmd
End
// Generic command send to the scope
// Example:
// LeCroyWrite(99, "MSG 'Your Ad Here'")
Function LeCroyWrite(ip, cmd)
Variable ip
String cmd
Variable defaultRM, instr
Variable status, ret
status = viOpenDefaultRM(defaultRM)
if (status)
LocalReportVISAError("viOpenDefaultRM", instr, status)
endif
String resourceName
sprintf resourceName, "VICP::192.168.1.%d", ip
status = viOpen(defaultRM, resourceName, 0, 0, instr)
if (status)
LocalReportVISAError("viOpen", instr, status)
endif
status=viWrite(instr, cmd, strlen(cmd), ret)
if (status)
LocalReportVISAError("viWrite", instr, status)
endif
// Intermittent bug
// The write command is not completed, and is lost, unless some
// other command is done before the instrument is closed. If
// the following line is removed, the scope does not see the
// data sent by the viWrite command above.
// status = viGetAttribute(instr, VI_ATTR_TMO_VALUE, ret)
// if (status)
// LocalReportVISAError("viGetAttribute", instr, status)
// endif
viClose(instr)
End
// Returns Full Scale Minimum for a wave. Settable/viewable in Change Wave Scaling dialog.
Function FullScaleMin(w)
Wave w
String wi, fsinfo
wi= WaveInfo(w,0) // WaveInfo string is key-value pairs. Zero is required.
fsinfo = StringByKey("FULLSCALE", wi) // Fullscale info is HasFSinfo, FSMin, FSMax
if (str2num(StringFromList(0, fsinfo, ","))) // True means Full scale info is present
return str2num(StringFromList(1, fsinfo, ","))
else
return NaN // If no full scale info, then return Nan
endif
End
// Returns Full Scale Minimum for a wave. Settable/viewable in Change Wave Scaling dialog.
Function FullScaleMax(w)
Wave w
String wi, fsinfo
wi= WaveInfo(w,0) // WaveInfo string is key-value pairs. Zero is required.
fsinfo = StringByKey("FULLSCALE", wi) // Fullscale info is HasFSinfo, FSMin, FSMax
if (str2num(StringFromList(0, fsinfo, ","))) // True means Full scale info is present
return str2num(StringFromList(2, fsinfo, ","))
else
return NaN // If no full scale info, then return Nan
endif
End
// Tests if wave is clipped (range extends to or beyond Full Scale maximum or minimum)
// If full scale information is not present, returns FALSE (depends on funky NaN math).
Function WaveIsClipped(w)
Wave w
WaveStats/Q w
return (V_max>=FullScaleMax(w)) || (V_min<=FullScaleMin(w))
End
// File LeCroyGetTrace.ipf
// Version 2009-02-19
//
// Read a trace from LeCroy digital oscilloscope over IP
// Assumes 'scope is on local LAN with fixed IP address of the form 192.168.1.x
// This allows using multiple scopes (with different IP addresses).
//
// Only tested on WinXP and LeCroy 104Xi
//
// Requires NI-VISA - ftp.ni.com/support/visa/drivers/win32/4.4/visa441full.exe
// Requires LeCroy VICP Passport - lecroy.com/tm/Library/Software/LabView/VICP.asp?menuid=8
// Requires VISA.xop - comes with Igor, put into C:\Program Files\Igor\Igor Extensions
//
// Synopsis:
//
// Make awave // Number of points doesn't matter, it will be redimensioned
// LeCroyGetTrace(99, "C1", awave) // Scope has IP address of 192.168.1.99
// Display awave
// print SelectString(WaveIsClipped(a), "Wave is not clipped", "Wave is clipped")
//
// Wave is redimensioned to number of points in the trace, so original size doesn't matter
// Wave scaling is correct for both horizontal and vertical axes.
//
// Averaged traces are handled properly, using 2-byte transfer to maintain higher precision
//
// Author: Robert Hiller bob AT roberthiller DOT com
//
// This function is copied from VISA.ipf (and renamed) to allow operation
// even if the VISA.ipf procedure file is not present.
Function LocalReportVISAError(name, session, status)
String name // VISA function name, e.g., viRead or other identifier
Variable session // Session ID obtained from viOpen
Variable status // Status code from VISA library
String desc
viStatusDesc(session, status, desc)
Printf "%s error (%x): %s\r", name, status, desc
End
Structure LeCroy_time // Time Stamp structure for trigger time from LeCroy scope
double seconds // seconds and fractional seconds
char minutes // (0-59)
char hours // (0-23)
char days // (0-31)
char months // (1-12)
int16 year // (0-16000)
int16 unused //
EndStructure
Structure WAVEDESC // Wave descriptor block for LeCroy scope
char DEF9header[16] // 'DESC,#9000000346' (Not part of template)
char DESCRIPTOR_NAME[16] // 'WAVEDESC '
char TEMPLATE_NAME[16] // 'LECROY_2_3 ' Name of the template
int16 COMM_TYPE // 0=byte, 1=word
int16 COMM_ORDER // 0=hifirst, 1=lofirst
int32 WAVE_DESCRIPTOR // length of block WAVEDESC
int32 USER_TEXT // length of block USERTEXT
int32 RES_DESC1 // unknown
int32 TRIGTIME_ARRAY // length of TRIGTIME array
int32 RIS_TIME_ARRAY // length of RIS_TIME array
int32 RES_ARRAY1 // (expansion entry)
int32 WAVE_ARRAY_1 // lengh of first data array
int32 WAVE_ARRAY_2 // length of second data array
int32 RES_ARRAY2 // (expansion entry)
int32 RES_ARRAY3 // (expansion entry)
char INSTRUMENT_NAME[16] // string name of instrument
int32 INSTRUMENT_NUMBER // serial number
char TRACE_LABEL[16] // identifies the waveform
int16 RESERVED1 // (expansion entry)
int16 RESERVED2 // (expansion entry)
int32 WAVE_ARRAY_COUNT // number of data points in data array
int32 PNTS_PER_SCREEN // nominal number of points on screen
int32 FIRST_VALID_PNT // number points to skip before first valid
int32 LAST_VALID_PNT // index of last good point
int32 FIRST_POINT // offset from beginning of trace buffer
int32 SPARSING_FACTOR // sparsing into the data block
int32 SEGMENT_INDEX // index of the transmitted segment
int32 SUBARRAY_COUNT // acquired segment count
int32 SWEEPS_PER_ACQ // for average or extrama
int16 POINTS_PER_PAIR // for peak detect
int16 PAIR_OFFSET // for peak detect, offset array2 to array1
float VERTICAL_GAIN // to get floating values from raw data:
float VERTICAL_OFFSET // value = VERTICAL_GAIN * data - VERTICAL_OFFSET
float MAX_VALUE // maximum allowed value, upper edge of the grid.
float MIN_VALUE // minimum allowed value, lower edge of the grid.
int16 NOMINAL_BITS // intrinsic precision: ADC data is 8 bit, averate 10-12
int16 NOM_SUBARRAY_COUNT // for Sequence, nominal segment count, else 1
float HORIZ_INTERVAL // sampling interval for time domain waveforms
double HORIZ_OFFSET // seconds between trigger and the first data point
double PIXEL_OFFSET // "needed to know how to display the waveform"
char VERTUNIT[48] // units of the vertical axis
char HORUNIT[48] // units of the horizontal axis
float HORIZ_UNCERTAINTY // uncertainty of the horizontal offset in seconds
STRUCT LeCroy_time TRIGGER_TIME // time of the trigger
float ACQ_DURATION // duration of the acquisition (in sec)
int16 RECORD_TYPE // 0=single_sweep, etc
int16 PROCESSING_DONE // 0=no_processing, etc
int16 RESERVED5 // (expansion entry)
int16 RIS_SWEEPS // for RIS, number of sweeps, else 1
int16 TIMEBASE // 0=1_ps/div, 1=2_ps/div, etc
int16 VERT_COUPLING // 0=DC_50_Ohms, 1=ground, 2=DC_1MOhm, 3=ground, 4=AC_1MOhm
float PROBE_ATT // probe attenuation
int16 FIXED_VERT_GAIN // 0=1_uV/div, 1=2_uv/div, etc
int16 BANDWIDTH_LIMIT // 0=off, 1=on
float VERTICAL_VERNIER // vertical gain fine adjust
float ACQ_VERT_OFFSET // unknown
int16 WAVE_SOURCE // 0=CHANNEL_1, 1=CHANNEL_2, 2=CHANNEL_3, 3=CHANNEL_4, 9=UNKNOWN
EndStructure
// Get waveform data from LeCroy scope into a wave using IP (ethernet)
// Scope is expected to be on local network with ip address 192.168.1.X
//
// The wave is properly scaled for time. Vertical scaling shows scope range.
// The wave is re-dimensioned to hold the number of points in the waveform.
// Averaged waves are handled properly, using 2-byte transfer to maintain higher precision
//
// This function requires: NIVisa, LeCroy VICP passport, and the Igor VISA xop.
//
Function LeCroyGetTrace(ip, trace, datawave)
Variable ip // Last number of IPv4 address: 192.168.1.ip
String trace // Trace to fetch, e.g. "C1" or "Z2" or "F3" or "M4"
Wave datawave // Wave to receive data. This wave is redimensioned to fit the data.
Variable defaultRM // Resource manager session ID
Variable instr // Instrument ID, used for subsequent read and write calls
Variable status // Status return from vi calls. Non-zero value indicates failure
String cmd
STRUCT WAVEDESC wd
String wdstring
Variable NominalBits, DataBits, DataOffset, Minimum, Maximum
// Create a Resource Manager for VISA calls. It will be closed at the end of the function.
status = viOpenDefaultRM(defaultRM)
if (status != 0)
LocalReportVISAError("viOpenDefaultRM", instr, status)
return status
endif
// Open a session with the scope. The variable instr is used for subsequent communication.
String resourceName
sprintf resourceName, "VICP::192.168.1.%d", ip // VICP requires LeCroy VICP passport plugin
status = viOpen(defaultRM, resourceName, 0, 0, instr)
if (status != 0)
viClose(defaultRM)
LocalReportVISAError("viOpen", instr, status)
return status
endif
// Turn off command headers for predictable response from scope
VISAwrite instr, "CHDR OFF" // Turn off command headers (echo) in return
if (V_flag == 0) // Problem with communication
LocalReportVISAError("VISAWrite", instr, V_status)
viClose(instr)
viClose(defaultRM)
return V_status
endif
// Non-averaged data has 8 bits of resolution. Averaged or calculated data can have more bits.
// The number of bits is interrogated first. If data has more than 8 bits of resolution
// then the data is transferred as words. This has to be resolved before the header is read.
// Bad return for NOMINAL_BITS means bad communication or bad trace identifier
sprintf cmd, "%s:INSPECT? 'NOMINAL_BITS'", trace // Get bit depth
VISAwrite instr, cmd
VISAread/T="\r\n" instr, cmd
Sscanf cmd, "\"NOMINAL_BITS : %d", NominalBits // This scan only works with CHDR OFF
if (NominalBits == 8)
VISAwrite instr, "CFMT DEF9, BYTE,BIN" // Use single byte transfer if bit depth is 8
elseif (NominalBits > 8)
VISAwrite instr, "CFMT DEF9, WORD,BIN" // Use 2 byte transfer if bit depth is greater
else
Print "Failure: bad return for NOMINAL_BITS:", NominalBits, " Trace requested:", trace
viClose(instr)
viClose(defaultRM)
Return NominalBits
endif
// Get the Wave Descriptor block, and parse into WAVEDESC structure
sprintf cmd, "%s:WF? DESC", trace // Request waveform descriptor
VISAwrite instr, cmd
VISAReadBinary/S=362 instr, wdstring // Header is 346 bytes, plus 16 byte IEEE488.2 DEF9 leadin
StructGet/S/b=3 wd, wdstring // String is parsed into header template. Little-endian byte order.
// Redimension wave to fit the data. Wave is made single precision (to save space).
Redimension/S/N=(wd.WAVE_ARRAY_COUNT) datawave
// Get the data array block. Bytes per datum (1 or 2) is set above, depending on nominal bits.
// Data is scaled on the fly with /Y option to VISAReadBinaryWave to true vertical scaling (volts)
sprintf cmd, "%s:WF? DAT1", trace // Request data array (bytes or words depending on CommType)
VISAwrite instr, cmd
DataOffset = -wd.VERTICAL_OFFSET / wd.VERTICAL_GAIN // This is needed for on-the-fly scaling in VISAReadBinary
VISAReadBinary/S=16 instr, cmd // Get past IEEE488 DEF9 leadin "DAT1,#9xxxxxxxx"
DataBits = (wd.COMM_TYPE + 1) * 8 // TYPE flag is 8 for byte, 16 for word
VISAReadBinaryWave/B/TYPE=(DataBits)/Y={DataOffset,wd.VERTICAL_GAIN} instr, datawave // Vertical scale data on the fly
// Set horizontal scaling for correct timing on each point
SetScale/P x, wd.HORIZ_OFFSET, wd.HORIZ_INTERVAL, "s", datawave // Change wave scaling for correct horizontal
// Set vertical scaling to represent absolute maximum and minimum.
// If data values exceed these it may be assumed to be clipped.
// Use functions FullScaleMax(w) and FullScaleMin(w) (in this .ipf file) to retrieve these values
Minimum = wd.MIN_VALUE*wd.VERTICAL_GAIN-wd.VERTICAL_OFFSET
Maximum = wd.MAX_VALUE*wd.VERTICAL_GAIN-wd.VERTICAL_OFFSET
SetScale d Minimum,Maximum, "V", datawave // Set vertical units to volts
viClose(instr)
viClose(defaultRM)
End
// Get scope channel trace by channel number. See LeCroyGetTrace for details.
Function LeCroyGetChannel(ip, channel, datawave)
Variable ip // Last number of IPv4 address: 192.168.1.ip
Variable channel // Channel to fetch, 1-4
Wave datawave // Wave to receive data. This wave is redimensioned to fit the data.
String trace
sprintf trace, "C%d", channel
LeCroyGetTrace(ip, trace, datawave)
End
// Generic command send and receive from the scope
// Example:
// print LeCroyQuery(99, "*IDN?")
Function/S LeCroyQuery(ip, cmd)
Variable ip
String cmd
Variable defaultRM, instr
Variable status
status = viOpenDefaultRM(defaultRM)
String resourceName
sprintf resourceName, "VICP::192.168.1.%d", ip
status = viOpen(defaultRM, resourceName, 0, 0, instr)
VISAwrite instr, cmd
VISAread/T="\r\n" instr, cmd
viClose(instr)
viClose(defaultRM)
Return cmd
End
// Generic command send to the scope
// Example:
// LeCroyWrite(99, "MSG 'Your Ad Here'")
Function LeCroyWrite(ip, cmd)
Variable ip
String cmd
Variable defaultRM, instr
Variable status, ret
status = viOpenDefaultRM(defaultRM)
if (status)
LocalReportVISAError("viOpenDefaultRM", instr, status)
endif
String resourceName
sprintf resourceName, "VICP::192.168.1.%d", ip
status = viOpen(defaultRM, resourceName, 0, 0, instr)
if (status)
LocalReportVISAError("viOpen", instr, status)
endif
status=viWrite(instr, cmd, strlen(cmd), ret)
if (status)
LocalReportVISAError("viWrite", instr, status)
endif
// Intermittent bug
// The write command is not completed, and is lost, unless some
// other command is done before the instrument is closed. If
// the following line is removed, the scope does not see the
// data sent by the viWrite command above.
// status = viGetAttribute(instr, VI_ATTR_TMO_VALUE, ret)
// if (status)
// LocalReportVISAError("viGetAttribute", instr, status)
// endif
viClose(instr)
End
// Returns Full Scale Minimum for a wave. Settable/viewable in Change Wave Scaling dialog.
Function FullScaleMin(w)
Wave w
String wi, fsinfo
wi= WaveInfo(w,0) // WaveInfo string is key-value pairs. Zero is required.
fsinfo = StringByKey("FULLSCALE", wi) // Fullscale info is HasFSinfo, FSMin, FSMax
if (str2num(StringFromList(0, fsinfo, ","))) // True means Full scale info is present
return str2num(StringFromList(1, fsinfo, ","))
else
return NaN // If no full scale info, then return Nan
endif
End
// Returns Full Scale Minimum for a wave. Settable/viewable in Change Wave Scaling dialog.
Function FullScaleMax(w)
Wave w
String wi, fsinfo
wi= WaveInfo(w,0) // WaveInfo string is key-value pairs. Zero is required.
fsinfo = StringByKey("FULLSCALE", wi) // Fullscale info is HasFSinfo, FSMin, FSMax
if (str2num(StringFromList(0, fsinfo, ","))) // True means Full scale info is present
return str2num(StringFromList(2, fsinfo, ","))
else
return NaN // If no full scale info, then return Nan
endif
End
// Tests if wave is clipped (range extends to or beyond Full Scale maximum or minimum)
// If full scale information is not present, returns FALSE (depends on funky NaN math).
Function WaveIsClipped(w)
Wave w
WaveStats/Q w
return (V_max>=FullScaleMax(w)) || (V_min<=FullScaleMin(w))
End
Forum
Support
Gallery
Igor Pro 9
Learn More
Igor XOP Toolkit
Learn More
Igor NIDAQ Tools MX
Learn More
February 19, 2009 at 10:59 pm - Permalink
This routine is fairly bulletproof; it's been running in a number of applications, and has come to be a workhorse around our lab. The combination of Igor and digitizing scopes makes a powerful tool. The data transfer runs faster than 10 MSa/s, even through multiple IP switches.
If more folks are interested in modifying this, it might do better as an Igor Exchange project. I don't know how to set that up, but would be supportive if somebody else did.
A few notes on this routine:
The wavedescriptor structure is taken from the template from the scope. The scope has a text description of these fields built-in, which can be accessed with the TMPL? query. All of the fields are automatically filled in, but only a few are used in this script. An improved version would put many of these into global variables, such as BANDWIDTH_LIMIT, VERT_COUPLING, the trigger time, and so on.
Note that the scope query function LeCroyQuery(ip, cmd) as is currently written will terminate on the first endline, so it cannot read the template without modification. Change VISAread/T="\r\n" instr, cmd to VISAread/N=65535 instr, cmd
The maximum and minimum possible values are stored in the wave's 'd' scaling. They may be accessed with the addition functions FullScaleMin(w) and FullScaleMax(w) in this procedure file. The scope *does* return values outside of these limits (by a few bits) but I think it's safe to assume the wave is clipped if any values are outside.
Robert Hiller
February 25, 2009 at 03:50 pm - Permalink
Nice work!!!
I have used Igor Pro 8 to communicate to the LeCroy HDO6104, HDO4014, and Wave Surfer 4054HD.
I have confirmed that this routine works fine on these systems (although I have modified the code somewhat).
August 18, 2021 at 06:50 pm - Permalink
I solved a similar problem with a Tek scope in 2008, with much help from WaveMetrics. For further details see https://www.wavemetrics.com/forum/general/visa-control-oscilloscope
There is no code in that thread, but some overall comments that some folks may find helpful. It is impressive that code from IP6 in 2008, 2009 still holds up. Kudos to WaveMetrics.
August 19, 2021 at 04:42 am - Permalink