
/*********************					*
**************************				**
 functions to treat strings ***************
**************************				**
*********************/	

//extension of the standard 'number find(string,string)' command allowing to search STARTING with the 'ini' position
number MSA112_find(string content,string toFind,number ini)
	{
	number pos = content.right(len(content)-ini).find(toFind)
	
	if(pos!=(-1)) return (ini+pos)
	else return (-1)
	}
	
	
//extract the name incorporated between two IDENTICAL symbols in a string STARTING with the 'ini' position
string MSA112_ExtractNameBetweenSymbols(string content,string symbol,number &ini)
	{
	number start = content.MSA112_find(symbol,ini)+1
	number finish = content.MSA112_find(symbol,start)

	return content.mid(start,finish-start)
	}


//replace a given symbol in a string with another one
string MSA112_replaceSymbol(string content,string symbol,string replace)
	{
	string corrected = content
	number length = len(content)
	number Replength  = len(replace)

	number pos
	while(pos!=(-1))
		{
		pos = corrected.find(symbol)
		if(pos!=(-1))
			{
			corrected = corrected.left(pos)+replace+corrected.right(length-pos-Replength)
	//result("\n"+pos+" "+s1+" "+" "+s2+" "+corrected)		
			}
		}
		
	return corrected	
	}

//check which symbol appears first AFTER the 'ini' position
number MSA112_WhatsNext(string content,number &first,number &second,string sym1,string sym2, number ini)
//return 1 if the first symbol, 2 if the second symbol, 0 if none of them are found
	{
	first = content.MSA112_find(sym1,ini)
	second = content.MSA112_find(sym2,ini)

	if(first==(-1))
		{
		if(second==(-1)) return 0
		else return 2
		}
	else 
		{
		if(second==(-1)) return 1
		else 
			{
			if(first<second) return 1
			else return 2
			}
		}
		
	}
	
	
//shorter variant of the command
number MSA112_WhatsNext(string content,string sym1,string sym2, number ini)
	{
	number first,second 

	return content.MSA112_WhatSNext(first, second, sym1,sym2,ini)
	}


//find nearest left OR right bracket symbol STARTING with 'ini' position, 'incomlete' is needed for counting brackets
number MSA112_findBracket(string content,string LBracket,string RBracket,number ini,number &incomplete)
	{
	number left,right
	
	number what = content.MSA112_WhatsNext(left,right,LBracket,RBracket,ini)
	
	if(what==1) {incomplete ++; return left;}
	if(what==2) {incomplete --; return right;}
	if(what==0) {incomplete =(-1); return right;}
	}


//find an interval (ini,fin) incorporated between brackets, inserted sub-brakets are ruled out
void MSA112_findBracketUnit(string content,number &ini,number &fin,string LBracket,string RBracket)
	{
	ini = content.MSA112_find(LBracket,ini)
	number incomplete = 1
//result("\nini "+ini+" incomplete "+incomplete)

	fin = ini
	while(incomplete>0)
		{
		fin = content.MSA112_findBracket(LBracket,RBracket,fin+1,incomplete)
//result("\nfin "+fin+" incomplete "+incomplete)
		if(incomplete==(-1)) result("\nUnclosed brackets !!")
		}
	}


//build the TagGroup from a string, The item Identificator is marked by 2 Symbols, bracketsare marked by LBracket and RBreacket
void MSA112_ExtractAllNotes(string content, tagGroup &Tgs,string SymbolIdent,string SymbolAssign,string SymbolBreak,string LBracket,string RBracket)
	{
	number pos = 0
	while(pos!=(-1))
		{
		string Identificator = content.MSA112_extractNameBetweenSymbols(SymbolIdent,pos)

		pos = content.MSA112_find(SymbolAssign,pos)
//result("\n"+identificator+" "+pos)
		
		number next = content.MSA112_WhatsNext(SymbolIdent,LBracket,pos)	
		if(next==1)
			{
			string Item = content.MSA112_extractNameBetweenSymbols(SymbolIdent,pos)
//result(" simple "+Item)
			Tgs.MSA112_setStringInTags(Identificator,Item)
			//Always STRING NOTES even if it is a number
			}
		if(next==2)
			{
//result(" brackets ")		
			number BracketEnd
			content.MSA112_findBracketUnit(pos,BracketEnd,LBracket,RBracket)

			//sub TagGroup
			TagGroup sTgs = newTagGroup()
			string sContent = content.mid(pos,BracketEnd-pos+1)
//result("\n#"+sContent+"#")

			//recurrent call
			sContent.MSA112_ExtractAllNotes(sTgs,SymbolIdent,SymbolAssign,SymbolBreak,LBracket,RBracket)
			Tgs.TagGroupSetTagAsTagGroup(Identificator,sTgs)
					
			pos = BracketEnd
			}
			
		pos = content.MSA112_find(SymbolBreak,pos)
		
		if(pos==(-1)) break
		}
	}

//split full HDF5 path on the lowest hierarchic name and its upper path
string MSA112_SplitOnPathAndName(string full,string &path)
	{
	number length = len(full)
	
	number i
	for (i=1;i<=length;i++)	//approach the deliminator from the right
		{
		if(full.right(i).left(1)=="/") break;
		}
	path = full.left(length-i)
	string name = full.right(i-1)
	
	return name
	}



//extract two numbers separated by dephis in the string
void MSA112_extractTwoNumbersFromString(number &first,number &second,string StringWithDephis)
{
number length = len(StringWithDephis)
number delim = StringWithDephis.find("-")

first = val(StringWithDephis.left(delim))
second = val(StringWithDephis.right(length-delim-1))
}


