#!/usr/bin/perl -w

use strict;				

sub trim
{
	my($string)=@_;
	for ($string)
	{
		s/^\s+//;
		s/\s+$//;
   }
   return $string;
}

sub PrintHelp
{
	print "\nThis script analyses all logic analyzer listings containing OASE RX data\n";
	print "for correct data values and counts and displays this information along\n";
	print "with statistical information.\n\n";
	print "\nUsage: Analyse.pl <Path with Listing files> <Verbosity> <Sampling delay>\n\n";
	print " *Note: The path must NOT end with a slash!\n";
	print " *Verbosity: -qq -q -v -vv\n\n";
	print "\n";
}

#
# Parsing settings.
#
my $SamplingDelay = 0;		# Number of logic analyser samples after valid edge to sample data.
									# 0 = directly the first sample, 1=...

#
# Process command line parameters.
#

my $ListingPath = "./Listing*.txt"; # default only, overriden by command line parameter.

my $VerboseMode = 1;   # 0=statistics only, 1=errors only, 2=errors and some info, 3=all.

if (@ARGV == 0)
{
	# No command line parameters: print help.
	PrintHelp;
	exit;
}
elsif (@ARGV >= 1)
{
	# Command line parameter is -h, -help, --h, --help: print help.
	if (($ARGV[0] eq "-h") | ($ARGV[0] eq "-help") | ($ARGV[0] eq "--h") | ($ARGV[0] eq "--help"))
	{
		PrintHelp;
		exit;
	}
	else
	{
		# The first command line parameter must contain the path the listing files are to be found.
		$ListingPath = $ARGV[0];
	}

	# Parse for additional parameters.  
	if (@ARGV >= 2)
	{
		if ($ARGV[1] eq "-qq")
		{
			$VerboseMode = 0;
		}
		elsif ($ARGV[1] eq "-q")
		{
			$VerboseMode = 1;
		}
		elsif ($ARGV[1] eq "-v")
		{
			$VerboseMode = 2;
		}
		elsif ($ARGV[1] eq "-vv")
		{
			$VerboseMode = 3;
		}
	}
	if (@ARGV >= 3)
	{
		$SamplingDelay = $ARGV[2];	
	
	}

	
}

if ($VerboseMode == 0)
{
	print "Analysing...\n";
} 

#
# Build list of listing files.
#
opendir(DIR, $ListingPath);
my @ListingFiles = grep(/\.txt$/, readdir(DIR));
closedir(DIR);

#
# Initialisation of global loop variables.
#

my $Loop_Count = 0;        # The number of listing files processed.
my $Loop_DataCount = 0;    # The number of valid data bytes recognised.
my $Loop_Errors = 0;
my $Loop_Mismatches = 0;
my $Loop_Idles = 0;
my @Loop_BitErrors = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
my $Loop_RXERR_Samples = 0;   # Number of logic analyser samples, that have RX-ER bit set.
my @Loop_ErrorsByRunIndex;
my $Loop_MaxRunIndex = -1;
my $Loop_ExcessiveDataBytes = 0;
my @Loop_MultipleErrorBits = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);


