Load Tek ISF File

I recommend that you download the attached file rather than copying this code because I had some formatting problems with the URLs in the comments.

 

 

#pragma rtGlobals=3		// Use modern global access method.

// File Loader for Tektronix ISF files
// A Tektronix ISF file is the contents of what a Tek oscilloscope sends in response
// to a WAVFrm? command. See https://www.tek.com/support/faqs/what-format-isf-file
//
// A Tek ISF file is not the same as a Tek WFM file. There exists a loader for Tek WFM files.
// See https://www.wavemetrics.com/project/LoadTekWaveform.
//
// It is recommended that you name your Tek ISF files with the ".isf" extension to make
// it clear what kind of file it is.

// The way to determine the header length is to look for the ":CURVE" keyword. It is formatted like this:
//		":CURVE <digit><number>"		.e.g., ":CURVE 41000"
// where <digit> represents the number of digits is the following number and <number> represents the number of
// samples of binary data that follow. In this case, "4" is the number of digits in "1000" and 1000 is the number of samples.

static Constant kHeaderLength = 282				// Length of header in bytes. Empirically determined.
static Constant kWaveDataType = 2				// Single precision floating point

//	LoadTekISFFile(pathName, fileName, createTable, createGraph)
//
//	pathName is the name of an Igor symbolic path or "".
//	fileName is one of the following:
//		The name of a file in the folder designated by pathName.
//		A partial path from the folder designated by pathName to the file to be loaded.
//		A full path to the file to be loaded.
//		"" to get an Open File dialog.

// Add an item to Igor's Load Waves submenu (Data menu)
Menu "Load Waves"
	"Load Tek ISF File...", LoadTekISFFileMenuItem()
	"Load All Tek ISF Files In Folder...", LoadAllTekISFFiles("", "", 0, 0)
End

Function/S StripDoubleQuotes(str)
	String str					// A string starting and ending with double quotes

	if (CmpStr(str[0], "\"") != 0)
		return str				// String did not start with double quotes
	endif
	
	Variable len  = strlen(str)
	str = str[1,len-2]
	return str
End

Structure TekISFHeaderInfo
	Variable encoding			// 0 = binary. -1 = unknown. Currently the only encoding I know about is binary. From ENCDG keyword.
	Variable byteOrder			// 0 = big-endian, 1 = little-endian.  From BYT_OR keyword.
	Variable dataType			// See WaveType for possible values. From BIT_NR and BN_FMT keywords.
	Variable numPoints			// Number of points in waveform. From NR_PT keyword.
	String xUnits				// From XUNIT keyword.
	Variable x0					// From XZERO keyword. 
	Variable dx					// From XINCR keyword. 
	String yUnits				// From YUNIT keyword.
	Variable yOffset				// From YOFF keyword.
	Variable yZero				// From YZERO keyword.
	Variable yMultiplier		// From YMULT keyword.
EndStructure

Function ExtractISFHeaderInfo(refNum, headerInfo, quiet)
	Variable refNum			// File reference number
	STRUCT TekISFHeaderInfo &headerInfo
	Variable quiet				// 0 - print information about errors
	
	Variable result = 0
	
	Variable originalPos
	FStatus refNum
	originalPos = V_filePos
	
	// Read header string
	String headerStr = ""
	Variable headerStrLength = kHeaderLength
	FSetPos refNum, 0
	headerStr = PadString(headerStr, kHeaderLength, 0x20)
	FBinRead refNum, headerStr
	FSetPos refNum, originalPos
	
	// Parse header string
	String str
	str = StringByKey("ENCDG", headerStr, " ")
	strswitch(str)
		case "BIN":
			headerInfo.encoding = 0
			break
		default:
			headerInfo.encoding = -1			// Unknown encoding
			result -= 1
			break
	endswitch

	str = StringByKey("BYT_OR", headerStr, " ")
	strswitch(str)
		case "MSB":
			headerInfo.byteOrder = 0
			break
		case "LSB":
			headerInfo.byteOrder = 1
			break
		default:
			headerInfo.byteOrder = -1			// Unknown byte order
			result -= 1
			break
	endswitch
	
	headerInfo.numPoints = NumberByKey("NR_PT", headerStr, " ")

	headerInfo.xUnits = StripDoubleQuotes(StringByKey("XUNIT", headerStr, " "))
	headerInfo.x0 = NumberByKey("XZERO", headerStr, " ")
	headerInfo.dx = NumberByKey("XINCR", headerStr, " ")
	
	headerInfo.yUnits = StripDoubleQuotes(StringByKey("YUNIT", headerStr, " "))
	headerInfo.yOffset = NumberByKey("YOFF", headerStr, " ")
	headerInfo.yZero = NumberByKey("YZERO", headerStr, " ")
	headerInfo.yMultiplier = NumberByKey("YMULT", headerStr, " ")
	
	Variable numBitsPerPoint = NumberByKey("BIT_NR", headerStr, " ")
	String binaryFormat = StringByKey("BN_FMT", headerStr, " ")
	strswitch(binaryFormat)
		case "RI":								// Presumably "real integer"
			switch (numBitsPerPoint)
				case 8:
					headerInfo.dataType = 8		// 8 bit signed integer
					break
			
				case 16:
					headerInfo.dataType = 16	// 16 bit signed integer
					break
			
				case 32:
					headerInfo.dataType = 32	// 32 bit signed integer
					break
			
				default:
					headerInfo.dataType = -1	// Unknown type
					result -= 1
					break
			endswitch		
			break
		
		default:
			headerInfo.dataType = -1			// Don't understand datatype
			result -= 1
			break
	endswitch
	
	return result
