#!/usr/bin/perl -w

#
#  OASE Replacement System Bit Error Rate Test 2.
#

use strict;				
use utils;

sub CalcStats;
sub PrintHelpAndExit;

#
# Default values.
#
my $OutputPath = "";								# Path in which report file and error files are written to.
my $EventFile = "./event.dat";				# File containing the TRAP event.
my $DumpFilePrefix = "Optical";				# Prefix for the compare output files.
my $ReportFile = "Report.txt";				# Report file documenting runs and errors.
my $DisplayLoops = 25;							# Display statistics information every x loops.	
my $RunFile = "Runs.txt";						# File containing the info about previous runs.
my $InheritData = 0;								# Inherit data from previous runs.
my $DisplayNioaseOutput = 0;					# Display output of nioase programm.
my $LTCFile = "LTC.txt";						# File to which the LTC status data are written.
my $LTCLoops = 250;								# LTC status information every x loops.

#
# Statistical variables to be measured.
#
my $DataTotal = 0;								# Total number of bytes transferred.
my $BitErrorCountTotal = 0;					# Total number of individual bit errors.
my @BitErrorsTotal = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);	# Total number of bit errors for each bit.
my $LinkErrorsTotal = 0;						# Total number of clock cycles with DV=1 and ERR=1.
my $BitErrorRate = 0;							# Bit error rate.
my $LinkErrorCountTotal = 0;					# Total number of runs with DV=1 and ERR=1.

#
# Process command line parameters.
#
if (@ARGV >= 1)
{
	my $ArgIndex = 0;
	while($ArgIndex < @ARGV)
	{
		my $Arg = $ARGV[$ArgIndex];
		
		# Display help screen.
		if (($Arg =~ "-h") or ($Arg =~ "--h"))
		{
			PrintHelpAndExit;
		}
		
		# Output path.
		elsif ($Arg eq "-o")
		{
			if (CheckArgs($ArgIndex, 1) == 1)
			{
				$OutputPath = $ARGV[$ArgIndex + 1];
				$ArgIndex++;
			}
		}
		
		# Inheritance from previous runs.
		elsif ($Arg eq "-i")
		{
			$InheritData = 1;
		}
		
		# Event file name.
		elsif ($Arg eq "-e")
		{
			if (CheckArgs($ArgIndex, 1) == 1)
			{
				$EventFile = $ARGV[$ArgIndex + 1];
				$ArgIndex++;
			}
		}
		
		# Display loop setting.
		elsif ($Arg eq "-d")
		{
			if (CheckArgs($ArgIndex, 1) == 1)
			{
				$DisplayLoops = $ARGV[$ArgIndex + 1];
				$ArgIndex++;
			}
		}
		
		# LTC loop setting.
		elsif ($Arg eq "-ltc")
		{
			if (CheckArgs($ArgIndex, 1) == 1)
			{
				$LTCLoops = $ARGV[$ArgIndex + 1];
				$ArgIndex++;
			}
		}
		
		# Inheritance from previous runs.
		elsif ($Arg eq "-no")
		{
			$DisplayNioaseOutput = 1;
		}
		
		# Unknown option.
		else
		{
			print "\nInvalid option: $Arg\n\n";
			PrintHelpAndExit;
		}
		
		$ArgIndex++;
	}
}
else
{
	PrintHelpAndExit;
}

#
# Check for plausibility of setup.
#
if ($OutputPath eq "")
{
	print "*** You must specify an output path using -o <path>.\n\n";
	exit;
}

#
# Setup of the report file.
#
my $StartTime = DateTimeStr;
open(RPTDAT, ">>$OutputPath/$ReportFile") || die "Report file '$OutputPath/$ReportFile' could not be opened for writing!\n";
print RPTDAT "Started $StartTime\n\n";

# Tidy up screen.
system("clear");

#
# Read information about previous runs.
#
if ((-e "$OutputPath/$RunFile") && ($InheritData > 0))
{
	# Open the run file and read in the lines.
	my @RunLines = ("");
	open(RUNDAT, "<$OutputPath/$RunFile") || die "Run file '$OutputPath."/".$RunFile' could not be opened.\n";
	while(<RUNDAT>)
	{
		push(@RunLines, $_);
	}
	close(RUNDAT);
	foreach(@RunLines)
	{
		# Expected line format: RUN <start day> <start time> - <stop day> <stop time> <runs> <data volume> <bit errors> <link errors> <bit 15 errors> ... <bit 0 errors>
		my @RunParts = split(/ +|\t/, $_);
		if (@RunParts > 0)
		{
			$DataTotal = hex($RunParts[7]);
			$BitErrorCountTotal = hex($RunParts[8]);
			$LinkErrorCountTotal = hex($RunParts[9]);
			
			for (my $BitIndex=15; $BitIndex>=0; $BitIndex--)
			{
				$BitErrorsTotal[$BitIndex] = hex($RunParts[10+15-$BitIndex]);
			}

		}
	}
	my $Str = sprintf("\nInherited from previous runs: ".FormatSize($DataTotal)." transferred, %d bit errors, %d link errors.\n\n", $BitErrorCountTotal, $LinkErrorCountTotal);
	print $Str;
	print RPTDAT $Str;
}