//extract value preceeded by its Name from a line string
number MSA112_extractNamedValueFromLine(string Line,string Name)
	{
	number length = len(line)
	number lengthName = len(Name)
//result("\nLength "+length+" lengthName "+lengthName )

	number posName = Line.find(Name)
	number posInteger = posName + lengthName
//result("\n"+ Line+" "+Name+" posInteger "+posInteger)
	if(Line.left(lengthName-posName)==Name) 
		{
		while(posInteger<length)
			{
			//result("\npos "+posInteger+"#"+Line.mid(posInteger,1)+"#"+val(Line.mid(posInteger,1)))
			if(val(Line.mid(posInteger,1))>0)break	//search for first digit
			posInteger ++
			}
		}

	string StrValue =Line.mid(posInteger,length-posInteger)
	length = len(StrValue)
	
	//case of Oxford when number ended with ")"
	posInteger =StrValue.find(")")	
//result("\n#"+ StrValue+"# "+length+" "+posInteger)			
	//if(posInteger !=(-1)) StrValue =StrValue.left(length-posInteger+1) //remove right bracket NOT NEEDED
//result("\n#"+ StrValue+"#")
		
	return val(StrValue)
	}



/*******End of string functions ********************************************/
 












/*********************					*
**************************				**
 functions to read from stream  ***********
**************************				**
*********************/









TagGroup MSA112_ReadTagFromStream(object stream,string TagType,number Order)
	{
	//prepare taggroup
	TagGroup tg = NewTagGroup();
		if(TagType=="bool") 	tg.TagGroupSetTagAsBoolean(TagType, 0 )
		if(TagType=="UInt16") 	tg.TagGroupSetTagAsUInt16(TagType, 0 )
		if(TagType=="SInt16") 	tg.TagGroupSetTagAsShort(TagType, 0 )
		if(TagType=="UInt32") 	tg.TagGroupSetTagAsUInt32(TagType, 0 )
		if(TagType=="SInt32") 	tg.TagGroupSetTagAsLong(TagType, 0 )
		if(TagType=="Float") 	tg.TagGroupSetTagAsFloat(TagType, 0 )		
		if(TagType=="Double") 	tg.TagGroupSetTagAsDouble(TagType, 0 )
		//if(TagType=="Complex") tg.TagGroupSetTagAsDoubleComplex(TagType, 0 )
		
	//store in taggroup	
 	TagGroupReadTagDataFromStream( tg,TagType, stream,Order); 
 	
 	return Tg
	}

number  MSA112_readFromStream(object stream,string TagType,number Order)
	{
	number value

	if(TagType== "bool")	MSA112_ReadTagFromStream(stream,TagType,Order).TagGroupGetTagAsBoolean(TagType, value)  
	if(TagType=="UInt16") 	MSA112_ReadTagFromStream(stream,TagType,Order).TagGroupGetTagAsUInt16(TagType, value)
	if(TagType=="SInt16") 	MSA112_ReadTagFromStream(stream,TagType,Order).TagGroupGetTagAsShort(TagType, value)
	if(TagType=="UInt32") 	MSA112_ReadTagFromStream(stream,TagType,Order).TagGroupGetTagAsUInt32(TagType, value)
	if(TagType=="SInt32") 	MSA112_ReadTagFromStream(stream,TagType,Order).TagGroupGetTagAsNumber(TagType, value)
	if(TagType=="Float")  	MSA112_ReadTagFromStream(stream,TagType,Order).TagGroupGetTagAsFloat(TagType, value)
	if(TagType=="Double")  	MSA112_ReadTagFromStream(stream,TagType,Order).TagGroupGetTagAsDouble(TagType, value)
			
	return value
	}

string  MSA112_readByteFromStream(object stream,number Order)
	{
	//needed as plain functions of DigitalMicrograph cannot read one byte from stream
	
	//prepare taggroup
	TagGroup tg = NewTagGroup()
	tg.TagGroupSetTagAsUInt16("UInt16", 0 ) 
	
	//read 2 bytes
	number value
	MSA112_ReadTagFromStream(stream,"UInt16",Order).TagGroupGetTagAsUInt16("UInt16", value)
	
	//set stream 1 byte back
	stream.StreamSetPos(1,-1)
	
	//code in HEX
	string mark =hex(value)
	if(order ==2) mark =mark.right(1)	//litle endian
	if(order ==1) mark =mark.left(1)	//big endian
		
	return mark	
	}

		
string MSA112_JeolType(number code)
{
if((code ==2)||(code ==7)||(code ==14)) 	return "SInt16"
if((code ==3)||(code ==8)) 					return "SInt32"
if((code ==4)||(code ==9)) 					return "Float"
if((code ==5)||(code ==10)) 				return "Double"
if((code ==12)||(code ==13)||(code ==20)) 	return "String"
if(code ==11) 								return "bool"

else return "X"
}














/*********************					*
**************************				**
functions for import ESPRIT ***************
**************************				**
*********************					*/

	
	
	
	
	
	
	

 string MSA112_OpenBinaryfile(string &BaseName )
	{
	string path 
	if(!openDialog(path)) 
		{
		throw("")
		}
	//can be also rpl file	
	if(path=="") throw("") 	
	//string ext = PathExtractExtension(path,0)
	string Name = left(path,len(path)-4)	//assumed .raw
	
	BaseName = PathExtractBaseName(path,0)
	
	return Name
	}

number MSA112_ReadParameterFromTextLine(number &format, number fileID, string name1, string name2)
	{
	string line
	readfileline(FileID,line)
	
	number parameter =Line.MSA112_extractNamedValueFromLine(name1)	//first option
	if(parameter !=0) 
		{
		format =1; 
		return parameter;
		}
	else
		{
		parameter =Line.MSA112_extractNamedValueFromLine(name2)	//second option
		if(parameter !=0) 
			{
			format =2; 
			return parameter;
			}
		else 
			{
			format =0;
			return 0
			}
		}
	}