# Loop over all files, except some special files that are no listing files.
foreach (@ListingFiles)
{

	my $Filename = $ListingPath."/".$_;

	# Do not parse some special files.
	if (($_ ne "note.txt") and ($_ ne "Acq.log"))
	{
  
		$Loop_Count = $Loop_Count + 1;
  
		#
		# Analysis of the listing file.
		#
  
  		# Read lines of the file into @Lines.
		my @Lines = ("");
		open(LISTINGFILE, "<$Filename") || die "Listing file $Filename could not be opened!\n";
		while(<LISTINGFILE>)
		{
			push(@Lines, $_);
		}
		close(LISTINGFILE);

		#
		#  Initialisation of the analysis variables.
		#

		my $LastClockValue = -1;	# Contains the clock value of the last line, needed to detect edges.
		my $LastTimeStamp = 0;		# Logic analyser sample index of the last data word, needed to calculate delta time.
		my $SamplesSinceEdge = -1; # Number of logic analyser samples since last edge, needed to take data delayed.
		my $DataCount = 0;			# Number of valid (=DV=1, ER=0) data words (=16 Bits)
		my $IdleCount = 0;			# Number of Idle words.
		my $Count = 0;					# Number of lines analysed.
		my $ErrorCount = 0;			# Number of data words with RX-ER bit set.
		my $MismatchCount = 0;		# Number of data words with differences between real and correct data values.
		my $ExcessiveBytes = 0;		# Number of excessive data bytes.

		# Determine number of the run and highest run number.
		my $dummy = $Filename;
		$dummy =~ m|.*/.*?(\d+)|;
		my $RunIndex = $1;
		if ($RunIndex > $Loop_MaxRunIndex)
		{
			$Loop_MaxRunIndex = $RunIndex;
		}
	
		# Some output that makes the output better to understand.
		if ($VerboseMode > 0)
		{
			print "---$Filename----------------------------------------\n";
		}
		if ($VerboseMode > 2)
		{    
			print "MEASURED    CORRECT           XORED               TIME          REL. TIME\n";
		}
		
		#
		# Start the line checking work.
		#

		for(my $i = 2; $i < @Lines; $i++)
		{

			# Expected line format:     <Sample index>  <RX-CLK Bit>  <RX-DATA hex>  <RX-DV Bit>  <RX-ER Bit>  <Delta time> ps
			my @parts = split(/ +|\t/, $Lines[$i]);
   
			my $Sample = $parts[1];
			my $RXCLK = $parts[2];
			my $RXDATA = hex($parts[4]);
			my $RXDV = $parts[6];
			my $RXERR = $parts[8];
			my $CorrectValue;
   
			$Count = $Count + 1;

			# Count all samples that have RX-ER Bit set.
			if ($RXERR > 0)
			{
				$Loop_RXERR_Samples = $Loop_RXERR_Samples + 1;
			}
			
			#
			# Valid data word edge detection.
			#
			if ( ($LastClockValue == 0) & ($RXCLK == 1) & ($RXDV == 1) & ($RXERR == 0))
			{
				$SamplesSinceEdge = 0;
			}
			
			if ( ($SamplesSinceEdge > -1) & ($SamplesSinceEdge < $SamplingDelay) & ($RXCLK == 1) & ($RXDV == 1) & ($RXERR == 0))
			{
				$SamplesSinceEdge = $SamplesSinceEdge + 1;
			}
			#
			# Data word sampling: rising edge of RX-CLK while RX-DV=1, RX-ER=0.
			#
#			if ( ($LastClockValue == 0) & ($RXCLK == 1) & ($RXDV == 1) )
			
			elsif ( ($SamplesSinceEdge >= $SamplingDelay) & ($RXCLK == 1) & ($RXDV == 1) & ($RXERR == 0))
			{

				# Index of the data word within the packet.   
				$DataCount = $DataCount + 1;
	
				#
				# Generate the predicted data word based on the index of the data word within the packet.
				#
				if ($DataCount <= 4)
				{
					$CorrectValue = 0xAAAA;
				}
				elsif ($DataCount < (128 + 5))
				{
					$CorrectValue = ((2*($DataCount - 4)-1) + ((2*($DataCount - 4)-2) << 8)) & 0xFFFF; 
				}
				else
				{
					$CorrectValue = 0;
				}

				#
				# Check for excessive data words. 
				#
				if ($DataCount > (128 + 5 + 3))
				{
					$ExcessiveBytes = $ExcessiveBytes + 2;
					print sprintf("%05d   0x%04X        -excessive data-                    t=%04.1fns    TD=%1.1fns\n", $Count-1, $RXDATA, $Sample*500/1000, ($Sample-$LastTimeStamp)*500/1000);
				}
				# Data word not excessive, thus check for the correct value.
				elsif ($RXDATA == $CorrectValue)
				{
					#
					# Measured data are equal to the predicted value.
					# That is as it should be.
					#
					if ($VerboseMode > 2)
					{
						print sprintf("0x%04X      0x%04X                                t=%04.1fns    TD=%1.1fns\n", $RXDATA, $CorrectValue, $Sample*500/1000, ($Sample-$LastTimeStamp)*500/1000);
					}
				}
				else
				{
					#
					# Measured data are not correct.
					# This is the main error condition analysis looks for.
					#
					
					# Simply count the wrong data word.
					$MismatchCount = $MismatchCount + 1;
					
					# Build XOR of correct and wrong data to show the error bits
					# and group the data word into to separated bytes.
	    
					my $MismatchMask = (($RXDATA ^ $CorrectValue) & 0xFFFF);
					my $MismatchMask_High = (($RXDATA ^ $CorrectValue) & 0xFF00) >> 8;
					my $MismatchMask_Low = (($RXDATA ^ $CorrectValue) & 0xFF);
	    
					if ($VerboseMode > 0)
					{   
						# Print out error line.
						print sprintf("%05d  0x%04X  !=  0x%04X     %08b %08b          t=%04.1fns    TD=%1.1fns\n", $Count-1, $RXDATA, $CorrectValue, $MismatchMask_High, $MismatchMask_Low, $Sample*500/1000, ($Sample-$LastTimeStamp)*500/1000);
					}
	    
					#
					# Identify the error bits within the wrong data word.
					# Histogram: error count for each bit line.
					#
					my $av = 1;
					my $wbc = 0;
					for (my $bi=0; $bi<16; $bi++)
					{
						if (($MismatchMask & $av) > 0)
						{
							$Loop_BitErrors[$bi] = $Loop_BitErrors[$bi] + 1;
							$wbc = $wbc + 1;
						}
						$av = $av << 1;
					}
					
					#
					# Histogram: occurences of one-bit, two-bit, three-bit, ... errors.
					#
					$Loop_MultipleErrorBits[$wbc] = $Loop_MultipleErrorBits[$wbc] + 1;
	    
				}
	 
				$LastTimeStamp = $Sample;
				$SamplesSinceEdge = -1;
			}
			#
   		# For idle words we are sensitive to the rising edge of $RXCLK
			# while $RXDV is low and $RXERR is low.  
			#
			elsif ( ($LastClockValue == 0) & ($RXCLK == 1) & ($RXDV == 0) )
			{
				$IdleCount = $IdleCount + 1;
			}
			#
			# For error words we are sensitive to the rising edge of $RXCLK while RXERR is high.  
			#
			elsif ( ($LastClockValue == 0) & ($RXCLK == 1) & ($RXERR == 1) )
			{
				$ErrorCount = $ErrorCount + 1;
			}

   
   		$LastClockValue = $RXCLK;
   	}
		
		#
		# Detect missing data words.
		#
		if ($DataCount < 136)
		{
			for(my $v=$DataCount+1; $v<137; $v++)
			{
			
				#
				# Generate the predicted data word based on the index of the data word within the packet.
				#
				my $CorrectValue = 0;
				if ($v <= 4)
				{
					$CorrectValue = 0xAAAA;
				}
				elsif ($v < (128 + 5))
				{
					$CorrectValue = ((2*($v - 4)-1) + ((2*($v - 4)-2) << 8)) & 0xFFFF; 
				}
				else
				{
					$CorrectValue = 0;
				}
				print sprintf("         --       0x%04X   --- missing data ---  %d\n", $CorrectValue, $v);
			}
		}
		
		# End of listing file line analysis.

		#		
		# Print analysis information for the single listing file.
		#
		if ($VerboseMode > 1)
		{
			print "\n";
			print "$Count samples analysed in '$Filename':\n";
			if ((2 * $DataCount) == 272)
			{
				print "Correct number of data bytes recognized: 272.\n";
			}
			else
			{
				print "Wrong number of valid data bytes recognized: ".2*$DataCount." (should be 272).\n";
			}
			print "$MismatchCount wrong data words.\n";
			print "$IdleCount idle words recognized.\n";
			print "$ErrorCount error words flagged (RX-ERR bit set).\n";
			if ($ExcessiveBytes > 0)
			{
				print "$ExcessiveBytes excessive data bytes.\n";
			}
		}
		
		#
		# Accumulate data of the single listings over all files.
		#
		
		$Loop_ErrorsByRunIndex[$RunIndex] = $MismatchCount;
		$Loop_Mismatches = $Loop_Mismatches+$MismatchCount;
		$Loop_Errors = $Loop_Errors+$ErrorCount;
		$Loop_Idles = $Loop_Idles+$IdleCount;
		$Loop_DataCount = $Loop_DataCount+2*$DataCount; # The number of data bytes is counted.
		$Loop_ExcessiveDataBytes = $Loop_ExcessiveDataBytes+$ExcessiveBytes;
  
	}
}

