#!/bin/bash -p
#To decode the output of rdsfp -d <port>  or sfp -d <port>  or DU/XMU/RU/RRU
#Based on SFF-8472 : Specification for Diagnostic Monitoring Interface for Optical Transceivers

moshelldir=`dirname "$0"`
if [[ $moshelldir != /* ]] ; then moshelldir=`pwd`/$moshelldir ; fi
vobsinstallation=0
unamea=$(uname -a)
gawkext=""
gawklib=""
if [[ $unamea = [Ll][iI][nN][uU][xX]*x86_64* ]] ; then gawklib="lin64"   ; if [[ $vobsinstallation = 1 ]] ; then gawkext=".lin64" ; fi
elif [[ $unamea = [Ll][iI][nN][uU][xX]* ]]      ; then gawklib="linux"   ; if [[ $vobsinstallation = 1 ]] ; then gawkext=".linux" ; fi
elif [[ $unamea = SunOS*sparc* ]]               ; then gawklib="solaris" ; if [[ $vobsinstallation = 1 ]] ; then gawkext=".solaris" ; fi
elif [[ $unamea = SunOS* ]]                     ; then gawklib="sol86"   ; if [[ $vobsinstallation = 1 ]] ; then gawkext=".sol86" ; fi
else gawklib="cygwin" ; gawkext=".exe"
fi
gawkprog="gawk${gawkext}"
gawk="$moshelldir/$gawkprog"
#special case where moshell has been installed on a linux 32 bit machine but should be run on linux 64 sharing the same file system
if [[ $unamea = [Ll][iI][nN][uU][xX]*x86_64* && $vobsinstallation != 1 && `file "$moshelldir/gawk"` = *32-bit* ]] ; then gawklib="linux" ; fi
filefunc="$moshelldir/commonjars/lib/${gawklib}/filefuncs"
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+${LD_LIBRARY_PATH}:}$moshelldir/commonjars/lib/${gawklib}
export LANG=C
export LC_ALL=C

globmoshellrc=$moshelldir/jarxml/moshellrc
commonjardir=$moshelldir/commonjars
jarxmldir=$moshelldir/jarxml
defLogDir=$commonjardir/defLogDir.sh
moshellrc=$($defLogDir)


$gawk -f $moshelldir/funcs.awk -v moshelldir="$moshelldir" -v vobsinstallation="$vobsinstallation" -v globmoshellrc="$globmoshellrc" -v moshellrc="$moshellrc" -l "$filefunc" --source '
BEGIN{
	loadFilefuncs()
	parse_user_variables(moshell)
	parse_user_variables(globmoshellrc)
	parse_user_variables(moshellrc)
	if (java=="") java="java"
	if (os~"cygwin") { "cygpath -m "moshelldir | getline moshelldir ; close("cygpath -m "moshelldir) }
	#print java,moshelldir
	sfpdecoder=java" -cp "moshelldir"/commonjars/sfpdecoder.jar Main"
}
{
	if (!($0~/^====================+$/ || $2=="lhsh" || $0=="$ " || $1=="coli>/fruacc/lhsh")) print
	if ($1~/^[^: ]+:$/)
	{
		#dont use any of the below as prefix
		#sfpChecksumBase: Valid
		#sfpChecksumExtended: Valid
		#sfpChecksumDMI: Valid
		if ($1!~/^sfp/) prefix=$1" "
		sub(/^[^: ]+: +/,"")
		$0=$0
	}
	#=============================================================================================
	#lhsh xxx sfp -d 0
	#lhsh xxx sfp -d 0 -m
	#coli>/fruacc/lhsh BXP_2 sfp -d 0
	if ($0~/^====================+$/ || $2=="lhsh" || $0=="$ " || $1=="coli>/fruacc/lhsh") 
	{ 
		if ($2=="lhsh" || $1=="coli>/fruacc/lhsh")
		{
			if ($NF=="-m") port=$(NF-1) 
			else port=$NF 
			tcommand=$0
		}
		if (buffer!="") { sdec(buffer) ; buffer="" }
		print
	}
	if (/^ *[0-9A-Fa-f]{4} +[0-9A-Fa-f]{8} +[0-9A-Fa-f]{8} +[0-9A-Fa-f]{8} +[0-9A-Fa-f]{8} +["]?................["]? *$/)
	{
		buffer=buffer"\n"$0
	}
	else if ((tcommand ~ /\/sfpdump / && $0 ~ / \((C|V|mA|mW)\): /) ||\
	         (tcommand ~ /\/sfpdump / && $1=="|" && $0~/ (Temperature \(C\)|Voltage \(mV\)|TX Power \(uW\)|RX Power \(uW\)|TX Bias Low Warning \(mA\)|TX Bias \(mA\)|TX Bias High Warning \(mA\)) /))
	{
		#coli>/fruacc/lhsh 000100 /fruacc/du/sfpdump 0
		#Temperature           (C): 30.816
		#Vcc                   (V):  3.319
		#TX Power             (mW):  0.701
		#RX Power             (mW):  0.729
		#TX Bias              (mA): 40.314
		#TX Bias Low Warning  (mA): 20.000
		#TX Bias High Warning (mA): 80.000

		#New format in 18.Q2:
		#| Temperature (C)              | 52               |
		#| Voltage (mV)                 | 3323             |
		#| TX Power (uW)                | 763              |
		#| RX Power (uW)                | 685              |
		#| TX Bias Low Warning (mA)     | 12               |
		#| TX Bias (mA)                 | 45               |
		#| TX Bias High Warning (mA)    | 93               |
		if ($1=="|" && $2~/^(Temperature|Voltage|TX|RX)$/)
		{
			#convert the new format to old format
			gsub(/\|/,"") 
			if ($1=="Voltage" && $2=="(mV)") { $1="Vcc" ; $2="(V)" ; $NF=sprintf("%.3f",$NF/1000) }
			else if ($2=="Power" && $3=="(uW)") { $3="(mW)" ; $NF=sprintf("%.3f",$NF/1000) }
			$0=$0
			#print "DEBUG: "$0
		}
		
		t_header=prefix "SFPDECODER ("port"): "
		if ($1=="Temperature") t_buff=t_header "SFP Temperature = "sprintf("%.0f C",$NF)
		else if ($1=="Vcc")    t_buff=t_buff"\n"t_header "SFP Voltage = "sprintf("%.2f Volts",$NF)
		else if ($2=="Power")  t_buff=t_buff"\n"t_header "SFP "$1" power = "sprintf("%.5f dBm",10*mylog($NF)/log(10))
		else if ($2=="Bias")
		{
			if ($3=="(mA):" || $3=="(mA)") 
			{
				t_bias=$NF
				t_buff=t_buff"\n"t_header "SFP TX Bias = "sprintf("%.1f mA",$NF)
			}
			else if ($3=="Low") t_low=$NF
			else if ($3=="High")
			{
				t_high=$NF
				t_buff=t_buff"\n"t_header "SFP TX Bias Level = "sprintf("%.0f%",mDiv(100 * (t_bias - t_low) , (t_high - t_low)))
				print t_buff
				t_buff=""
			}
		}
	}
	else if (tcommand ~ /\/fruacc\/lhlist +ldn/)
	{
		#ManagedElement=1,Equipment=1,FieldReplaceableUnit=2              BXP_0                XMU 03 01              KDU 137 949/1          R1D/A          D16K238685          20141107       
		#ManagedElement=1,Equipment=1,FieldReplaceableUnit=2              BXP_1                XMU 03 01              KDU 137 949/1          R1D/A          D16K238685          20141107       
		#ManagedElement=1,Equipment=1,FieldReplaceableUnit=3              BXP_2                XMU 03 01              KDU 137 949/1          R1D/A          D16N036434          20150217       
		#ManagedElement=1,Equipment=1,FieldReplaceableUnit=3              BXP_3                XMU 03 01              KDU 137 949/1          R1D/A          D16N036434          20150217       
		#ManagedElement=1,Equipment=1,FieldReplaceableUnit=4              BXP_4                Baseband R503          KDU 137 949/1          R1H            D16T149957          20160503       
		#ManagedElement=1,Equipment=1,FieldReplaceableUnit=4              BXP_5                Baseband R503          KDU 137 949/1          R1H            D16T149957          20160503       
		if (/ XMU *03|d *R503 /) is_xmu[$2]=1
	}
	else if (tcommand ~ /\/sfpvalues/)
	{
		if (/ SFP thresholds on /)
		{
			tboard=tport=""
			match($0,/ SFP thresholds on DU_([_0-9]+) for port ([0-9]+)/,a)
			tport=a[2]
			if (a[1]~/^[0-5]$/ && is_xmu["BXP_"a[1]]==1)
			{
				if (tport~/^(8|9|10|11)$/) a[1]-=1   #XMU ports on second splitter group RU side
				else if (tport~/^[3-7]$/)  a[1]-=2   #XMU ports on third  splitter group RU side
				else if (tport==1) a[1]-=1 #XMU port on second splitter group DU side
				else if (tport==2) a[1]-=2 #XMU port on third  splitter group DU side
			}
			tboard="BXP_"a[1]
		}
		if (/High Warning/) twh=$9 
		else if (/Low Warning/) twl=$9
		else if ($2~/^[1-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]$/ && $3~/^[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\.[0-9]+$/) 
		{ 
			invals=1;tt1=$5;tt2=$7;tt3=$9;tt4=$11;tt5=$13 
			#current bugs in sfpvalues command:
			#TX bias value is doubled on RU
			#TX/RX power is 10 times too high on XMU
			if (is_xmu[tboard]==1 && (tt4+0)>1000) { tt4=tt4/10 ; tt5=tt5/10 }
			if (is_xmu[tboard]!=1) tt3*=2
		}
		else if (invals==1)
		{
			invals=0
			if (tboard=="" || tport=="") next
			print "coli>/fruacc/lhsh "tboard" /fake/sfpdump "tport
			print tboard": SFPDECODER ("tport"): SFP Temperature = "sprintf("%.0f C",tt1)
			print tboard": SFPDECODER ("tport"): SFP Voltage = "tt2" Volts"
			print tboard": SFPDECODER ("tport"): SFP TX Bias = "sprintf("%.1f mA",tt3/1000)
			print tboard": SFPDECODER ("tport"): SFP TX Bias Level = "sprintf("%.0f%",mDiv(100 * (tt3 - twl),(twh - twl)))
			print tboard": SFPDECODER ("tport"): SFP TX power = "sprintf("%.3f dBm",10*mylog(tt4/1000)/log(10))
			print tboard": SFPDECODER ("tport"): SFP RX power = "sprintf("%.3f dBm",10*mylog(tt5/1000)/log(10))
		}
	}
}
END{ 
	if (buffer!="") sdec(buffer)
}
function sdec(input,  n,a,i,b,j,c,s,z,v,k,m,w,d,t,start,stop,area,byte,theVal,f,cal,r,r0,r1,r2,r3,r4,docheck,checkRes,tsum,tres,ec,e)
{
	header=prefix "SFPDECODER ("port"): "
	idec()
	n=split(input,a,"\n")
	for (i=1;i<=n;i++)
	{
		w=split(a[i],b," ")
		if (w<5) continue
		s=h2d(b[1])+0
		if (s>=256) 
		{
			s-=256
			#On most RU/RRU , the byte numbering for A2 starts again at 0000 . 
			#But on RRUS31, the byte number for A2 starts at 0100 (= 256) so we remove 256 if s is greater than 256
		}
		#print s,a[i]
		if (s==0) z++
		#print z,s,b[2],b[3],b[4],b[5]
		t=b[2] b[3] b[4] b[5]
		v=0
		split(t,c,"")
		for (k=1;k<=32;k++)
		{
			byte[z,s+v]=c[k] c[k+1]
			v++
			k++
		}
	}
	#for (m in byte) { split(m,d,SUBSEP) ; printf("%3s %3s %s\n",d[1],d[2],byte[m]) }
	#consistency checks
	docheck=tres=0
	ec=""
	for (i=0;i<=255;i++) { if ((1,i) in byte && byte[1,i]!="00") docheck=1 }
	if (tcommand ~ /tnsfpinfo/) docheck=0
	delete checkRes
	checkRes[1]=checkRes[2]=checkRes[3]=0
	if (docheck==1)
	{
		if (sprintf("%s%s",byte[1,0],byte[1,1])!="0304") { checkRes[1]=tres=1 ; ec="1" }
		tsum=0
		for (i=0;i<=62;i++) tsum+=sprintf("%d",strtonum("0x"byte[1,i]))
		if (tsum%256 != sprintf("%d",strtonum("0x"byte[1,63]))) { checkRes[2]=tres=1 ; ec=ec",2" }
		tsum=0
		for (i=64;i<=94;i++) tsum+=sprintf("%d",strtonum("0x"byte[1,i]))
		if (tsum%256 != sprintf("%d",strtonum("0x"byte[1,95]))) { checkRes[3]=tres=1 ; ec=ec",3" }
		if (checkRes[1]==1 || checkRes[2]==1)
		{
			print header "sfpVendorName = FAULTY_SFP"
			print header "sfpProductNumber = FAIL#"gensub(/^,/,"",1,ec)
			return
		}
	}
	#decoding
	for (i=1;i<=Max;i++)
	{
		split(fPos[i],e,",")
		if (e[1]=="A0") area=1 ; else area=2
		start=e[2] ; stop=e[3]
		for (j=start;j<=stop;j++)
		{
			thebyte=byte[area,j]
			if (i<Max1)
			{
				if (j==2) thebyte=translate_connector_type(thebyte)
				else if (byte[area,j]=="00" || byte[area,j]=="20") thebyte=""
				else thebyte=sprintf("%c",strtonum("0x"byte[area,j]))
			}
			else if (i==Max1) thebyte=h2d(byte[area,j])
			fVal[i]=fVal[i] thebyte
		}
		if (fName[i]=="sfpManufactureDate") 
		{
			fVal[i]=todate(fVal[i])
			if (checkRes[3]==1) fVal[i]="FAIL#3"
		}
		else if (fName[i]=="EricssonProductRev") 
		{
			split(fVal[i],f,"")
			if (f[1]==0 || f[1]>31) fVal[i]=""
			else fVal[i]="R" f[1] int2char(f[2])
		}
		else if (i>Max1 && i<=Max3) fVal[i]=h2d(fVal[i])
		if (fName[i]=="sfpSerialNumber" && checkRes[3]==1) fVal[i]="FAULTY_SFP"
		#print i" "fName[i]" "fVal[i]
	}
	if (fVal[1]=="" && fVal[2]=="" && fVal[3]=="" && fVal[4]=="" && fVal[5]=="" && fVal[6]=="") return
	for (i=1;i<=Max2;i++)
	{
		gsub(/\xa1|\x01/,"",fVal[i])
		print header fName[i]" = "fVal[i]
	}
	#print byte[1,92],h2b(byte[1,92])
	split(h2b(byte[1,92]),a,"")  #internal cal : a[3] , external cal: a[4]
	if (a[3]==a[4]) 
	{
		print header "Erroneous value in byte 92 internalcal=externalcal"
		return
	}
	if (a[3]==1) cal="i" ; else cal="e"
	for (i=Max3+1;i<=Max;i++) fVal[fName[i]]=fVal[i]
	print header "Diagnostics data ("(cal=="i"?"internal":"external")" calibration)"
	for (i=Max2+1;i<=Max3;i++)
	{
		if (fName[i]=="SFP TX Bias")
		{
			if (cal=="i") fVal[i]=sprintf("%.1f mA",fVal[i]/500)
			else
			{
				#print r,r1,r2
				r=fVal[i] ; r1=h2p(fVal["ISlope"]) ; r2=h2s(fVal["IOffset"])
				fVal[i]=sprintf("%.1f mA",(r*r1+r2)/500)
			}
		}
		else if (fName[i]=="SFP TX Bias Level")
		{
			fVal[i]=sprintf("%.0f%",mDiv(100 * (fVal[i] - h2d(fVal["BiasLowWarning"])) , (h2d(fVal["BiasHighWarning"]) - h2d(fVal["BiasLowWarning"]))))
		}
		else if (fName[i]~"SFP .X power")
		{
			if (cal=="i")
			{
				fVal[i]=10*mylog(fVal[i]/10000)/log(10)" dBm"
			}
			else if (fName[i]=="SFP RX power")
			{
				r=fVal[i] ; r0=h2f(fVal["RxPwr0"]) ; r1=h2f(fVal["RxPwr1"]) ; r2=h2f(fVal["RxPwr2"]) ; r3=h2f(fVal["RxPwr3"]) ; r4=h2f(fVal["RxPwr4"])
				#print r,r0,r1,r2,r3,r4
				#print (r0+ r1*r + r2*(r^2) + r3*(r^3) + r4*(r^4)) 
				fVal[i]=10*mylog((r0+ r1*r + r2*(r^2) + r3*(r^3) + r4*(r^4))/10000)/log(10)" dBm"
			}
			else if (fName[i]=="SFP TX power")
			{
				r=fVal[i] ; r1=h2p(fVal["TxPwrSlope"]) ; r2=h2s(fVal["TxPwrOffset"])
				#print r,r1,r2,h2p(r1),h2s("ffff"),h2s("7fff")
				fVal[i]=10*mylog((r*r1+r2)/10000)/log(10)" dBm"
			}
		}
		else if (fName[i]=="SFP Temperature")
		{
			#fVal[1] contains the sfpVendorName . For OCLARO (OPNEXT), there is a issue with temperature calibration values so we always consider internal calibration
			if (cal=="i" || fVal[1]~/^(OPNEXT|OCLARO|AOI)/) fVal[i]=sprintf("%.0f",fVal[i]/256)
			else
			{
				r=fVal[i] ; r1=h2p(fVal["TSlope"]) ; r2=h2s(fVal["TOffset"])
				#print r,fVal["TSlope"],r1,fVal["TOffset"],r2
				fVal[i]=sprintf("%.0f",(r*r1+r2)/256)
			}
			#Now do the unsigned to signed conversion
			if ((fVal[i]+0) >= 128) fVal[i]-=256
			fVal[i]=fVal[i]" C"
		}
		else if (fName[i]=="SFP Voltage")
		{
			if (cal=="i") fVal[i]=sprintf("%.2f Volts",fVal[i]/10000)
			else
			{
				r=fVal[i] ; r1=h2p(fVal["VSlope"]) ; r2=h2s(fVal["VOffset"])
				#print r,r1,r2
				fVal[i]=sprintf("%.2f Volts",(r*r1+r2)/10000)
			}
		}
		print header fName[i]" = "fVal[i]
	}
}
function h2b(h,   a,h2b_table,res,i,z)
{
	h2b_table["0"]="0000"
	h2b_table["1"]="0001"
	h2b_table["2"]="0010"
	h2b_table["3"]="0011"
	h2b_table["4"]="0100"
	h2b_table["5"]="0101"
	h2b_table["6"]="0110"
	h2b_table["7"]="0111"
	h2b_table["8"]="1000"
	h2b_table["9"]="1001"
	h2b_table["A"]="1010"
	h2b_table["B"]="1011"
	h2b_table["C"]="1100"
	h2b_table["D"]="1101"
	h2b_table["E"]="1110"
	h2b_table["F"]="1111" 
	h=toupper(h)
	z=split(h,a,"")
	res=""
	for(i=1;i<=z;i++) res=res h2b_table[a[i]]
	return res
}
function todate(h,   a,n)
{
	n=split(h,a,"")
	if (n==6) h="20"a[1] a[2]"-"a[3] a[4]"-"a[5] a[6]
	return h
}	
function h2d(h)
{
	return sprintf("%d",strtonum("0x"h))
}
function idec(    ref,i,a)
{
	#initialisation, done for each individual A0+A2 buffer
	delete ref
	delete fName
	delete fPos
	delete fVal
	Max=0
	Max1=0
	#fist ASCII fields until Max1 (except the Ericsson product rev)
	ref[++Max]="sfpConnectorType=A0,2,2"
	ref[++Max]="sfpVendorName=A0,20,35"
	ref[++Max]="sfpProductNumber=A0,40,55"
	ref[++Max]="sfpProductRevision=A0,56,59"
	ref[++Max]="sfpSerialNumber=A0,68,83"
	ref[++Max]="sfpManufactureDate=A0,84,89"
	ref[++Max]="Logo=A0,96,99"
	ref[++Max]="EricssonProductNrPart1=A0,119,127"
	ref[++Max]="EricssonProductNrPart2=A0,112,117"
	ref[++Max]="EricssonProductRev=A0,118,119"  #Important: the EricssonProductRev must always be in position "Max1"
	Max1=Max   #the number of ascii fields
	#then decimal values until Max3
	ref[++Max]="sfpBitRate (units of 100MBd)=A0,12,12"
	ref[++Max]="sfpWaveLength(nm)=A0,60,61"
	ref[++Max]="sfpSingleModeMaxLength(1Km)=A0,14,14"
	ref[++Max]="sfpSingleModeMaxLength(100m)=A0,15,15"
	ref[++Max]="sfpLinkLengthSupported(50UM-OM2, 10m)=A0,16,16"
	ref[++Max]="sfpLinkLengthSupported(62.5UM-OM1, 10m)=A0,17,17"
	ref[++Max]="sfpLinkLengthSupported(Active Cable or Copper, 1m)=A0,18,18"
	ref[++Max]="sfpLinkLengthSupported(50UM-OM3, 10m)=A0,19,19"
	Max2=Max
	#start diagnostics at Max2+1
	ref[++Max]="SFP Temperature=A2,96,97"
	ref[++Max]="SFP Voltage=A2,98,99"
	ref[++Max]="SFP TX Bias=A2,100,101"
	ref[++Max]="SFP TX Bias Level=A2,100,101"
	ref[++Max]="SFP TX power=A2,102,103"
	ref[++Max]="SFP RX power=A2,104,105"
	Max3=Max
	#additional values needed for diagnostics calculations, starts at Max3+1, not printed, and kept as hexadecimal
	ref[++Max]="BiasLowWarning=A2,22,23"
	ref[++Max]="BiasHighWarning=A2,20,21"
	ref[++Max]="RxPwr4=A2,56,59"
	ref[++Max]="RxPwr3=A2,60,63"
	ref[++Max]="RxPwr2=A2,64,67"
	ref[++Max]="RxPwr1=A2,68,71"
	ref[++Max]="RxPwr0=A2,72,75"
	ref[++Max]="TxPwrSlope=A2,80,81"
	ref[++Max]="TxPwrOffset=A2,82,83"
	ref[++Max]="TSlope=A2,84,85"
	ref[++Max]="TOffset=A2,86,87"
	ref[++Max]="VSlope=A2,88,89"
	ref[++Max]="VOffset=A2,90,91"
	ref[++Max]="ISlope=A2,76,77"
	ref[++Max]="IOffset=A2,78,79"
	
	for (i=1;i<=Max;i++)
	{
		split(ref[i],a,"=")
		fName[i]=a[1]
		fPos[i]=a[2]
		#print i" "fName[i],fPos[i]
	}
}
function int2char(i,   char)
{
	char["1"]="A"
	char["2"]="B"
	char["3"]="C"
	char["4"]="D"
	char["5"]="E"
	char["6"]="F"
	char["7"]="G"
	char["8"]="H"
	char["9"]="I"
	char["10"]="J"
	char["11"]="K"
	char["12"]="L"
	char["13"]="M"
	char["14"]="N"
	char["15"]="O"
	char["16"]="P"
	char["17"]="Q"
	char["18"]="R"
	char["19"]="S"
	char["20"]="T"
	char["21"]="U"
	char["22"]="V"
	char["23"]="W"
	char["24"]="X"
	char["25"]="Y"
	char["26"]="Z"
	char["27"]="AA"
	char["28"]="AB"
	char["29"]="AC"
	char["30"]="AD"
	char["31"]="AE"
	return char[i]
}
function h2s(h,   a,v,b2h_table,sign)
{
	#16bit hex to signed integer. The first bit is the sign, the next 15 bits are the value
	b2h_table["0000"]="0"
	b2h_table["0001"]="1"
	b2h_table["0010"]="2"
	b2h_table["0011"]="3"
	b2h_table["0100"]="4"
	b2h_table["0101"]="5"
	b2h_table["0110"]="6"
	b2h_table["0111"]="7"
	b2h_table["1000"]="8"
	b2h_table["1001"]="9"
	b2h_table["1010"]="A"
	b2h_table["1011"]="B"
	b2h_table["1100"]="C"
	b2h_table["1101"]="D"
	b2h_table["1110"]="E"
	b2h_table["1111"]="F" 
	split(h,a,"")
	split(h2b(a[1]),b,"")
	if (b[1]=="1") sign="-"
	a[1]=b2h_table["0"b[2] b[3] b[4]]
	h=a[1] a[2] a[3] a[4]
	return sprintf("%s%d",sign,strtonum("0x"h))
}
function h2p(h,   a,b1,b2)
{
	#16bit hex to fixed point: the first byte is 0 to 255, the second byte is the decimal part 0 to 255/256  
	split(h,a,"")
	b1=a[1] a[2]
	b2=a[3] a[4]
	b1=sprintf("%f",strtonum("0x"b1))
	b2=sprintf("%f",strtonum("0x"b2)/256)
	return b1+b2
}
function h2f(h,   a,b0,b1,b2,b3)
{
	#convert a raw 4 byte hexadecimal to single precision float 
	#can also be done with perl:
	#perl -e \x27 my $hex = "be88a84d";print unpack "f", reverse pack "H*", $hex;\x27
	split(h,a,"")
	b3=a[1] a[2] ; b2=a[3] a[4] ; b1=a[5] a[6] ; b0=a[7] a[8]
	return HexToFP(b3,b2,b1,b0)
}
# function to convert 4 "bytes" of hex chars into a floating point
# bytes are ordered
# [high....low]
#  b3 b2 b1 b0
# based on http://ecpe.ece.iastate.edu/arun/CprE281_F05/ieee754/ie5.html
# http://babbage.cs.qc.edu/IEEE-754/Decimal.html was used to test my code
# 
function HexToFP(b3,b2,b1,b0,   i0,i1,i2,i3,tmp,exponent,fraction,decfrac,fval,bias,sign,i)
{
	fval = 0
	bias=127
	
	# convert the character bytes into numbers
	i3=strtonum("0x" b3)
	i2=strtonum("0x" b2)
	i1=strtonum("0x" b1)
	i0=strtonum("0x" b0)
	
	# first, we need to separate all the parts of the floating point
	
	# upper most bit is the sign
	sign=and(i3, 0x80)
	
	# bits 23-30 (next 8) are the exponent
	exponent=and(i3,0x7F)
	tmp=and(i2,0x80)
	tmp=rshift(tmp, 7)
	exponent=lshift(exponent, 1)			
	exponent=or(exponent,tmp)	
	
	# bits 0-22 are the fraction
	fraction=and(i2,0x7F)
	fraction=lshift(fraction,8)
	fraction=or(fraction,i1)
	fraction=lshift(fraction,8)
	fraction=or(fraction,i0)
	
	#convert the fraction part into a decimal. need to look at the 
	# individual bits and compute the base 10 value
	decfrac=0
	for(i=22;i>=0;i--)
	{			
		if( and( fraction , lshift(0x01,i) ))
			decfrac = decfrac+2^(i-23)
	}

	fval=(1+decfrac) * 2^(exponent-bias)
	
	if(sign)
		fval=fval*-1
		
	return fval 
}
function translate_connector_type(theb,   t,i)
{
	t["01"]="SC"
	t["02"]="Fibre Channel Style 1 copper connector"
	t["03"]="Fibre Channel Style 2 copper connector"
	t["04"]="BNC/TNC"
	t["05"]="Fibre Channel coaxial headers"
	t["06"]="FiberJack"
	t["07"]="LC"
	t["08"]="MT-RJ"
	t["09"]="MU"
	t["0A"]="SG"
	t["0B"]="Optical pigtail"
	t["0C"]="MPO Parallel Optic"
	t["20"]="HSSDC II"
	t["21"]="Copper pigtail"
	t["22"]="RJ45"
	for (i in t) t[tolower(i)]=t[i]
	return t[theb]
}
function mylog(theval)
{
	if (theval+0 < 0) theval=0
	return log(theval)
}
'