number MSA112_ReadRPLfile(number &width, number &height, number &depth, number &offset, number &byteDepth, string Name)
	{
	string headFile = name+".rpl"
	number FileID
	try
		{
		FileID = openfileforreading(headFile)
		}
	catch
		{
		result("\nrpl file "+headFile+" is not found !!")
		return 0	
		}
		
	//read text file
	string line
		readfileline(FileID,line) //skip first line in both Bruker and Oxford
	number format,dummy	
	width =MSA112_ReadParameterFromTextLine(format,FileID,"width","WIDTH");	//determine format on width-line only
	height =MSA112_ReadParameterFromTextLine(dummy,FileID,"height","HEIGHT");
	depth =MSA112_ReadParameterFromTextLine(dummy,FileID,"depth","DEPTH");
	offset =MSA112_ReadParameterFromTextLine(dummy,FileID,"offset","OFFSET");
	byteDepth =MSA112_ReadParameterFromTextLine(dummy,FileID,"data-Length","DATA-LENGTH");
		
	closefile(FileID)
	
	return format
	}

object MSA112_ReadBinaryRawfile(number &FileID,string Name)
	{
	string rawFile = Name+".raw"
	
	try
		{
		FileID = openfileforreading(rawFile)
		}
	catch
		{
		OKdialog("Raw file "+rawFile+" is not found !!")
		throw("")	
		}
	object stream = newStreamfromFileReference(FileID,1)
	
	return stream
	}

image MSA112_ReadOneBinaryline(object stream, number byte, number depth,number width)
	{
	image temp := integerimage("",byte,0,1,depth*width)
	ImageReadImageDataFromStream(temp,stream,1)
	
	image line := integerimage("",byte,0,width,1,depth)
	line = Slice3(temp,0,0,0,	1,width,depth,	1,1,1, 	1,depth,1)
	
	return line	
	}

image MSA112_ReadOneBinaryplane(object stream, number byte,number width, number height)//
	{
	image temp := integerimage("",byte,0,1,width*height)
	ImageReadImageDataFromStream(temp,stream,1)	//Byte order 1 (big endian)
	
	image plane := integerimage("",byte,0,width,height,1)
	plane = Slice3(temp,0,0,0,	1,width,1,	1,height,width,		1,1,1)
	
	return plane	
	}
	
/********* end of reading Binary *************************************/



	










/*********************					*
**************************				**
functions for import Velox EDX  ***********
**************************				**
*********************					*/

	
	
	
	
	
	
	
	
	
	
/*************************************************
functions to read SI stream
**************************************************/

void MSA112_ManageVeloxFrames(number &first, number &last, number frames)
	{
	first = 1
	last = 1
	if(frames>1)	
		{
		last = frames	

		string interval = first+" - "+last	
		if(!getString(last+" frames available\nconfirm which frames to read",interval,interval)) throw("")
		MSA112_extractTwoNumbersFromString(first,last,interval)
		}	
	}
	
	
//Add OneFrameFragment to a matrix(X-energy,Y-probeNo)
void MSA112_AddOneFrame(image cube, image frame, number width, number upsample)
	{
	image PixelNo = imageclone(frame)

	//FEI store stream using the following convention:
	//the number in stream indicates the channel of the EDX-event
	//if the number is 65535 (max 2byte), this means 'go to the next pixel'
	//scanning proceeds along X(fast direction) and Y(slow direction), no marker at the end of X-line
	//the size of a frame is therefore width*height + number of events
				//frame is column!! (1,size)
	PixelNo = irow
	PixelNo = tert(frame==65535,PixelNo[0,irow-1]+1,PixelNo[0,irow-1]) 
	
	//convert the 1D coordinate to 2D coordinates
	image X = mod(PixelNo,width)	//increment till width, then repeats
	image Y = trunc(PixelNo/width)	
	X /=upsample
	Y /=upsample 

	//add a count at the right X,Y pixel and the right energy channel
	cube.Index(X,Y,frame) ++
	}

//read stream to a cube with energy along Z
/*
image MSA112_ConvertAllFrames(image cube, image SIstream, image FLTable,number first, number last, numbernumber upsample)
	{	
	number frames = FLTable.imageGetDimensionSize(1)	
	
	//all complete frames
	for(number j=(first-1);j<(last-1);j++)
		{
		number ini = FLTable.getpixel(0,j)
		number fin = FLTable.getpixel(0,j+1)
	//result("\n"+j+" "+ini+" "+fin)

	MSA112_AddOneFrame(cube,SIstream[ini,0,fin,1],upsample)	
		}

	//last (possibly incomplete) frame
	number LastIni = FLTable.getpixel(0,last-1)
	number end
	if(last==frames)
		{	
		//incomplete
		number end = SIstream.ImageGetDimensionSize(1)		
		}
	else
		{
		//complete
		number end = FLTable.getpixel(0,last)
		}
	MSA112_AddOneFrame(cube,SIstream[lastIni,0,end,1],width,upsample)	
	
	
	return cube
	}
*/
	
/*******End of stream reading functions *******************************************/




/*************************************************
functions to explore metadata
**************************************************/
//Extract a string from MetaTags
string MSA112_ExtractStringFromMetaTags(TagGroup metaTg,string Path)
	{
	string Str =metaTg.MSA112_GetStringFromTags(Path)
	
	return Str
	}


//Extract a number from MetaTags	
number MSA112_ExtractNumberFromMetaTags(TagGroup metaTg,string Path)
	{
	return val(metaTg.MSA112_ExtractStringFromMetaTags(Path))
	}