#
# Final summary and statistics output.
#

#
# Time ordered list of error counts per packet, only erroneous packets are printed.
#
if ($VerboseMode > 0)
{
 
	print "\n";
	print "\n==ORDERED BY TIME======================\n";
	print "Showing packets with error only.\n";

	if (1 == 2)
	{
		for (my $u=1; $u<=$Loop_MaxRunIndex; $u++)
		{
			if ($Loop_ErrorsByRunIndex[$u] > 0)
			{
				print sprintf("%04d. run: %d\n", $u, $Loop_ErrorsByRunIndex[$u]);
			}
		}
	}
}

#
# Sum up the number of bit errors from all bit lines.
#
my $BitErrorCount = 0;
for (my $bi=0; $bi<16; $bi++)
{
	if ($Loop_BitErrors[$bi] > 0)
	{
		$BitErrorCount = $BitErrorCount + $Loop_BitErrors[$bi];
	}
}


if ($VerboseMode > 0)
{
	print "\n";
	print "\n==STATISTICS===========================\n";
}
else
{
	system "clear";
}

if ($SamplingDelay > 0)
{
	print "--Sampling data $SamplingDelay samples after valid clock edge!\n";
}
print "Listing files analysed:  $Loop_Count\n";

if ($Loop_DataCount == 272*$Loop_Count)
{
	print "Valid data bytes:        $Loop_DataCount (correct)\n";
}
else
{
	print "Valid data bytes:        $Loop_DataCount (wrong, should be ".272*$Loop_Count.", Diff=".($Loop_DataCount-272*$Loop_Count).")\n";
}

