Surfer 7 Grid file format

I need to load (image) data files that are saved in the Surfer 7 Grid format. The file is binary and contains different sections (e.g. header, grid info and data). The format is well documented,

http://grapherhelp.goldensoftware.com/subsys/surfer_7_grid_file_format…

and I managed to put a simple loader together:

Function GRDLoader(filepath)
	string filepath
	
	Variable refNum
	Open/R/F="GRID Files (*.grd):.grd;" refNum as filepath
	if (refNum == 0)
		return -1		
	endif
	
	// extract file name from path as wave name
	String wName = ParseFilePath(3, S_FileName, ":", 0, 0)
	
	// pre-define byte positions of grid info section and data 
	variable GridInfoStartByte = 20
	variable DataStartByte = 100
	
	// load GRID info section, first two enties are "long",
	// rows and columns of grid/image
	Make/I/O/N=2/FREE W_RowsCols
	FSetPos refNum, GridInfoStartByte
	FBinRead/B=3 refNum, W_RowsCols
	
	// load rest of the grid info section as double
	Make/D/O/N=8/FREE W_Scaling
	FSetPos refnum, GridInfoStartByte+8
	FBinRead/B=3 refNum, W_Scaling
	
	// load DATA section
	Make/O/N=(W_rowsCols[0] * W_rowsCols[1])/D $wName /Wave = M_Data
	FSetPos refNum, DataStartByte
	FBinRead/B=3 refNum, M_Data
	// redimension to matrix (note that cols and rows are swapped for propper display)
	Redimension/N=(W_rowsCols[1],W_rowsCols[0]) M_Data
	
	// ... more code to set wave scaling
	
	Close refNum
	return 0
End

However, at the moment the start byte positions of the different section are hardcoded and were identified using the binary file reader (https://www.wavemetrics.com/code-snippet/rudimentary-binary-file-reader). This works for all files I have encountered so far but a more versatile solution is preferred. The sections seem to be tagged by hex values, but I was unable to identify those in the binary files (an example file is attached). 

Any hint to what I'm missing is highly appreciated!

Best

Christian

WDS1_Si_TAP_Quant.grd_.zip (794.65 KB)

This is not the cleanest code, but may give you a start (sorry - I have not had a chance to check it with your file yet):


Function GRDLoader(filepath)
	string filepath
   
	Variable vRefNum
	Open/R/F="GRID Files (*.grd):.grd;" vRefNum as filepath
	if (vRefNum == 0)
		return -1      
	endif
   
	// extract file name from path as wave name
	String wName = ParseFilePath(3, S_FileName, ":", 0, 0)
	
	// check for minimum length
	FStatus vRefNum
	variable vFileLength=V_logEOF
   	if(vFileLength<12)
		print "Error: not a GRD file."
		Close vRefNum
		return -1
	endif
	
	// Intel/Windows little endian
	variable vByteOrder=3 
	
	string sID
	variable vLength
	variable vError = 0
	
	do
		// read tag: ID then length
   		sID = "    " // 4 characters
		FBinRead vRefNum, sID // 4-character ID
   		FBinRead/U/B=(vByteOrder)/F=3 vRefNum, vLength // 32-bit word - length
   		
   		strswitch (sID)
   			case "DSRB": // 0x42525344  - Header
   			
   				variable vVersion
   				FBinRead/U/B=(vByteOrder)/F=3 vRefNum, vVersion
   				Print "GRD File Version Number: " + num2str(vVersion)
   				
   				break
   			case "GRID": // 0x44495247 - GRID 
   			
				// load GRID info section, first two enties are "long",
				// rows and columns of grid/image
				Make/I/O/N=2/FREE W_RowsCols
				FBinRead/B=3 vRefNum, W_RowsCols
				
				// load rest of the grid info section as double
				Make/D/O/N=8/FREE W_Scaling
				FBinRead/B=3 vRefNum, W_Scaling
				
   				break
   			case "DATA": // 0x41544144 - Data
   				
				// load DATA section
				Make/O/N=(W_rowsCols[0] * W_rowsCols[1])/D $wName /Wave = M_Data
				FBinRead/B=3 vRefNum, M_Data
				// redimension to matrix (note that cols and rows are swapped for propper display)
				Redimension/N=(W_rowsCols[1],W_rowsCols[0]) M_Data
   
				// ... more code to set wave scaling
   
   				break
   			case "FLTI": // 0x49544c46 - Fault Information
   				
   				// .. code to read fault info section
   				
   				
   				break
   			default:
   				print "Error: Unknown tag."
   				vError = -1
   				break
   		endSwitch
   		
   		if (vError != 0)
   			break
   		endif
   		
   		// ... code to check end of file
   		FStatus vRefNum
   		if (V_filePos >= V_logEOF)
   			break
   		endif
   		
	while(1)
   
	Close vRefNum
	return vError
End

Hope this helps,

Kurt

Hi Kurt,

thanks a lot! Much appreciated!

The obvious never occurred to me that hex is converted to characters (or vice versa), even though I could spot it in the binary reader snippet, however, it seems that I looked at the hex values in the wrong byte order. I also understand now better how FBinRead works!

Thanks again!

C

 

You are very welcome.

I took inspiration from a TIFF reader that I posted previously here.

Cheers,

Kurt