//extract scanning size (width of height from metaTg)
number MSA112_ExtractScanWidthFromMetaTags(TagGroup metaTg,string Company,string PathScanSize,string PathScanTop,string PathScanBottom)
	{
	number ScanSize = metaTg.MSA112_ExtractNumberFromMetaTags(Company+":"+PathScanSize)	//in pixels
//result("\nScanSize "+ScanSize)			

	number ScanTop = metaTg.MSA112_ExtractNumberFromMetaTags(Company+":"+PathScanTop)
//result(" ScanTopStr "+ScanTop)			//this is in fraction of total scan size

	number ScanBottom = metaTg.MSA112_ExtractNumberFromMetaTags(Company+":"+PathScanBottom)
//result(" ScanBottomStr "+ScanBottom)	//this is in fraction of total scan size

	return round((ScanBottom-ScanTop)*ScanSize)	//recalculated in pixels
	}


//find the path of the first available EDX detector, its parameters will be used for calibration
string MSA112_FindPathOfFirstEDXDetector(TagGroup metaTg,string Company, string DetectorList, string dispersionName)
	{
	number detNo
	for(detNo = 1 ;detNo <= 10 ; detNo++)
		{
		if(metaTg.MSA112_ExtractStringFromMetaTags(Company+":"+DetectorList+detNo+":"+dispersionName)!="") break;
		}
	
	return  (DetectorList+detNo)
	}
	
/********End of explore metadata *******************************************/
	

/*************************************************
Exploring the HDF5 structure
**************************************************/	
	
//find the right branch in the HDF5 path structure marked by: upName (upper hierarchy) and subName (lower in hierarchy)
void MSA112_VeloxEMDFindPath(TagGroup HDF5tags,string &path,TagGroup &DetectorTag,number &Found,string upName,string subName)
	{		// found=1 indicates that the upName is found; 
			// found=2 indicates that the subName is found
    string type,FullName
    if(!TagGroupGetTagAsString(HDF5tags, "Type", type)) return ;
	if(!TagGroupGetTagAsString(HDF5tags, "Name", FullName)) return ;
	
	string name = MSA112_SplitOnPathAndName(FullName,path)  //decompose: FullName = path/name  , only last name needed
	
	if((type=="Group")&&(name==upName)) 
		{
		Found = 1	//branch with upName found
		DetectorTag =HDF5tags
		}
		
//result ("\npath: "+path+" name " +name+" type "+type+" Found "+Found)	
	if((type=="DataSet")&&(name==subName)&&(Found==1)) {Found=2; return;} //branch with subName found
	if(type=="Group") 
			{
			TagGroup child	//explore sub Tags
			if (!TagGroupGetTagAsTagGroup(HDF5tags, "Contents", child)) return 

			for (number i=0; i<TagGroupCountTags(child);i++) 
				{
//result ("\n["+i+"]")
				TagGroup childItem	//item in sub Tags list
				TagGroupGetIndexedTagAsTagGroup(child, i, childItem)
				
				if(Found<2) MSA112_VeloxEMDFindPath(childItem,path,DetectorTag,Found,upName,subName) //reccurent call
				}	
			}
	}

string MSA112_VeloxEMDFindPath(TagGroup HDF5tags,TagGroup &DetectorTag,string upName,string subName)
	{
	string path
	number Found
	MSA112_VeloxEMDFindPath(HDF5tags,path,DetectorTag,Found, upName,subName)
	
	return path
	}


number MSA112_CountEDXdetectors(TagGroup &DetectorTag, number reduce)
{
TagGroup Content	
if(!TagGroupGetTagAsTagGroup(DetectorTag, "Contents", Content)) return 0 
if(reduce)TagGroupDeleteTagWithLabel(Content,"[0]")	//then [j+1] ->[j]

return TagGroupCountTags(Content)
}
	
	
/*************End of explore HDF5 *******************************************/



//convert metaimage into string (adopted from the Wu's script
String MSA112_img2str (image img){
// Velox stores the metadata as ASCII valued image dataset,
// this function convert the image to a single string
	Number sx, sy
	object stream = NewStreamFromBuffer( 0 )
	img.GetSize(sx,sy)
	ImageWriteImageDataToStream( img, stream, 0 )
	stream.StreamSetPos(0,0)
	Return StreamReadAsText( stream, 0, sx*sy )
}


/******************************************************************
Functions: Warning on Memory limits with import
******************************************************************/	
number MSA112_CheckImportMemory(number width,number height, number depth, number Byte)
{
number limit = 2	//2GB limit
limit *= (1024*1024*1024)

number size = width*height*depth*Byte
if(size>limit)
	{
	if(!OKcancelDialog("Imported data cube is too large -\n"\
	+"High-energy channels will be cut.\n"\
	+"Memory crash is possible.\n"\
	+"if you have unsaved data, press Cancel\n"\
	+"and save everything prior importing")) throw("canceled")
	
	return trunc(limit/Byte/width/height)
	}
	
return depth	
}
	
	

/*************************************************
head Velox reading commands
**************************************************/
	
string MSA112_OpenVeloxfile()
	{
	// Open Velox file 
	String filename  
	if (!OpenDialog(Null, "Select Velox XEDS file", "*.emd", filename))
		{
		throw("")
		}
	if(filename=="") throw("")  
	if(!h5_is_file(filename)) 
		{
		OkDialog(filename + "is not a valid a HDF5-type file")
		throw("")
		}
	
	return filename	
	}

TagGroup MSA112_BuildVeloxMetaTags(string fileName, string &path, string metaName, string Company)
	{
	//read metadata
	image meta := h5_read_dataset(filename,path+"/"+MetaName) 
	//meta.showimage()
	number MetaSize = meta.ImageGetDimensionSize(1)
	string metaString = meta[0,0,MetaSize,1].MSA112_img2str()

	//this is needed because [ and ] symbols are misinterpreted in DigitalMicrograph tag structure
	metaString = MSA112_replaceSymbol(metaString,"[","(")
	metaString = MSA112_replaceSymbol(metaString,"]",")")
	
	
	//build a TagGroup from metaString
	metaString = "\""+Company+"\":" + metastring
	TagGroup MetaTg = newTagGroup()
	metaString.MSA112_ExtractAllNotes(MetaTg, "\""	,   ":"  ,    ","  ,  "{"   ,     "}")

	return MetaTg
	}

