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

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