End

//	LoadTekISFFileDialog(createTable, createGraph)
//	Displays a dialog in which the user can enter parameters.
//	All of the parameters function both as inputs, to preset the dialog values,
//	and as output, to return the values to the calling routine.
//	The function result is 0 if OK or -1 if cancel.
Function LoadTekISFFileDialog(createTable, createGraph)
	Variable &createTable				// Input and output.
	Variable &createGraph				// Input and output.

	if ( (createTable<1) || (createTable>2) )
		createTable = 1
	endif
	if ( (createGraph<1) || (createGraph>2) )
		createGraph = 1
	endif
	
	// This is necessary because you can't pass an & parameter to Prompt.
	Variable createTable2 = createTable
	Variable createGraph2 = createGraph
	
	Prompt createTable2, "Create table", popup "Yes;No"
	Prompt createGraph2, "Create graph", popup "Yes;No"
	DoPrompt "Load Tek ISF File", createTable2, createGraph2
	if (V_Flag != 0)
		return -1			// User cancelled.
	endif
	
	createTable = createTable2
	createGraph = createGraph2
	
	return 0
End

Function LoadTekISFFile(pathName, fileName, createTable, createGraph, quiet, newWaveName)
	String pathName			// Igor symbolic path name or "" for dialog.
	String fileName				// file name, partial path and file name, or full path or "" for dialog.
	Variable createTable		// 1 = Yes, 2 = No, 0 for dialog.
	Variable createGraph		// 1 = Yes, 2 = No, 0 for dialog.
	Variable quiet				// 0 = print diagnostic and error messages, 1 = print error messages only, 2 = do not print diagnostic or error messages
	String &newWaveName		// Output: Name of wave created.
	
	newWaveName  = ""
	
	Variable err
	
	if ( (createTable==0) || (createGraph==0) )
		err = LoadTekISFFileDialog(createTable, createGraph)
		if (err != 0)
			return err
		endif
	endif

	// This puts up a dialog if the pathName and fileName do not specify a file.
	String message = "Select a Tek ISF file"
	Variable refNum
	Open /R /Z=2 /P=$pathName /T="????" /M=message refNum as fileName	// /T flag presents "All files" in Open File dialog.
	
	// Save outputs from Open in a safe place.
	err = V_Flag
	String fullPath = S_fileName

	if (err != 0)
		return err			// -1 means user canceled.
	endif
	
	// Load the header information
	STRUCT TekISFHeaderInfo headerInfo
	if (ExtractISFHeaderInfo(refNum, headerInfo, quiet!=2) != 0)
		if (quiet != 2)
			Print "Error while loading header section of the file."
		endif
		Close refNum
		return -1
	endif

	Close refNum
	
	switch (headerInfo.encoding)
		case 0:								// Binary
			break
		default:
			if (quiet != 2)
				Print "The file's encoding is unknown. Only binary encoding is supported."
			endif
			return -1
	endswitch
	
	if (quiet == 0)
		Printf "Loading Tek ISF data from \"%s\"\r", fullPath
	endif
	
	GBLoadWave /B=(headerInfo.byteOrder) /A=tekWave /T={headerInfo.dataType,kWaveDataType} /S=(kHeaderLength) /W=1 /U=(headerInfo.numPoints) /Q fullPath
	if (V_flag == 0)
		if (quiet != 2)
			Print "Error while loading binary data section of the file."
		endif
		return -1
	endif
	newWaveName = StringFromList(0, S_waveNames)
	Wave w = $newWaveName
	
	SetScale/P x, headerInfo.x0, headerInfo.dx, ""+headerInfo.xUnits, w
	SetScale d, 0, 0, ""+headerInfo.yUnits, w
	
	// I don't know what headerInfo.yZero is all about (YZERO keyword).
	
	w = (w - headerInfo.yOffset) * headerInfo.yMultiplier
	
	if (quiet == 0)
		Printf "Created wave %s, %d points.\r", newWaveName, numpnts(w)
	endif
	
	if (createTable == 1)
		Edit w.id
		ModifyTable format[1]=3,digits[1]=9		// Display time in suitable format.
	endif
	if (createGraph == 1)
		Display w
	endif

	return 0			// Zero signifies no error.	