number MSA112_ExtractVeloxSpectraDepth(string fileName, string path, string pathSettings,string NumberChannelsName)
	{
	//read acquisition settings (this consists of information not included in metaData)
	string AcquisitionSettings = h5_read_string_dataset(filename,path+"/"+PathSettings)
	string Company = "FEI"
	AcquisitionSettings = "\""+Company+"\":" + AcquisitionSettings //that is needed to standartise the convertion into TagGroup
	TagGroup SettingsTg = newTagGroup()
	AcquisitionSettings.MSA112_ExtractAllNotes(SettingsTg, "\""	,   ":"  ,    ","  ,  "{"   ,     "}")
	//SettingsTg.TagGroupOpenBrowserWindow(0)				
	number depth = MSA112_ExtractNumberFromMetaTags(SettingsTg,Company+":"+NumberChannelsName) //the only number of channels is needed
	
	return depth
	}
	


void MSA112_calibrateVeloxCube(image &Cube,TagGroup MetaTg,string Company,string PixelCalibration,string PixelOffset,string DetectorList, string dispersionName, string EnergyOriginName,number upsample)
	{
	number calX = metaTg.MSA112_ExtractNumberFromMetaTags(Company+":"+PixelCalibration+":width")
	calX *= 1000000000	//convert to nm (FEI calibration is in m)
	calX *=upsample	
	number originX = metaTg.MSA112_ExtractNumberFromMetaTags(Company+":"+PixelOffset+":x")
	originX *= 1000000000		//convert to nm (FEI calibration is in m)
	cube.ImageSetDimensionCalibration(0,originX,calX,"nm",1)
	
	number calY = metaTg.MSA112_ExtractNumberFromMetaTags(Company+":"+PixelCalibration+":height")
	calY *= 1000000000		//convert to nm (FEI calibration is in m)
	number originY = metaTg.MSA112_ExtractNumberFromMetaTags(Company+":"+PixelOffset+":y")
	originY *= 1000000000		//convert to nm (FEI calibration is in m)
	calY *=upsample	
	cube.ImageSetDimensionCalibration(1,originY,calY,"nm",1)
	
	string DetectorPath = metaTg.MSA112_FindPathOfFirstEDXDetector(Company,DetectorList,dispersionName)	
	string EnergyCalibration = DetectorPath+":"+dispersionName
	string EnergyOrigin = DetectorPath+":"+EnergyOriginName
	
	number calE = metaTg.MSA112_ExtractNumberFromMetaTags(Company+":"+EnergyCalibration)
	calE /= 1000		//convert to keV (FEI calibration is in eV)
	number originE = metaTg.MSA112_ExtractNumberFromMetaTags(Company+":"+EnergyOrigin)
	originE /= 1000		//convert to keV (FEI calibration is in eV)
	
	cube.ImageSetDimensionCalibration(2,originE,calE,"kV",0)	
	}


