
Load JPK Data File


hrodstein
#pragma rtGlobals=3 // Use modern global access method and strict wave access. // Load JPK Data File // This file loader load a JPK Instruments segmented data file like the one posted at http://www.igorexchange.com/node/7321 // These files contain one or more "segments" of data. // To use the file loader, choose Data->Load JPK Data File. // The file loader creates a data folder based on the file name. // It creates a data folder for each segment in the file. These are named Segment0, Segment1, ... // The data from each segment is loaded into the corresponding data folder // in the form of waves. The waves are named according to the "# columns" tag in the segment. Menu "Load Waves" "Load JPK Data File...", LoadJPKDataFile("", "") End Structure JPKDataFileHeaderInfo Variable numSegments // Specified by "# force-settings.segments.size:" tag Variable startSegmentsFPos // File position of first segment, specified by "# segmentIndex: 0" tag Variable startSegmentsLineNumber // Zero-based line number of start of first segment EndStructure static Function TextStartsWith(text, tagText, tagValueText) // Returns the truth that text starts with tagText String text String tagText String& tagValueText // The text after the tagText and followign space tagValueText = "" Variable len = strlen(tagText) String subText = text[0,len-1] if (CmpStr(subText,tagText) == 0) tagValueText = text[len+1,10000] // +1 to skip space after tag text return 1 endif return 0 End // GetJPKDataFileHeaderInfo(refNum, fileInfo) // Returns information about a JPK file from the file header. static Function GetJPKDataFileHeaderInfo(refNum, fileInfo) Variable refNum // File reference number from Open command STRUCT JPKDataFileHeaderInfo& fileInfo FSetPos refNum, 0 Variable lineNumber = 0 do Variable fPos FStatus refNum fPos = V_FilePos // Current file position for the file in bytes from the start String text FReadLine refNum, text if (strlen(text) == 0) // Reached end-of-file Printf "GetJPKDataFileHeaderInfo hit end of file without reading all required fields" return -1 endif String tagText, tagValueText tagText = "# force-settings.segments.size:" if (TextStartsWith(text,tagText, tagValueText)) fileInfo.numSegments = str2num(tagValueText) endif tagText = "# segmentIndex: 0" if (TextStartsWith(text,tagText, tagValueText)) // Hit start of segments fileInfo.startSegmentsFPos = fPos fileInfo.startSegmentsLineNumber = lineNumber break endif lineNumber += 1 while(1) return 0 End // MakeJPKCatalog(refNum) // Returns a 2D wave describing the segments of a file. // Column 0: File position of start of segment ("# segmentIndex:" tag) // Column 1: Line number of start of segment ("# segmentIndex:" tag) // Column 2: Line number of column names line ("# columns:" tag) // Column 3: Line number of start of segment data // Column 4: Line number of last line of segment data // All line numbers are zero-based. static Function/WAVE MakeJPKCatalog(refNum) Variable refNum // File reference number from Open command FSetPos refNum, 0 // The information about the file is returned in this wave Make /O /N=(1000,6) JPKCatalog = NaN // Assume never more than 1000 segments SetDimLabel 1, 0, SegmentStartFPos, JPKCatalog // Column 0 contains the segment file position SetDimLabel 1, 1, SegmentStartLine, JPKCatalog // Column 1 contains the segment start line number SetDimLabel 1, 2, ColumnNamesFPos, JPKCatalog // Column 2 contains the column names file position SetDimLabel 1, 3, ColumnNamesLine, JPKCatalog // Column 3 contains the column names line number SetDimLabel 1, 4, DataStartLine, JPKCatalog // Column 5 contains the data start line number SetDimLabel 1, 5, DataEndLine, JPKCatalog // Column 6 contains the data end line number Variable numSegments = 0 Variable segmentNumber = -1 // No segments yet Variable inData = 0 Variable lineNumber = 0 do Variable fPos FStatus refNum fPos = V_FilePos // Current file position for the file in bytes from the start String text FReadLine refNum, text if (strlen(text) == 0) // Reached end-of-file if (segmentNumber >= 0) // Hit start of segments already? JPKCatalog[segmentNumber][%DataEndLine] = lineNumber - 1 endif break endif String tagText, tagValueText tagText = "# segmentIndex:" if (TextStartsWith(text,tagText, tagValueText)) segmentNumber += 1 // Start of new segment numSegments += 1 JPKCatalog[segmentNumber][] = NaN JPKCatalog[segmentNumber][%SegmentStartFPos] = fPos JPKCatalog[segmentNumber][%SegmentStartLine] = lineNumber endif tagText = "# columns:" if (TextStartsWith(text,tagText, tagValueText)) JPKCatalog[segmentNumber][%ColumnNamesFPos] = fPos JPKCatalog[segmentNumber][%ColumnNamesLine] = lineNumber endif if (segmentNumber >= 0) // Hit start of segments already? if (!inData) if (CmpStr(text[0],"#") != 0) // A line that does not start with # is a data line JPKCatalog[segmentNumber][%DataStartLine] = lineNumber inData = 1 endif endif endif if (segmentNumber >= 0) // Hit start of segments already? if (inData) if (CmpStr(text,"\r") == 0) // Blank line signifies end of segment data JPKCatalog[segmentNumber][%DataEndLine] = lineNumber - 1 inData = 0 endif endif endif lineNumber += 1 while(1) Redimension /N=(numSegments,6) JPKCatalog return JPKCatalog End static Function SkipSpaces(text, len, pos) String text Variable len Variable pos Variable i for(i=0; i<len; i+=1) if (CmpStr(text[pos]," ") != 0) break else pos += 1 endif endfor return pos End // GetJPKSegmentColumnInfoStr(refNum, columnNamesFPos) // Returns a string suitable for the LoadWave /B flag. The string specifies the column // name to use for each column in the segment. The name line in the segment looks // something like this: // # columns: tipSampleSeparation vDeflection height error smoothedCapacitiveSensorHeight capacitiveSensorHeight hDeflection seriesTime time xTipPosition yTipPosition // There may be multiple spaces between names. static Function/S GetJPKSegmentColumnInfoStr(refNum, columnNamesFPos) Variable refNum // File reference number from Open command Variable columnNamesFPos FSetPos refNum, columnNamesFPos String text FReadLine refNum, text Variable len = strlen(text) String tagText = "# columns: " text = ReplaceString(tagText, text, "") // Remove tag String str = "" Variable pos = 0 Variable numNames = 0 do Variable origPos = pos pos = SkipSpaces(text, len, pos) String name sscanf text[pos,len-1], "%s", name name = CleanupName(name, 0) // This converts, e.g., Time into Time0. Time is a built-in function and is not allowed for wave names. Variable tmp = Exists(name) if (tmp!=0 && tmp!=1) name = UniqueName(name, 1, 0) endif String temp = "N='" + name + "';" // e.g., "N=vDeflection;" str += temp numNames += 1 pos += strlen(name) while(pos < len) return str End static Function LoadJPKSegment(pathName, filePath, refNum, segmentNumber, segmentStartFPos, segmentStartLine, columnNamesFPos, dataStartLine, dataNumLines) String pathName // Name of an Igor symbolic path or "" String filePath // Name of file or partial path relative to symbolic path or full path to file Variable refNum // File reference number from Open command Variable segmentNumber Variable segmentStartFPos Variable segmentStartLine Variable columnNamesFPos Variable dataStartLine Variable dataNumLines // Each segment is loaded into a separate data folder String dfName = "Segment" + num2istr(segmentNumber) NewDataFolder /O /S $dfName String columnInfoStr = GetJPKSegmentColumnInfoStr(refNum, columnNamesFPos) LoadWave /G /O /A=Column /L={0, dataStartLine, dataNumLines, 0, 0} /B=columnInfoStr /P=$pathName /Q filePath SetDataFolder :: // Reset current data folder to original return 0 End static Function/S FileNameToDFName(filePath) String filePath String fileName = ParseFilePath(3, filePath, ":", 0, 0) String dfName = CleanupName(fileName, 0) // Convert to standard Igor name for easier programming return dfName End // LoadJPKDataFile(pathName, filePath) // Loads Ωdata and header information from JPK Instruments segmented .txt files. // If pathName or filePath is "", an Open File dialog is displayed. // Creates a main data folder based on the file name. // Each segment of the file is loaded into a separate data folder in the main data folder. // For example if your file is named "MyFile" and contains 3 segments and you call this while // the current data folder is root:, you get the following hierarchy: // root: // MyFile // Segment0 // Segment1 // Segment2 // The data and header information for a given segment is loaded into the corresponding // segment data folder. // If a data folder already exists, conflicting waves and variables are overwritten. Function LoadJPKDataFile(pathName, filePath) String pathName // Name of an Igor symbolic path or "" String filePath // Name of file or partial path relative to symbolic path or full path to file Variable refNum = 0 // First get a valid reference to a file. if ((strlen(pathName)==0) || (strlen(filePath)==0)) // Display dialog looking for file. String fileFilters = "JPK Files (*.txt):.txt;" fileFilters += "All Files:.*;" Open/D/R/F=fileFilters/P=$pathName refNum as filePath filePath = S_fileName // S_fileName is set by Open/D if (strlen(filePath) == 0) // User cancelled? return -1 endif endif // Open the file for reading Open /R /P=$pathName refNum as filePath if (refNum == 0) return -1 // Error opening file endif STRUCT JPKDataFileHeaderInfo fileInfo Variable result = GetJPKDataFileHeaderInfo(refNum, fileInfo) if (result != 0) Close refNum return result endif // Print fileInfo // For debugging only Wave/Z catalog = MakeJPKCatalog(refNum) if (!WaveExists(catalog)) Print "Error in MakeJPKCatalog" Close refNum return -1 endif // Make a data folder for the file String dfName = FileNameToDFName(filePath) NewDataFolder /O /S $dfName Variable numSegments = DimSize(catalog,0) Variable segmentNumber Variable segmentsLoaded = 0 for(segmentNumber = 0; segmentNumber<numSegments; segmentNumber+=1) Variable segmentStartFPos = catalog[segmentNumber][%SegmentStartFPos] Variable segmentStartLine = catalog[segmentNumber][%SegmentStartLine] Variable columnNamesFPos = catalog[segmentNumber][%ColumnNamesFPos] Variable columnNamesLine = catalog[segmentNumber][%ColumnNamesLine] Variable dataStartLine = catalog[segmentNumber][%DataStartLine] Variable dataEndLine = catalog[segmentNumber][%DataEndLine] Variable dataNumLines = dataEndLine - dataStartLine + 1 result = LoadJPKSegment(pathName, filePath, refNum, segmentNumber, segmentStartFPos, segmentStartLine, columnNamesFPos, dataStartLine, dataNumLines) if (result != 0) Printf "Error loading segment %d\r", segmentNumber break endif segmentsLoaded += 1 endfor SetDataFolder :: // Reset current data folder to original Close refNum Printf "Created data folder %s containing %d segments from \"%s\"\r", dfName, segmentsLoaded, filePath return 0 End

Forum

Support

Gallery
Igor Pro 9
Learn More
Igor XOP Toolkit
Learn More
Igor NIDAQ Tools MX
Learn More