End

Function LoadTekISFFileMenuItem()
	String newWaveName
	LoadTekISFFile("", "", 1, 1, 0, newWaveName)
End

//	LoadAllTekISFFilesDialog(extension, createTable, createGraph)
//	Displays a dialog in which the user can enter parameters.
//	All of the parameters function both as inputs, to preset the dialog values,
//	and as output, to return the values to the calling routine.
//	The function result is 0 if OK or -1 if cancel.
Function LoadAllTekISFFilesDialog(extension, createTable, createGraph)
	String &extension					// Input and output
	Variable &createTable				// Input and output.
	Variable &createGraph				// Input and output.

	if (CmpStr(extension,".isf")!=0 && CmpStr(extension,"????")!=0)
		extension = ".isf"
	endif
	if ( (createTable<1) || (createTable>2) )
		createTable = 1
	endif
	if ( (createGraph<1) || (createGraph>2) )
		createGraph = 1
	endif
	
	// This is necessary because you can't pass an & parameter to Prompt.
	String extension2 = extension
	Variable createTable2 = createTable
	Variable createGraph2 = createGraph
	
	Prompt extension2, "Tek ISF file name extension", popup ".isf;Any extension"
	Prompt createTable2, "Create table", popup "Yes;No"
	Prompt createGraph2, "Create graph", popup "Yes;No"
	DoPrompt "Load All Tek ISF Files", extension2, createTable2, createGraph2
	if (V_Flag != 0)
		return -1			// User cancelled.
	endif

	if (CmpStr(extension2, "Any extension") == 0)
		extension2 = "????"
	endif
	
	extension = extension2
	createTable = createTable2
	createGraph = createGraph2
	
	return 0
End

Function LoadAllTekISFFiles(pathName, extension, createTable, createGraph)
	String pathName			// Name of an Igor symbolic folder created by Misc->NewPath
	String extension			// e.g., ".isf". Pass "????" for any extension. Pass "" for dialog.
	Variable createTable		// 1 = Yes, 2 = No, 0 for dialog.
	Variable createGraph		// 1 = Yes, 2 = No, 0 for dialog.

	if (CmpStr(extension,"")==0 || createTable<1 || createTable>2 || createGraph<1 || createGraph>2)
		if (LoadAllTekISFFilesDialog(extension, createTable, createGraph) != 0)
			return -1					// User cancelled.
		endif
	endif
	
	if (strlen(pathName) == 0)
		String message
		
		if (CmpStr(extension, "????") == 0)
			message = "Select a directory containing Tek ISF files with any extension"
		else
			sprintf message, "Select a directory containing Tek ISF files with %s extension", extension
		endif
				
		NewPath/O/M=message TekISFDataPath		// This displays a dialog in which you can select a folder
		if (V_flag != 0)
			return V_flag							// -1 means user canceled
		endif
		pathName = "TekISFDataPath"
	endif
	
	Variable err = 0

	String newWaveName
	String fileName
	Variable i = 0
	do
		fileName = IndexedFile($pathName, i, extension)
		if (strlen(fileName) == 0)
			break										// No more files
		endif
	
		err = LoadTekISFFile(pathName, fileName, 2, 2, 1, newWaveName)
		if (err != 0)
			return err
		endif
		
		if (createTable == 1)
			if (i == 0)
				Edit $newWaveName
			else
				AppendToTable $newWaveName
			endif
		endif
		
		if (createGraph == 1)
			if (i == 0)
				Display $newWaveName
			else
				AppendToGraph $newWaveName
			endif
		endif
	
		i += 1
	while(1)
	
	return 0
End

 

LoadTekISFFile.zip (4.11 KB)

Forum

Support

Gallery

Igor Pro 10

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More