image MSA112_ImportVelox(object frame,string FileName,TagGroup &HDF5tagsIni, number upsample, number manageFrames)
{
	//these standard pathes are not acccesed through settings
	//only direct access
	string branchName = frame.MSA112P_branchName()
	string streamName = frame.MSA112P_streamName()
	string MetaName = frame.MSA112P_MetaName()
	string FLTname = frame.MSA112P_FLTname()
	string PathSize = frame.MSA112P_PathSize()
	string PathArea = frame.MSA112P_PathArea()
	string PathSettings = frame.MSA112P_PathSettings()
	string NumberChannelsName = frame.MSA112P_NumberChannelsName()
	string PixelCalibration = frame.MSA112P_PixelCalibration()
	string PixelOffset = frame.MSA112P_PixelOffset()
	string DetectorList = frame.MSA112P_DetectorList()
	string DispersionName = frame.MSA112P_DispersionName()
	string EnergyOriginName = frame.MSA112P_EnergyOriginName()
	string Company = frame.MSA112P_Company()

	number NumberOfDetectors =frame.MSA112P_MaxNumberEDXDetectors()
	number ByteDepth = frame.MSA112P_ImportVeloxDepth()		

	//read the HDF5 path structure
	HDF5tagsIni = h5_info(filename,"/")
	TagGroup HDF5tags =TagGroupClone(HDF5tagsIni) //this taggroup will be changed

	number width,height,widthB,heightB,depth
	number first,last //1st and last frames
	image cube //imported datacube
	TagGroup MetaTg 
	
	for(number j=0;j<NumberOfDetectors;j++)
		{
result("\nDetector "+(j+1))		
		//finding the EDX-stream path (only one stream assumed in emd)
		TagGroup DetectorTag
		string path = MSA112_VeloxEMDFindPath(HDF5tags,DetectorTag,branchName,streamName)
//result("\npath\n"+path+"#")

		if(j==0)	//metadata are read from 1st detector only
			{	
			//count detectors
			NumberOfDetectors =MSA112_CountEDXdetectors(DetectorTag,0)
					
			//read metadata						
			try{
				MetaTg = MSA112_BuildVeloxMetaTags(fileName, path, metaName, Company)
				
				//read scan sizes
				width = MSA112_ExtractScanWidthFromMetaTags(MetaTg,Company,PathSize+":width",PathArea+":left",PathArea+":right")
				height = MSA112_ExtractScanWidthFromMetaTags(MetaTg,Company,PathSize+":height",PathArea+":top",PathArea+":bottom")
				widthB =round(width/upsample)
				heightB =round(height/upsample)
result("\nwidth "+width+" height " + height+" -> "+widthB+","+heightB)
				}
			catch{result("\nProblem with reading metadata");break;}
			
			if(width==0) Getnumber("insert width of SI",width,width)
			if(height==0) Getnumber("insert height of SI",height,height)
			
			
			//read spectrum depth from acquisition settings (this consists of information not included in metaData)				
				//string AcquisitionSettings
			try {
				depth = MSA112_ExtractVeloxSpectraDepth(fileName,path,pathSettings,NumberChannelsName)
result(" depth "+depth)
				}
			catch{result("\nProblem with reading acquisition settings");break;}

			if(depth==0) Getnumber("insert depth of SI",depth,depth)
			
			//check memory
			depth = MSA112_CheckImportMemory(widthB,heightB,depth,ByteDepth)
			result(" -> "+depth)
			}
		
		//read SI stream
		image SIstream := h5_read_dataset(filename,path+"/"+streamName)

		//read Frame Location Table	
		image FLTable
		try{ FLTable := h5_read_dataset(filename,path+"/"+FLTname);} catch{break;}
		//this table consists of offsets for new frames in the stream
		//There is a a tricky point: FLTable is 4-byte array not supported by DigitalMicrograph
		//However some operations like getPixel are possible. Do not play much with this image. DM might crash !!
		number frames = FLTable.ImageGetDimensionSize(1) 	//first entrance is always 0

		if(j==0)	//on 1st detector only
			{
			//manage the number of frames
			if(manageFrames) MSA112_ManageVeloxFrames(first,last, frames)
			else {first =1;last =frames;}
			//prepare a cube
			cube:= integerImage("",ByteDepth,0,widthB,heightB,depth)	
			}
			
		number steps = last-first+1
		number stepped
		

		//convert SI-stream into a cube
		if(frames==1) MSA112_AddOneFrame(cube,SIstream,width,upsample)	
		if(frames>1)
			{
result("\nframes ")			
			//all surely complete frames
			for(number j=(first-1);j<(last-1);j++)
				{
result(" "+(j+1))
				
				number ini = FLTable.getpixel(0,j)
				number fin = FLTable.getpixel(0,j+1)

				MSA112_AddOneFrame(cube,SIstream[ini,0,fin,1],width,upsample)
				

				}		
			//last (possibly incomplete) frame
			number LastIni = FLTable.getpixel(0,last-1)
			number end
			if(last==frames)
				{	
				//incomplete
				number end = SIstream.ImageGetDimensionSize(1)		
				}
			else
				{
				//complete
				number end = FLTable.getpixel(0,last)
				}
			MSA112_AddOneFrame(cube,SIstream[lastIni,0,end,1],width,upsample)	
			}
	
		//delete current detector tags
		if(MSA112_CountEDXdetectors(DetectorTag,1)==0) break
		}


	//remove artefact - last energy channel collects all 65535-events
	cube[0,0,depth-1,widthB,heightB,depth] = 0 
		
	//calibrate cube and set MetaTags
	try{
		cube.MSA112_calibrateVeloxCube(MetaTg,Company,PixelCalibration,PixelOffset,DetectorList,DispersionName,EnergyOriginName,upsample)	

		TagGroup cubeTags = cube.ImageGetTagGroup()
		TagGroupCopyTagsFrom(cubeTags,MetaTg)
		}
	catch{result("\nCalibration failed");break;}

return cube
}


/*************end of reading Velox *******************************************/

















/*********************					*
**************************				**
functions for import JEOL   ***************
**************************				**
*********************					*/