print "Data word mismatches:    $Loop_Mismatches = " . sprintf("%1.3f%%\n", $Loop_Mismatches/$Loop_DataCount*2*100);
print "Bit Error Rate:          " . sprintf("%1.0e\n", $BitErrorCount/(8*$Loop_DataCount));
print "Words with error flag:   $Loop_Errors\n";
print "Error Bit high samples:  $Loop_Errors\n";

if ($Loop_ExcessiveDataBytes > 0)
{
  print "Excessive data bytes:    $Loop_ExcessiveDataBytes\n";
}

# Print histogram errors per bit line
# and histogram one-bit, two-bit, ... errors.
if ($BitErrorCount > 0)
{
	print "\nBit errors:       ";
	for (my $bi=0; $bi<16; $bi++)
	{
		if ($Loop_BitErrors[$bi] > 0)
		{
			print "Bit $bi: $Loop_BitErrors[$bi] Fehler\n                  ";
		}
	}
	
	print "\nSimult. errors:   ";
 
	for (my $wbc=1; $wbc<16; $wbc++)
	{
		if ($Loop_MultipleErrorBits[$wbc] > 0)
		{
			print "$wbc-bit errors: $Loop_MultipleErrorBits[$wbc]\n                  ";
		}
	}
}

print "\n";
