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
// 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