TagGroup ReadJeolHeader(object Stream,number headerS, number headerE,number Order)
{
//This extracts only few selected notes

number maxKwrdLenght =30	//eventually the mark can poit to the wrong keywords

number begin =headerS+12	//It seems the first 12 bytes are dummy
Stream.StreamSetPos(0,begin)

TagGroup JeolTg = NewTagGroup()

for(number i=0;i<(headerE-headerS);i++)//
	{
	Stream.StreamSetPos(0,begin+i)
	string mark =Stream.MSA112_readByteFromStream(Order)

	if(mark=="1")
		{
		number length =Stream.MSA112_readFromStream("UInt32",Order)
		if((length!=0)&&(length<maxKwrdLenght))
			{	
//result("\n"+i+":")
//result(length)
			string kwrd =Stream.StreamReadAsText(0,length)
			kwrd =left(kwrd,length-1)
//result(" "+kwrd)
			number typeCode =Stream.MSA112_readFromStream("UInt32",Order)
			number size =Stream.MSA112_readFromStream("UInt32",Order)
			string Type =MSA112_JeolType(typeCode)
//result(" "+type+" "+size)	
		
			if(TypeCode !=0)
				{				
				if(type =="String")
					{
					string value =Stream.StreamReadAsText(0,size)
					
					value =left(value,size-1)
//result(" "+value)
					
					if(kwrd =="Pixels") //Only STRING record analysed
						{
						JeolTg.TagGroupSetTagAsText("Pixels:string",value)
						
						number middle =value.find("x")
						number width =value.left(middle).val()
						number height =value.left(middle).val()
						
						JeolTg.TagGroupSetTagAsShort("Pixels:width",width)
						JeolTg.TagGroupSetTagAsShort("Pixels:height",height)
						}
											
						if(kwrd =="SEM") 
						{
						JeolTg.TagGroupSetTagAsText("Microscope:Name",value)
						}
						
						if(kwrd =="DetT") 
						{
						JeolTg.TagGroupSetTagAsText("Microscope:Detector",value)
						}	
					}
				else
					{
					number value =Stream.MSA112_readFromStream(type,Order)	//read only the first value of array
//result(" "+value)
					//for(number j=0;j<size;j++)result("#"+Stream.MSA112_readFromStream(type,2))

					//general
					if(kwrd =="AccKV") 
						{
						JeolTg.TagGroupSetTagAsFloat("Microscope:kV",value)
						}	
					if(kwrd =="MAG") 
						{
						JeolTg.TagGroupSetTagAsNumber("Microscope:MAG",value)
						}						
																	
					//energy
					if(kwrd =="NumCH") 
						{
						JeolTg.TagGroupSetTagAsNumber("Energy:channels",value)
						}
					if(kwrd =="CH Res") 
						{
						JeolTg.TagGroupSetTagAsFloat("Energy:dispersion",value)
						}						
					/*
					if(kwrd =="CoefA") 
						{
						JeolTg.TagGroupSetTagAsFloat("Energy:dispersion",value)
						}
					*/	
					if(kwrd =="CoefB") 
						{
						JeolTg.TagGroupSetTagAsFloat("Energy:initial",value)
						}
					if(kwrd =="DigZ") //last DigZ note is taken, previous overwritten
						{
						JeolTg.TagGroupSetTagAsShort("Energy:NULL position",value)
						}
					if(kwrd =="MnKaRES") 
						{
						JeolTg.TagGroupSetTagAsNumber("Energy:MnKaRES",value)
						}
						
												
					//angles	
					if(kwrd =="XtiltAng") 
						{
						JeolTg.TagGroupSetTagAsFloat("Angles:Xtilt angle",value)
						}
					if(kwrd =="TakeAng") 
						{
						JeolTg.TagGroupSetTagAsFloat("Angles:TakeOff angle",value)
						}	
					if(kwrd =="DetH") 
						{
						JeolTg.TagGroupSetTagAsFloat("Angles:Detector height",value)
						}
					if(kwrd =="DirAng") 
						{
						JeolTg.TagGroupSetTagAsFloat("Angles:azimuth angle",value)
						}
					if(kwrd =="ElevAng") 
						{
						JeolTg.TagGroupSetTagAsFloat("Angles:elevation angle",value)
						}

						
					//acquisition
					if(kwrd =="DwellTime(msec)") 
						{
						JeolTg.TagGroupSetTagAsDouble("Acquisition:DwellTime(msec)",value)
						}		
					if(kwrd =="LiveTime") 
						{
						JeolTg.TagGroupSetTagAsDouble("Acquisition:LiveTime",value)
						}
					if(kwrd =="RealTime") 
						{
						JeolTg.TagGroupSetTagAsDouble("Acquisition:RealTime",value)
						}	
					if(kwrd =="CountRate") 
						{
						JeolTg.TagGroupSetTagAsNumber("Acquisition:CountRate",value)
						}
						
					//scanning
					if(kwrd =="ScanLine") 
						{
						JeolTg.TagGroupSetTagAsShort("Scanning:ScanLine",value)
						}
					if(kwrd =="ScanSize") 
						{
						JeolTg.TagGroupSetTagAsFloat("Scanning:scan size",value)
						}							
					if(kwrd =="Sweep") 
						{
						//JeolTg.TagGroupSetTagAsShort("Scanning:Sweep",value)
						}	
					if(kwrd =="ROT") 
						{
						JeolTg.TagGroupSetTagAsDouble("Scanning:ROT",value)
						}
																																							
					}
				}				
			}
		}	
	}
	
return 	JeolTg
}
	
image MSA112_Counter(image &Data,number lower, number upper,number step,number gaps)
	{
	//between lower and upper limits
	number length = Data.ImageGetDimensionSize(0)
	image mask :=binaryimage("mask",length,1)
	mask =tert((data<upper)&&(data>=lower),1,0)	
	//mask.showimage()

	image counter :=Data.imageclone()
	counter =tert(mask,Data,0)

	//fill gaps
	if(gaps) counter=tert(counter==0,counter[icol-1,0],counter)
	
	//scale
	counter -=lower
	counter /=step

	return counter
	}

	
number MSA112_IncompleteFrame(image Y)
	{
	//check if scanning was finished without completing a frame
	number length = Y.ImageGetDimensionSize(0)
	number lastMax,dummy

	if(Y.getPixel(length-1,0)<max(Y))
		{
	result("\nincomplete frame")
		image Ymirr =Y.imageclone()
		Ymirr =Y[length-icol,0]
		
		Ymirr.max(lastMax,dummy)
	result(" "+lastMax)

		return lastMax
		}
		
	else return 0	
	}


