Tektronix Query File Loader

Average rating
(0 votes)

// Tek Query File Loader
// This code was written to load a Tektronix Query File which is a dump of a query to a Tek scope.
// NOTE: This is not the Tek WFM file format.
 
// 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
 
//	LoadTekQueryFile(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 Query File...", LoadTekQueryFileMenuItem()
	"Load All Tek Query Files In Folder...", LoadAllTekQueryFiles("", "", 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 TekQueryHeaderInfo
	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 ExtractQueryHeaderInfo(refNum, headerInfo, quiet)
	Variable refNum			// File reference number
	STRUCT TekQueryHeaderInfo &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
 
//	LoadTekQueryFileDialog(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 LoadTekQueryFileDialog(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 Query File", createTable2, createGraph2
	if (V_Flag != 0)
		return -1			// User cancelled.
	endif
 
	createTable = createTable2
	createGraph = createGraph2
 
	return 0
End
 
Function LoadTekQueryFile(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 = LoadTekQueryFileDialog(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 Query 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 TekQueryHeaderInfo headerInfo
	if (ExtractQueryHeaderInfo(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 Query 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 LoadTekQueryFileMenuItem()
	String newWaveName
	LoadTekQueryFile("", "", 1, 1, 0, newWaveName)
End
 
//	LoadAllTekQueryFilesDialog(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 LoadAllTekQueryFilesDialog(extension, createTable, createGraph)
	String &extension					// Input and output
	Variable &createTable				// Input and output.
	Variable &createGraph				// Input and output.
 
	if (CmpStr(extension,".wfm")!=0 && CmpStr(extension,"????")!=0)
		extension = ".wfm"				// .wfm is really the wrong extension for a Tek Query file
	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 Query file name extension", popup ".wfm;Any extension"
	Prompt createTable2, "Create table", popup "Yes;No"
	Prompt createGraph2, "Create graph", popup "Yes;No"
	DoPrompt "Load All Tek Query 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 LoadAllTekQueryFiles(pathName, extension, createTable, createGraph)
	String pathName			// Name of an Igor symbolic folder created by Misc->NewPath
	String extension			// e.g., ".wfm". 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 (LoadAllTekQueryFilesDialog(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 Query files with any extension"
		else
			sprintf message, "Select a directory containing Tek Query files with %s extension", extension
		endif
 
		NewPath/O/M=message TekQueryDataPath		// This displays a dialog in which you can select a folder
		if (V_flag != 0)
			return V_flag								// -1 means user canceled
		endif
		pathName = "TekQueryDataPath"
	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 = LoadTekQueryFile(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

Back to top