from pathlib import Path
import numpy as np
import matplotlib.pyplot as plt
class sdtread():
#Reads the ASCII Header from the File
def getsdtheader():
fid.seek(0)
sdtData = fid.read()
startStr = "|^Data Set^|"
#Find Header End
hLength = sdtData.find(startStr.encode()) + len(startStr) + 1
#Get Header as String
hContent = sdtData[0:hLength].decode('ansi').strip()
return hContent,hLength+1
#Reads the binary data
def readbinary(nelements, dt, elType = ''):
if elType.lower().__contains__('complex'):
tempArr = np.fromfile(fid, dt, nelements*2)
data_array = tempArr[0::2] + 1j*tempArr[1::2]
else:
data_array = np.fromfile(fid, dt, nelements)
data_array.shape = (nelements, 1)
return data_array
#Parses the ASCII Header to a Dictionary
def get_data_description(hContent):
hDict = {}
section = ''
for item in hContent:
itemInfo = item.strip().split(':')
if len(itemInfo) == 2:
temp = itemInfo[0].strip()
key = f'{section}_{temp}'.strip('_')
hDict.update({key : itemInfo[1].strip()})
elif len(itemInfo) == 1:
if 'First Axis' in itemInfo[0]:
section = 'First Axis'
elif 'Second Axis' in itemInfo[0]:
section = 'Second Axis'
else:
section = itemInfo[0].replace('-','').replace('Data Axis',' ').replace('( )','').strip()
#Get sdt version
sdtversion = hDict.get('Version')[0:3]
global hSubsetIndex
if sdtversion == '5.4' or sdtversion == '5.0':
hSubsetIndex = 0
else:
hSubsetIndex = 1
return hDict
#Format Lookup
def subsetformat(dataFormat):
eltypeDict = {'FLOAT 32':np.float32,'DOUBLE 64':np.double,'INTEGER 16':np.int16,'INTEGER 12':np.int16,'UNSIGNED INTEGER 16':np.uint16,'BYTE 8':np.uint8,'CHAR 8':np.int8,'COMPLEX16 16':np.int16}
elbitsDict = {'FLOAT 32':32,'DOUBLE 64':64,'INTEGER 16':16,'INTEGER 12':12,'UNSIGNED INTEGER 16':16,'COMPLEX16 16':16}
elType = eltypeDict.get(dataFormat,np.int8)
elbits = elbitsDict.get(dataFormat,8)
return elType,elbits
def prop(prop,subsetIndex = -1):
if subsetIndex != -1:
propKey = f'Data Subset {subsetIndex}_{prop}'
else:
propKey = prop
propVal = dataDesc.get(propKey, None)
return propVal
# Extract Axes Info from the Header
def get_axes(axisName = '',index = -1):
axInf = None
axUnits = ''
if ('Measurement Range' in axisName or 'Interpretation' in axisName) and index > -1:
measRange = str(sdtread.prop(axisName,index-1)).split(' ')
if len(measRange) == 3:
axUnits = measRange[2]
if len(measRange) >= 2:
if index > -1:
undef = sdtread.prop('Undefined Element',index-1)
else:
undef = None
axInf = {'Start' : measRange[0],'Range' : measRange[1],'Undefined':undef,'Units':axUnits}
elif 'Swept Axis' in axisName and index > -1:
swept = str(sdtread.prop(axisName,index-1)).split(' ')
if len(swept) == 4:
axUnits = swept[3]
if len(swept) >= 3:
axInf = {'Points':swept[0],'Start':swept[1],'Resolution':swept[2],'Units':axUnits}
else:
if index > -1:
axisName += f' {index}'
points = sdtread.prop(f'{axisName}_Number of Sample Points')
if points is not None:
start = str(sdtread.prop(f'{axisName}_Minimum Sample Position')).split(' ')
res = str(sdtread.prop(f'{axisName}_Sample Resolution')).split (' ')
if len(start) == 2:
axUnits = start[1]
if len(start) >= 1:
axInf = {'Points':points,'Start':start[0],'Resolution':res[0],'Units':axUnits}
return axInf
#
def getdata():
readlog = ''
subsetCount = int(dataDesc.get('Number of Data Subsets'))
axis1,axis2 = sdtread.get_axes('First Axis'),sdtread.get_axes('Second Axis')
subsets = {}
fid.seek(headerLength)
for i in range(subsetCount):
si = i + hSubsetIndex
#dataFormat = dataDesc.get(formatStr)
dFormat,dBits = sdtread.subsetformat(sdtread.prop('Element Representation',si))
subsetLabel = sdtread.prop('Subset Label',si)
if subsetLabel == None or subsetLabel == '':
subsetLabel = f'Subset {i}'
axes = [None,None,None]
sweptAxis = sdtread.get_axes('Swept Axis',si+1)
axis3 = sdtread.get_axes(f'Data Subset {si}')
element = sdtread.get_axes('Measurement Range',si+1)
element['Type'] = sdtread.prop('Element Representation',si)
if element is None:
element = sdtread.get_axes('Interpretation',si+1)
if sweptAxis is not None: #Slice with Swept axis
axes[0] = sweptAxis
axes[1] = axis1
if axis3 is not None and int(axis3['Points']) > 1:
axes[0] = axis1
axes[1] = sweptAxis
axes[2] = axis3
elif axis1 is None and axis2 is None: # AScan or strip chart
axes[2] = axis3
elif axis1 is not None and axis2 is None: #No Second Axis: BScan Slice
axes[0] = axis3
axes[1] = axis1
elif axis3 is not None and int(axis3['Points']) == 1: #CScan
axes[0] = axis2
axes[1] = axis1
else: #Wfm Data
axes[0] = axis2
axes[1] = axis1
axes[2] = axis3
axis1Points,axis2Points,axis3Points = 1,1,1
if axes[0] is not None:
axis1Points= int(axes[0]['Points'])
if axes[1] is not None:
axis2Points= int(axes[1]['Points'])
if axes[2] is not None:
axis3Points = int(axes[2]['Points'])
points2read = axis1Points * axis2Points * axis3Points
data = sdtread.readbinary(points2read,dFormat,element['Type'])
if axis1Points == 1:
subset = data
elif axis2Points == 1:
subset = data
elif axis3Points > 1:
subset = data.reshape(axis1Points,axis2Points,axis3Points)
elif sweptAxis is not None:
#subset = data.reshape(axis1Points,axis2Points)
subset = data.reshape(axis2Points,axis1Points)
else:
subset = data.reshape(axis1Points,axis2Points)
subsets[i] ={'Subset Name': subsetLabel,'Shape':subset.shape,'Axes':axes}
if element is not None:
element['Size'] = subset.itemsize
subsets[i]['Element'] = element
if element is not None and element['Undefined'] != 'None':
subset = np.where(subset==np.int64(element['Undefined']), np.nan, subset)
if element is not None:
scaling = float(element['Range'])/(2**int(dBits))
if element['Type'] == 'CHAR 8':
if abs(float(element['Start'])*2) == float(element['Range']):
offset = 0
else:
offset = 127
subset = (offset + subset)*scaling
elif dFormat is not np.float32 and dFormat is not np.double:
subset = subset*scaling
subsets[i]['Data'] = subset
readlog += f'{i}\tStart : {fid.tell()}\tFormat : {dFormat}\tShape : {subset.shape}\n'
return subsets,readlog
class utils():
def FileExists(path):
file = Path(path)
return file.is_file()
#Plot Data
def plotdata(data,subsetindex):
#Replace undefined Before Plotting
subset = data[subsetindex]['Data']
element = data[subsetindex]['Element']
if element is not None and element['Undefined'] != 'None':
subset = np.where(subset==np.int64(element['Undefined']), np.nan, subset)
#Plot the second subset of the sample datafile
dims = len(subset.shape)
if dims == 2:
plt.imshow(subset,interpolation = 'none',origin='lower')
elif dims == 3:
plt.imshow(subset[0,:,:],interpolation = 'none',origin='lower')
elif dims == 1 or dims == 0:
plt.plot(subset)
plt.show()
return data
def OpenSdt(fPath):
# Launch File Browser
global fid,dataDesc,headerLength
# Open Binary file
if utils.FileExists(fPath) == False:
return None,-1,'File path was invalid!'
fid = open(fPath,'rb')
#Read Header
storageDescription,headerLength = sdtread.getsdtheader()
dataDesc = sdtread.get_data_description(storageDescription.split('\n'))
#Read Data into Numpy Array
subsets,readlog = sdtread.getdata()
ver = sdtread.prop('Version')
readlog += f'{fPath}\n'
readlog += storageDescription
return subsets,readlog
def main():
print('Enter File Path : ')
filePath = input().strip('\"')
data,read_log = OpenSdt(filePath)
print(f'{read_log}\n{data.items()}')
utils.plotdata(data,0)
if __name__ == '__main__':
main()