image MSA112_ImportJeol(object frame,string FileName, number upsample,number SignalID)
	{
	//fixed parameters
	number Order =2	//byte order: little endian in Jeol
	number Xlower =32768	//range for X-mark
	number Xupper =36864	
	number Ylower =36864	//range for Y-mark
	number Yupper =40960
	number Elower1 =40960	//range for E-mark, signal 1
	number Eupper1 =45056	
	number Elower2 =45056	//range for E-mark, signal 2
	number Eupper2 =49152	

	//open pts file			
	number fileID = OpenFileForReading( FileName)
	object Stream = NewStreamFromFileReference(fileID,1)

	//check if it is right format	
	if(Stream.MSA112_readFromStream("UInt32",Order) !=304) OKdialog("Choose pts file !!")

	//read offsets
	for(number i=0;i<4;i++)Stream.MSA112_readFromStream("UInt32",Order)//dummy bytes 
	number headerS =Stream.MSA112_readFromStream("UInt32",Order)	//header
	number headerE =Stream.MSA112_readFromStream("UInt32",Order)
	number dataP =Stream.MSA112_readFromStream("UInt32",Order)		//binary data
	number dataL =Stream.MSA112_readFromStream("UInt32",Order)
//result("\n"+headerS+"\n"+headerE+"\n"+dataP+"\n"+dataL)

	//read header
	TagGroup JeolTg =Stream.ReadJeolHeader(headerS,headerE,Order)
	//JeolTg.TagGroupOpenBrowserWindow( 0 )
	number width0,height0 
	JeolTg.TagGroupGetTagAsShort("Pixels:width",width0)
	JeolTg.TagGroupGetTagAsShort("Pixels:height",height0)
result("\nwidth "+width0+" height "+height0)
	number ScanSize,mag
	JeolTg.TagGroupGetTagAsNumber("Microscope:MAG",mag)
	JeolTg.TagGroupGetTagAsFloat("Scanning:scan size",ScanSize)
	number scale =ScanSize/mag/width0*1000000*upsample
result(" MAG "+MAG +" scale " +scale)
	number iniE,dispE,iniCh, Channels
	JeolTg.TagGroupGetTagAsFloat("Energy:initial",iniE)
	JeolTg.TagGroupGetTagAsFloat("Energy:dispersion",dispE)
	JeolTg.TagGroupGetTagAsFloat("Energy:NULL position",iniCh)
	JeolTg.TagGroupGetTagAsNumber("Energy:channels",Channels)
	number origin =round(iniCh +iniE/dispE)
result("\dispersion "+dispE+" origin "+origin +" channels "+channels)

	//read data
	Stream.StreamSetPos(0,dataP)
	image data :=integerimage("",2,0,(dataL-dataP)/2,1)
	data.ImageReadImageDataFromStream(Stream,Order)	//Byte order 2 (little Endian)
	//data.showimage()

	//convert data
	number length = data.ImageGetDimensionSize(0)
	//result("\nlength "+length)

	number stepX =4096/width0
	number stepY =4096/height0
	image x =data.MSA112_Counter(Xlower,Xupper,stepX,1)
	x=tert(x==0,x[icol-1,0],x)//fill gaps
	image y =data.MSA112_Counter(Ylower,Yupper,stepY,1)
	y=tert(y==0,y[icol-1,0],y)//fill gaps
	//x.showimage()
	//y.showimage()

	image dataX =data.imageclone()
	dataX =0
	if(SignalID ==1) dataX +=data.MSA112_Counter(Elower1,Eupper1,1,0)
	if(SignalID ==2) dataX +=data.MSA112_Counter(Elower2,Eupper2,1,0)
	//dataX.showimage()

	//prepare cube
	number width =width0/upsample
	number height =height0/upsample
	image cube :=integerimage("",2,0,width,height,channels)
	x /=upsample
	y /=upsample

	//cut incomplete frame
	number incomplete =MSA112_IncompleteFrame(Y)
	//result("\nincomplete "+incomplete)
	if(incomplete>0)
		{
		x[0,length -incomplete,1,length] =0
		y[0,length -incomplete,1,length] =0
		}

	//build cube
	cube.index(x,y,dataX) ++
	cube[0,0,0,width,height,1] =0

	//calibrate
	cube.ImageSetDimensionCalibration(0,0,scale,"nm",1)
	cube.ImageSetDimensionCalibration(1,0,scale,"nm",1)
	cube.ImageSetDimensionCalibration(2,origin,dispE,"kV",1)

	//attach Tags and set X-ray note
	TagGroup CubeTags = cube.ImageGetTagGroup()
	CubeTags.TagGroupSetTagAsTagGroup("JEOL",JeolTg)
	CubeTags.TagGroupSetTagAsString("Meta Data:Signal","X-ray")

	return cube

	CloseFile(fileID)
	}
















/*********************					*
**************************				**
set SI type  ***************
**************************				**
*********************					*/


void MSA112_setSpectrumType(image imag,string type)
{
TagGroup tg = imag.ImageGetTagGroup()
tg.TagGroupSetTagAsString("Meta Data:Signal",type)//"EELS";"X-ray"
//if the old file, requires re-registering (save + open)
}
















/*********************					*
**************************				**
functions for EXPORT  ***********************
**************************				**
*********************					*/

	
	
	
	
	
	
	
	
	
	

void MSA112_SaveAsTextCore(image in,number FileID,number comp,number comma,number transpose)
{
number width,height
in.getSize(width,height)

string line,val

if(comp!=0)
	{
	if(transpose==0) if(comp<width) width = comp
	if(transpose==1) if(comp<height) height = comp
	}
	
if(transpose==0) for(number j=0;j<height;j++)
	{
	line = ""
	for(number i=0;i<width;i++)
		{
		val = "" + in.getPixel(i,j)
		if(comma==1) val = MSA112_replaceSymbol(val,".",",")
		line = line + val
		if(i<(width-1)) line = line + " "
		}
	line += "\n"
	WriteFile(fileID,line)	
	}

if(transpose==1) for(number i=0;i<width;i++)
	{
	line = ""
	for(number j=0;j<height;j++)
		{
		val = "" + in.getPixel(i,j)
		if(comma==1) val = MSA112_replaceSymbol(val,".",",")
		line = line + val
		if(j<(height-1)) line = line + " "
		}
	line += "\n"
	WriteFile(fileID,line)	
	}
}



void MSA112_SaveAsText(image in,number comp,number comma,number transpose)
{
String filename
Number fileID
If (!SaveAsDialog("Save text file as", GetApplicationDirectory(2,0) + ".txt", filename)) Exit(0)
Result("\nSelected file path:"+filename)
fileID = CreateFileForWriting(filename)

try{MSA112_SaveAsTextCore(in,fileID,comp,comma,transpose);}
catch(CloseFile(fileID))
result("\nASCI stored")
CloseFile(fileID)
}	

void MSA112_SaveAsText(image in,number transpose,number comma)
{
number comp = 0

MSA112_SaveAsText(in,comp,comma,transpose)
}

void MSA112_SaveAsText(image in,number transpose)
{
number comp = 0
number comma = 0

MSA112_SaveAsText(in,comp,comma,transpose)
}