#
# Set up STRG-C event handling.
#
my $Terminated = 0;

sub INT_Handler
{
	$Terminated = 1;
	print "TERM\n";
}

$SIG{'INT'} = 'INT_Handler';

#
# Initialisation of the hardware.
#
qx(make -C ../../I2CJ2C init);
print "TRAP initialised (make init)\n";

qx(./nioase -r);
print "ACEX design resetted (nioase -r).\n";	

#
# Perform a number of repeated measurement runs.
#

my $LoopIndex=0;
while($Terminated == 0)
{

	#
	# Read out LTC status data.
	#
	if (($LTCLoops > 0) && ($LoopIndex % $LTCLoops == 0))
	{
		my $MsgStr0 = qx(./ltc);
		print RPTDAT "\n---LTC status - ".DateTimeStr."\n$MsgStr0\n------------------\n";
	}

	#
	# Draw 4 random number for the pattern generator
	# and send them to the TRAP.
	#
	my $RSLL = 0x0001;	# Lower limit for random number range
	my $RSUL = 0xFFFD;	# Upper limit for random number range
	my @PatternSeed = (random_int($RSLL, $RSUL), random_int($RSLL, $RSUL), random_int($RSLL, $RSUL), random_int($RSLL, $RSUL));
	my $MsgStr0 = qx(pc2tp -gw 0xc00 $PatternSeed[0] -gw 0xc08 $PatternSeed[1] -gw 0xc10 $PatternSeed[2] -gw 0xc18 $PatternSeed[3]);
	
	#
	# Start TRAP sending the data pattern.
	#
	my $MsgStr1 = qx(pc2tp -c0 -i $EventFile -o pc2tp.out);

	#
	# Read data from ACEX, compare against reference and clear ACEX design.
	#
	my $MsgStr2 = qx(./nioase -pat $PatternSeed[0] $PatternSeed[1] $PatternSeed[2] $PatternSeed[3] -c16 $OutputPath/$DumpFilePrefix$LoopIndex.dat -r);

	if ($DisplayNioaseOutput > 0)
	{
		print "\nnioase: $MsgStr2\n";
	}
	
	my $BitErrorCount = 0;
	if ($Terminated == 0)
	{
		# Parse nioase.c output string: Number of words 0x12c00, dv0er1=0x0, dv1er1=0x0 (Bit errors: MSB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 LSB)
		my @MsgStr2Parts = split(/ +|\t|=|,/, $MsgStr2);
	
		my $DataTransferred = hex($MsgStr2Parts[3]) * 4;	# Number of bytes transferred.
		my $DV0ERR1 = hex($MsgStr2Parts[6]);					# Number of clock cycles with DV=0 and ERR=1.
		my $DV1ERR1 = hex($MsgStr2Parts[9]);					# Number of clock cycles with DV=1 and ERR=1.

		my @BitErrors;													# Numbers of individual bit errors.
		$BitErrorCount=0;												# Number of all bit errors.
		for (my $BitIndex=15; $BitIndex>=0; $BitIndex--)
		{
			$BitErrors[$BitIndex] = $MsgStr2Parts[13+15-$BitIndex];
			$BitErrorsTotal[$BitIndex] = $BitErrorsTotal[$BitIndex] + $BitErrors[$BitIndex];
			$BitErrorCount = $BitErrorCount + $BitErrors[$BitIndex];
		}
		
		#
		# Check whether data were transferred at all,
		# if not, the optical fiber was removed.
		#
		if (($DataTransferred == 0) && ($DV1ERR1 > 0))
		{
			$LinkErrorCountTotal++;
			my $Str = sprintf("*** Optical fiber removed?   $LinkErrorCountTotal such link errors (cycles with DV=1, ERR=1: %1.3e).\n", $DV1ERR1);
			print $Str;
			print RPTDAT $Str;
		}
		else
		{
			#
			# Calculate statistics and print.
			#
			$DataTotal = $DataTotal + $DataTransferred;
			$BitErrorCountTotal = $BitErrorCountTotal + $BitErrorCount;
			$LinkErrorsTotal = $LinkErrorsTotal + $DV1ERR1;
			CalcStats;
	
		#
		# Display intermediate run information.
		#
	
		if ($BitErrorCount == 0)
		{
			if ($LoopIndex % $DisplayLoops == 0)
			{
				my $Str;
				if ($BitErrorCountTotal == 0)
				{
					$Str = sprintf ("Run $LoopIndex: No bit errors. (No bit errors, bit error rate < %1.1e, %1.1e/%d link errors, ".FormatSize($DataTotal)." transferred)\n", $BitErrorRate, $LinkErrorsTotal, $LinkErrorCountTotal);
				}
				else
				{
					$Str = sprintf ("Run $LoopIndex: No bit errors. ($BitErrorCountTotal bit erros, bit error rate = %1.1e, %1.1e/%d link errors, ".FormatSize($DataTotal)." transferred)\n", $BitErrorRate, $LinkErrorsTotal, $LinkErrorCountTotal);
				}
				print $Str;
				print RPTDAT $Str;
			}
		}
		else
		{
			my $Str = sprintf ("Run $LoopIndex: $BitErrorCount bit errors. ($BitErrorCountTotal bit errors, bit error rate < %1.1e, %1.1e/%d link errors, ".FormatSize($DataTotal)." transferred)\n", $BitErrorRate, $LinkErrorsTotal, $LinkErrorCountTotal);
			print $Str;
			print RPTDAT $Str;
			
			print RPTDAT $MsgStr2."\n";
		}
	}
	
	#
	# Remove output file if no errors did occur.
	#
	if ($BitErrorCount == 0)
	{
		qx(rm $OutputPath/$DumpFilePrefix$LoopIndex.dat)
	}
	
	}

	$LoopIndex++;	
}	

	#
	# Do some final calculations and adjustments.
	#
	$LoopIndex--;

	#
	# Print final statistics.
	#
	CalcStats;
	
	my $Str = "\n=======================\n".
				 "$LoopIndex runs done.\n".
				 sprintf ("$BitErrorCountTotal bit errors. (Bit error rate <= %1.1e, %d/%d link errors, ".FormatSize($DataTotal)." transferred)\n", $BitErrorRate, $LinkErrorsTotal, $LinkErrorCountTotal);
	print $Str;
	print RPTDAT $Str;
	print "\n\n";
	
	#
	close(RPTDAT);
	
	#
	# Final cleanup.
	# 
	qx(rm pc2tp.out);
	
	#
	# Write run information file.
	#
	open(RUNDAT, ">>$OutputPath/$RunFile") || die "Runfile '$OutputPath/$RunFile' could not be opened for writing!\n";
	print RUNDAT sprintf("RUN $StartTime - ".DateTimeStr." 0x%08x 0x%08x 0x%08x 0x%08x ", $LoopIndex, $DataTotal, $BitErrorCountTotal, $LinkErrorCountTotal);
	for (my $BitIndex=15; $BitIndex>=0; $BitIndex--)
	{
		print RUNDAT sprintf("0x%02x ", $BitErrorsTotal[$BitIndex]);
	}
	print RUNDAT "\n";
	close(RUNDAT);

#
# ----------- local sub routines -----------
# 

#
# Calculate the statistics.
#
sub CalcStats
{
	if ($DataTotal > 0)
	{
		if ($BitErrorCountTotal == 0)
		{
			$BitErrorRate = 1 / ($DataTotal * 16);
		}
		else
		{
			$BitErrorRate = $BitErrorCountTotal / ($DataTotal * 16);
		}
	}
	else
	{
		$BitErrorRate = 0;
	}

}

sub PrintHelpAndExit
{
	print "\n";
	print "bert.pl - Bit Error Rate Tester for the OASE replacement system.\n";
	print "\n";
	print "  Usage: ./bert.pl <options>\n";
	print "  \n";
	print "  Options\n";
	print "  -o <output path>  Path in which all files will be written. Must be specified.\n";
	print "  -d <num>          Print statistics every num loops. Default is 25.\n";
	print "  -e <event file>   Event file to be sent. Default is ./event.dat.\n";
	print "  -i                Inherit previous statistics.\n";	
	print "  -ltc <num>        Print LTC status every num loops. Default is 250.\n";	
	print "  -no               Print output of nioase.\n";	
	print "\n";
	exit;
}
