#!/usr/bin/perl
#
# @(#) $Header: /opt/ntp/timelord/shm/RCS/gpsdisplay,v 2.0.0.226 2006/08/22 21:07:43 skia Exp $
# @(#) Read GPS data from the NTP Oncore Shared memory connector,
# @(#) or a serial port and display in various formats
# @(#)
# @(#) This software is relased under the GPL and
# @(#) is Copyright (C)GS3 Technologies Inc. 2000-2006
# @(#) For updates check: http://www.gs3tech.com/
# ---Section--- This string separates all sections below.
#
use strict;
use IO::Socket;
use POSIX 'setsid';
use POSIX qw(:sys_wait_h);
use Curses;
#
# Now create all the global variables
#
use vars qw ($MYVERSION $MYNAME $GlobalInitFlag $RESTART $MyUptime);
use vars qw ($SystemRestartCount $NARGS $DEBUG $VERBOSE $DAEMON $RemoteSystem $RemotePort);
use vars qw ($htmltable $htmldest $htmlgen);
#
use vars qw ($TopWIN $LiveDataWIN $LiveDataWIN_Exists $StatusWIN $havecolor);
use vars qw ($TopWIN_Height $CursesWindowInit);
use vars qw ($LiveDataWIN_MaxHeight $LiveDataWIN_Height $FullScreenEnable);
use vars qw ($MinimumScreenHeight $MinimumScreenWidth $UpdateOutput $SuspendOutput);
#
use vars qw ($CmdWIN $OutputWIN $VersionWIN $SatWin $SingleTopWIN $TitleWIN $ScrollInfoWIN);
use vars qw ($OutputWIN_Height $SatWIN $SatWIN_Height $SatWIN_HeightNeeded $TopWIN_HeightNeeded);
use vars qw ($CursesInitFlag $GotSignal_WINCH $sigINT $SingleTopWIN_Height $SingleTopWIN_Enable);
#
use vars qw ($LiveDataWIN_RowStart $SatWin_RowStart $ScrollInfoWIN_RowStart);
#
use vars qw ($KRdate $KRtime $KRlat $KRlon $KRheight $KRvel $KRgeom $KRant);
use vars qw ($KRsat %KRsatinfo %KRvsatinfo @KRsatsig);
use vars qw (@KRid $KRidlines $KRsfwVer $KRsfwRev $KRsfwModel);
use vars qw ($KRpps_m $KRpps_r $KRsfwDate $KRserNum);
use vars qw (@KRraw $KRrawline);
#
use vars qw ($TimeFormat);
#
use vars qw ($KRtraim_e $KRtraim_s $leapFlag $leapString);
#
use vars qw ($NumChannels $WWWUpdate);
use vars qw ($SOCK $GPSRAW);
use vars qw ($InputMethod);							# 0 = Shared TCP/IP Port, 1 = Serial port
use vars qw ($SerialPort $BAUD $DATABITS $PARITY $STOPBITS $SerialLine $PortObj);		# Serial port device name
use vars qw ($SerialCodeLoaded);
#
use vars qw ($ReceiverProcessedDataLine);			# This is the line that contains the receiver input line
#
use vars qw (@LiveDataBuffer);						# This buffer contains the sat info. 100 Lines
use vars qw ($LiveDataBufCursor $LiveDataBufMax);
use vars qw (@OutputLineBuffer);					# This circular buffer contains The buffer from the command line response
use vars qw ($OutputLineBufCursor $OutputLineBufMax);
use vars qw (@CmdLineBuffer);						# This circular buffer contains the previous command lines
use vars qw ($CmdLineBufCursor $CmdLineBufCursorLive $CmdLineBufMax);
#
# This circular buffer contains the commands that we received but did not process
use vars qw (@UnprocessedCmdBuffer);
use vars qw ($UnprocessedBufCursor $UnprocessedBufMax);
#
use vars qw ($gsc1 $gsc2 $gsc3);					# Global Serial Character x
use vars qw ($ReceiverTimer);						# Global Serial Character x
#
use vars qw (@HelpBuffer);
use vars qw ($HelpBufMax);
#
use vars qw ($CmdLineHCursor);						# The horizontal position of the cursor in the command line
use vars qw ($CmdLineCurrent);						# The current unprocessed command line
use vars qw ($CmdLineUpdate);						# Check for command line update
use vars qw ($KbdChar);								# User input char
#
use vars qw (@ReceiverStatusBuf);					# Normally filled by @@Fa or @@Ia
use vars qw ($ReceiverStatusBufMax);
use vars qw ($HPModelNumber);						# If the receiver is HP then this is it's model #
use vars qw ($HPDisplayVerbose);					# If the user needs more verbosity from the HP box
use vars qw ($GotRcvrPrompt $HPLifetime $HPEFC);	# Lifetime and EFC from receiver
#
# Note:	You could be in terminal mode (where all the commands are passed to the receiver)
#		and NOT be in curses mode. For instance, you could be in DAEMON mode or screen dump mode
#		and not in fullscreen mode.
#		If the user specifies "DaemonMode" (in the .ini file) or -k (command line option)
#		then the program will not do to fullscreen mode.
#		If the user specifies "TerminalMode" in the .ini file or -t command line option then
#		we WILL go to fullscreen mode.
use vars qw ($TerminalMode);						# Go to terminal mode
use vars qw (@TerminalModeBuffer);
use vars qw ($TerminalModeBufCursor $TerminalModeBufMax);
#
use vars qw ($ReceiverModel);						# VP, UT, M12, etc
use vars qw ($ReceiverType);						# Motorola or HP
use vars qw ($InitialDataReceive);					# This flag is set when the first data is received from a source
#
use vars qw ($ConfigFile);							# The name of the configuration file
use vars qw ($DebugFile);							# The name of the debug output log file
#
use vars qw ($KR_HPSync $KR_HPAcq $KR_HPhmon $KR_HPll1 $KR_HPll2 $KR_HPll3);
use vars qw ($KR_HPSync $KR_HPAcq $KR_HPhmon $KR_HPll1 $KR_HPll2 $KR_HPll3);
use vars qw ($KR_HPtfom $KR_HP1PPS $KR_HPffom $KR_HPll4 $KR_HPll5 $KR_HPll6 $KR_HPsmart);
use vars qw ($KR_hthr $KR_unce $DisplayDash);
use vars qw ($MotoAt $MotoBb $MotoEn0 $MotoEn1 $MotoSelfTest);
#
use vars qw ($ReceiverTimeMode);$ReceiverTimeMode="???";
use vars qw ($ReceiverTime);$ReceiverTime=0;
use vars qw ($ReceiverTimeFrac);$ReceiverTimeFrac=0;
use vars qw ($ReceiverMaskAngle);$ReceiverMaskAngle=-1;
use vars qw ($ReceiverCableDelay);$ReceiverCableDelay=-1;
use vars qw ($M12TRAIMMode);$M12TRAIMMode=0;
use vars qw ($M12TRAIMMessage);$M12TRAIMMessage=0;
use vars qw ($M121PPSControl);$M121PPSControl=0;
use vars qw ($SystemLock);$SystemLock=0;
use vars qw ($SerialWriteTime);
#
# The commands the we send to the receiver from the .ini file
use vars qw (@AutoProcessBuffer $AutoProcessCursor $AutoProcessMax $AutoProcessFlag);
@AutoProcessBuffer=();$AutoProcessCursor=0;$AutoProcessMax=0;$AutoProcessFlag=0;
#
use vars qw (@SysLog $SysLogCursor);$SysLogCursor=0;	# The system log
use vars qw (@TimelogBuf $TimeLogCursor);$TimeLogCursor=0;	# The HP Time Reference Log
use vars qw ($TimeLog $StartTimeLog $TimeLogSemaphore);
#
use vars qw (%MonthName);								# The associative array with the month names assigned to numbers
#
# The variable below always points to the current window that is "live" on the top screen
use vars qw ($CurrentTopWindow);
#
# The variable below always points to the window that we return to
use vars qw ($ReturnTopWindow);
#
# In a scrolling window, and the var that tracks the bottom line number
use vars qw (@ScrollWindowArray $ScrollWindowStart $ScrollWindowMax $ScrollEnabled);
use vars qw ($SystemSleep);$SystemSleep=0;
use vars qw ($LEDActive $LEDActiveState $LEDEnabled $LEDEnabledState);
$LEDActive=0;$LEDActiveState=0;$LEDEnabled=0;$LEDEnabledState=0;
use vars qw ($ColorTitleBg $ColorTitleFont $ColorTextBg $ColorTextLabels $ColorTextdata);
$ColorTitleBg='#0099cc', $ColorTitleFont="White"; $ColorTextBg="White"; $ColorTextLabels="Blue"; $ColorTextdata="Red";
use vars qw ($HTMLTitle);
use vars qw ($DoSysinfoPrint);$DoSysinfoPrint=0;		# This is for debugging
#
use vars qw ($RstrTimeMode $RstrRdate $RstrRtime $RstrLdate $RstrLtime $RstrUptime $RstrIsoRdate);
my(@HPSched, $HPSchedmax);		# HP command schedule queue
use vars qw ($postmsg);
use vars qw ($OutputWinLimit);$OutputWinLimit=0;		# Output window will grow with screen size
#
&GlobalVarInit;					# Initialize the system variables
#
# Trap signals for graceful exit
#
$SIG{'INT'} = \&procSigINT;		# Ctrl-C
$SIG{'HUP'} = \&procSigHUP;		# Parent process had died
$SIG{'TERM'} = \&procSigTERM;	# Same as procSigINT
$SIG{'USR1'} = \&procSigUSR1;	# Restart system
$SIG{'USR2'} = \&procSigUSR2;	# Start debug
$SIG{'USR3'} = \&procSigUSR3;	# Stop debug
$SIG{'WINCH'} = \&procSigWINCH;	# ReSize
$SIG{'CHLD'} = \&procSigCHLD;	# Wait for dying children
$SIG{'QUIT'} = 'IGNORE';		# Ignore for now
#
&ProcCmdLine;					# Process command line args
#
# This small section below insures that we have the serial code loaded
# only if needed! -- Tnx CRF
if( $InputMethod ) {
	my($SerialCheck) = eval "require Device::SerialPort";
	if( $SerialCheck ) {
		import Device::SerialPort qw( :PARAM :STAT 0.07 );
		$SerialCodeLoaded=1;
	} else {
		$SerialCodeLoaded=0;
	}
}
#
&HPqueue;						# Fill the HP command queue (if needed)
#
&GenHTML_BlankPage;				# Start with a blank HTML page
#
&SysinfoPrint;					# Send initial info messages if possible
#
&daemonize if $DAEMON;			# See if we become a daemon
#
&SerialReceiverInit;			# If we are connected via serial port then init
#
# Make sure this is the last of the initialization functions
&CursesInitData;				# Initialize the curses system variables etc.
$|=1;							# Don't buffer output
&MainLoop;						# Now, start the main loop
#
# All done, let's prepare to exit
&GenHTML_BlankPage;				# Put up blank page
&StopCurses if $FullScreenEnable;
#
if( $DEBUG ) {
	close(DEBUGHANDLE);
}
#
# And print out any pending messages
if( $postmsg !~ /^$/ ) {
	print STDERR $postmsg . "\r\n";
}
exit;
#--------------------------------------------------------------------
# End of main. Start of subroutine definitions.
#--------------------------------------------------------------------
# ---Section--- GPS receiver message process loop
#
sub MainLoop {
	my($i, $j, $k, $mst,$SerialReadCount,$SerialAccessTimer,$LocalSocketStatus,$ltime);
	my($errorsend, $to, $rcmd);
	#
	$errorsend=0; $postmsg="";												# We send this out at program end
	$ReceiverTimer=0;
	while( !$sigINT ) {
		#
		# Read from the appropriate port
		if( $InputMethod ) {
			#
			# Here we read from the serial port
			&PromptAndStopTimeout;											# We assume that we have a prompt
			SERLOOP: for(;;) {
				#
				# If debug is enabled (by signal USR3) we need to re-open the debug file
				if( $DoSysinfoPrint ) {
					$DoSysinfoPrint=0;
					&SysinfoPrint;
				}
				$SerialAccessTimer++;
				if($SerialAccessTimer > 5) {								# A 5 here means 50ms
					$SerialAccessTimer=0;
					#
					# Let's read the serial port
					($SerialReadCount, $SerialLine) = $PortObj->read(255);	# Count and data
					while( $SerialReadCount ) {
						$GPSRAW=$SerialLine;
						&BuildReceiverDataLine;
						$InitialDataReceive++;
						($SerialReadCount, $SerialLine) = $PortObj->read(255);
					}
				}
				#
				# First, check if we are still connected to the receiver
				if( &Check4SerialTimeOut ) {
					$errorsend++;
					if( $errorsend == 1 ) {					# First, send a CR
						&SerialWriteWithTimeOut(0,"\r");	# Just send a single CR. Don't touch the current timeout value.
					} elsif( $errorsend == 100 ) {			# Then send the message after a second
						$mst="No response from receiver. Bad connection or a time consuming task is running.";
						&PushOutputWinBuffer($mst);
						&Log($mst);
					} elsif( $errorsend > 200 )  {
						#
						# Here we try to do a reset. If we are in TerminalMode mode
						# then the user should handle it.
						if( !$TerminalMode ) {
							&PromptAndStopTimeout;
							$RESTART++;
							$errorsend=0;
						}
					}
				} else {
					$errorsend=0;
				}
				if( $SystemSleep ) {
					#
					# We come here every 10ms.
					# In system sleep mode we do not send any commands.
					# We do not read the keyboard. We just listen for data.
					$SystemSleep--;
					if( !$SystemSleep ) {
						$mst="Sleep interval is over. Yawn!";
						&PushOutputWinBuffer($mst);
						&Log($mst);
					}
				} else {
					#
					# Anything to show to the screen?
					&CheckForOutputUpdates;
					#
					# Any commands to process from the .ini file?
					if( $AutoProcessFlag ) {
						#
						# Process the commands from the .ini file.
						# If the commands don't get executed by the receiver
						# the $GotRcvrPrompt will remain valid so we can just
						# continue running.
						if( $GotRcvrPrompt ) {
							$CmdLineCurrent=$AutoProcessBuffer[$AutoProcessCursor];
							&ProcessEnterKey;								# We act like the user typed this in
							$AutoProcessCursor++;
							if( $AutoProcessCursor < $AutoProcessMax ) {
								if( $AutoProcessBuffer[$AutoProcessCursor] =~ /^\s*$/ ) {
									select(undef, undef, undef, 0.01);		# Need to wait a bit
									$CmdLineCurrent=$AutoProcessBuffer[$AutoProcessCursor];
									&ProcessEnterKey;						# We act like the user typed this in
									$AutoProcessCursor++;
									if( $AutoProcessCursor >= $AutoProcessMax ) {
										$AutoProcessFlag=0;
									}
								}
							} else {
								$AutoProcessFlag=0;
							}
						}
					} else {
						#
						# Now we do normal processing.
						if( $ReceiverType eq 'H' ) {
							#
							# The HP receiver is polled. We need to sync what we send.
							if( $ReceiverTimer > 100 ) {					# A 100 here means 1 sec
								$ReceiverTimer=0;							# Reset at the 1 second mark
								#
								# Are we ready to send another command?
								if( (!$TerminalMode) && $GotRcvrPrompt ) {
									#
									# We run our task scheduler
									# First add to the weight for each task
									for( $i=0; $i < $HPSchedmax; $i++ ) {
										$HPSched[$i][0]+= $HPSched[$i][1];
									}
									#
									# So, find out who is at the top of the priority list
									$j=0;$k=0;
									for( $i=0; $i < $HPSchedmax; $i++ ) {
										if( $HPSched[$i][0] > $j ) {
											$j=$HPSched[$i][0];
											$k=$i;
										}
									}
									#
									# Now, prepare the receiver command
									if( $k > 1 ) {							# Look for commands that require toggles
										$i=$HPSched[$k][4];					# Get value
										$HPSched[$k][4]^=1;					# Toggle value
										$to=$HPSched[$k][2];				# Timeout
										$rcmd=$HPSched[$k][3]. " ${i}";		# The modified command
									} else {
										$to=$HPSched[$k][2];				# Timeout
										$rcmd=$HPSched[$k][3];				# Command
									}
									$HPSched[$k][0]=0;						# Reset the task priority
									&SerialWriteWithTimeOut($to,"${rcmd}\r");
								}
							} else {
								$ReceiverTimer++;
							}
						} else {
							#
							# The Motorola receiver is not polled. We still just need to send a heartbeat.
							if( $ReceiverTimer > 500 ) {					# A 500 here means 5 sec
								$ReceiverTimer=0;							# Reset at the 1 second mark
								#
								# Are we ready to send another command?
								if( (!$TerminalMode) && $GotRcvrPrompt ) {
									if( $NumChannels == 12 ) {
										&MotoWriteWithParity("Gj");			# 12 channel Leap Second Status
									} else {
										&MotoWriteWithParity("Bj\x00");		# 6+8 channel Leap Second Status
									}
								}
							} else {
								$ReceiverTimer++;
							}
						}
					}
				}	# End of !sleep mode
				last SERLOOP if $sigINT;
				last SERLOOP if $RESTART;
				#
				# A select of less that .01 seems to cause problems with the serial code
				select(undef, undef, undef, 0.01);
			}
			&SerialStop;
		} else {
			#
			# Here we read from TCP/IP
			if( $SOCK = IO::Socket::INET->new( Proto => "tcp",
					PeerAddr => $RemoteSystem,
					PeerPort => $RemotePort,
					Blocking => 0) )
			{
				$ltime=time+5;
				sleep 1;								# A small sleep to set things up
				WLOOP: for(;;) {
					#
					# If debug is enabled (by signal USR3) we need to re-open the debug file
					if( $DoSysinfoPrint ) {
						$DoSysinfoPrint=0;
						&SysinfoPrint;
					}
					if( $GPSRAW = <$SOCK> ) {
						&BuildReceiverDataLine;
						if( $UpdateOutput ) {			# Check if we are getting any real data
							$ltime=time+5;
							$InitialDataReceive=1;
						}
					}
					#
					# Let's check that we are receiving data
					if( $ltime < time ) {
						#
						# I guess we have a dropped session
						if( ! $InitialDataReceive ) {
							# If we never got data, then we must quit
							$sigINT++;
							$postmsg="Warning: Timeout waiting for TCP/IP socket data. Bye";
						} else {
							$RESTART++;
						}
					} else {
						#
						# The weird behavior here is that we can show connected although
						# the socket has been dropped.
						undef($LocalSocketStatus);
						$LocalSocketStatus = $SOCK->connected;
						if( defined($LocalSocketStatus) ) {
							#
							# Anything to show?
							if( $SystemSleep ) {
								#
								# We come here every 10ms.
								# In system sleep mode we do not send any commands.
								# We do not read the keyboard. We just listen for data.
								$SystemSleep--;
								if( !$SystemSleep ) {
									$mst="Sleep interval is over. Yawn!";
									&PushOutputWinBuffer($mst);
									&Log($mst);
								}
							} else {
								&CheckForOutputUpdates;
							}
						} else {
							#
							# I guess we have a dropped session
							if( ! $InitialDataReceive ) {
								# If we never got data, then we must quit
								$sigINT++;
								$postmsg="Warning: Timeout waiting for TCP/IP socket data. Bye";
							} else {
								$RESTART++;
							}
						}
					}
					last WLOOP if $sigINT;
					last WLOOP if $RESTART;
					select(undef, undef, undef, 0.01);
				}
				$InitialDataReceive=0;
				close $SOCK if $SOCK->connected;
			}
		}
		&GenHTML_BlankPage;
		#
		# Maybe send out some messages to the debug file
		if( $sigINT ) {
			&Log("Caught signal. Bye!");
		} elsif( $RESTART ) {
			&Log("Restart...");
		} else {
			&Log("Lost socket. Retrying...");
		}
		#
		# If don't want to quit, maybe we need to reset a few things
		if( !$sigINT ) { 
			sleep 2;
			$GlobalInitFlag=0;
			$ReceiverProcessedDataLine="";						# Clear the GPS data queue and start from scratch
			$RESTART=0;											# Reset flags
			$SystemRestartCount++;								# Keep count of restarts
			&Log("Restart count: " . $SystemRestartCount);
			&BuildStatusVersionWIN if $FullScreenEnable;
			&SerialReceiverInit;							# If we are connected via serial port then init
		}
	}
}
#
#--------------------------------------------------------------------
# ---Section--- This is where the Receiver commands are processed
#
# Build the line from the serial port or TCP/IP port
# In the future, receivers from other vendors would
# need to be recognized here and processed accordingly.
sub BuildReceiverDataLine {
	my($i, $j, $lc, $llen, $fcc, @ll1, $ln);
	$llen = length($GPSRAW);
	#
	if( $ReceiverType eq 'H' ) {
		#
		# Process as HP data
		for($i=0; $i < $llen; $i++ ) {
			$lc=substr($GPSRAW,$i,1);
			$gsc1=$gsc2;$gsc2=$gsc3;$gsc3=$lc;			# These 3 characters look for the prompt
			#
			if( ($gsc3 =~ / /) && ($gsc2 =~ />/) && ($gsc1 =~ /[ 0-9]/) ) {		# Find out if we reached the end
				&PromptAndStopTimeout;											# This tells everyone that we can send another command
				&ProcessHPCommand;
				$ReceiverProcessedDataLine="";
				if( $gsc1 =~ /[0-9]/ ) {
					# We need to read the Error Queue, so pop the top value
					&SerialWriteWithTimeOut(1,":SYST:ERR?\r");
					select(undef, undef, undef, 0.1);
					&SerialWriteWithTimeOut(1,"\r");
				}
				$gsc1="";$gsc2="";$gsc3="";
			}
			$ReceiverProcessedDataLine .= $lc;
		}
	} else {
		#
		# Process as Motorola data
		for( $i = 0; $i< $llen; $i++ ) {
			$lc=substr($GPSRAW,$i,1);
			if( $lc eq "@" ) {					# Find out if this is a '@'
				&ProcessOncoreCommand;			# Process this (possible) Oncore command
			}
			$ReceiverProcessedDataLine .= $lc;
		}
	}
	$GPSRAW="";									# Empty the GPS data buffer and go again
}
#--------------------------------------------------------------------
# ---Section--- Process HP command
#
sub ProcessHPCommand {
	my($i, $j, $k, $l, $n, @ll1, $ln);
	#
	# If the output contains \r\n then split along those lines
	if( $ReceiverProcessedDataLine =~ /\r\n/ ) {
		@ll1 = split( /\r\n/, $ReceiverProcessedDataLine);
		$j = @ll1;									# Count of number of lines
		$ln = $ll1[0];
		if( $ln =~ /Receiver Status/ ) {
			&cmdHPSystemStatus;
			&GenerateTimeLogEntry;					# So we can gen the graphs
		} else {
			&PushTermWinBuffer($ln);
			&Vprint(2,$ln);							# Add to the log
			if( $ln =~ /HEWLETT/i ) {				# I guess we got a response to *IDN?
				#
				# HEWLETT-PACKARD,Z3801A,3542A03994,3543-A
				($k,$l,$HPModelNumber, $n) = split(/,/,$ln);
				$HPModelNumber="${l},${HPModelNumber},${n}";
				&BuildStatusVersionWIN;
				&PushLiveDataWinBuffer($ln);
			} elsif( $ln =~ /MOTOROLA/i ) {			# I guess we got a response to :DIAG:IDEN:GPS?
				#
				# In the order presented:
				# Copyright,Software part#,Software Version,Software Revision,Software date,
				# Model #, Hardware Part #, Serial Number, Manufacture Date, Option list
				$ReceiverProcessedDataLine=$ln;
				&cmdCj;								# Process as if it was a Motorola command
				&BuildStatusVersionWIN;
				&PushLiveDataWinBuffer($ln);
			} elsif( $ln =~ /^\s*\+/ ) {			# It is either EFC or lifetime
				$ln =~ s/\s//g;
				if( $ln =~ /E\s*\+/ ) {				# Must be EFC
					$HPEFC = $ln;
				} else {
					if( !$HPLifetime ) {			# This is only executed once
						$HPLifetime = $ln;			# So, we only read it if it was undefined
					}
				}
			} else {
				#
				# If not, then it is just an unprocessed command
				&Log("Unprocessed command: ${ln}");
				&PushLiveDataWinBuffer($ln);
			}
		}
		$UpdateOutput++;
	} else {
		#
		# If not, then it is just a plain "scpi > " message. They have no CR LF
		&PushTermWinBuffer($ReceiverProcessedDataLine);
	}
}
#
# Process HP command: ":SYSTEM:STATUS?"
# I am crossing my fingers here that many of the positional
# strings don't move. As of 5/2006, we only support the Z3801A
# so I also hope that this works for other receivers.
sub cmdHPSystemStatus {
	my($i, $j, $k, $l, $m, $n, @llt, $ln);
	@llt = split( /\r\n/, $ReceiverProcessedDataLine);
	$j = @llt;													# Count of number of lines
	$ln = $llt[0];
	if( !$GlobalInitFlag ) {
		$GlobalInitFlag++;				# We are past the point of initialization
	}
	#
	# Now, we need to break this up and possibly display it
	if( $HPDisplayVerbose ) {
		for ($i = 0; $i <= $j; $i++) {
			&PushTermWinBuffer(sprintf("%2d: %s", $i, $llt[$i]));
		}
	}
	#
	#              1         2         3         4         5         6         7
	#    0123456789012345678901234567890123456789012345678901234567890123456789012345678
	# 2: SYNCHRONIZATION ............................................. [ Outputs Valid ]
	#              1         2         3         4         5         6         7
	#    0123456789012345678901234567890123456789012345678901234567890123456789012345678
	#22: Self Test: OK    Int Pwr: OK   Oven Pwr: OK   OCXO: OK   EFC: OK   GPS Rcv: OK
	#
	#              1         2         3         4         5         6         7
	#    0123456789012345678901234567890123456789012345678901234567890123456789012345678
	#12: Tracking: 6        Not Tracking: 3            UTC      23:42:44     02 May 2006
	#12: Tracking: 6        Not Tracking: 3            LOCAL    23:42:44     02 May 2006
	#12: Tracking: 6        Not Tracking: 3            GPS      23:42:44     02 May 2006
	#12: Tracking: 6        Not Tracking: 3            LOCL GPS 23:42:44     02 May 2006
	$ReceiverTimeMode = substr($llt[12],46,7);$ReceiverTimeMode =~ s/\s*$//;
	$KRdate = substr($llt[12],63,);$KRdate =~ s/^\s*//g;$KRdate =~ s/^(.*)\s*$/$1/;
	$KRtime = substr($llt[12],55,8);$KRtime =~ s/^(.*)\s*$/$1/;
	($i,$j,$k) = split(/ /, $KRdate);
	($l,$m,$n) = split(/:/, $KRtime);
	$k-=1900;
	$j=$MonthName{$j};
	$ReceiverTime=&Time2Unix($k, $j, $i, $l, $m, $n);
	#
	#              1         2         3         4         5         6         7
	#    0123456789012345678901234567890123456789012345678901234567890123456789012345678
	#18:  30  39  56  114                              LAT      N  40:46:18.874
	$KRlat = substr($llt[18],55);$KRlat =~ s/\s\s*/ /g;
	#
	#              1         2         3         4         5         6         7
	#    0123456789012345678901234567890123456789012345678901234567890123456789012345678
	#19:  30  38  55  155                              LON      W  73:54:41.603
	$KRlon = substr($llt[19],55);$KRlon =~ s/\s\s*/ /g;
	#
	#              1         2         3         4         5         6         7
	#    0123456789012345678901234567890123456789012345678901234567890123456789012345678
	#20: ELEV MASK  5 deg                              HGT               +43.19 m  (MSL)
	$KRheight = substr($llt[20],59);$KRheight =~ s/\s\s*/ /g;$KRheight =~ s/^\s*(.*)$/$1/;
	#
	#              1         2         3         4         5         6         7
	#    0123456789012345678901234567890123456789012345678901234567890123456789012345678
	#12: Tracking: 6        Not Tracking: 3            UTC      23:42:44     02 May 2006
	$i = substr($llt[12],9,2);
	$j = substr($llt[12],32,2);
	$KRsat = "${i}/${j}"; $KRsat =~ s/\s//g;
	#
	#              1         2         3         4         5         6         7
	#    0123456789012345678901234567890123456789012345678901234567890123456789012345678
	#14:   1  75 354  232     6  16  96                ANT DLY  93 ns
	$KRant = substr($llt[14],49); $KRant =~ s/\s\s*/ /g; $KRant =~ s/^\s*(.*)$/$1/;
	$j="${KRdate},${KRtime} -- Lat: $KRlat, Lon: $KRlon, Height: $KRheight, Sats: $KRsat";
	if( !$HPDisplayVerbose ) {
		&PushTermWinBuffer($j);
	}
	&PushLiveDataWinBuffer($j);
	&Vprint(1,$j);							# Add to the log
	#
	#              1         2         3         4         5         6         7
	#    0123456789012345678901234567890123456789012345678901234567890123456789012345678
	#20: ELEV MASK  5 deg                              HGT               +43.19 m  (MSL)
	$ReceiverMaskAngle=substr($llt[20],9,7);$ReceiverMaskAngle =~ s/\s\s*/ /g;$ReceiverMaskAngle =~ s/^\s*(.*)$/$1/;
	#
	#              1         2         3         4         5         6         7
	#    0123456789012345678901234567890123456789012345678901234567890123456789012345678
	#16:  18  34 279  108     9  13 163                MODE     Hold
	$KRvel=substr($llt[16],50); $KRvel =~ s/\s\s*/ /g; $KRvel =~ s/^\s*(.*)$/$1/;
	$KRgeom=$KRvel;
	#              1         2         3         4         5         6         7
	#    0123456789012345678901234567890123456789012345678901234567890123456789012345678
	#13: PRN  El  Az   SS   PRN  El  Az                1PPS CLK Synchronized to UTC
	#
	$KRpps_m=substr($llt[13],51);$KRpps_m=~ s/\s\s*/ /g;
	#
	# HP Receiver sync mode:
	$KR_HPSync=substr($llt[2],40);$KR_HPSync =~ s/[.]//g;$KR_HPSync =~ s/\s\s*/ /g;$KR_HPSync =~ s/^\s*(.*)$/$1/;
	#
	# HP Receiver Acquizition Mode:
	$KR_HPAcq=substr($llt[10],40);$KR_HPAcq =~ s/[.]//g;$KR_HPAcq =~ s/\s\s*/ /g;$KR_HPAcq =~ s/^\s*(.*)$/$1/;
	#
	# HP Receiver health Monitor
	$KR_HPhmon=substr($llt[21],40);$KR_HPhmon =~ s/[.]//g;$KR_HPhmon =~ s/\s\s*/ /g;$KR_HPhmon =~ s/^\s*(.*)$/$1/;
	#
	# HP Receiver health Monitor
	$llt[22] =~ s/\s\s*/ /g;
	($j,$j,$KR_HPll1, $j,$j,$KR_HPll2, $j,$j,$KR_HPll3,
		$j,$KR_HPll4, $j,$KR_HPll5, $j,$j,$KR_HPll6) = split(/ /,$llt[22]);
	#             1         2         3         4         5         6         7
	#   0123456789012345678901234567890123456789012345678901234567890123456789012345678
	#4: >> Locked to GPS                              TFOM     3             FFOM     0
	$KR_HPtfom=substr($llt[4],50,10);$KR_HPtfom =~ s/\s//g;
	$KR_HPffom=substr($llt[4],73);$KR_HPffom =~ s/\s//g;
	#             1         2         3         4         5         6         7
	#   0123456789012345678901234567890123456789012345678901234567890123456789012345678
	#5:    Recovery                                   1PPS TI +900 ps relative to GPS
	$KR_HP1PPS=substr($llt[5],50);$KR_HP1PPS =~ s/\s\s*/ /g;$KR_HP1PPS =~ s/^\s*(.*)$/$1/;
	#             1         2         3         4         5         6         7
	#   0123456789012345678901234567890123456789012345678901234567890123456789012345678
	#6:    Holdover                                   HOLD THR 1.000 us
	$KR_hthr=substr($llt[6],54);$KR_hthr =~ s/\s\s*/ /g;$KR_hthr =~ s/^\s*(.*)$/$1/;
	#             1         2         3         4         5         6         7
	#   0123456789012345678901234567890123456789012345678901234567890123456789012345678
	#8:                                               Predict  3.6 us/initial 24 hrs
	$KR_unce=substr($llt[8],53);$KR_unce =~ s/\s\s*/ /g;$KR_unce =~ s/^\s*(.*)$/$1/;
	#
	# Smart clock mode. This is tricky since it could be in any one
	# of 4 lines.
	$j=substr($llt[4],0,40);				# Get the first line
	if( $j =~ />>/ ) {
		#
		# Found it! It is Locked to GPS
		$KR_HPsmart="Locked to GPS";
	} else {
		$j=substr($llt[5],0,40);				# Get the second line
		if( $j =~ />>/ ) {
			#
			# Found it! It is in Recovery
			$KR_HPsmart="Recovery";
		} else {
			$j=substr($llt[6],0,40);				# Get the third line
			if( $j =~ />>/ ) {
				#
				# Found it! It is in Holdover
				$KR_HPsmart="Holdover";
			} else {
				$j=substr($llt[7],0,40);				# Get the fourth line
				if( $j =~ />>/ ) {
					#
					# Found it! It is in Power-up
					$KR_HPsmart="Power-up";
				} else {
					$KR_HPsmart="Unknown";
				}
			}
		}
	}
	#
	# And last but not least, process the sat info
	#              1         2         3         4         5         6         7
	#    0123456789012345678901234567890123456789012345678901234567890123456789012345678
	#11: Satellite Status __________________________   Time ____________________________
	#12: Tracking: 6        Not Tracking: 3            UTC      23:13:35     05 May 2006
	#13: PRN  El  Az   SS   PRN  El  Az                1PPS CLK Synchronized to UTC
	#14:   1  50 309  228    11   8 290                ANT DLY  93 ns
	$n=0;$i=14;
	HPSATL1: for($i=14; $i < 20; $i++) {
		$j=substr($llt[$i],0,16);$j =~ s/\s\s*/ /g;$j =~ s/^\s*(.*)$/$1/;
		($j,$k,$l,$m) = split(/ /,$j);
		if( $j !~ /^$/ ) {						# Make sure we have something first
			$KRsatinfo{$n,0} =$j;				# PRN
			$KRsatinfo{$n,1} =$k;				# Elevation
			$KRsatinfo{$n,2} =$l;				# Azimuth
			$KRsatinfo{$n,3} =$m;				# Signal Strength
		} else {
			last HPSATL1;
		}
		$n++;
	}
	$i=14;										# Get the non-tracked column 1
	HPSATL2: for($i=14; $i < 21; $i++) {
		$j =~ s/[*]//;							# Get rid of the stars
		$j=substr($llt[$i],19,11);$j =~ s/\s\s*/ /g;$j =~ s/^\s*(.*)$/$1/;
		($j,$k,$l) = split(/ /,$j);
		if( $j !~ /^$/ ) {
			$KRsatinfo{$n,0} =$j;				# PRN
			$KRsatinfo{$n,1} =$k;				# Elevation. Could be text
			$KRsatinfo{$n,2} =$l;				# Azimuth. Could be text
			$KRsatinfo{$n,3} =0;				# Signal Strength
		} else {
			last HPSATL2;
		}
		$n++;
	}
	$i=14;										# Get the non-tracked column 2
	HPSATL3: for( $i=14; $i < 21; $i++) {
		$j =~ s/[*]//;							# Get rid of the stars
		$j=substr($llt[$i],30,12);$j =~ s/\s\s*/ /g;$j =~ s/^\s*(.*)$/$1/;
		($j,$k,$l) = split(/ /,$j);
		if( $j !~ /^$/ ) {
			$KRsatinfo{$n,0} =$j;				# PRN
			$KRsatinfo{$n,1} =$k;				# Elevation. Could be text
			$KRsatinfo{$n,2} =$l;				# Azimuth. Could be text
			$KRsatinfo{$n,3} =0;				# Signal Strength
		} else {
			last HPSATL3;
		}
		$n++;
	}
	$NumChannels=$n;
}
#--------------------------------------------------------------------
# ---Section--- Process Oncore command
#
sub ProcessOncoreCommand {
	my($tlen, $i, $j, $checksum, $fcc, $rst);
	$tlen=length($ReceiverProcessedDataLine);
	return if( $tlen < 3 );		# Maybe we need more chars
	if( substr($ReceiverProcessedDataLine, 0, 2) =~ /@@/ ) {
		#
		# We have a command. Run some checks.
		#
		$fcc=substr($ReceiverProcessedDataLine, 2, 2);										# Get command
		if( ($fcc =~ /[A-Z][ABDEMNOPQSa-z]/) && ($ReceiverProcessedDataLine =~ /\r\n$/) ) {	# make sure we have complete line
			$checksum=0;
			for( $i=2; $i < $tlen-3; $i++ ) {
				$checksum ^= ord(substr($ReceiverProcessedDataLine, $i, 1));
			}
			#
			$j=substr($ReceiverProcessedDataLine, $tlen-3, 1);
			if( $checksum != ord($j) ) {
				$ReceiverProcessedDataLine="";
				return;
			}
			#
			&PromptAndStopTimeout;							# This tells everyone that we can send another command
			$rst=&FormatMoto($ReceiverProcessedDataLine);
			$KRrawline=$rst;
			$UpdateOutput++;								# Yes we have data and we can update the screen
			#
			&PushLiveDataWinBuffer($rst);
			if( $InputMethod ) {
				&PushTermWinBuffer($rst);					# Always show if we are in local serial connection
			} else {
				if( !$SuspendOutput ) {
					&PushTermWinBuffer($rst);				# Stop the display if network connected
				}
			}
			&Vprint(2,$rst);								# If the verbose level is high also send to log
			#
			CBLK: {
				if( $fcc =~ /Ag/ ) {&cmdAg; last CBLK;}
				if( $fcc =~ /At/ ) {&cmdAt; last CBLK;}
				if( $fcc =~ /Aw/ ) {&cmdAw; last CBLK;}
				if( $fcc =~ /Az/ ) {&cmdAz; last CBLK;}
				#
				if( $fcc =~ /Ba/ ) {$NumChannels=6; &cmdBaEaHa; last CBLK;}
				if( $fcc =~ /Bb/ ) {&cmdBb; last CBLK;}
				if( $fcc =~ /Bj/ ) {&cmdBj; last CBLK;}
				if( $fcc =~ /Bk/ ) {&cmdBk; last CBLK;}
				if( $fcc =~ /Bn/ ) {$NumChannels=6; &cmdEn; last CBLK;}
				#
				if( $fcc =~ /Cg/ ) {$NumChannels=6; &cmdCg; last CBLK;}
				if( $fcc =~ /Ca/ ) {$NumChannels=6; &cmdCaFaIa; last CBLK;}
				if( $fcc =~ /Cj/ ) {&cmdCj; last CBLK;}
				#
				if( $fcc =~ /Ea/ ) {$NumChannels=8; &cmdBaEaHa; last CBLK;}
				if( $fcc =~ /En/ ) {&cmdEn; last CBLK;}
				#
				if( $fcc =~ /Fa/ ) {&cmdCaFaIa; last CBLK;}
				#
				if( $fcc =~ /Gc/ ) {$NumChannels=12; &cmdGc; last CBLK;}
				if( $fcc =~ /Ge/ ) {$NumChannels=12; &cmdGe; last CBLK;}
				if( $fcc =~ /Gf/ ) {$NumChannels=12; &cmdGf; last CBLK;}
				if( $fcc =~ /Gj/ ) {$NumChannels=12; &cmdGj; last CBLK;}
				#
				if( $fcc =~ /Ha/ ) {$NumChannels=12; &cmdBaEaHa; last CBLK;}
				if( $fcc =~ /Hn/ ) {$NumChannels=12; &cmdHn; last CBLK;}
				#
				if( $fcc =~ /Ia/ ) {$NumChannels=12; &cmdCaFaIa; last CBLK;}
				else {
					$UnprocessedCmdBuffer[$UnprocessedBufCursor]=$rst;
					$UnprocessedBufCursor++;
					if( $UnprocessedBufCursor == $UnprocessedBufMax ) {
						$UnprocessedBufCursor=0;
					}
					last CBLK;
				}
			}
		}
		else {
			return;		# Probaly got an uncompleted command. Continue
		}
		$ReceiverProcessedDataLine = "";
	}
	#
	# Single '@' in msg body. Not the start of command. Just return.
	#
	return;
}
#
# Format the Motorola line into hex codes
sub FormatMoto {
	my($i,$j,$rst,$fcc,$ll,$mc,$tlen);
	$ll=$_[0];
	$tlen=length($ll);
	#
	# This little section formats and prints the raw data
	#
	$fcc=substr($ll, 2, 2);									# Get command
	$rst = sprintf("@@%s ", $fcc);
	$j=0;
	for( $i=4; $i < $tlen; $i++ ) {
		$mc=ord(substr($ll, $i, 1));		# ord only checks the first char!
		$rst .= sprintf("%2.2X", $mc);
		if( $j >= 3 ) {
			$rst .= ' ';
			$j=0;
		}
		else { $j++; }
	}
	return $rst;
}
#
# Process At. Position Hold mode. All receivers.
# Normally not sent.
sub cmdAt {
	my($s, $pv);
	$pv=ord(substr($ReceiverProcessedDataLine, 4, 1));
	#
	if( $pv == 1 ) { $s.="Enabled"; }
	elsif ( $pv == 0 ) { $s.="Disabled"; }
	else { $s.="Start Automatic Site Survey"; }
	#
	$MotoAt=$s;
}
#
# Process Ag. Satellite mask angle. All receivers.
sub cmdAg {
	$ReceiverMaskAngle=ord(substr($ReceiverProcessedDataLine, 4, 1));
}
#
# Process Az. Antenna Cable delay. All receivers.
sub cmdAz {
	$ReceiverCableDelay=&s2sn(4,4);
}
#
# Process Aw. Time Mode. All receivers.
# Normally sent in the beginning.
sub cmdAw {
	my($i);
	$i=&s2sn(4,1);
	if( $i ) {
		$ReceiverTimeMode="UTC";
	} else {
		$ReceiverTimeMode="GPS";
	}
}
#
# Process Bk. Motorola Basic, Model
# 6 Channel Position/Status/Data Extension Message
sub cmdBk {
	#
	# Just a place holder for the message to keep the noise level down.
}
#
# Process Cg. 6 Channel receiver status.
sub cmdCg {
	my($i);
	$i=ord(substr($ReceiverProcessedDataLine, 4, 1));
	if( !$i ) {
		&PushOutputWinBuffer("Warning: Receiver is in Idle Mode. To restart do: \@\@Cg#01");
	}
}
#
# Process Ca, Fa. 1 Byte - VP, UT Model, Ia 24bits - M12 model
# Receiver Self-Test. Note, this command will take a while to complete
# and afterwards, the receiver will not be sending any messages again.
sub cmdCaFaIa {
	my($i, $pv, $bit);
	#
	if( $NumChannels  < 12 ) {
		$MotoSelfTest=sprintf("0x%02x",&s2sn(4,2));
	} else {
		$MotoSelfTest=sprintf("0x%03x",&s2sn(4,3));
	}
}
#
# Process Cj. This is just a list of strings terminared by
# CR/LF. We should keep count of how many lines and
# create an array of our own.
#
# As far as I can tell, This command works for all versions
# of Motorola receivers. We will use it to build the custom strings
# needed for each receiver.
#
# After reading the ntp source code for refclock_oncore.c the following
# is true:
#
# Non-Standard firmware can lead to wrong number of channels
# if the determination is based on the Version and revision of the @@Cj
# command.
#
# It is best if we get the number from the @@Ba or @@Ea or @@Ha commands
#
# Thank goodness this only executes once.... [CRF]
#
# Thank you to: Poul-Henning Kamp and "Richard M. Hambly" <rick@cnssys.com>
#
sub cmdCj {
	my($i, $j, $k);
	#
	if( $ReceiverType eq 'M' ) {
		@KRid = split( /\r\n/, $ReceiverProcessedDataLine);
	} else {
		#
		# In order presented:
		# Copyright,Software part#,Software Version,Software Revision,Software date,
		# Model #, Hardware Part #, Serial Number, Manufacture Date, Option list
		($k = $ReceiverProcessedDataLine) =~ s/"//g;
		@KRid = split( /,/, $k);
	}
	$KRidlines = @KRid;
	for ($i = 0; $i <= $KRidlines; $i++) {
		if ($KRid[$i] =~ /VER/) {
			($KRsfwVer = substr($KRid[$i], 14)) =~ tr/ //d;
		} elsif ($KRid[$i] =~ /REV/) {
			($KRsfwRev = substr($KRid[$i], 14)) =~ tr/ //d;
		} elsif ($KRid[$i] =~ /DATE/) {
			$KRsfwDate = substr($KRid[$i], 15);
		} elsif ($KRid[$i] =~ /MODEL/) {
			($KRsfwModel = substr($KRid[$i], 10)) =~ tr/ //d;
		} elsif ($KRid[$i] =~ /SERIAL/) {
			($KRserNum = substr($KRid[$i], 11)) =~ tr/ //d;
		}
	}
	#
	# Perform preliminary model number check
	$j=substr($KRsfwModel,0,1);
	if( $KRsfwModel =~ /^PVT6.*$/ ) {
		$ReceiverModel="PVT6";			# Oncore "PVT6"
	} elsif( $j =~ /A/ ) {
		$ReceiverModel="Basic";			# Oncore "Basic"
	} elsif( $j =~ /B/ ) {
		$ReceiverModel="VP";			# Oncore "VP"
	} elsif( $j =~ /P/ ) {
		$ReceiverModel="M12";			# Oncore "M12"
	} elsif( ($j == 'P') || ($j == 'D') || ($j == 'S') ) {
		$j=substr($KRsfwModel,1,1);
		$k=substr($KRsfwModel,5,1);
		if( $k =~ /N/ ) {
			$ReceiverModel="GT";
		} elsif( ( ($j =~ /3/) || ($j =~ /3/) ) && ($k =~ /G/) ) {
			$ReceiverModel="GT+";
		} elsif( ( ($j =~ /5/) && ($k =~ /U/) ) || ( ($j =~ /1/) && ($k =~ /A/) ) ) {
			$ReceiverModel="UT";
		} elsif( ($j =~ /5/) || ($j =~ /G/) ) {
			$ReceiverModel="UT+";
		} elsif( ($j =~ /6/) || ($j =~ /G/) ) {
			$ReceiverModel="SL";
		} else {
			$ReceiverModel="Unknown";
		}
	} else {
		$ReceiverModel="Unknown";
	}
	&BuildStatusVersionWIN;
}
#
# Process Ba, Ea and Ha
#
sub cmdBaEaHa {
	my($tt, $pe, $ii, $i, $j, $k, $l, $m, $n, $vsat, $tsat);
	my($si, $rs);
	#
	$k=&s2n(6,2);			# Year
	$j=&s2n(4,1);			# Month
	$i=&s2n(5,1);			# Day
	#
	$l=&s2n(8,1);			# Hour
	$m=&s2n(9,1);			# Min
	$n=&s2n(10,1);			# Sec
	#
	$KRdate=sprintf("%4d", $k) . '/' . sprintf("%02d",$j) . '/' . sprintf("%02d",$i);
	#
	# This is the time that the GPS receiver is giving us
	#
	if( !$GlobalInitFlag ) {
		$GlobalInitFlag++;				# We are past the point of initialization
	}
	$tt=sprintf("%02d", $l) . ':' . sprintf("%02d", $m) . ':' . sprintf("%02d", $n) . '.';
	$KRtime=$tt;
	$ReceiverTimeFrac = &s2n(11,4);		# Fractional Seconds
	$KRtime.= $ReceiverTimeFrac;
	#
	$k-=1900;$j--;
	$ReceiverTime=&Time2Unix($k, $j, $i, $l, $m, $n);
	#
	#
	if( $NumChannels < 12 ) { $rs=15; } else { $rs=31; }
	$i = &s2sn($rs, 4) * 0.00000027777777777777;
	$pe = sprintf "%+11.7f", $i;
	$KRlat=$pe . " deg.";
	#
	#
	if( $NumChannels < 12 ) { $rs=19; } else { $rs=35; }
	$i = &s2sn(19, 4) * 0.00000027777777777777;
	$pe = sprintf "%+12.7f", $i;$pe =~ s/^\s*(.*)$/$1/;
	$KRlon=$pe . " deg.";
	#
	#
	if( $NumChannels < 12 ) { $rs=23; } else { $rs=39; }
	$i = &s2sn(23, 4) / 100;
	$pe = sprintf "%8.2f", $i;$pe =~ s/^\s*(.*)$/$1/;
	$KRheight=$pe . ' meters';
	#
	# At this point, the 6/8 channel receivers differ from the 12 channel ones. 
	#
	if( $NumChannels < 12 ) {
		if( $NumChannels < 8 ) {
			$i=64;
		} else {
			$i=72;
		}
		if (&s2n($i, 1) & 8) {					# Bit 3, Position Hold or Acquiring Sats
			if (&s2n( 38, 1) == 0) {			# Verify against # of sats tracked
				$KRvel = 'Acquiring Satellites';
			} else {
				$KRvel = 'Position Hold';
			}
		} else {								# Tracking Mode
			$i = &s2n(31, 2) / 100;
			$pe = sprintf "%5.2f", $i;
			$KRvel = $pe . ' m/s, ';
			$i = &s2n(33, 2) / 100;
			$pe = sprintf "%5.1f", $i;
			$KRvel .= $pe . ' deg';
		}
		#
		# Geometry
		$i = &s2n(35, 2);
		$ii = &s2n(72, 1);
		if( $NumChannels == 8 ) {
			if( !$i ) {
				$KRgeom = 'Uncomputable';
				$KRgeom = 'Position Propagate' if ($ii & 0x80);
				$KRgeom = 'Bad (DOP > 12)' if ($ii & 0x40);
				$KRgeom = 'Position Hold' if ($ii & 0x08);
				$KRgeom = 'Acquiring Satellites' if ($ii & 0x08 && &s2n(38, 1) == 0);
			} else {
				$i /= 10;
				$pe = sprintf "%4.1f", $i;
				$KRgeom= $pe . ' DOP';
				if (&s2n(37, 1) & 1) {
					$KRgeom .= ', 2D';
				} else {
					$KRgeom .= ', 3D';
				}
			}
		} else {
			if( !$i ) {
				$KRgeom = 'Position Hold';
			} else {
				$KRgeom = 'Acquiring Satellites';
			}
		}

		#
		# Antenna status
		$i = &s2n( 37, 1) & 192;
		if( $i == 0 ) { $pe='OK';}
		elsif( $i == 128 ) { $pe='Undercurrent';}
		else { $pe='Overcurrent';}
		if ((&s2n(37, 1) & 0x20) == 0x20) { $pe .= ', Site Survey';}
		if (&s2n(72, 1) & 0x01) { $pe .= ', Bad Almanac';}
		$KRant=$pe;
		#
		# Satellite status
		$tsat = &s2n( 39, 1);		# Tracked Satellites
		$vsat = &s2n( 38, 1);		# Visible Satellites
		$vsat = '12\'' if ($vsat == 13);	# Receiver can lie
		$vsat = '12"'  if ($vsat == 14);	# Receiver can *really* lie
		$vsat = '12\!' if ($vsat >  14);	# Receiver can *really* lie

		if ($vsat == 0) {
			$KRsat = $tsat;
		} else {
			$KRsat = $tsat . "/" . $vsat;
		}
		#
		# Satellite discovery loop
		# the structure we return by position is: (0, PRN) (1, CTM) (2, C/No) (3, CSF)
		$k=40;									# Position of sat in the rcvr msg
		%KRsatinfo=();							# Clear Satellite info
		for( $j=0; $j < $NumChannels; $j++ ) {
			$KRsatinfo{$j,0}=&s2n($k,1);		# PRN
			$i=&s2n($k+1,1);
			if( $i == 0 ) { $pe= 'code_search';}
			elsif( $i == 1 ) { $pe= 'code_acquire';}
			elsif( $i == 2 ) { $pe= 'AGC_set';}
			elsif( $i == 3 ) { $pe= 'preq_acq';}
			elsif( $i == 4 ) { $pe= 'bit_sync_detect';}
			elsif( $i == 5 ) { $pe= 'msg_sync_detect';}
			elsif( $i == 6 ) { $pe= 'sat_time_avail';}
			elsif( $i == 7 ) { $pe= 'ephemeris_acq';}
			elsif( $i == 8 ) { $pe= 'avail_for_position';}
			else { $pe= sprintf("0x%02.2x", $i);}
			$KRsatinfo{$j,1}=$pe;				# CTM - Mode
			#
			$i=&s2n($k+2,1);
			$KRsatinfo{$j,2}=$i;				# Signal Strength
			#
			$i=&s2n($k+3,1);					# CSF
			$pe= sprintf("0x%02.2x", $i);
			$KRsatinfo{$j,3}=$pe;
			$k+=4;
		}
	} else {
		#
		# Process the more detailed M12 receivers
		$si=&s2n(129, 2);		# Get Receiver status
		if( $si & 0x6000 ) {							# Position Hold or Acquiring Sats
			$KRvel = 'Acquiring Satellites';
		} elsif( $si & 0x8000 ) {
			$KRvel = 'Position Hold';
		} elsif( $si & 0x4000 ) {
			$KRvel = 'Bad Geometry';
		} else {
			$i = &s2n(47, 2) / 100;
			$pe = sprintf "3D: %5.2fm/s", $i;
			$i = &s2n(49, 2) / 100;
			$pe.= sprintf "2D: %5.2fm/s", $i;
			$i = &s2n(51, 2) / 100;
			$pe.= sprintf ", %5.2f deg", $i;
			$KRvel = $pe;
		}
		#
		# Geometry
		$i = &s2n(53, 2);
		if( $si & 0x6000 ) {							# Bits Position Hold or Acquiring Sats
			$KRgeom = 'Acquiring Satellites';
		} elsif( $si & 0x8000 ) {
			$KRgeom = 'Position Hold';
		} elsif( $si & 0x4000 ) {
			$KRgeom = 'Bad Geometry';
		} else {
			$i /= 10;
			$pe = sprintf "%4.1f", $i;
			$KRgeom= $pe . ' DOP';
			if (&s2n(37, 1) & 1) {
				$KRgeom .= ', 2D';
			} else {
				$KRgeom .= ', 3D';
			}
		}
		#
		# Antenna status
		$i = &s2n(129, 2) & 0x0006;
		if( $i == 0 ) { $pe='OK';}
		elsif( $i == 0x0004 ) { $pe='Undercurrent';}
		elsif( $i == 0x0001 ) { $pe='Overcurrent';}
		else { $pe='invalid';}
		#
		if(&s2n(129, 2) & 0x0010) { $pe .= ', Site Survey';}
		if(&s2n(129, 2) & 0x0080) { $pe .= ', Bad Almanac';}
		$KRant=$pe;
		#
		# Satellite status
		$vsat = &s2n( 55, 1);		# Visible Satellites
		$tsat = &s2n( 56, 1);		# Tracked Satellites

		if ($vsat == 0) {
			$KRsat = $tsat;
		} else {
			$KRsat = $tsat . "/" . $vsat;
		}
		#
		# Satellite discovery loop
		# the structure we return by position is: (0, PRN) (1, CTM) (2, SS) (3, CSF)
		$k=57;									# Position of sat in the rcvr msg
		%KRsatinfo=();							# Clear Satellite info
		for( $j=0; $j < $NumChannels; $j++ ) {
			$KRsatinfo{$j,0}=&s2n($k,1);		# PRN
			$i=&s2n($k+1,1);
			if( $i == 0 ) { $pe= 'code_search';}
			elsif( $i == 1 ) { $pe= 'code_acquire';}
			elsif( $i == 2 ) { $pe= 'AGC_set';}
			elsif( $i == 3 ) { $pe= 'preq_acq';}
			elsif( $i == 4 ) { $pe= 'bit_sync_detect';}
			elsif( $i == 5 ) { $pe= 'msg_sync_detect';}
			elsif( $i == 6 ) { $pe= 'sat_time_avail';}
			elsif( $i == 7 ) { $pe= 'ephemeris_acq';}
			elsif( $i == 8 ) { $pe= 'avail_for_position';}
			else { $pe= sprintf("0x%02.2x", $i);}
			$KRsatinfo{$j,1}=$pe;				# CTM - Mode
			#
			$i=&s2n($k+2,1);
			$KRsatinfo{$j,2}=$i;				# Signal Strength
			#
			$i=&s2n($k+4,2);					# CSF
			$pe= sprintf("0x%04x", $i);
			$KRsatinfo{$j,3}=$pe;
			$k+=6;
		}
	}
}
#
# Process Bb
#
sub cmdBb {
	my($pe, $i, $j, $k);
	#
	#
	$i = &s2n(4, 1);
	$pe = sprintf "%d", $i;
	$MotoBb=$pe;
	#
	# Create the array
	#
	for( $i=0; $i < 12; $i++ ) {
		$k=($i*7)+5;
		$KRvsatinfo{$i,0}=&s2n($k,1);		# Sat ID
		$KRvsatinfo{$i,1}=&s2sn($k+1,2);	# Doppler
		$KRvsatinfo{$i,2}=&s2n($k+3,1);		# Elevation
		$KRvsatinfo{$i,3}=&s2n($k+4,2);		# Azimuth
		$j=&s2n($k+6,1);			# Health
		if( $j == 0 ) { $KRvsatinfo{$i,4}="OK "; }
			elsif( $j == 1 ) { $KRvsatinfo{$i,4}="RMV"; }
		else { $KRvsatinfo{$i,4}="Sick"; }
		$KRvsatinfo{$i,5}=1;				# New data
	}
}
#
# Process Gc 1PPS Control message
sub cmdGc {
	$M121PPSControl = &s2sn(4, 1);
}
#
# Process Ge Current Time RAIM Mode
# For 12 channel receivers, we also need the Hn and Gf commands.
sub cmdGe {
	$M12TRAIMMode = &s2sn(4, 1);
}
#
# Process Gf Current Time RAIM alarm message
# For 12 channel receivers, we also need the Hn and Ge commands.
sub cmdGf {
	$M12TRAIMMessage = &s2sn(4, 2)*100;
}
#
# Process Hn (Time RAIM)
# This code is copied from the En code provided by Mr. Carl R. Friend
# For 12 channel receivers, we also need the Gc, Ge and Gf commands.
sub cmdHn {
	my($ppsmode, $ppsref, $capability, $status, $alarm);
	# Parse the assorted data from the @@hn string
	$ppsmode    = &s2sn(4, 1);
	$ppsref     = &s2sn(5, 1);
	$status     = &s2sn(6, 1);
	$capability = &s2sn(7, 1);
	#
	$KRtraim_e = 'On';
	#
	$KRtraim_e .= ', Detect/Isolate' if ($capability == 0);
	$KRtraim_e .= ', Detect only'    if ($capability == 1);
	$KRtraim_e .= ', Impossible'     if ($capability == 2);

	if ($status == 0) { $KRtraim_s='OK'}
	elsif ($status == 1) { $KRtraim_s='ALARM'}
	elsif ($status == 2) { $KRtraim_s='UNKNOWN'}

	$KRtraim_s.=", $M12TRAIMMessage nS";

	if($ppsmode) {
		if ($M121PPSControl == 0) { $KRpps_m = 'Disabled'; }
		elsif ($M121PPSControl == 1) { $KRpps_m = 'On'; }
		elsif ($M121PPSControl == 2) { $KRpps_m = 'On when tracking'; }
		else { $KRpps_m = 'On when TRAIM OK'; }
	}
	#
	if($ppsmode > 0) {
		if ($ppsref == 0)    { $KRpps_r = ', UTC ref'; }
		elsif ($ppsref == 1) { $KRpps_r = ', GPS ref'; }
	}
	$MotoEn0=$KRpps_m;
	$MotoEn1=$KRpps_r;
}
#
# Process En (Time RAIM)
# This code is provided by Mr. Carl R. Friend
# Leap second changes provided by Carl R. Friend on 12/01/2005
#
sub cmdEn {
	my($ppsmode, $ppsref, $capability, $status, $alarm, $enable);

	# Parse the assorted data from the @@En string
	$enable     = &s2sn(5, 1);
	$alarm      = &s2sn(6, 2) * 100;
	$ppsmode    = &s2sn(8, 1);
	$ppsref     = &s2sn(20, 1);
	$status     = &s2sn(21, 1);
	$capability = &s2sn(22, 1);

	$KRtraim_e = 'Off' if ($enable == 0);
	$KRtraim_e = 'On'  if ($enable == 1);

	$KRtraim_e .= ', Detect/Isolate' if ($capability == 0);
	$KRtraim_e .= ', Detect only'    if ($capability == 1);
	$KRtraim_e .= ', Impossible'     if ($capability == 2);

	if ($status == 0) { $KRtraim_s='OK'}
	elsif ($status == 1) { $KRtraim_s='ALARM'}
	elsif ($status == 2) { $KRtraim_s='UNKNOWN'}

	$KRtraim_s.=", $alarm nS";

	if ($ppsmode == 0) { $KRpps_m = 'Disabled'; }
	elsif ($ppsmode == 1) { $KRpps_m = 'On'; }
	elsif ($ppsmode == 2) { $KRpps_m = 'On when tracking'; }
	else { $KRpps_m = 'On when TRAIM OK'; }

	if ($ppsmode > 0) {
		if ($ppsref == 0)    { $KRpps_r = ', UTC ref'; }
		elsif ($ppsref == 1) { $KRpps_r = ', GPS ref'; }
	}
	$MotoEn0=$KRpps_m;
	$MotoEn1=$KRpps_r;
}
#
# Process Bj
# This code is provided by Mr. Carl R. Friend
# Leap second changes provided by Carl R. Friend on 12/01/2005
# Leap second updates provided by Carl R. Friend on 05/28/2006
#
sub cmdBj {
	my($x, $month);
	($x, $month) = split /\//,$RstrIsoRdate;
	if ($month != 6 && $month != 12) {
		undef $leapString;
		return;
	}
	$leapFlag = &s2n(4, 1);
	if ($leapFlag == 0) {$leapString = ', No Leap';}
	if ($leapFlag == 1) {$leapString = ', + Leap';}
	if ($leapFlag == 2) {$leapString = ', - Leap';}
}
#
# Process Gj
#	12 channel Leap second message
# Response to above command:
#	@@GjpfyymdiffffhmsC<CR>LF>
# where:
#	p = present leap second value
#	f = future leap second value
#	yy = year of the future leap second application
#	m = month of the future leap second application
#	d = day of the future leap second application
#	I = integer part of current UTC offset (seconds)
#	ffff = fractional part of current UTC offset (nanoseconds)
#	h = hour of the leap second application 0.23
#	m = minute of the leap second application 0.59
#	s = second of the leap second application 0.60
#
sub cmdGj {
	my($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst);
	my($lm, $ld, $lyear, $lh, $li, $ls, $x, $i);
	#
	&Log(&FormatMoto($ReceiverProcessedDataLine));
	($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = gmtime(time);
	$year+=1900;$mon++;$yday++;
	undef $leapString;
	#
	$lyear	= &s2n(6, 2);
	$lm		= &s2n(8, 1);
	$ld		= &s2n(9, 1);
	return if $lyear != $year;
	return if $lm < $mon;
	$lh		= &s2n(15, 1);
	$li		= &s2n(16, 1);
	$ls		= &s2n(17, 1);
	$x = &s2n(10,1);
	if( $x > 7 ) {
		$i = 15 - $x;
		$x='-';
	} else {
		$i=$x;
		$x='+';
	}
	$leapString=sprintf("%02d/%02d/%4d - %02d:%02d:%02d, %s%d", $ld, $lm, $lyear, $lh, $li, $ls, $x, $i);
}
#--------------------------------------------------------------------
# ---Section--- Screen building main section
#
# Check if anything needs to be updated
sub CheckForOutputUpdates {
	#
	# Update the screen/HTML
	if( $GotSignal_WINCH ) {
		if( $FullScreenEnable ) {				# There is no point if curses is disabled
			&Log("Window change detected!!");
			&StopCurses;
			select(undef, undef, undef, 0.01);	# One moment please...
			&StartCurses;&BuildCursesWindows;
			$CmdLineHCursor=0;
			getch();							# Ignore any junk here
			&BuildCmdlineWin;
			&BuildOutputWin;
			if( $TerminalMode ) {
				&BuildTerminalWin;
			} else {
				if( $SingleTopWIN_Enable ) {
					&SingleTopWINDel;
				}
			}
			&BuildStatusVersionWIN;
		}
		$GotSignal_WINCH=0;
	}
	#
	&BuildMainScreen;										# Give a full screen display
	if( $GlobalInitFlag && $htmlgen ) { &HTMLgentable; };	# Gen HTML if we started counting time
	#
	# Keep in mind that the output will only be updated when a complete line is read-in from
	# the receiver. The command line window and output window will updated dynamically.
	if( $UpdateOutput ) {
		$UpdateOutput=0;
	}
	#
	if( $FullScreenEnable ) {
		if( ($KbdChar = getch()) ne ERR ) {
			$CmdLineUpdate++;
		}
		if( $CmdLineUpdate ) {
			&BuildCmdlineWin;
		}
	}
}
#--------------------------------------------------------------------
# ---Section--- Build the main screen
#
sub BuildMainScreen {
	my($i, $j, $k, $l, $v, $v1, $v1p, $mwin, $s1, $pos, $satwinmax);
	#
	return if !$UpdateOutput;				# No need to update if no data
	&FormatTime;							# Let's get the proper time in the right format
	#
	return if !$CursesInitFlag;				# Wait for curses initialization
	return if $SingleTopWIN_Enable;			# Return if the top window is merged into 1
	return if $SuspendOutput;				# No need to update if no data
	#
	$s1 = $COLS / 2 - 1;					# Vertical statistics column split
	if( $COLS < 90) {
		$s1-=3;
	}
	#
	# Build top window
	#
	attron($TopWIN, A_NORMAL);
	move($TopWIN,0,0);
	if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
	addstr($TopWIN, "Rcvr Date: ");
	if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
	addstr($TopWIN, $RstrRdate);
	#
	# TODO Possible option here to show the format. For now We don't show it.
	#if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
	#addstr($TopWIN, " Fmt: ");
	#if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
	#addstr($TopWIN, $TimeFormat);
	clrtoeol($TopWIN);
	#
	move($TopWIN, 0, $s1);
	if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
	addstr( $TopWIN, "Rcvr Time: ");
	if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
	addstr( $TopWIN, $RstrRtime );
	#
	move($TopWIN,1,0);
	if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
	addstr( $TopWIN, "PC   Date: ");
	if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
	addstr( $TopWIN, $RstrLdate);
	#
	# TODO Possible option here to show the format. For now We don't show it.
	#if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
	#addstr($TopWIN, " Fmt: ");
	#if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
	#addstr($TopWIN, $TimeFormat);
	#
	clrtoeol($TopWIN);
	#
	move($TopWIN, 1, $s1);
	if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
	addstr( $TopWIN, "PC   Time: ");
	if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
	addstr( $TopWIN, $RstrLtime);
	if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
	addstr( $TopWIN, " Up: ");
	if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
	addstr( $TopWIN, $RstrUptime);
	#
	move($TopWIN, 2,0);
	if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
	addstr( $TopWIN, "Latitude: ");
	if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
	addstr($TopWIN, $KRlat);
	clrtoeol($TopWIN);
	#
	move($TopWIN, 2, $s1);
	if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
	addstr( $TopWIN, "Longitude: ");
	if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
	addstr($TopWIN, $KRlon);
	#
	move($TopWIN, 3,0);
	if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
	addstr( $TopWIN, "Height: ");
	if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
	addstr($TopWIN, $KRheight);
	clrtoeol($TopWIN);
	#
	move($TopWIN, 3, $s1);
	if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
	addstr( $TopWIN, "Velocity: ");
	if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
	addstr($TopWIN, $KRvel);
	#
	if( $ReceiverType eq 'M' ) {
		move($TopWIN, 4,0);
		if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
		addstr( $TopWIN, "Geometry:  ");
		if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
		addstr($TopWIN, $KRgeom);
		clrtoeol($TopWIN);
	} else {
		move($TopWIN, 4,0);
		if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
		addstr( $TopWIN, "SmartClock Mode: ");
		if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
		addstr($TopWIN, $KR_HPsmart);
		clrtoeol($TopWIN);
	}
	#
	if( $ReceiverType eq 'M' ) {
		move($TopWIN, 4, $s1);
		if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
		addstr($TopWIN, "SelfTest: ");
		if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
		addstr($TopWIN, $MotoSelfTest);
		#
		if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
		addstr( $TopWIN, " Antenna: ");
		if( $havecolor ) {
			if ($KRant == "OK") {
				attrset($TopWIN, COLOR_PAIR(1));
			} else {
				attrset($TopWIN, COLOR_PAIR(5));
			}
		}
		addstr($TopWIN, $KRant);
		if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
		addstr( $TopWIN, " Delay: ");
		if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
		addstr( $TopWIN, sprintf("%dns", $ReceiverCableDelay));
	} else {
		move($TopWIN, 4, $s1);
		if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
			addstr( $TopWIN, "Antenna: ");
		if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
			addstr($TopWIN, "${KRant}, ");
	}
	#
	if( $TopWIN_Height > 5 ) {
		if( $ReceiverType eq 'M' ) {
			#
			# Receiver is Motorola
			move($TopWIN, 5,0);
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
			addstr( $TopWIN, "Satellites: ");
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
			addstr($TopWIN, $KRsat);
			#
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
				addstr( $TopWIN, " Elev Mask: ");
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
			if( $ReceiverMaskAngle == -1 ) {
				addstr($TopWIN, "Default");
			} else {
				addstr($TopWIN, $ReceiverMaskAngle);
			}
			#
			clrtoeol($TopWIN);
			move ($TopWIN, 5, $s1);
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
			addstr( $TopWIN, "PPS: ");
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
			addstr($TopWIN, $KRpps_m);
			addstr($TopWIN, $KRpps_r);
			if( $leapString ) {
				if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
				addstr( $TopWIN, " Leap: ");
				if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
				addstr($TopWIN, $leapString);
			}
			clrtoeol($TopWIN);
			#
		} else {
			#
			# Receiver is HP
			move($TopWIN, 5,0);
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
			addstr( $TopWIN, "Satellites: ");
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
			addstr($TopWIN, $KRsat);
			#
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
				addstr( $TopWIN, " Elev Mask: ");
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
				addstr($TopWIN, $ReceiverMaskAngle);
			#
			clrtoeol($TopWIN);
			#
			move ($TopWIN, 5, $s1);
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
			addstr( $TopWIN, "PPS: ");
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
			addstr($TopWIN, $KRpps_m);
			clrtoeol($TopWIN);
		}
	}
	if( $TopWIN_Height > 6 ) {
		if( $ReceiverType eq 'M' ) {
			#
			# Receiver is Motorola
			move ($TopWIN, 6,0);
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
			addstr( $TopWIN, "Time RAIM: ");
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
			addstr($TopWIN, $KRtraim_e);
			clrtoeol($TopWIN);
			move ($TopWIN, 6, $s1);
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
			addstr( $TopWIN, "TRAIM Status: ");
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
			addstr($TopWIN, $KRtraim_s);
			clrtoeol($TopWIN);
		} else {
			#
			move ($TopWIN, 6,0);
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
			addstr( $TopWIN, "TFOM/FFOM: ");
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
			addstr($TopWIN, $KR_HPtfom);addch($TopWIN,'/');addstr($TopWIN, $KR_HPffom);
			clrtoeol($TopWIN);
			move ($TopWIN, 6, $s1);
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
			addstr( $TopWIN, "1PPS: ");
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
			addstr($TopWIN, $KR_HP1PPS);
			clrtoeol($TopWIN);
		}
	}
	#
	if( $TopWIN_Height > 7 ) {
		if( $ReceiverType eq 'H' ) {
			#
			move ($TopWIN, 7,0);
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
			addstr( $TopWIN, "Hold Threshold: ");
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
			addstr($TopWIN, $KR_hthr);
			clrtoeol($TopWIN);
			move ($TopWIN, 7, $s1);
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
			if( $COLS < 85 ) {
				addstr( $TopWIN, "Holdover Unc: ");
			} else {
				addstr( $TopWIN, "Holdover Uncertainty: ");
			}
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
			addstr($TopWIN, $KR_unce);
			clrtoeol($TopWIN);
		}
	}
	if( $TopWIN_Height > 8 ) {
		#
		move ($TopWIN, 8,0);
		if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
			addstr( $TopWIN, "Current EFC: ");
		if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
			addstr($TopWIN, $HPEFC);
		move ($TopWIN, 8, $s1);
		if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
			addstr( $TopWIN, "Lifetime: ");
		if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
			addstr($TopWIN, $HPLifetime);
		clrtoeol($TopWIN);
		#
	}
	if( $TopWIN_Height > 10 ) {
		if( $ReceiverType eq 'H' ) {
			#
			move ($TopWIN, 9,0);
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
				addstr( $TopWIN, "Synchronization: ");
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
				addstr($TopWIN, $KR_HPSync);
			move ($TopWIN, 9, $s1);
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(6)); }
				addstr( $TopWIN, "Acquisition: ");
			if( $havecolor ) { attrset($TopWIN, COLOR_PAIR(1)); }
				addstr($TopWIN, $KR_HPAcq);
			clrtoeol($TopWIN);
			#
			move($TopWIN, 10, 0);
			if( $havecolor ) {
				attrset($TopWIN, COLOR_PAIR(9));
			}
			attron($TopWIN, A_REVERSE);
			if( $KR_HPll1 !~ /^$/ ) {
				# 
				# Let's see if we can spread this out a bit
				$v1=" ";
				$v1p=" ";
				$v=" ";
				STSP: while(1) {
					if( length($v) > ($COLS-1) ) {
						$v1=$v1p;
						last STSP;
					}
					$v1p=$v1;
					$v1.=" ";
					$v="Self Test: ${KR_HPll1}${v1}Int Pwr: ${KR_HPll2}${v1}Oven Pwr: ${KR_HPll3}${v1}OCXO: ${KR_HPll4}${v1}EFC: ${KR_HPll5}${v1}GPS Rcv: ${KR_HPll6}";
				}
				$v="Self Test: ${KR_HPll1}${v1}Int Pwr: ${KR_HPll2}${v1}Oven Pwr: ${KR_HPll3}${v1}OCXO: ${KR_HPll4}${v1}EFC: ${KR_HPll5}${v1}GPS Rcv: ${KR_HPll6}";
				addstr($TopWIN, $v);
				hline($TopWIN, ' ', 6);
			} else {
				hline($TopWIN, ' ', $COLS);
			}
			attroff($TopWIN, A_REVERSE);
		}
	}
	#
	if( $ReceiverType eq 'M' ) {
		if( ! $DisplayDash ) {
			if( $TopWIN_Height > 7 ) {;
				move($TopWIN, $TopWIN_Height-1, 0);
				for( $i=0; $i < $COLS; $i++) {
					addstr( $TopWIN, "-");
				}
			}
		}
		#
		# Build The Sat Window
		$mwin=0;
		if( $LiveDataWIN_Exists ) {
			$satwinmax=$SatWIN_Height-3;
		} else {
			$satwinmax=$SatWIN_Height-2;
		}
		move($SatWIN, $mwin,0);
		if( $havecolor ) { attrset($SatWIN, COLOR_PAIR(6)); }
		attron($SatWIN, A_UNDERLINE);addstr($SatWIN, "Pos");attroff($SatWIN, A_UNDERLINE);
		move($SatWIN, $mwin,4);
		attron($SatWIN, A_UNDERLINE);addstr($SatWIN, "PRN");attroff($SatWIN, A_UNDERLINE);
		move($SatWIN, $mwin,8);
		attron($SatWIN, A_UNDERLINE);addstr($SatWIN, "CTM               ");attroff($SatWIN, A_UNDERLINE);
		move($SatWIN, $mwin,27);
		attron($SatWIN, A_UNDERLINE);addstr($SatWIN, "CSF   ");attroff($SatWIN, A_UNDERLINE);
		move($SatWIN, $mwin,34);
		attron($SatWIN, A_UNDERLINE);addstr($SatWIN, "Azim");attroff($SatWIN, A_UNDERLINE);
		move($SatWIN, $mwin,39);
		attron($SatWIN, A_UNDERLINE);addstr($SatWIN, "Elev");attroff($SatWIN, A_UNDERLINE);
		move($SatWIN, $mwin,44);
		attron($SatWIN, A_UNDERLINE);addstr($SatWIN, "Stat");attroff($SatWIN, A_UNDERLINE);
		move($SatWIN, $mwin,49);
		if( $NumChannels == 8 ) {
			attron($SatWIN, A_UNDERLINE);addstr($SatWIN, "C/No ");attroff($SatWIN, A_UNDERLINE);
		} else {
			attron($SatWIN, A_UNDERLINE);addstr($SatWIN, "SS   ");attroff($SatWIN, A_UNDERLINE);
		}
		move($SatWIN, $mwin,55);
		attron($SatWIN, A_UNDERLINE);addstr($SatWIN, "Signal Strength Meter");attroff($SatWIN, A_UNDERLINE);
		clrtoeol($SatWIN);
		if( $havecolor ) { attrset($SatWIN, COLOR_PAIR(7)); }
		$mwin++;
		#
		for( $l=0; $l < 12; $l++ ) { $KRvsatinfo{$l,5}=1; }		# Tag all sats as "no track
		$pos=0;
		OFLOOP:
		for( $i=0; $i < $NumChannels; $i++ ) {
			if( $KRsatinfo{$i,0} == 0 ) { next OFLOOP; }		# Skip if blank sat position
			move($SatWIN, $pos+$mwin, 0);
			clrtoeol($SatWIN);
			# Pos
			if( $havecolor ) { attrset($SatWIN, COLOR_PAIR(8)); }
			addstr($SatWIN, sprintf("%3d", $pos+1) );
			if( $havecolor ) { attrset($SatWIN, COLOR_PAIR(7)); }
			# PRN
			move($SatWIN, $pos+$mwin, 4);
			addstr($SatWIN, sprintf( " %02d", $KRsatinfo{$i,0}) );
			# CTM
			move($SatWIN, $pos+$mwin, 8);
			addstr($SatWIN, $KRsatinfo{$i,1});
			# CSF
			move($SatWIN, $pos+$mwin, 27);
			addstr($SatWIN, $KRsatinfo{$i,3});
			#
			# visible sat info
			#
			move($SatWIN, $pos+$mwin, 34);
			IFLOOP:
			for( $l=0; $l < 12; $l++ ) {
				if( $KRvsatinfo{$l,0} == $KRsatinfo{$i,0} )  {
					# Azim
					move($SatWIN, $pos+$mwin, 34);
					addstr($SatWIN, sprintf( "%4d", $KRvsatinfo{$l,3}) );
					# Elev
					move($SatWIN, $pos+$mwin, 39);
					addstr($SatWIN, sprintf( "%4d", $KRvsatinfo{$l,2}) );
					# Stat
					move($SatWIN, $pos+$mwin, 44);
					addstr($SatWIN, sprintf( "%4s", $KRvsatinfo{$l,4}) );
					$KRvsatinfo{$l,5}=0;			# Tag this sat as "tracked"
					last IFLOOP;
				}
			}
			#
			# Now, display the C/No and Signal strength meter
			#
			move($SatWIN, $pos+$mwin, 49);
			$j=$KRsatinfo{$i,2}/256;
			$k=($COLS-55)*$j;
			addstr($SatWIN, "[" . sprintf("%3d", $KRsatinfo{$i,2}) . "] " );
			#
			if( $havecolor ) { attrset($SatWIN, COLOR_PAIR(2)); }
			else { attron($SatWIN, A_REVERSE); }
			for( $l=0; $l < $k; $l++ ) {
				addch($SatWIN, '|');
			}
			if( $havecolor ) { attrset($SatWIN, COLOR_PAIR(7)); }
			else { attroff($SatWIN, A_REVERSE); }
			$pos++;
			last OFLOOP if ($pos > $satwinmax);	# Safety belt for too many sats.
		}
		#
		# Now, display the sats that are not tracked
		#
		if($pos < $satwinmax) {
			move($SatWIN, $pos+$mwin, 0);
			NFLOOP:
			for( $i=0; $i < 12; $i++ ) {
				if( $KRvsatinfo{$i,5} ) {
					next NFLOOP if( !$KRvsatinfo{$i,0} );	# Skip blank sat position
					move($SatWIN, $pos+$mwin, 0);
					clrtoeol($SatWIN);
					# Pos
					if( $havecolor ) { attrset($SatWIN, COLOR_PAIR(8)); }
					addstr($SatWIN, sprintf("%3d", $pos+1) );
					if( $havecolor ) { attrset($SatWIN, COLOR_PAIR(7)); }
					# PRN
					move($SatWIN, $pos+$mwin, 4);
					addstr($SatWIN, sprintf( " %02d", $KRvsatinfo{$i,0}) );
					# CTM
					move($SatWIN, $pos+$mwin, 8);
					addstr($SatWIN, sprintf( "%18s", "--- no channel --- ") );
					# Azim
					move($SatWIN, $pos+$mwin, 34);
					addstr($SatWIN, sprintf( "%4d", $KRvsatinfo{$i,3}) );
					# Elev
					move($SatWIN, $pos+$mwin, 39);
					addstr($SatWIN, sprintf( "%4d", $KRvsatinfo{$i,2}) );
					# Stat
					move($SatWIN, $pos+$mwin, 44);
					addstr($SatWIN, sprintf( "%4s", $KRvsatinfo{$i,4}) );
					$pos++;
					last NFLOOP if ($pos > $satwinmax);	# Safety belt if 13 sats.
				}
			}
			if( $havecolor ) { attrset($SatWIN, COLOR_PAIR(1)); }
			#
			# Just blank out the unused sat positions
			#
			if($pos < $satwinmax) {
				for( ;$pos < $satwinmax; $pos++ ) {
						move($SatWIN, $pos+$mwin, 0);
						clrtoeol($SatWIN);
				}
			}
		}
	} else {
		#
		# And now, build the HP receiver Sat Window
		$mwin=0;
		if( $LiveDataWIN_Exists ) {
			$satwinmax=$SatWIN_Height-3;
		} else {
			$satwinmax=$SatWIN_Height-2;
		}
		move($SatWIN, $mwin,0);
		if( $havecolor ) { attrset($SatWIN, COLOR_PAIR(6)); }
		attron($SatWIN, A_UNDERLINE);addstr($SatWIN, "Pos");attroff($SatWIN, A_UNDERLINE);
		move($SatWIN, $mwin,4);
		attron($SatWIN, A_UNDERLINE);addstr($SatWIN, "PRN");attroff($SatWIN, A_UNDERLINE);
		move($SatWIN, $mwin,8);
		attron($SatWIN, A_UNDERLINE);addstr($SatWIN, "Azim");attroff($SatWIN, A_UNDERLINE);
		move($SatWIN, $mwin,13);
		attron($SatWIN, A_UNDERLINE);addstr($SatWIN, "Elev");attroff($SatWIN, A_UNDERLINE);
		move($SatWIN, $mwin,18);
		attron($SatWIN, A_UNDERLINE);addstr($SatWIN, "Stat");attroff($SatWIN, A_UNDERLINE);
		move($SatWIN, $mwin,23);
		attron($SatWIN, A_UNDERLINE);addstr($SatWIN, "SigS");attroff($SatWIN, A_UNDERLINE);
		move($SatWIN, $mwin,28);
		attron($SatWIN, A_UNDERLINE);addstr($SatWIN, "Signal Strength Meter");attroff($SatWIN, A_UNDERLINE);
		clrtoeol($SatWIN);
		if( $havecolor ) { attrset($SatWIN, COLOR_PAIR(7)); }
		$mwin++;
		$pos=0;
		OFLOOPHP:
		for( $i=0; $i < $NumChannels; $i++ ) {
			move($SatWIN, $pos+$mwin, 0);
			clrtoeol($SatWIN);
			# Pos
			if( $havecolor ) { attrset($SatWIN, COLOR_PAIR(8)); }
			addstr($SatWIN, sprintf("%3d", $pos+1) );
			if( $havecolor ) { attrset($SatWIN, COLOR_PAIR(7)); }
			# PRN
			move($SatWIN, $pos+$mwin, 4);
			addstr($SatWIN, sprintf( " %2d", $KRsatinfo{$i,0}) );
			# Azim
			move($SatWIN, $pos+$mwin, 8);
			if( $KRsatinfo{$i,3} ) {
				addstr($SatWIN, sprintf( "%4d", $KRsatinfo{$i,2}) );
				# Elev
				move($SatWIN, $pos+$mwin, 13);
				addstr($SatWIN, sprintf( "%4d", $KRsatinfo{$i,1}) );
				# Stat
				move($SatWIN, $pos+$mwin, 18);
				addstr($SatWIN, "[OK]");
				# SS
				move($SatWIN, $pos+$mwin, 23);
				addstr($SatWIN, sprintf( "%4d", $KRsatinfo{$i,3}) );
				#
				# Now, display the Signal strength meter
				#
				move($SatWIN, $pos+$mwin, 28);
				$j=$KRsatinfo{$i,3}/256;
				$k=($COLS-31)*$j;
				$v1 = sprintf("[%3d] ", $k);
				addstr($SatWIN, $v1);
				#
				if( $havecolor ) { attrset($SatWIN, COLOR_PAIR(2)); }
				else { attron($SatWIN, A_REVERSE); }
				for( $l=0; $l < $k; $l++ ) {
					addch($SatWIN, '|');
				}
			} else {
				# Azim
				if( $KRsatinfo{$i,2} =~ /^\d*$/ ) {
					addstr($SatWIN, sprintf( "%4d", $KRsatinfo{$i,2}) );
					# Elev
					move($SatWIN, $pos+$mwin, 13);
				} else {
					addstr($SatWIN, sprintf( "%s", $KRsatinfo{$i,2}) );
				}
				if( $KRsatinfo{$i,1} =~ /^\d*$/ ) {
					addstr($SatWIN, sprintf( "%4d", $KRsatinfo{$i,1}) );
				} else {
					addstr($SatWIN, sprintf( "%s", $KRsatinfo{$i,1}) );
				}
			}
			if( $havecolor ) { attrset($SatWIN, COLOR_PAIR(7)); }
			else { attroff($SatWIN, A_REVERSE); }
			$pos++;
			last OFLOOPHP if (($pos+$mwin) > ($SatWIN_Height-2));	# Safety belt for too many sats.
		}
	}
	#
	if( ! $DisplayDash ) {
		if( $LiveDataWIN_Exists ) {
			move($SatWIN, $SatWIN_Height-1, 0);
			for( $i=0; $i < $COLS; $i++) {
				addstr( $SatWIN, "-");
			}
		}
	}
	refresh($TopWIN);
	refresh($SatWIN);
	&MoveCmdlineCursor;
	$DisplayDash=1;
}
#--------------------------------------------------------------------
# ---Section--- Build the command line window and perform command line movement processing
#
sub BuildCmdlineWin {
	my($i, $j, $k, $l);
	if( ! $CmdLineUpdate ) {
		return;
	}
	$CmdLineUpdate=0;
	#
	if( !$CmdLineHCursor ) {
		move($CmdWIN,0,0);
		if( $havecolor ) { attrset($CmdWIN, COLOR_PAIR(8)); }
		addch($CmdWIN, ">");
		$CmdLineHCursor=1;
		&MoveCmdlineCursor;
	}
	move($CmdWIN,0,$CmdLineHCursor);
	#
	if( $SystemLock ) {
		if( ($KbdChar eq '') || ($KbdChar == 0x0113) ) {		# F11
			$SystemLock=0;
			&PushOutputWinBuffer("System is unlocked");
		}
		return;
	}
	#
	KBDCHARTOP:
	#
	# Despite our best efforts, sometimes we still get this "-1"
	if( $KbdChar eq "-1" ) {
		return;
	}
	if( ($KbdChar eq KEY_UP) || ($KbdChar eq '') ) {
		$l=$CmdLineBufCursor;
		$CmdLineBufCursor--;
		if( $CmdLineBufCursor < 0 ) {
			$CmdLineBufCursor=$CmdLineBufMax;
		}
		if( $CmdLineBuffer[$CmdLineBufCursor] !~ /^\s*$/ ) {
			$CmdLineCurrent=$CmdLineBuffer[$CmdLineBufCursor];
			$CmdLineHCursor=length($CmdLineCurrent)+1;
			move($CmdWIN,0,1);
			addstr($CmdWIN, $CmdLineCurrent);
			move($CmdWIN,0,$CmdLineHCursor);
			clrtoeol($CmdWIN);
		} else {
			$CmdLineBufCursor=$l;
		}
	} elsif( ($KbdChar eq KEY_DOWN) || ($KbdChar eq '') ) {
		$l=$CmdLineBufCursor;
		$CmdLineBufCursor++;
		if( $CmdLineBufCursor > $CmdLineBufMax ) {
			$CmdLineBufCursor=0;
		}
		if( $CmdLineBuffer[$CmdLineBufCursor] !~ /^\s*$/ ) {
			$CmdLineCurrent=$CmdLineBuffer[$CmdLineBufCursor];
			$CmdLineHCursor=length($CmdLineCurrent)+1;
			move($CmdWIN,0,1);
			addstr($CmdWIN, $CmdLineCurrent);
			move($CmdWIN,0,$CmdLineHCursor);
			clrtoeol($CmdWIN);
		} else {
			$CmdLineBufCursor=$l;
		}
	} elsif( ($KbdChar eq KEY_PPAGE) || ($KbdChar eq '') ) {
		$ScrollWindowStart-=$SingleTopWIN_Height;
		&BuildScrollingInfoTopWin;
	} elsif( ($KbdChar eq KEY_NPAGE) || ($KbdChar eq '') ) {
		$ScrollWindowStart+=$SingleTopWIN_Height;
		&BuildScrollingInfoTopWin;
	} elsif( ($KbdChar eq KEY_LEFT) || ($KbdChar eq '') ) {
		if( $CmdLineHCursor > 1 ) {
			$CmdLineHCursor--;
		}
	} elsif( ($KbdChar eq KEY_RIGHT) || ($KbdChar eq '') ) {
		$i=length($CmdLineCurrent);
		if( $CmdLineHCursor < ($i+1) ) {
			$CmdLineHCursor++;
		}
	} elsif( ($KbdChar eq KEY_HOME) || ($KbdChar eq '') ) {
		$CmdLineHCursor=1;
		move($CmdWIN,0,1);
	} elsif( ($KbdChar eq KEY_END) || ($KbdChar eq '') ) {
		$i=length($CmdLineCurrent);
		$CmdLineHCursor=$i+1;
		if( $CmdLineHCursor > ($COLS-1) ) {
			$CmdLineHCursor=($COLS-1);
		}
	} elsif( ($KbdChar eq KEY_BACKSPACE) || ($KbdChar eq '') ) {
		$i=length($CmdLineCurrent);
		if( $i ) {
			if( ($i+1) > $CmdLineHCursor) {
				#
				# We must split the line
				$j=substr($CmdLineCurrent,0,$CmdLineHCursor-1);
				$k=substr($CmdLineCurrent,$CmdLineHCursor);
				$CmdLineCurrent=$j . $k;
				move($CmdWIN,0,$i);
				clrtoeol($CmdWIN);
				$CmdLineHCursor--;
				if( $CmdLineHCursor < 1 ) {
					$CmdLineHCursor=1;
				}
				move($CmdWIN,0,$CmdLineHCursor);
				addstr($CmdWIN, $k);
			} else {
				chop $CmdLineCurrent;
				move($CmdWIN,0,$i);
				clrtoeol($CmdWIN);
				$CmdLineHCursor--;
				move($CmdWIN,0,$CmdLineHCursor);
			}
		}
	} elsif( ($KbdChar eq KEY_ENTER) || ($KbdChar eq '
') ) {
		&ProcessEnterKey;
	} elsif( $KbdChar eq '' ) {						# Delete char in front of cursor
		$i=length($CmdLineCurrent);
		if( $i ) {
			if( ($i+1) > $CmdLineHCursor) {
				#
				# We must split the line
				$j=substr($CmdLineCurrent,0,($CmdLineHCursor-1));
				$k=substr($CmdLineCurrent,$CmdLineHCursor);
				$CmdLineCurrent=$j . $k;
				move($CmdWIN,0,$i);
				clrtoeol($CmdWIN);
				move($CmdWIN,0,$CmdLineHCursor);
				addstr($CmdWIN, $k);
			}
		}
	} elsif( $KbdChar eq '' ) {						# Clear to end of line
		$i=length($CmdLineCurrent);
		if( $i ) {
			if( ($i+1) > $CmdLineHCursor) {
				#
				# We must split the line
				$j=substr($CmdLineCurrent,0,($CmdLineHCursor-1));
				$CmdLineCurrent=$j;
				$i=length($CmdLineCurrent);
				move($CmdWIN,0,$i+1);
				clrtoeol($CmdWIN);
			}
		}
	} elsif( $KbdChar eq '' ) {						# Clear screen
		$OutputLineBuffer[0] ="";
		$OutputLineBufCursor=0;$OutputLineBufMax=128;
		&BuildOutputWin;
	} elsif( $KbdChar eq '' ) {						# Return to previous screen
		if( $ReturnTopWindow =~ /term/i ) {
			&TermWINInit;
		} else {
			&MainWINInit;
		}
	} elsif( $KbdChar eq '' ) {						# Clear Line
		$CmdLineHCursor=1;
		$CmdLineCurrent="";
		move($CmdWIN,0,1);
		clrtoeol($CmdWIN);
	} elsif( $KbdChar eq '' ) {						# Increase verbosity for the HP receiver
		$HPDisplayVerbose^=1;
		if( $HPDisplayVerbose ) {
			&PushOutputWinBuffer("Verbose mode enabled");
		} else {
			&PushOutputWinBuffer("Verbose mode disabled");
		}
	} elsif( $KbdChar eq '' ) {
		$SuspendOutput^=1;								# Toggle suspend
		if( $SuspendOutput ) {
			&PushOutputWinBuffer("Wait mode enabled. Receiver Read/Write poll disabled");
		} else {
			&PushOutputWinBuffer("Wait mode Disabled. Receiver Read/Write poll enabled");
		}
	} elsif( $KbdChar eq KEY_F(1) ) {
		&MainWINInit;
	} elsif( $KbdChar eq KEY_F(2) ) {
		#
	} elsif( $KbdChar eq KEY_F(3) ) {
		&MissedCmdWINInit;
	} elsif( $KbdChar eq KEY_F(4) ) {
		#
	} elsif( $KbdChar eq KEY_F(5) ) {
		#
	} elsif( $KbdChar eq KEY_F(6) ) {
		#
	} elsif( $KbdChar eq KEY_F(7) ) {
		&LogWINInit;
	} elsif( $KbdChar eq KEY_F(8) ) {
		&TermWINInit;
	} elsif( $KbdChar eq KEY_F(9) ) {
		&HelpWINInit;
	} elsif( $KbdChar eq KEY_F(10) ) {
		#
	} elsif( $KbdChar eq KEY_F(11) ) {
		$SystemLock=1;								# Toggle lock
		&PushOutputWinBuffer("System is locked. Ctrl-L/F11 to unlock.");
	} elsif( $KbdChar eq KEY_F(12) ) {
		$sigINT++;
	} else	{
		if( $KbdChar eq '' ) {
			#
			# What we do here is try to work with the PC keyboard
			# Anything that falls through is ignored. Sorry...
			$j=$KbdChar;
			#
			# First, read anything that is in the kbd queue
			while( ($KbdChar = getch()) ne ERR ) {
				$j.=$KbdChar;
			}
			#
			# Try to find a mapping
			if( $j eq '[11~' ) {
				$KbdChar=KEY_F(1);
				goto KBDCHARTOP;
			} elsif( $j eq '[12~' ) {
				$KbdChar=KEY_F(2);
				goto KBDCHARTOP;
			} elsif( $j eq '[13~' ) {
				$KbdChar=KEY_F(3);
				goto KBDCHARTOP;
			} elsif( $j eq '[14~' ) {
				$KbdChar=KEY_F(4);
				goto KBDCHARTOP;
			} elsif( $j eq '[1~' ) {		# Home
				$KbdChar=KEY_HOME;
				goto KBDCHARTOP;
			} elsif( $j eq '[4~' ) {		# End
				$KbdChar=KEY_END;
				goto KBDCHARTOP;
			} else {
				&Log("Got Unprocessed Keyboard Entry");
				$KbdChar="";
			}
		} else {
			if( $KbdChar < 0x00ff ) {
				#
				# Process the normal character input
				$i=length($CmdLineCurrent);
				if( ($i+1) != $CmdLineHCursor) {
					#
					# We must be in the middle of the line
					$j=substr($CmdLineCurrent,0,($CmdLineHCursor-1));
					$k=substr($CmdLineCurrent,($CmdLineHCursor-1));
					$CmdLineCurrent=$j . $KbdChar . $k;
					$i=length($CmdLineCurrent);
					if( length($CmdLineCurrent) > ($COLS-1) ) {
						chop $CmdLineCurrent;
						chop $k;
					}
					move($CmdWIN,0,$CmdLineHCursor);
					clrtoeol($CmdWIN);
					addch($CmdWIN, $KbdChar);
					addstr($CmdWIN, $k);
					$CmdLineHCursor++;
					if( $CmdLineHCursor > ($COLS-1) ) {
						$CmdLineHCursor = ($COLS-1);
					}
				} else {
					$CmdLineHCursor++;
					if( $CmdLineHCursor > ($COLS-1) ) {
						$CmdLineHCursor = ($COLS-1);
						chop $CmdLineCurrent;
					}
					$CmdLineCurrent .= $KbdChar;
					addch($CmdWIN, $KbdChar);
				}
			} else {
				#
				# Curses will also give us some extended F key codes
				# and we need to process them as well.
				if( $KbdChar == 0x010d ) {
					#
					#F5
					$KbdChar=KEY_F(5);
					goto KBDCHARTOP;
				} elsif( $KbdChar == 0x010e ) {
					#
					#F6
					$KbdChar=KEY_F(6);
					goto KBDCHARTOP;
				} elsif( $KbdChar == 0x010f ) {
					#
					#F7
					$KbdChar=KEY_F(7);
					goto KBDCHARTOP;
				} elsif( $KbdChar == 0x0110 ) {
					#
					#F8 - Terminal mode
					$KbdChar=KEY_F(8);
					goto KBDCHARTOP;
				} elsif( $KbdChar == 0x0111 ) {
					#
					#F9 - Help
					$KbdChar=KEY_F(9);
					goto KBDCHARTOP;
				} elsif( $KbdChar == 0x0112 ) {
					#
					$KbdChar=KEY_F(10);
					goto KBDCHARTOP;
				} elsif( $KbdChar == 0x0113 ) {
					#
					# F11 - Lock
					$KbdChar=KEY_F(11);
					goto KBDCHARTOP;
				} elsif( $KbdChar == 0x0114 ) {
					#
					# F12 - Quit
					$KbdChar=KEY_F(12);
					goto KBDCHARTOP;
				} else {
					&Log("key hit: " . sprintf("0x%04x\r\n", ${KbdChar}));
				}
			}
		}
	}
	&MoveCmdlineCursor;
}
#
# Move the cursor on the command line window to the appropriate place
#
sub MoveCmdlineCursor {
	move($CmdWIN,0,$CmdLineHCursor);
	refresh($CmdWIN);
}
#--------------------------------------------------------------------
# ---Section--- Window init/create/destroy functions
#
# Delete the top three windows and enable the single top window
sub SingleTopWINInit{
	my($ls, $i);
	return if !$CursesInitFlag;			# Wait for curses initialization
	$ls=$_[0];							# Get the title for $TitleWIN
	if( ! $SingleTopWIN_Enable ) {
		#
		if( $CursesWindowInit ) {		# This is here to properly initialize the system
			delwin($LiveDataWIN);
			delwin($SatWIN);
			delwin($TopWIN);
		}
		#
		# The title window is initialized here. It is only alive when 
		# $SingleTopWIN is active.
		$TitleWIN = newwin(1, $COLS, 0, 0);
		$ScrollInfoWIN = newwin(1, $COLS, $ScrollInfoWIN_RowStart, 0);
		$SingleTopWIN = newwin($SingleTopWIN_Height, $COLS, 1, 0);
		scrollok($SingleTopWIN,1);
	}
	#
	$SingleTopWIN_Enable=1;
	#
	# Now, set the title
	move($TitleWIN,0,0);
	if( $havecolor ) { attrset($TitleWIN, COLOR_PAIR(9)); }
	attron($TitleWIN, A_REVERSE);
	#
	addstr($TitleWIN, $ls);
	for( $i=length($ls); $i < $COLS; $i++) {
		addch($TitleWIN, ' ');
	}
	attroff($TitleWIN, A_REVERSE);
	refresh($TitleWIN);
	move($SingleTopWIN,0,0);
	erase($SingleTopWIN);
	if( $havecolor ) { attrset($SingleTopWIN, COLOR_PAIR(6)); }
	else { attron($SingleTopWIN, A_NORMAL); }
}
#
# Delete the single top window and enable the three top ones
sub SingleTopWINDel {
	if( $SingleTopWIN_Enable ) {
		delwin($SingleTopWIN);
		# The title window is killed here
		delwin($TitleWIN);
		delwin($ScrollInfoWIN);
		$LiveDataWIN = newwin($LiveDataWIN_Height, $COLS, $LiveDataWIN_RowStart, 0);
		scrollok($LiveDataWIN,1);
		$SatWIN = newwin($SatWIN_Height, $COLS, $SatWin_RowStart, 0);
		$TopWIN = newwin($TopWIN_Height, $COLS, 0, 0);
		$SingleTopWIN_Enable=0;
	}
}
#
# Short routines to build some windows from scratch
sub MainWINInit {
	if( $CurrentTopWindow !~ /w1/ ) {
		$CurrentTopWindow="w1";
		&SingleTopWINDel;
		$SuspendOutput=0;							# No suspend
		$TerminalMode=0;
		$UpdateOutput++;							# Force update
		$DisplayDash=0;								# Redraw the dashes
		$ScrollEnabled=0;
		$ReturnTopWindow="main";
		&PushOutputWinBuffer("You are in the main screen. Enter \"help\" for info.");			# Send this out first
	}
	&BuildMainScreen;
}
#
#
sub TermWINInit {
	if( !$TerminalMode ) {
		$CurrentTopWindow="Term";
		$TerminalMode=1;																	# Switch to terminal mode
		$ScrollEnabled=0;
		$ReturnTopWindow = "main";
		&BuildTerminalWin("---init---");
		&PushOutputWinBuffer("Terminal Mode Started. All keyboard input will be sent to the receiver.");	# Send this out first
		&PushOutputWinBuffer("Program commands must be prefixed with a \"!\"");				# Send this out first
		&PushOutputWinBuffer("Ctrl-T returns to the main screen. \"!help\" for info.");		# Send this out first
	} else {
		&PushOutputWinBuffer("You are already in terminal mode");							# Send this out first
	}
}
#
#
sub HelpWINInit {
	if( $CurrentTopWindow !~ /Help/ ) {
		$CurrentTopWindow="Help";
		$TerminalMode=0;									# Take ownership
		$ScrollEnabled=1;
		&PushOutputWinBuffer("To return hit Ctrl-T");		# Send this out first
	}
	&BuildHelpWin;
}
#
#
sub LogWINInit {
	if( $CurrentTopWindow !~ /Log/ ) {
		$CurrentTopWindow="Log";
		$TerminalMode=0;									# Take ownership
		&PushOutputWinBuffer("To return hit Ctrl-T  --  F7 to refresh");		# Send this out first
		$ScrollEnabled=1;
	}
	&BuildLogWin;
}
#
#
sub MissedCmdWINInit {
	if( $CurrentTopWindow !~ /w3/ ) {
		$CurrentTopWindow="w3";
		$TerminalMode=0;									# Take ownership
		$ScrollEnabled=1;
		&PushOutputWinBuffer("To return hit Ctrl-T  --  F3 to refresh");		# Send this out first
	}
	&BuildUnprocessedCommandsWin;
}
#--------------------------------------------------------------------
# ---Section--- All other window builds
#
# Build the live data window
sub BuildLiveDataWin {
	my($j);
	#
	return if !$CursesInitFlag;				# Wait for curses initialization
	return if !$LiveDataWIN_Exists;
	return if $SingleTopWIN_Enable;
	return if $SuspendOutput;				# No need to update if no data
	#
	if( $havecolor ) { attrset($LiveDataWIN, COLOR_PAIR(3)); }
	$j = $LiveDataBufCursor-1;
	if( $j < 0 ) {
		$j=$LiveDataBufMax;
	}
	#
	# We put the "\n" in front so we can scroll before updating
	addstr($LiveDataWIN, "\n" . substr($LiveDataBuffer[$j],0,$COLS-1));
	refresh($LiveDataWIN);
}
#
#
# Build the output window
#
sub BuildOutputWin {
	return if !$CursesInitFlag;				# Wait for curses initialization
	addstr($OutputWIN, "\n" . $OutputLineBuffer[$OutputLineBufCursor]);
	refresh($OutputWIN);
	&MoveCmdlineCursor;
}
#
# Update the status, Version Info windows
#
sub BuildStatusVersionWIN {
	my($i, $ls);
	#
	return if !$CursesInitFlag;			# Wait for curses initialization
	if( $InputMethod ) {
		$ls= $MYNAME . " " . $MYVERSION . "- Connect: " . $SerialPort . ", BAUD: ". $BAUD;
	} else {
		$ls= $MYNAME . " " . $MYVERSION . "- Connect: " . $RemoteSystem . "[${RemotePort}], Reset: ${SystemRestartCount}";
	}
	$ls.= ", Size: " . $LINES . "x" . $COLS;
	if( $havecolor ) { $ls.=", C"; }
	else { $ls.=", M"; }
	$ls.=" ";
	move($StatusWIN, 0, 0);
	if( $havecolor ) { attrset($StatusWIN, COLOR_PAIR(4)); }
	attron($StatusWIN, A_REVERSE);
	addstr($StatusWIN, $ls);
	for( $i=length($ls); $i < $COLS; $i++) {
		addch( $StatusWIN, ' ');
	}
	attroff($StatusWIN, A_REVERSE);
	refresh($StatusWIN);
	#
	#
	# Now, update the version Window
	move($VersionWIN, 0, 0);
	if( $havecolor ) { attrset($VersionWIN, COLOR_PAIR(4)); }
	attron($VersionWIN, A_REVERSE);
	#
	if( $KRsfwVer !~ /^$/ ) {
		($ls = $KRsfwDate) =~ s/\s\s*//g;
		if( $ReceiverType eq 'M' ) {
			if( $COLS < 100) {
				$ls="GPS Receiver V${KRsfwVer}.${KRsfwRev}, Date: $ls, Model: $KRsfwModel/\[${ReceiverModel}\], Serial: $KRserNum";
			} else {
				$ls="GPS Receiver Version: $KRsfwVer, Revision: $KRsfwRev, Software Date: $ls, Model: $KRsfwModel/\[${ReceiverModel}\], Serial: $KRserNum";
			}
		} else {
			if( $COLS < 91) {
				$ls="HP:${HPModelNumber}, GPS:V${KRsfwVer}.${KRsfwRev}, Date: $ls, Model: $KRsfwModel/\[${ReceiverModel}\]+";
			} else {
				$ls="HP:${HPModelNumber}, GPS Receiver: V${KRsfwVer}.${KRsfwRev}, Date: $ls, Model: $KRsfwModel/\[${ReceiverModel}\], Serial: $KRserNum";
			}
		}
		addstr($VersionWIN, substr($ls,0,($COLS-1)));
		for( $i=length($ls); $i < $COLS; $i++) {
			addch( $VersionWIN, ' ');
		}
	} else {
		for( $i=0; $i < $COLS; $i++) {
			addch( $VersionWIN, ' ');
		}
	}
	attroff($VersionWIN, A_REVERSE);
	refresh($VersionWIN);
}
#
# Display and update the Terminal window
sub BuildTerminalWin {
	my($i, $j, $ls);
	#
	return if !$CursesInitFlag;				# Wait for curses initialization
	return if !$TerminalMode;				# Just making sure
	return if $CurrentTopWindow !~ /Term/i;	# Just making sure
	$ls=$_[0];
	if( $ls =~ /^---init---$/i ) {
		&SingleTopWINInit("Terminal Window");
		&BuildScrollInfoStatusWin;
	}
	$i=$TerminalModeBufCursor-1;
	if($i < 0) {
		$i=$TerminalModeBufMax;
	}
	$ls="\n" . substr($TerminalModeBuffer[$i],0,$COLS-1);
	addstr($SingleTopWIN, $ls);
	refresh($SingleTopWIN);
	&MoveCmdlineCursor;
}
#
# Display the unprocessed commands window
sub BuildUnprocessedCommandsWin {
	&SingleTopWINInit("Unprocessed GPS Receiver Commands Window");
	@ScrollWindowArray = @UnprocessedCmdBuffer;
	$ScrollWindowMax=$UnprocessedBufMax;
	$ScrollWindowStart = 0;
	$ScrollEnabled=1;
	&BuildScrollingInfoTopWin;
	&BuildScrollInfoStatusWin;
}
#
# Display the help window
sub BuildHelpWin {
	#
	&SingleTopWINInit("Help Window");
	@ScrollWindowArray = @HelpBuffer;
	$ScrollWindowMax=$HelpBufMax;
	$ScrollWindowStart = 0;
	$ScrollEnabled=1;
	&BuildScrollingInfoTopWin;
	&BuildScrollInfoStatusWin;
}
#
# Display the Log window
sub BuildLogWin {
	#
	&SingleTopWINInit("System Log Window");
	@ScrollWindowArray = @SysLog;
	$ScrollWindowMax=$SysLogCursor;
	if( $SysLogCursor < $SingleTopWIN_Height ) {
		$ScrollWindowStart = 0;
	} else {
		$ScrollWindowStart = $ScrollWindowMax+1;
	}
	$ScrollEnabled=1;
	&BuildScrollingInfoTopWin;
	&BuildScrollInfoStatusWin;
}
#
# BuildScrollingInfoTopWin
sub BuildScrollingInfoTopWin {
	my($i, $j, $k, $ls);
	#
	# The top window must be scrollable. This is a logical scroll,
	# in other words, there is a buffer somewhere that contains
	# more data than can fit in a single window so it needs to be paged
	# via the PgUp and PgDn keys.
	return if !$ScrollEnabled;
	return if !$SingleTopWIN_Enable;
	return if !$CursesInitFlag;				# Wait for curses initialization
	#
	# Now check for boundary conditions
	if( $ScrollWindowStart < 0 ) {
		$ScrollWindowStart=0;
	}
	if( $ScrollWindowStart >= $ScrollWindowMax ) {
		$ScrollWindowStart=$ScrollWindowMax-$SingleTopWIN_Height;
	}
	#
	$k=$SingleTopWIN_Height;
	$i=$ScrollWindowStart;
	$j=0;
	SCWHILE: while( $i < $ScrollWindowMax ) {
		$ls=substr($ScrollWindowArray[$i],0,$COLS-1);
		addstr($SingleTopWIN, "\n" . $ls);
		$j++;
		if( $j > ($k-1) ){
			last SCWHILE;
		}
		$i++;
	}
	refresh($SingleTopWIN);
}
#
# Build the Scroll Info window
sub BuildScrollInfoStatusWin {
	return if !$CursesInitFlag;				# Wait for curses initialization
	move($ScrollInfoWIN, 0, 0);
	if( $havecolor ) { attrset($ScrollInfoWIN, COLOR_PAIR(4)); }
	else { attron($ScrollInfoWIN, A_BOLD); }
	addstr($ScrollInfoWIN, "--- PgUp/Ctrl-E - Page Back, PgDn/Ctrl-Y - Page Forward ---");
	refresh($ScrollInfoWIN);
}
#--------------------------------------------------------------------
# ---Section--- Push functions for screen updates
#
# Push a line to the Live Data window buffer, then update
sub PushLiveDataWinBuffer {
	my($line) = @_;
	#
	$LiveDataBuffer[$LiveDataBufCursor]=$line;
	$LiveDataBufCursor++;
	if( $LiveDataBufCursor > $LiveDataBufMax ) {
		$LiveDataBufCursor=0;
	}
	&BuildLiveDataWin;
}
#
# Push a line to the terminal window buffer but don't update
sub PushTermWinBufferNoUpdate {
	my($line) = @_;
	$TerminalModeBuffer[$TerminalModeBufCursor]=$line;
	$TerminalModeBufCursor++;
	if( $TerminalModeBufCursor > $TerminalModeBufMax ) {
		$TerminalModeBufCursor=0;
	}
}
#
# Push a line to the terminal window buffer and update
sub PushTermWinBuffer {
	my($line) = @_;
	&PushTermWinBufferNoUpdate($line);
	&BuildTerminalWin;
}
#
# Push a line to the output window buffer and update
sub PushOutputWinBuffer {
	my($line) = @_;
	&PushOutputWinBufferNoUpdate($line);
	&BuildOutputWin;
}
#
# Push a line to the output window buffer don't update window
sub PushOutputWinBufferNoUpdate {
	my($line) = @_;
	#
	# Now, let's generate the output window
	$OutputLineBufCursor++;
	if( $OutputLineBufCursor > $OutputLineBufMax ) {
		$OutputLineBufCursor=0;
	}
	$OutputLineBuffer[$OutputLineBufCursor]=$line;
}
#--------------------------------------------------------------------
# ---Section--- Curses sub-system init
#
# If requested, initialize the curses sub-system
#
sub CursesInitData {
	my($s);
	if( $FullScreenEnable ) {
		$OutputLineBufCursor=1;$OutputLineBufMax=128;
		$CmdLineHCursor=0;$CmdLineUpdate=1;
		$CmdLineBufCursor=0;$CmdLineBufCursorLive=0;$CmdLineBufMax=128;
		$CmdLineBuffer[0]="";
		#
		&StartCurses;&BuildCursesWindows;
		&BuildStatusVersionWIN;
		$s="Welcome to gpsdisplay. For info enter: ";
		if( $TerminalMode ) {
			$CurrentTopWindow="Term";
			$ReturnTopWindow="Term";
			$s .= "\"!help\"";
		} else {
			$CurrentTopWindow="Main";
			$ReturnTopWindow="Main";
			$s .= "\"help\"";
		}
		$s .= " or hit F9";
		&PushOutputWinBuffer($s);
		#
		# Now, check if we want to go to terminal mode
		&BuildTerminalWin("---init---");
		&BuildLiveDataWin;
		&Log("Display geometry is: ${LINES}x${COLS}");
	}
}
#
# Check curses and build windows
#
sub BuildCursesWindows {
	my($i, $j, $ef, $s);
	return if !$FullScreenEnable;			# There is no point going on
	#
	if( $COLS < $MinimumScreenWidth ) {
		&StopCurses;
		print STDERR "Hey! You need at least: ${MinimumScreenWidth} columns in your display. Now you have: ${COLS}\n";
		if( $InputMethod ) {
			&SerialStop;
		}
		exit;
	}
	#
	# We try to fit even a minimal window
	#
	# 4/2006: We added a command line and made everything a seperate window
	#
	# We will provide seperate screens which will have all the data
	# and can be accessed via Ctrl-Key combinations.
	#
	# We always have $TopWIN, $SatWIN, $CmdWin, $OutputWIN and $VersionWin.
	# In alternate screens (like help etc.) we just add $SingleTopWIN
	#
	# The main screen is adjusted as follows:
	# First we create a screen with the bare minimun window sizes.
	# If we have space, we increase the SatWIN and LiveDataWIN sizes.
	#
	# If there is no screen space for the minimum information, then:
	# The Sat display window could shrink to only show partial sats and
	# the Live Data display window could shrink to nothing.
	#
	if( $ReceiverType eq 'H' ) {
		$TopWIN_Height=11;										# Start from here
		$TopWIN_HeightNeeded=11;								# Best bet top window height
		$SatWIN_HeightNeeded=12;								# Best bet sat window height
		$i=$TopWIN_Height;										# We Don't need a dash line here
	} else {
		$TopWIN_Height=8;										# Start from here
		$TopWIN_HeightNeeded=8;									# Best bet top window height
		$SatWIN_HeightNeeded=16;								# Best bet sat window height
		$i=$TopWIN_Height + 1;									# We try have at least 1 dash line
	}
	#
	# Does the user want a short version of output window and live data window?
	if( $OutputWinLimit ) {
		$OutputWIN_Height=1;									# How high is the Output window -- if limited
		# Let's assume that we have at least 1 line of live data
		$LiveDataWIN_Height=1;$LiveDataWIN_Exists=1;			#
		$LiveDataWIN_MaxHeight=1;								# Maximum Hight is the LiveData Window
	} else {
		$OutputWIN_Height=5;									# How high is the Output window
		# Let's assume that we have at least 1 line of live data
		$LiveDataWIN_Height=1;$LiveDataWIN_Exists=1;			#
		$LiveDataWIN_MaxHeight=4;								# Maximum Hight is the LiveData Window
	}
	#
	# So far 34 Lines - (as of 4/21/2006)
	#
	$i+=$LiveDataWIN_Height;
	$i+=$SatWIN_HeightNeeded + $OutputWIN_Height + 3;			# The "3" is for the command line, version info and status
	$SatWIN_Height=$SatWIN_HeightNeeded;
	#
	# Do we have enough?
	$ef=0;														# Let's assume that we have enough
	if( $i > $LINES ) {
		$ef=$i;													# And break that assumption
		#
		# Let's start taking away features
		$i--;$TopWIN_Height--;									# We sacrifice the extra space first
		if( $i > $LINES ) {
			if( $ReceiverType eq 'H' ) {
				$i-=3;$TopWIN_Height-=3;						# Then the extra stats go
			} else {
				$i-=2;$TopWIN_Height-=2;						# Then the extra stats go
			}
			if( $i < $LINES ) {
				if( ! $OutputWinLimit ) {
					$LiveDataWIN_Height++;
				}
			} else {
				if( $i > $LINES ) {
					#
					# Then, we try to shrink the Sats Window down to 6 lines
					LTFOR: for( $j=$SatWIN_Height; $j > 6; $j--) {
								$i--;
								last LTFOR if($i <= $LINES);
							}
					$SatWIN_Height=$j;
					if( $i > $LINES ) {
						#
						# Then, we try to delete the Live Data window
						$i--;$LiveDataWIN_Height=0; $LiveDataWIN_Exists=0;
						if( $i > $LINES ) {
							#
							# Oh-Oh! Not enough space. Bye!!!
							&StopCurses;
							$MinimumScreenHeight = ($i - $LINES) + $LINES;
							print STDERR "Hey! You need at least: ${MinimumScreenHeight} lines in your display. Now you have: ${LINES}\n";
							if( $InputMethod ) {
								&SerialStop;
							}
							exit;
						}
					}
				}
			}
		}
	} else {
		STFOR: for($j=$i; $j <= $LINES; $j++) {
			$SatWIN_Height++;
			if( $SatWIN_Height > $SatWIN_HeightNeeded ) {
				$SatWIN_Height--;
				last STFOR;
			}
		}
		if( $j <= $LINES ) {
			if( !$OutputWinLimit ) {
				$LiveDataWIN_Height+=($LINES-$j);			# I guess the live data window is lucky tonight!
				if( $LiveDataWIN_Height > $LiveDataWIN_MaxHeight ) {
					$LiveDataWIN_Height=$LiveDataWIN_MaxHeight;
					$OutputWIN_Height+=($LINES-$j-$LiveDataWIN_Height);
				}
			}
		}
	}
	#
	# Ok, let's build the windows starting from the bottom
	# newwin(int nlines, int ncols, int begin_y, int begin_x);
	$i=$LINES-1;
	&Log("StatusWIN: y: $i, Size: 1");
	$StatusWIN = newwin(1, $COLS, $i, 0);
	#
	$i--;
	&Log("CmdWIN: y: $i, Size: 1");
	$CmdWIN = newwin(1, $COLS, $i, 0);
	#
	$i-=$OutputWIN_Height;
	&Log("OutputWIN: y: $i, Size: ${OutputWIN_Height}");
	$OutputWIN = newwin($OutputWIN_Height, $COLS, $i, 0);
	scrollok($OutputWIN,1);
	#
	$i--;
	&Log("VersionWin: y: $i, Size: 1");
	$VersionWIN = newwin(1, $COLS, $i, 0);
	#
	# The scroll window always sits on top of the version window
	$ScrollInfoWIN_RowStart=$i-1;
	#
	if( $LiveDataWIN_Exists ) {
		$i-=$LiveDataWIN_Height;
		&Log("LiveDataWIN: y: $i, Size: ${LiveDataWIN_Height}");
		if( !$TerminalMode ) {
			$LiveDataWIN = newwin($LiveDataWIN_Height, $COLS, $i, 0);
			scrollok($LiveDataWIN,1);
		}
		$LiveDataWIN_RowStart=$i;
	}
	#
	# If there is anything extra, we give it to the sats display.
	$i-=$SatWIN_Height;
	while( ($i - $TopWIN_Height) > 0 ) {
	   $SatWIN_Height++;
	   $i--;
	}
	&Log("SatWIN_Height: y: $i, Size: ${SatWIN_Height}");
	if( !$TerminalMode ) {
		$SatWIN = newwin($SatWIN_Height, $COLS, $i, 0);
	}
	$SatWin_RowStart=$i;
	#
	$i-=$TopWIN_Height;
	if($i < 0) {
		$i*=-1;
		$TopWIN_Height-=$i;
		$i=0;
	}
	&Log("TopWIN_Height: y: $i, Size: ${TopWIN_Height}");
	if( !$TerminalMode ) {
		$TopWIN = newwin($TopWIN_Height, $COLS, $i, 0);
	}
	#
	$SingleTopWIN_Height=$LiveDataWIN_Height + $SatWIN_Height + $TopWIN_Height-2;	# +Title +Info
	if( $TerminalMode ) {
		&SingleTopWINInit("Terminal Window");
	}
	$CursesWindowInit=1;		# This is only set once per program invocation
	clear($OutputWIN);			# This should force a clear and reset the background for all wins
	#
	# Generate a warning
	if( $ef ) {
		$s="Optimun screen size of ${ef} rows has not been reached.";
		&PushOutputWinBuffer($s);&Log($s);
		$s="Some receiver information will not be displayed.";
		&PushOutputWinBuffer($s);&Log($s);
		$s="Please adjust your screen size and try again.";
		&PushOutputWinBuffer($s);&Log($s);
		$s=" ";
		&PushOutputWinBuffer($s);&Log($s);
	}
	move($CmdWIN, 0, 0);
}
#
# Start curses
#
sub StartCurses {
	#
	initscr();
	raw();
	nonl();
	cbreak();
	noecho();
	timeout(0);
	clear();
	refresh();
	keypad(1);
	nodelay(1);
	getch();								# Weird behavior if this is not done here
	$KbdChar="";
	$CursesInitFlag=1;						# I guess we are ready for data
	$havecolor = eval { has_colors() };
	if($havecolor) {
		start_color();
		# Default color
		init_pair(1, COLOR_YELLOW, COLOR_BLACK);
		# Strength meter
		init_pair(2, COLOR_WHITE, COLOR_RED);
		# Data window color
		init_pair(3, COLOR_GREEN, COLOR_BLACK);
		# Status bar
		init_pair(4, COLOR_WHITE, COLOR_BLUE);
		# Red on black
		init_pair(5, COLOR_RED, COLOR_BLACK);
		# GREEN on black - Header color
		init_pair(6, COLOR_GREEN, COLOR_BLACK);
		# Cyan on black - Satellite info
		init_pair(7, COLOR_CYAN, COLOR_BLACK);
		# Sat number on far left
		init_pair(8, COLOR_WHITE, COLOR_BLACK);
		# Status bar of HP receiver
		init_pair(9, COLOR_BLUE, COLOR_WHITE);
	}
	# Avoid excessive screen output
	$DisplayDash=0;
}

#
# stop curses
#
sub StopCurses {
	return if( !$CursesInitFlag );
	standend();
	clear();
	refresh();
	endwin();
}
#
# Window change size signal handler
#
sub procSigWINCH {
	$GotSignal_WINCH=1;
}
#--------------------------------------------------------------------
# ---Section--- Process the user commands
#
# Process the enter key. This is also the entry point for automatic
# command processing (that requires retreive buffer history).
# When we get here, we assume that the user command is in $CmdLineCurrent
sub ProcessEnterKey {
	my($k);
	($k = $CmdLineCurrent) =~ s/^\s*(.*)\s*$/$1/;	# Delete any additional space
	$CmdLineCurrent=$k;
	$CmdLineHCursor=1;
	#
	# Let's be careful about full screen output
	if( $FullScreenEnable ) {
		move($CmdWIN,0,1);
		clrtoeol($CmdWIN);
	}
	#
	# Process blank line or comment differently
	if( $CmdLineCurrent !~ /^\s*$/ ) {
		#
		# Don't add the command string to the command buffer if we are reading commands in
		if( !$AutoProcessFlag ) {
			$CmdLineBuffer[$CmdLineBufCursorLive]=$CmdLineCurrent;
			#
			# Now, let's add it to the buffer
			$CmdLineBufCursorLive++;
			if( $CmdLineBufCursorLive > $CmdLineBufMax ) {
				$CmdLineBufCursorLive=0;
			}
			$CmdLineBufCursor=$CmdLineBufCursorLive;
		}
	}
	#
	# Process that command
	&ProcessUserCommand;
	$CmdLineCurrent="";
}
#
# User command processor
#
# Here we determine how the Enter key will be processed.
#
# In TerminalMode the command is sent straight through, to the serial port,
# unless it is prefixed by: ! in which case we process it as
# a local command.
#
# In Non-TerminalMode, we do the opposite. We only send
# the command to the serial device if it is prefixed by a !
#
sub ProcessUserCommand {
	my($ll);
	# When we get here, we assume that the user command is in $CmdLineCurrent
	&Vprint(1,$CmdLineCurrent);							# Add to the log
	#
	&PushOutputWinBuffer($CmdLineCurrent);				# Send this out first
	return if $CmdLineCurrent =~ /^#/;					# Ignore comments
	if( $TerminalMode ) {
		$ll = substr($CmdLineCurrent,0,1);
		if( $ll =~ /^!/ ) {
			$CmdLineCurrent=substr($CmdLineCurrent,1);	# Strip the first !
			&ProcessCommandMatrix;						# We can still send commands locally
		} else {
			&ProcessCommandRcvr($CmdLineCurrent);
		}
	} else {
		&ProcessCommandMatrix;							# Process the command normally
	}
}
#
# This sub-routine holds the user command matrix
sub ProcessCommandMatrix {
	my($lcmd, $lparm, $llog);
	# When we get here, we assume that the user command is in $CmdLineCurrent
	$lcmd = $CmdLineCurrent;							# Make a local copy
	$lcmd =~ s/^\s*//;
	undef $llog;
	#
	# Process internal command
	if( $lcmd =~ /^help/i ) {							# Help display
		&HelpWINInit;
	} elsif($lcmd =~ /^blink/i ) {						# Blink LED command
		if( $ReceiverType eq 'H' ) {
			($lcmd,$lparm) = split(/ /, $CmdLineCurrent);
			if( $lparm =~ /^\s*A/i ) {					# Active or
				$lcmd="LED \"Active\" will now blink";
				$LEDActive=1;
			} elsif( $lparm =~ /^\s*E/i ) {				# Enabled
				$lcmd="LED \"Enabled\" will now blink";
				$LEDEnabled=1;
			} else {
				$lcmd="Invalid blink LED parameter: ${lparm}";
			}
		} else {
			$lcmd="Blink command is only valid for HP receivers";
		}
		$llog=$lcmd;
	} elsif( $lcmd =~ /^w1/i ) {						# Default display
		&MainWINInit;
	} elsif($lcmd =~ /^w3/i ) {							# Show missed commands
		&MissedCmdWINInit;
	} elsif($lcmd =~ /^term/i ) {						# Start the terminal mode window
		&TermWINInit;
	} elsif($lcmd =~ /^sleep/i ) {						# Sleep command
		($lcmd,$lparm) = split(/ /, $CmdLineCurrent);
		if( $lparm !~ /^\s*[0-9]*\s*$/ ) {
			$lcmd="Invalid sleep parameter: ${lparm}";
		} else {
			$SystemSleep=$lparm;						# Sleep in tenths of seconds.
			$lparm*=0.01;								# Sleep in tenths of seconds.
			$lcmd=sprintf("System will sleep for: %8.3F seconds", $lparm);
		}
		$llog=$lcmd;
	} elsif($lcmd =~ /^read/i ) {						# Read a command file
		($lcmd,$lparm) = split(/ /, $CmdLineCurrent);
		&FillAutoBuffer($lparm);
	} elsif($lcmd =~ /^log/i ) {						# Start the log window
		&LogWINInit;
	} elsif($lcmd =~ /^lock/i ) {						# Enable lock mode
		&PushOutputWinBuffer("System is locked. Ctrl-L/F12 to unlock.");
		$SystemLock=1;
	} elsif($lcmd =~ /^unlock/i ) {						# Enable lock mode
		&PushOutputWinBuffer("System is unlocked.");
		$SystemLock=0;
	} elsif( $lcmd =~ /^hpv/i ) {						# Increase verbosity for the HP receivers
		$HPDisplayVerbose^=1;
	} elsif( ($lcmd =~ /^exit/i) || ($lcmd =~ /quit/i) ) {				# Bye!
		#
		$sigINT++;										# Set the quit flag
	} elsif( $lcmd =~ /^!/ ) {							# Send the command to the serial port
		&ProcessCommandRcvr(substr($lcmd,1));			# Send it to the receiver
	} else {
		if( ($lcmd !~ /^\s*$/) && ($lcmd !~ /^\s*#.*$/) ) {
			$llog="Unknown Command";
		}
	}
	#
	# And send out anything to the log, and screen.
	if( $llog ) {
		&Log($llog);
		&PushOutputWinBuffer($llog);
	}
}
#
# Process a command that is going to the receiver
sub ProcessCommandRcvr {
	my($lcmd, $lparm);
	if( $InputMethod ) {											# Are we connected via serial mode?
		$lcmd = $_[0];
		#
		if( $lcmd =~ /^\s*terminalmode\s*$/i ) {					# We always ignore this command
			#
		} elsif($lcmd =~ /^read/i ) {								# Read a command file
			($lcmd,$lparm) = split(/ /, $_[0]);
			&FillAutoBuffer($lparm);
		} else {
			if( $ReceiverType eq 'M' ) {
				if( $lcmd =~ /^\s*$/ ) {							# Motorola will not responcd to a blank line
					&SerialWriteWithTimeOut(0,"\r");				# So we send it without a timeout
				} else {
					&MotoWriteWithParity($lcmd);					# Or with parity and timeout
				}
			} else {
				if( $lcmd =~ ":SYSTEM:STATUS" ) {
					&SerialWriteWithTimeOut(5,$lcmd . "\r");		# HP mode is straight through
				} else {
					&SerialWriteWithTimeOut(1,$lcmd . "\r");		# HP mode is straight through
				}
			}
		}
	} else {
		&PushOutputWinBuffer("Cannot send data to TCP/IP connected receiver");						# Send this out first
	}
}
#
# Fill the auto command processing buffer with the contents of a file
sub FillAutoBuffer {
	my($lfile, $llog);
	#
	if( $InputMethod ) {
		$lfile = $_[0];
		if( ! open(LIFH, "<$lfile") ) {
			$llog="Cannot open input file:: ${lfile}";
		} else {
			$llog="Reading from: ${lfile}";
			while(<LIFH>) {
				$AutoProcessBuffer[$AutoProcessMax]=$_;
				$AutoProcessMax++;
			}
			close(LIFH);
			$AutoProcessFlag++;
		}
		$SerialWriteTime=0;
		$GotRcvrPrompt=1;
	} else {
		$llog="Reading from a file is not supported in TCP/IP connection mode";
	}
	&Log($llog);
	&PushOutputWinBuffer($llog);
}
#---------------------------------------------------------------
# ---Section--- HTML Gen
#
# HTML Related code
#
# Build HTML page for tables
#
sub HTMLgentable {
	my($st, $lfmt, $fmte, $tfmt, $s);
	#
	return if !$UpdateOutput;			# No need to update if no data
	#
	$lfmt="<font size=\"+1\" color=\"${ColorTextLabels}\" face=\"geneva,arial,verdana,sans-serif\">";
	$tfmt="<font size=\"+1\" color=\"${ColorTextdata}\" face=\"geneva,arial,verdana,sans-serif\">";
	$fmte="</font>";
	open GPSDAT, ">${htmldest}" || &procSigINT;
	print GPSDAT "<TABLE align=\"center\" border=\"0\" cellspacing=\"0\" width=\"100%\">\n";
	print GPSDAT "	<tr align=\"center\">\n";
	print GPSDAT "		<td align=\"center\">\n";
	print GPSDAT "			<TABLE align=\"center\" border=\"0\" cellpadding=\"3\" cellspacing=\"0\" width=\"95%\">\n";
	print GPSDAT "				<tr align=\"center\">\n";
	print GPSDAT "					<td align=\"center\" bgcolor=\"${ColorTitleBg}\" valign=\"center\">\n";
	print GPSDAT "						<FONT SIZE=\"+2\" COLOR=\"${ColorTitleFont}\" face=\"geneva,arial,verdana,sans-serif\">${HTMLTitle}</FONT>\n";
	print GPSDAT "					</td>\n";
	print GPSDAT "				</tr>\n";
	print GPSDAT "			</table>\n";
	print GPSDAT "		</td>\n";
	print GPSDAT "	</tr><!-- End of title -->\n";
	print GPSDAT "	<tr align=\"center\">\n";
	print GPSDAT "		<td align=\"center\" valign=\"Center\">\n";
	print GPSDAT "			<TABLE align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"2\" width=\"95%\">\n";
	print GPSDAT "				<tr align=\"Center\">\n";
	print GPSDAT "				<td bgcolor=\"${ColorTextBg}\" align=\"Center\">\n";
	print GPSDAT "					<TABLE align=\"center\" border=\"0\" cellspacing=\"0\" cellpadding=\"1\" width=\"100%\">\n";
	print GPSDAT "						<tr align=\"Center\">\n";
	print GPSDAT "							<td align=\"Left\" width=\"50%\">${lfmt}Receiver Date:${fmte}${tfmt} ${RstrRdate}${fmte}</td>\n";
	print GPSDAT "							<td align=\"Left\" width=\"50%\">${lfmt}Receiver Time:${fmte}${tfmt} ${RstrRtime}${fmte}</td>\n";
	print GPSDAT "						</tr>\n";
	print GPSDAT "						<tr align=\"center\">\n";
	print GPSDAT "							<td align=\"Left\" width=\"50%\">${lfmt}Local Date:${fmte}${tfmt} ${RstrLdate}${fmte}</td>\n";
	print GPSDAT "							<td align=\"Left\" width=\"50%\">${lfmt}Local Time:${fmte}${tfmt} ${RstrLtime}${fmte}${lfmt}&nbsp;&nbsp;Uptime:${fmte}${tfmt} ${RstrUptime}${fmte}</td>\n";
	print GPSDAT "						</tr>\n";
	print GPSDAT "						<tr align=\"center\">\n";
	print GPSDAT "							<td align=\"Left\" width=\"50%\">${lfmt}Latitude:${fmte}${tfmt} ${KRlat}${fmte}</td>\n";
	print GPSDAT "							<td align=\"Left\" width=\"50%\">${lfmt}Longitude:${fmte}${tfmt} ${KRlon}${fmte}</td>\n";
	print GPSDAT "						</tr>\n";
	#
	print GPSDAT "						<tr align=\"center\">\n";
	if( $ReceiverType eq 'M' ) {
		print GPSDAT "							<td align=\"Left\" width=\"50%\">${lfmt}Height:${fmte}${tfmt} ${KRheight}${fmte}</td>\n";
		print GPSDAT "							<td align=\"Left\" width=\"50%\">${lfmt}Geometry:${fmte}${tfmt} ${KRgeom}${fmte}</td>\n";
	} else {
		print GPSDAT "							<td align=\"Left\" width=\"50%\">${lfmt}Height:${fmte}${tfmt} ${KRheight}${fmte}</td>\n";
		print GPSDAT "							<td align=\"Left\" width=\"50%\">${lfmt}SmartClock Mode:${fmte}${tfmt} ${KR_HPsmart}${fmte}</td>\n";
	}
	print GPSDAT "						</tr>\n";
	#
	print GPSDAT "						<tr align=\"center\">\n";
	if( $ReceiverType eq 'M' ) {
		print GPSDAT "							<td align=\"Left\" width=\"50%\">${lfmt}Receiver Type:${fmte}${tfmt} Motorola${fmte}&nbsp;&nbsp;${lfmt}Receiver Model:${fmte}${tfmt} ${ReceiverModel}${fmte}</td>\n";
		print GPSDAT "							<td align=\"Left\" width=\"50%\">${lfmt}Selftest:${fmte}${tfmt} ${MotoSelfTest}${fmte} ${lfmt}Antenna:${fmte}${tfmt} ${KRant}${fmte}, ${lfmt}Delay:${fmte}${tfmt} ${ReceiverCableDelay}ns${fmte}</td>\n";
	} else {
		print GPSDAT "							<td align=\"Left\" width=\"50%\">${lfmt}Unit:${fmte}${tfmt} HP ${HPModelNumber}${fmte}&nbsp;&nbsp;${lfmt}Receiver Model:${fmte}${tfmt} ${ReceiverModel}${fmte}</td>\n";
		print GPSDAT "							<td align=\"Left\" width=\"50%\">${lfmt}Antenna:${fmte}${tfmt} ${KRant}${fmte}</td>\n";
	}
	print GPSDAT "						</tr>\n";
	#
	print GPSDAT "						<tr align=\"center\">\n";
	if( $ReceiverType eq 'M' ) {
		$s="${lfmt}Satellites(Tracking/Visible):${fmte}${tfmt} ${KRsat}${fmte}&nbsp;&nbsp;${lfmt}Elev Mask:${fmte}${tfmt} ";
		if($ReceiverMaskAngle == -1 ) {
			$s.="No info";
		} else {
			$s.="${ReceiverMaskAngle}";
		}
			$s.="${fmte}";
		print GPSDAT "							<td align=\"Left\" width=\"50%\">${s}</td>\n";
		print GPSDAT "							<td align=\"Left\" width=\"50%\">${lfmt}PPS:${fmte}${tfmt} ${KRpps_m}${KRpps_r}${leapString}${fmte}</td>\n";
	} else {
		$s="${lfmt}Satellites(Tracking/Visible):${fmte}${tfmt} ${KRsat}${fmte}&nbsp;&nbsp;${lfmt}Elev Mask:${fmte}${tfmt} ${ReceiverMaskAngle}${fmte}";
		print GPSDAT "							<td align=\"Left\" width=\"50%\">${s}</td>\n";
		print GPSDAT "							<td align=\"Left\" width=\"50%\">${lfmt}PPS:${fmte}${tfmt} ${KRpps_m}${fmte}</td>\n";
	}
	print GPSDAT "						</tr>\n";
	if( $ReceiverType eq 'H' ) {
		print GPSDAT "						<tr>\n";
		$s="${lfmt}Holdover Uncertainty:${fmte}${tfmt} ${KR_unce}${fmte}";
		print GPSDAT "							<td align=\"Left\" width=\"50%\">${s}</td>\n";
		$s="${lfmt}Receiver Lifetime:${fmte}${tfmt} ${HPLifetime}${fmte}";
		print GPSDAT "							<td align=\"Left\" width=\"50%\">${s}</td>\n";
		print GPSDAT "						</tr>\n";
		print GPSDAT "						<tr>\n";
		$s="${lfmt}Self Test:${fmte}${tfmt} ${KR_HPll1}${fmte}${lfmt} Int Pwr:${fmte}${tfmt} ${KR_HPll2}${fmte}${lfmt} Oven Pwr:${fmte}${tfmt} ${KR_HPll3}${fmte}";
		print GPSDAT "							<td align=\"Left\" width=\"50%\">${s}</td>\n";
		$s="${lfmt}OCXO:${fmte}${tfmt} ${KR_HPll4}${fmte}${lfmt} EFC:${fmte}${tfmt} ${KR_HPll5}${fmte}${lfmt} GPS Rcv:${fmte}${tfmt} ${KR_HPll6}${fmte}";
		print GPSDAT "							<td align=\"Left\" width=\"50%\">${s}</td>\n";
		print GPSDAT "						</tr>\n";
	}
	print GPSDAT "					</table>\n";
	print GPSDAT "				</td>\n";
	print GPSDAT "				</tr>\n";
	#
	print GPSDAT "			</table>\n";
	print GPSDAT "		</td>\n";
	print GPSDAT "	</tr>\n";
	print GPSDAT "</table><!-- End of GPS data -->\n";
	close GPSDAT;
}
#
# Blank page generator
#
sub GenHTML_BlankPage {
	return if( !$htmlgen );
	open GPSDAT, ">${htmldest}" || &procSigINT;
	print GPSDAT "<TABLE align=\"center\" border=\"0\" cellspacing=\"0\" width=\"100%\">\n";
	print GPSDAT "<tr align=\"center\" valign=\"MIDDLE\">\n";
	print GPSDAT "	<td width=\"100%\" align=\"center\" valign=\"middle\">\n";
	print GPSDAT "		<TABLE align=\"center\" border=\"0\" cellspacing=\"0\" width=\"95%\">\n";
	print GPSDAT "		<tr align=\"center\" valign=\"MIDDLE\">\n";
	print GPSDAT "			<td width=\"100%\" align=\"center\" bgcolor=\"#506B70\" valign=\"middle\">\n";
	print GPSDAT "				<font size=\"+2\" color=\"White\">${HTMLTitle} - No GPS Data Available</font>\n";
	print GPSDAT "			</td>\n";
	print GPSDAT "		</tr>\n";
	print GPSDAT "		</table>\n";
	print GPSDAT "</td></tr></table>\n";
	close GPSDAT;
}
#---------------------------------------------------------------
# ---Section--- Serial Port
#
# Serial port related code
#
#---------------------------------------------------------------
# SerialStop()
# -------------
# Close the serial port
sub SerialStop() {
	$PortObj->close;
}

# SerialStart()
# -------------
# Open the serial port
sub SerialStart() {
	#
	if( $PortObj=Device::SerialPort->new($SerialPort) ) {
		$PortObj->databits($DATABITS);
		$PortObj->baudrate($BAUD);
		$PortObj->parity($PARITY);
		$PortObj->stopbits($STOPBITS);
		$PortObj->handshake("none");
		$PortObj->are_match("\n");					# possible end strings
		return 0;
	} else {
		print "$0: Can't open $SerialPort\r\n";
		return 1;
	}
}
#
# SerialWriteWithTimeSet
# ----------------------
# We send out a string to the serial port and set a local
# timer value to check for possible receiver problems and
# timeouts.
# A value of 0 for timeout means thet the previous timeout value
# remains unchanged.
sub SerialWriteWithTimeOut {
	my($timeout, $pline);
	$timeout=$_[0];									# Get the timeout value
	$pline=$_[1];									# Get the string
	# 
	if( $timeout ) {
		$SerialWriteTime=time+$timeout;				# Start the timer
	}
	$PortObj->write($pline);
	$GotRcvrPrompt=0;
}
#
# A simple function to check if we timed out or not
sub Check4SerialTimeOut {
	return 0 if !$SerialWriteTime;					# Zero here means we finished before timeout
	if( time > $SerialWriteTime ) {
		return (time-$SerialWriteTime);
	} else {
		return 0;
	}
}
#
# Stop the serial timer and set the prompt flag
sub PromptAndStopTimeout {
	my($i);
	$i=&Check4SerialTimeOut;
	if( &Check4SerialTimeOut ) {
		&PushOutputWinBuffer("Receiver has responded after: ${i} seconds");
	}
	$SerialWriteTime=0;
	$GotRcvrPrompt=1;
}
#
# MotoWriteWithParity
# ---------------------
# Write a line to the serial port by putting "@@" in front (if needed),
# then checksum at the end and a "\r\n" afterwards.
# IF there is a # after the @@Xx command, it means that the rest of the string is
# in hex and must be translated as such. Any spaces will be ignored.
# e.x. @@Ag#ff or @@Ag#FF will send the "ff" string as a single byte with the value of 0xff
#
sub MotoWriteWithParity {
	my($pline, $rs, $i, $j, $checksum);
	$pline=$_[0];									# Get the string
	$checksum=0;
	if( $pline =~ /^@@/ ) {
		if( $pline =~ /^@@..#/ ) {					# He wants to send hex code
			$pline =~ s/\s//g;
			#
			$rs=substr($pline,0,4);
			for( $i=5; $i < length($pline); $i+=2 ) {
				$rs.=chr(hex substr($pline, $i, 2));
			}
			$pline=$rs;
		}
		for( $i=2; $i < length($pline); $i++ ) {
			$checksum ^= ord(substr($pline, $i, 1));
		}
		$pline.=chr($checksum) . "\r\n";
	} else {
		if( $pline =~ /^..#/ ) {					# He wants to send hex code
			$pline =~ s/\s//g;
			#
			$rs=substr($pline,0,2);
			for( $i=3; $i < length($pline); $i+=2 ) {
				$rs.=chr(hex substr($pline, $i, 2));
			}
			$pline=$rs;
		}
		for( $i=0; $i < length($pline); $i++ ) {
			$checksum ^= ord(substr($pline, $i, 1));
		}
		$pline="@@" . $pline . chr($checksum) . "\r\n";
	}
	&SerialWriteWithTimeOut(1,$pline);
}
#---------------------------------------------------------------
# ---Section--- Misc
#
# This routine builds the time strings based on locale
sub FormatTime {
	my($i, $j, $k, $l);
	my($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst);
	my($lsec, $lmin, $lhour, $lmday, $lmon, $lyear, $lwday, $lyday, $lisdst);
	#
	($lsec, $lmin, $lhour, $lmday, $lmon, $lyear, $lwday, $lyday, $lisdst) = localtime(time);
	$lyear+=1900;$lmon++;$lyday++;
	($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = gmtime($ReceiverTime);
	$year+=1900;$mon++;$yday++;
	$RstrIsoRdate = sprintf("%04d-%02d-%02dT%02d:%02d:%02d", $year, $mon, $mday, $hour, $min, $sec);
	#
	# Now prepare the display for the time format
	# PB - Enjoy!
	if( $ReceiverTimeMode =~ /UTC/ ) {
		$RstrTimeMode="UT";							# We only need the "UT" symbols for this time format
	} else {
		$RstrTimeMode=$ReceiverTimeMode;
	}
	if( $TimeFormat =~ /USA1/ ) {
		$RstrRdate=sprintf("%02d/%02d/%04d", $mon, $mday, $year);
		$RstrLdate=sprintf("%02d/%02d/%04d", $lmon, $lmday, $lyear);
		#
		$RstrRtime=sprintf("%02d:%02d:%02d.%d %s", $hour, $min, $sec, $ReceiverTimeFrac, $ReceiverTimeMode);
		$RstrLtime=sprintf("%02d:%02d:%02d", $lhour, $lmin, $lsec);
	} elsif( $TimeFormat =~ /USA2/ ) {
		$RstrRdate=sprintf("%02d/%02d/%04d", $mon, $mday, $year);
		$RstrLdate=sprintf("%02d/%02d/%04d", $lmon, $lmday, $lyear);
		#
		$RstrRtime=sprintf("%3d-%02d:%02d:%02d.%d %s", $yday, $hour, $min, $sec, $ReceiverTimeFrac, $RstrTimeMode);
		$RstrLtime=sprintf("%3d-%02d:%02d:%02d", $lyday, $lhour, $lmin, $lsec);
	} elsif( $TimeFormat =~ /USA3/ ) {
		$RstrRdate=sprintf("%02d/%02d/%04d", $mon, $mday, $year);
		$RstrLdate=sprintf("%02d/%02d/%04d", $lmon, $lmday, $lyear);
		#
		$RstrRtime=sprintf("%04d.%3d-%02d:%02d:%02d.%d %s", $year, $yday, $hour, $min, $sec, $ReceiverTimeFrac, $RstrTimeMode);
		$RstrLtime=sprintf("%04d.%3d-%02d:%02d:%02d", $lyear,$lyday, $lhour, $lmin, $lsec);
	} elsif( $TimeFormat =~ /EUR1/ ) {
		$RstrRdate=sprintf("%02d/%02d/%04d", $mday, $mon, $year);
		$RstrLdate=sprintf("%02d/%02d/%04d", $lmday, $lmon, $lyear);
		#
		$RstrRtime=sprintf("%02d:%02d:%02d.%d %s", $hour, $min, $sec, $ReceiverTimeFrac, $ReceiverTimeMode);
		$RstrLtime=sprintf("%02d:%02d:%02d.%d", $lhour, $lmin, $lsec);
	} elsif( $TimeFormat =~ /EUR2/ ) {
		$RstrRdate=sprintf("%02d/%02d/%04d", $mday, $mon, $year);
		$RstrLdate=sprintf("%02d/%02d/%04d", $lmday, $lmon, $lyear);
		#
		$RstrRtime=sprintf("%3d-%02d:%02d:%02d.%d %s", $yday, $hour, $min, $sec, $ReceiverTimeFrac, $RstrTimeMode);
		$RstrLtime=sprintf("%3d-%02d:%02d:%02d", $lyday, $lhour, $lmin, $lsec);
	} elsif( $TimeFormat =~ /EUR3/ ) {
		$RstrRdate=sprintf("%02d/%02d/%04d", $mday, $mon, $year);
		$RstrLdate=sprintf("%02d/%02d/%04d", $lmday, $lmon, $lyear);
		#
		$RstrRtime=sprintf("%04d.%3d-%02d:%02d:%02d.%d %s", $year, $yday, $hour, $min, $sec, $ReceiverTimeFrac, $RstrTimeMode);
		$RstrLtime=sprintf("%04d.%3d-%02d:%02d:%02d", $lyear,$lyday, $lhour, $lmin, $lsec);
	} elsif( $TimeFormat =~ /ISO1/ ) {
		#
		# Code by CRF
		$RstrRdate=sprintf("%04d-%02d-%02d", $year, $mon, $mday);
		$RstrLdate=sprintf("%04d-%02d-%02d", $lyear, $lmon, $lmday);
		#
		$RstrRtime=sprintf("%02d:%02d:%02d.%d %s", $hour, $min, $sec, $ReceiverTimeFrac, $ReceiverTimeMode);
		$RstrLtime=sprintf("%02d:%02d:%02d", $lhour, $lmin, $lsec);
	} elsif( $TimeFormat =~ /ISO2/ ) {
		#
		# Code by CRF
		$RstrRdate=sprintf("%04d-%02d-%02d", $year, $mon, $mday);
		$RstrLdate=sprintf("%04d-%02d-%02d", $lyear, $lmon, $lmday);
		#
		$RstrRtime=sprintf("%04d-%02d-%02dT%02d:%02d:%02d.%d %s", $year, $mon, $mday, $hour, $min, $sec, $ReceiverTimeFrac, $ReceiverTimeMode);
		$RstrLtime=sprintf("%04d-%02d-%02dT%02d:%02d:%02d", $year, $mon, $mday, $lhour, $lmin, $lsec);
	}
	#
	# Format the uptime
	$l=(time-$MyUptime);
	$i=$l/3600;					# Hours
	$i=int($i);
	$j=($l-($i*3600))/60;		# Minutes
	$j=int($j);
	$k=$l-($i*3600)-($j*60);	# Seconds
	$RstrUptime=sprintf("%04d:%02d:%02d", $i, $j, $k);
}
#
# Convert a string based year, month, day, hour, minute, second date to UNIX time
#
# Usage: &Time2Unix($stringyear, $stringmonth, $stringday, $stringhour, $stringminute, $stringsecond);
sub Time2Unix {
	my($lt, $iday, $tm_year, $tm_mon, $tm_mday, $tm_hour, $tm_min, $tm_sec, $val, @DaysToMonth);
	#
	$tm_year=$_[0]; $tm_mon=$_[1]; $tm_mday=$_[2];
	$tm_hour=$_[3]; $tm_min=$_[4]; $tm_sec=$_[5];
	#
	@DaysToMonth = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365);
	$iday = 365 * ($tm_year - 70) + $DaysToMonth[$tm_mon] + ($tm_mday - 1);
	$lt=$iday;
	$iday = ($tm_year - 69) / 4;
	$iday=int($iday);
	$iday += $lt;
	if(($tm_mon > 1) && (($tm_year % 4) == 0)) {
		$iday++;
	}
	$val = $tm_sec + 60 * $tm_min + 3600 * ($tm_hour + 24 * $iday);
	return $val;
}
#
# Print if verbose level is greater than a number
#
sub Vprint {
	if( $VERBOSE >= $_[0] ) {
		&Log($_[1]);
	}
}
#
# Add to the log queue and possibly the debug file as well.
# This routine was called Dprint.
sub Log {
	my($f3);
	my($lsec, $lmin, $lhour, $lmday, $lmon, $lyear, $lwday, $lyday, $lisdst) = localtime(time);
	my($errmsg) = @_;
	$lyear-=100;$lmon++;$lyday++;
	if( $TimeFormat =~ /USA/ ) {
		$f3=sprintf("[%02d/%02d/%02d %02d:%02d:%02d] ", $lmon, $lmday, $lyear, $lhour, $lmin, $lsec);
	} else {
		$f3=sprintf("[%02d/%02d/%02d-%02d:%02d:%02d] ", $lmday, $lmon, $lyear, $lhour, $lmin, $lsec);
	}
	$errmsg=$f3 . $errmsg;
	$SysLog[$SysLogCursor]=$errmsg;$SysLogCursor++;
	#
	if( !$DEBUG ) {
		return;
	}
	if( $DebugFile eq 'STDERR' ) {
		#
		# If we are in daemon mode, we don't send stuff to STDERR (only log file).
		if( $DAEMON ) {
			return;
		}
		print STDERR  $errmsg . "\n";
	} else {
		print DEBUGHANDLE $errmsg . "\n";
	}
}
#
# Generate a time log entry
sub GenerateTimeLogEntry {
	my($i, $j, $val);
	#
	return if !$StartTimeLog;
	$i=time;
	if( $KR_HPsmart =~ /Locked to GPS/ ) {
		#
		# The fields are: time, EFC Value, Holdover Uncertainty, 1PPS TI, Number of satellites
		$val= "${i},${HPEFC},${KR_unce},${KR_HP1PPS},${KRsat}";
	} else {
		$val= "${i},undef,undef,undef,undef";
	}
	$TimelogBuf[$TimeLogCursor]=$val;
	$TimeLogCursor++;
	if( $TimeLogCursor < 12 ) {					# About once every minute
		return;
	}
	if( -f $TimeLogSemaphore ) {
		&Log("Found TimeLog semaphore: ${TimeLogSemaphore}");
		return;
	}
	#
	# Let's fork a process to append to the time log
	$i=fork;
	if($i) {
		&Vprint(1,"Wrote to timelog");
		$TimeLogCursor=0;
		return;				# Go back to the parent
	}
	#
	# The child process is running here.
	# Let's create the timelog semaphore
	if( open(SEMLOG, ">$TimeLogSemaphore") ) {
		print SEMLOG "${RstrRdate} ${RstrRtime} -- Local time: ${RstrLdate} ${RstrLtime}\n";
		close SEMLOG;
		#
		# Ok, let's append to the log
		if( open(TLOG, ">>$TimeLog") ) {
			for( $i=0; $i < $TimeLogCursor; $i++ ) {
				print TLOG $TimelogBuf[$i] . "\n";
			}
			close TLOG;
		}
		unlink($TimeLogSemaphore);				# And remove the semaphore
	}
	#
	# We MUST use exit here
	# TODO: Calling SerialStop does not work where there are large log files.
	# Unfortunately, we have to cold exit. More on this later...
	#&SerialStop if $InputMethod;				# If we don't do this, then we get weird messages
	sleep 1;									# Just making sure we settle down
	exit;
}
#
# Return a number from a string: s2n( string_start, count )
#
sub s2n {
	my($j, $i, $pe);
	$pe=0; $j=$_[0];
	for( $i=0; $i < $_[1]; $i++, $j++ ) {
		$pe *=256;
		$pe += ord(substr($ReceiverProcessedDataLine, $j, 1));
	}
	return $pe;
}

#
# Return a signed number from a string: s2sn( string_start, count )
#
sub s2sn {
	my($j, $i, $nf, $pe, $pf);
	$nf=1;
	$pe=0; $j=$_[0]; $pf=0;
	for( $i=0; $i < $_[1]; $i++, $j++ ) {
		$pf = ord(substr($ReceiverProcessedDataLine, $j, 1));
		if( $i == 0 ) { if( $pf > 127 ) { $nf=-1; } }
		if( $nf < 1 ) { $pf ^= 255}
		$pe *=256;
		$pe += $pf;
	}
	return $pe*$nf;
}

#
# Make us a daemon
#
sub daemonize {
	chdir '/' or die "Can't chdir to /: $!";
	open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
	open STDOUT, '>/dev/null' or die "Can't write to /dev/null: $!";
	defined(my $pid = fork) or die "Can't fork: $!";
	if( $pid ) {
		#
		# Parent just exits
		exit;
	}
	#
	# Child comes here
	setsid or die "Can't start a new session: $!";
	open STDERR, '>&STDOUT' or die "Can't dup stdout: $!";
}
#
# Print line number debug info
# call this function with: &PrintLineNumberDebug( __LINE__ );
#
sub PrintLineNumberDebug {
	my($ln);
	$ln=$_[0];
	&Log($0 . ": Line: " . $ln);
}
#---------------------------------------------------------------
# ---Section--- Interrupt handlers
#
#---------------------------------------------------------------
#
# Signal catcher
#
sub procSigINT {
	$sigINT=1;
}
#
# Signal catch for tty disconnect
#
sub procSigHUP {
	#
	# What happens here is that the terminal was disconnected.
	# If we are in daemon mode, no problem. Otherwise we quit.
	if( ! $DAEMON ) {
		$sigINT=1;
	}
}
#
# Signal catcher for restart
#
sub procSigUSR1 {
	$RESTART++;
}
#
# Signal catcher for Debug enable
sub procSigUSR2 {
	$DEBUG=1;
	$VERBOSE=10;
	#
	# Let's setup a temp filehandle
	$DebugFile="/tmp/gpsdebug";
	$DoSysinfoPrint++;
}
#
# Signal catcher for Debug disable
sub procSigUSR3 {
	$DEBUG=0;
	$VERBOSE=0;
	$DoSysinfoPrint++;
}
#
# Signal catch and quit
#
sub procSigTERM {
	$sigINT=1;
}
#
# Check for children dying
sub procSigCHLD {
	my $pid;
	$pid = waitpid(-1, &WNOHANG);

	if ($pid == -1) {
		# no child waiting.  Ignore it.
	} elsif (WIFEXITED($?)) {
		#&Log("Process $pid exited.");
	} else {
		#&Log("False alarm on $pid.");
	}
	$SIG{CHLD} = \&procSigCHLD;		# in case of unreliable signals
}
#---------------------------------------------------------------
# ---Section--- Command line and config file processing
#
# Process the command line arguments
#
sub ProcCmdLine {
	my($ii, $sarg, @arsave);
	#
	@arsave = @ARGV;
	if( !$NARGS ) {
		&ShowHelp;
	}
	#
	# We look through any possible config file first.
	# The command line arguments will override this
	CFTEST: foreach $ii ( 0 .. $NARGS ) {
		$sarg = shift @arsave;
		if( $sarg =~ /\s*-i/ ) {
			$sarg = shift @arsave;
			if( $sarg =~ /^-/ || !$sarg ) {		# Did we hit the next option?
				print STDERR "Configuration file: ${ConfigFile} does not exist\r\n";
				exit;
			}
			$ConfigFile=$sarg;
			if( ! -f $ConfigFile ) {
				print STDERR "Config file: ${ConfigFile} does not exist.\r\n";
				exit 1;
			}
			&ProcessConfigFile;
			next CFTEST;
		}
	}
	#
	CTEST: foreach $ii ( 0 .. $NARGS ) {
		$sarg = shift @ARGV;
		#
		# BAUD Rate
		if( $sarg =~ /\s*-B/ ) {
			$sarg = shift @ARGV;
			if( $sarg =~ /^-/ || !$sarg ) {		# Did we hit the next option?
				print "Invalid baud rate specified: $sarg\r\n";
				exit;
			}
			($BAUD,$DATABITS,$PARITY,$STOPBITS) = $sarg =~ /^\s*([^-]*)-([^-]*)-([^-]*)-(.*)\s*$/;
			if( $PARITY =~ /n/i ) {
				$PARITY="none";
			} elsif( $PARITY =~ /e/i ) {
				$PARITY="even";
			} elsif( $PARITY =~ /o/i ) {
				$PARITY="odd";
			} else {
				print "Invalid baud rate specified: $sarg\r\n";
				exit 1;
			}
			next CTEST;
		}
		if( $sarg =~ /\s*-d/ ) {				# Debug
			$sarg = shift @ARGV;
			if( $sarg =~ /^-/ || !$sarg ) {		# Did we hit the next option?
				print "Invalid debug file specified: $sarg\r\n";
				exit 1;
			}
			$DebugFile=$sarg;
			if( $DebugFile =~ /^\s*off\s*$/i ) {
				$DEBUG=0;
			} else {
				$DEBUG=1;
			}
			next CTEST;
		}
		if( $sarg =~ /\s*-t/ ) {$TerminalMode=1;$DAEMON=0;$FullScreenEnable=1; next CTEST;}
		if( $sarg =~ /\s*-k/ ) {$TerminalMode=0;$DAEMON=1; $FullScreenEnable=0; next CTEST;}
		if( $sarg =~ /\s*-c/ ) {$FullScreenEnable=1; $DAEMON=0; $InputMethod=0; next CTEST;}
		if( $sarg =~ /\s*-o/ ) {
			$DEBUG=1;
			$DAEMON=0;
			$DebugFile="STDERR";
			$VERBOSE++;
			$TerminalMode=0;
			$FullScreenEnable=0;
			next CTEST;
		}
		if( $sarg =~ /\s*-v/ ) {
			#
			#	Verbosity level explanation:
			#	v=0	Just display setup info
			#	v=1	Display a second ticker
			#	v=2
			#	v=3	Display the raw GPS data
			$sarg =~ s/\s*-(v*).*/$1/;
			$VERBOSE = length($sarg);
			next CTEST;
		}
		if( $sarg =~ /\s*-f/ ) {
			$sarg = shift @ARGV;
			if( $sarg =~ /^-/ || !$sarg ) {		# Did we hit the next option?
				print "Invalid Time Format Specified: $sarg\r\n";
				exit;
			}
			if( $sarg =~ /USA[123]\s*$/i ) {
				$TimeFormat=$sarg;
			} elsif( $sarg =~ /EUR[123]\s*$/i ) {
				$TimeFormat=$sarg;
			} elsif( $sarg =~ /ISO[12]\s*$/i ) {
				$TimeFormat=$sarg;
			} else {
				print STDERR "Invalid time format: ${sarg}\r\n";
				exit 1;
			}
			next CTEST;
		}
		if( $sarg =~ /\s*-l/ ) {
			$sarg = shift @ARGV;
			if( $sarg =~ /^-/ || !$sarg ) {		# Did we hit the next option?
				print "Invalid HTML output file specified: $sarg\r\n";
				exit;
			}
			$htmlgen=1;
			$htmldest=$sarg;
			next CTEST;
		}
		if( $sarg =~ /\s*-m/ ) {
			$OutputWinLimit=1;
			next CTEST;
		}
		if( $sarg =~ /\s*-i/ ) {
			$sarg = shift @ARGV;
			next CTEST;
		}
		if( $sarg =~ /\s*-e/ ) {
			$sarg = shift @ARGV;
			if( $sarg =~ /^-/ || !$sarg ) {		# Did we hit the next option?
				print "Invalid HTML Title specified: $sarg\r\n";
				exit;
			}
			$HTMLTitle=$sarg;
			next CTEST;
		}
		if( $sarg =~ /\s*-p/ ) {
			$sarg = shift @ARGV;
			if( $sarg =~ /^-/ || !$sarg ) {		# Did we hit the next option?
				print "Invalid remote port specified: $sarg\r\n";
				exit;
			}
			$RemotePort=$sarg;
			next CTEST;
		}
		if( $sarg =~ /\s*-C/ ) {
			$sarg = shift @ARGV;
			($ColorTitleBg, $ColorTitleFont, $ColorTextBg, $ColorTextLabels, $ColorTextdata) = split(/,/, $sarg);
			if( $ColorTextdata =~ /^\s*$/ ) {
				print "Please specify all 5 colors\r\n";
				exit;
			}
			next CTEST;
		}
		if( $sarg =~ /\s*-g/ ) {
			$sarg = shift @ARGV;
			if( $sarg =~ /^-/ || !$sarg ) {		# Did we hit the next option?
				print "Invalid timelog specified: $sarg\r\n";
				exit;
			}
			$TimeLog=$sarg;
			$StartTimeLog=1;
		}
		if( $sarg =~ /\s*-r/ ) {
			$sarg = shift @ARGV;
			if( $sarg =~ /^-/ || !$sarg ) {		# Did we hit the next option?
				print "Invalid Receiver type specified: $sarg\r\n";
				exit;
			}
			$ReceiverType=$sarg;
			if( $ReceiverType =~ /\s*M/i ) {
				$ReceiverType="M";
				$BAUD="9600";
				$DATABITS=8;
				$PARITY="none";
				$STOPBITS=1;
			} else {
				$ReceiverType="H";
				$BAUD="19200";
				$DATABITS=7;
				$PARITY="odd";
				$STOPBITS=1;
			}
			next CTEST;
		}
		if( $sarg =~ /\s*-s/ ) {			# Serial port
			$sarg = shift @ARGV;
			if( $sarg =~ /^-/ || !$sarg ) {		# Did we hit the next option?
				print "Invalid Serial port specified: $sarg\r\n";
				exit;
			}
			$SerialPort=$sarg;
			$InputMethod=1;
			next CTEST;
		}
		if( $sarg =~ /^$/ ) {next CTEST;}
		if( $sarg =~ /\s*-/ ) {				# Check for a possible option that we don't know about
			&ShowHelp;
		}
		else {
			#
			# We assume that the user wants a curses based connection to the system
			# specified
			$InputMethod=0;					# Network connection
			$RemoteSystem=$sarg;
		}
	}
	#
	# Based on all the command line options, can we gen any output?
	#
	if( !$htmlgen && !$FullScreenEnable && !$DEBUG ) {
		print STDERR "No output method specified. For a minimal output specify: -o hostname\n";
		exit;
	}
	#
	# Check the timelog and flag files
	if( $StartTimeLog ) {
		$TimeLogSemaphore=$TimeLog . ".SEM";
		if( -f $TimeLogSemaphore ) {
			print "Found TimeLog semaphore: ${TimeLogSemaphore}\r\n";
			print "Remove the file in order to continue\r\n";
			exit;
		}
	}
}
#
# Show the help info
sub ShowHelp {
	#
	print "\r\nUsage:\t$0 [-d] [-v[v[v...]]] [-ckotm]\r\n\t\t\t[-f time_format] [-i Configuration_file]";
	print "\r\n\t\t\t[-s serial_port_device] [-B baud]";
	print "\r\n\t\t\t[-p port] [-l html_output_file] [-[-]h]";
	print "\r\n\t\t\t[-C n,n,n,n,n] [-e HTML_Title] [-g timelog]";
	print "\r\n\t\t\t[-r receiver_model] hostname\n";
	print "\n\tWhere:\n\n";
	print "\t-d debug_file|off -- Debug file must be specified in order\n";
	print "\t   for debug to be enabled\n";
	print "\t-v[v[v...]]] verbose. Specify verbosity level of debug output\n";
	print "\t-c Run in full screen mode(curses). This is the default.\n";
	print "\t-k fork (run in the background)\n";
	print "\t-o Generate simple screen output (no curses).\n";
	print "\t-t Start terminal mode.\n";
	print "\t-f time_format -- USA1,USA2,USA3,EUR1,EUR2,EUR3,ISO1,ISO2\n";
	print "\t   The format used to display the time and date\n";
	print "\t-i Configuration_file -- System configuration file\n";
	print "\t   Note: Command line options override any configuration file settings\n";
	print "\t-s serial_port -- Serial port to use. e.x. /dev/ttyS0\n";
	print "\t-B baud -- Specify BAUD rate when using the serial port. e.x. 9600-8-N-1\n";
	print "\t-m Limit the Output and Livedata windows to 1 line. Only applies if we are in curses mode.\n";
	print "\t-p port -- port number to use for connecting to\n";
	print "\t   the NTP shared memory connector. Default is 12321\n";
	print "\t-C TitleBackground,TitleFont,TextBackground,TextLabels,Textdata\n";
	print "\t   Color selection for HTML page\n";
	print "\t-e HTML_Title_String -- The string that will define the <TITLE> tag\n";
	print "\t   in the HTML output file\n";
	print "\t-g timelog -- The name of the Time Log file\n";
	print "\t-l html_output_file -- The output destination of HTML code\n";
	print "\t-h Help (this screen)\n";
	print "\t-r receiver_model -- As of 4/2006, two types\n";
	print "\t   are supported: M(Motorola) and H(Hewlett Packard)\n";
	print "\n\thostname -- Well you know...\n";
	print "\r\n\tFor a quick display do: $0 www.darksmile.net\r\n\n";
	exit;
}
#
# Here we process the configuration file if specified
sub ProcessConfigFile {
	my($FCONFIG, $p1, $p2, $p3, $Linenum);
	if( !open($FCONFIG, $ConfigFile) ) {
		print STDERR "Can't open ${ConfigFile} $!\r\n";
		exit 1;
	}
	$Linenum=0;
	CNFTOP:
	while( <$FCONFIG> ) {
		$Linenum++;
		chop;					# Get rid of the "\n"
		if( $AutoProcessFlag ) {
			#
			# Here we just feed the AutoProcessBuffer
			$AutoProcessBuffer[$AutoProcessMax]=$_;
			$AutoProcessMax++;
			next CNFTOP;
		}
		s/^\s*//;s/\s$//;		# Effectively cut out space
		s/\s\s*/ /;				# Effectively cut out extra space
		next CNFTOP if /^\s*#/;	# Get rid of the comments
		next CNFTOP if /^\s*$/;	# Get rid of blank lines
		($p1,$p2) = split(/ /);
		#
		# SysInit indicates the end of option parsing and the start
		# of the AutoProcess array
		if( $p1 =~ /SysInit/i ) {
			$AutoProcessFlag=1;
			next CNFTOP;
		}
		#
		if( $p1 =~ /OutputWinLimit/i ) {
			$OutputWinLimit=1;
			next CNFTOP;
		}
		if( ($p1 =~ /^$/) || ($p2 =~ /^$/) ) {
			print "Line: ${Linenum}, Missing parameter after: $_\r\n";
			exit;
		}
		#
		if( /^hostname/i ) {
			$RemoteSystem=$p2;
		} elsif( /^Port/i ) {
			$RemotePort=$p2;
		} elsif( /^Timelog/i ) {
			$TimeLog=$p2;
			if( $TimeLog !~ /none/i ) {
				$StartTimeLog=1;
			}
		} elsif( /^HTTPFile/i ) {
			$htmldest=$p2;
			if( $htmldest =~ /none/i ) {
				$htmlgen=0;
			} else {
				$htmlgen=1;
			}

		} elsif( /^HTMLColors/i ) {
			($ColorTitleBg, $ColorTitleFont, $ColorTextBg, $ColorTextLabels, $ColorTextdata) = split(/,/, $p2);
			if( $ColorTextdata =~ /^\s*$/ ) {
				print "Line: ${Linenum}, Please specify all 5 colors\r\n";
				exit;
			}
		} elsif( /^HTMLTitle/i ) {
			if( $p2 =~ /^\s*$/ ) {
				print "Line: ${Linenum}, Please specify a proper HTML Title String\r\n";
				exit;
			}
			s/^\S*\s//g;				# Effectively cut out the keyword
			s/["]//g;					# Effectively cut out double quotes
			$HTMLTitle=$_;
		} elsif( /^SerialPort/i ) {
			$SerialPort=$p2;
			if( $SerialPort !~ /none/i ) {
				$InputMethod=1;
			}
		} elsif( /^ReceiverType/i ) {
			$ReceiverType=$p2;
			if( $ReceiverType =~ /\s*M/i ) {
				$ReceiverType="M";
				$BAUD="9600";
				$DATABITS=8;
				$PARITY="none";
				$STOPBITS=1;
			} else {
				$ReceiverType="H";
				$BAUD="19200";
				$DATABITS=7;
				$PARITY="odd";
				$STOPBITS=1;
			}
		} elsif( /^BaudRate/i ) {
			($BAUD,$DATABITS,$PARITY,$STOPBITS) = $_ =~ /^\s*([^-]*)-([^-]*)-([^-]*)-(.*)\s*$/;
			if( $PARITY =~ /n/i ) {
				$PARITY="none";
			} elsif( $PARITY =~ /e/i ) {
				$PARITY="even";
			} elsif( $PARITY =~ /o/i ) {
				$PARITY="odd";
			} else {
				print STDERR "Line: ${Linenum}, Invalid baud rate specified: $_\r\n";
				exit 1;
			}
		} elsif( /^FullScreenEnable/i ) {
			if( $p2 =~ /on/i ) {
				$FullScreenEnable=1; $DAEMON=0;
			}
		} elsif( /^TerminalMode/i ) {
			if( $p2 =~ /on/i ) {
				$TerminalMode=1;$DAEMON=0;$FullScreenEnable=1;
			}
		} elsif( /^TimeFormat/i ) {
			if( $p2 =~ /USA[123]\s*$/i ) {
				$TimeFormat=$p2;
			} elsif( $p2 =~ /EUR[123]\s*$/i ) {
				$TimeFormat=$p2;
			} elsif( $p2 =~ /ISO[12]\s*$/i ) {
				$TimeFormat=$p2;
			} else {
				print STDERR "Line: ${Linenum}, Invalid time format: ${p2}\r\n";
				exit 1;
			}
		} elsif( /^DaemonMode/i ) {
			if( $p2 =~ /on/i ) {
				$TerminalMode=0;$DAEMON=1;$FullScreenEnable=0;
			}
		} elsif( /^Debug/i ) {
			if( $p2 !~ /off/i ) {
				if( $p2 !~ /^$/ ) {
					$DebugFile=$p2;
					$DEBUG=1;
				}
			}
		} elsif( /^Verbose/i ) {
			#
			#	Verbosity level explanation:
			#	v=0	Just display setup info
			#	v=1	Display a second ticker
			#	v=2
			#	v=3	Display the raw GPS data
			if( $p2 =~ /[^0-9]/ ) {
				print STDERR "Line: ${Linenum}, Invalid Verbose setting: $p2\r\n";
				exit 1;
			}
			$VERBOSE=$p2;
		} else {
			print STDERR "Line: ${Linenum}, Invalid Option: $_\r\n";
			exit 1;
		}
	}
}
#---------------------------------------------------------------
#
# Send some initial commands to the serial connected Receivers
sub SerialReceiverInit {
	if( $InputMethod ) {
		if( !$SerialCodeLoaded ) {
			print STDERR 'perl module: Device::SerialPort' . " was not found. Serial port connection to receiver is impossible.\r\n";
			exit 1;
		}
		if( &SerialStart ) {
			exit;
		}
		printf("One moment while I communicate with the receiver...\r\n");
		if( $ReceiverType eq 'M' ) {
			#
			# Some Motorola receivers do not the TRAIM capability. The "Basic" model
			# is one. So, let's set this as a default.
			$KRpps_m = "N/A";
			#
			# For serially connected receivers, this message will not be output by default.
			$MotoSelfTest='N/A';
			#
			# Let's send a receiver id request
			select(undef, undef, undef, 0.01);
			&MotoWriteWithParity("Cj");
			select(undef, undef, undef, 0.01);
			#
			# Send all three and see which one takes
			&MotoWriteWithParity("Ba\x01");
			select(undef, undef, undef, 0.1);
			&MotoWriteWithParity("Ea\x01");
			select(undef, undef, undef, 0.1);
			&MotoWriteWithParity("Ha\x01");
			select(undef, undef, undef, 0.1);
			&MotoWriteWithParity("Aw\xff");						# Time mode request. UTC,GPS
			select(undef, undef, undef, 0.1);
			&MotoWriteWithParity("Ag\xff");						# Mask angle
			select(undef, undef, undef, 0.1);
			&MotoWriteWithParity("Az\xff\xff\xff\xff");			# Antenna delay
			select(undef, undef, undef, 0.1);
			&MotoWriteWithParity("En\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff");		# TIME RAIM
			select(undef, undef, undef, 0.1);
			&MotoWriteWithParity("Bn\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff");		# TIME RAIM
			select(undef, undef, undef, 0.1);
			&MotoWriteWithParity("Cg\x02");						# Receiver mode
			select(undef, undef, undef, 0.1);
			&MotoWriteWithParity("Cg\x02");						# Receiver mode	- Must be sent twice
			select(undef, undef, undef, 0.1);
			&MotoWriteWithParity("Bj\x01");						# Leap second
			select(undef, undef, undef, 0.1);
		} else {
			#
			# If the model is a Z3816A then we need to send the following two commands:
			# *PTIME:TCOD:CONT 0
			# :SYST:COMM:SER1:FDUP 0
			&SerialWriteWithTimeOut(1,"*IDN?\r");				# Let's figure out the model number
			print "3\r\n"; sleep 1;
			&SerialWriteWithTimeOut(1,":DIAG:IDEN:GPS?\r");		# And the GPS receiver model number
			print "2\r\n"; sleep 1;
			&SerialWriteWithTimeOut(1,":DIAG:LIF:COUNT?\r");	# And the lifetime count
			print "1\r\n"; sleep 1;
			&SerialWriteWithTimeOut(0,"\r");					# And get an initial scpi prompt
		}
	}
}
#
# Fill the HP schedule queue
sub HPqueue {
	if( $ReceiverType ne 'H' ) {
		return;
	}
	@HPSched=();
	$HPSched[0][0]=0;			# Task priority
	$HPSched[0][1]=3;			# Task weight
	$HPSched[0][2]=5;			# Task Timeout period
	$HPSched[0][3]=":SYSTEM:STATUS?";				# Task String
	$HPSched[0][4]=0;			# Task Misc
	#
	$HPSched[1][0]=0;			# Task priority
	$HPSched[1][1]=2;			# Task weight
	$HPSched[1][2]=1;			# Task Timeout period
	$HPSched[1][3]=":DIAG:ROSC:EFC:REL?";			# Task String
	$HPSched[1][4]=0;			# Task Misc
	#
	$HPSched[2][0]=0;			# Task priority
	$HPSched[2][1]=1;			# Task weight
	$HPSched[2][2]=1;			# Task Timeout period
	$HPSched[2][3]=":LED:ACT";	# Task String
	$HPSched[2][4]=0;								# Task Misc
	#
	$HPSched[3][0]=0;			# Task priority
	$HPSched[3][1]=1;			# Task weight
	$HPSched[3][2]=1;			# Task Timeout period
	$HPSched[3][3]=":LED:ENAB";	# Task String
	$HPSched[3][4]=0;								# Task Misc
	$HPSchedmax=4;
}
#
# Global variable init
sub GlobalVarInit {
	#
	#
	$MYVERSION='$Revision: 2.0.0.226 $';			# Automatically updated by RCS
	$MYVERSION =~ s/\$//g;
	$MYVERSION =~ s/Revision: /V/;
	($MYNAME) = $0 =~ /^.*\/(.*)$/;
	$GlobalInitFlag=0;					# Initialize flag
	$RESTART=0;							# Forced socket restart
	$SystemRestartCount=0;				# Count of restarts
	$NARGS=$#ARGV+1;					# Keep here to make life easier
	$DEBUG=0;
	$VERBOSE=0;
	$DAEMON=0;							# Don't run as daemon by default
	$RemoteSystem='localhost';			# ntpscon host to connect to
	$RemotePort=12321;					# ntpscon host to connect to
	#
	$InputMethod=0;						# By default we start with the TCP/IP connection
	$SerialPort="/dev/ttyS7";			# The default serial port to use
	$BAUD="9600";						# Baud rate
	$DATABITS='8'; $PARITY="none"; $STOPBITS=1;
	#
	# HTML vars
	#
	$htmltable="/var/www/htdocs/GPS/GPStable.html";			# Table based HTML code
	$htmldest=$htmltable;									# By default we point to the table based code
	$htmlgen=0;												# Don't gen html by default
	#
	@LiveDataBuffer=();						# No live data yet
	$LiveDataBufCursor=0;$LiveDataBufMax=128;
	@CmdLineBuffer=();						# Same for commands
	@OutputLineBuffer=();					# Same for output display
	$OutputLineBufCursor=0;
	$CmdLineHCursor=0;
	#
	@UnprocessedCmdBuffer=();
	$UnprocessedBufCursor=0;
	$UnprocessedBufMax=128;
	#
	@HelpBuffer=();
	$HelpBufMax=0;
	#
	# Curses related variables
	#
	$LiveDataWIN='';
	$LiveDataWIN_Exists=0;					# At < 21x80 we can't show the data window
	$havecolor=0;							# Does this term support color?
	#
	$CursesInitFlag=0;						# Curses not initialized yet!
	$GotSignal_WINCH=0;						# Trap for window change
	#
	$FullScreenEnable=1;					# Normally we start with curses
	$SatWIN_Height=0;						# Actual lines we have
	$MinimumScreenHeight=0;					# This is dynamic
	$MinimumScreenWidth=80;					# Minimum X needed
	#
	$UpdateOutput=0;						# Update output means that we have data to update the output screen
	$SuspendOutput=0;						# Suspend output means that we should not do so for the moment
	$KRdate="";$KRtime="";
	$KRlat='';$KRlon='';$KRheight='';$KRvel='';$KRgeom='';$KRant='';
	$KRsat='';
	%KRsatinfo=();				# Satellite info
	%KRvsatinfo=();				# Visible satellite info
	@KRsatsig=();
	#
	# Cj command global strings
	#
	@KRid=();					# The actual version strings
	$KRidlines=0;				# How many lines it is
	$KRsfwVer="";				# Software version
	$KRsfwRev="";				# Software revision
	#
	# TRAIM initialisation
	#
	$KRtraim_e = 'UNKNOWN';		# TRAIM Enable/Mode
	$KRtraim_s = 'UNKNOWN';		# TRAIM Status
	#
	# Leap second stuff passed in via @@Bj
	#
	$leapFlag = 0;				# No leap second
	$leapString = '';			# Null -- to be filled in later
	#
	# Receiver-specific bits
	#
	$NumChannels=0;				# The Real number is from @@Ba or @@Ea or @@Ha
	#
	$TerminalMode=0;			# By default, don't start in terminal mode
	@TerminalModeBuffer=();
	$TerminalModeBufCursor=0;$TerminalModeBufMax=128;
	#
	# By default we connect as Motorola. This could be 'H'(HP) or 'O'(Other)
	$ReceiverType="M";
	#
	# Below are the HTML build strings
	#
	$WWWUpdate=0;				# Initially The web data is stale
	#
	$InitialDataReceive=0;
	#
	$ConfigFile="";
	#
	$ReceiverStatusBufMax=0;
	#
	$HPModelNumber="";
	$HPDisplayVerbose=0;
	#
	$DebugFile="";
	#
	$sigINT=0;					# Make sure this is 0!
	#
	$CursesWindowInit=0;		# This is only set later when the program first starts
	#
	$GotRcvrPrompt=0;
	#
	$MyUptime=time;				# We need this for the display
	#
	$ScrollWindowStart=0;		# This means start at the top
	$ScrollEnabled=0;
	#
	$TimeFormat="USA1";			# We use this as a default
	#
	$MonthName{"Jan"}=0; $MonthName{"Feb"}=1; $MonthName{"Mar"}=2; $MonthName{"Apr"}=3;
	$MonthName{"May"}=4; $MonthName{"Jun"}=5; $MonthName{"Jul"}=6; $MonthName{"Aug"}=7;
	$MonthName{"Sep"}=8; $MonthName{"Oct"}=9; $MonthName{"Nov"}=10; $MonthName{"Dec"}=11;
	#
	$HTMLTitle=`uname -n`;chop $HTMLTitle;
	$HTMLTitle="GPS Receiver Data for: ${HTMLTitle}";
	#
	&BuildHelp;
}
#
# Send some system info to STDERR if debug is enabled
sub SysinfoPrint {
	my($s, $oldfh);
	#
	close(DEBUGHANDLE);					# In case something has turned it on
	if( $DEBUG ) {
		if( ($DebugFile !~ /STDERR/) && ($DebugFile !~ /^\s*$/) ) { 
			open(DEBUGHANDLE, ">$DebugFile") || die "Can't open ${DebugFile} $!\n";
			$oldfh = select(DEBUGHANDLE);
			$| = 1;						# This will autoflush the filehandle
			select($oldfh);
		}
	}
	#
	&Log("Name: " . $0);
	&Log("Port: ". $RemotePort);
	&Log("Output file: ". $htmldest);
	$s = "Output file is";
	$s.=" not" if( !$htmlgen );
	$s.=" generated";
	&Log($s);
	&Log("Target Host: ". $RemoteSystem);
	&Log("Debug Flag is: ". $DEBUG);
	&Log("Verbose Level: ". $VERBOSE);
	&Log("Full screen mode") if( $FullScreenEnable );
	&Log("Terminal Mode") if( $TerminalMode );
	#
	if( $ReceiverType eq 'M' ) {
		&Log("Motorola Receiver Mode");
	} else {
		&Log("HP Receiver Mode");
	}
	#
	if( $InputMethod ) {
		&Log("Serial Port Input.");
		&Log("Baud: ${BAUD}, Databits: ${DATABITS}, Parity: ${PARITY}, Stopbits: ${STOPBITS}");
	} else {
		&Log("TCP/IP Port Input.");
	}
	#
	if( $StartTimeLog ) {
		&Log("Timelog enabled on: ${TimeLog}");
		&Log("Timelog Semaphore file: ${TimeLogSemaphore}") if( $TimeLogSemaphore );
	}
}
#
# Build the help screen
sub BuildHelp {
	my($i);
	$i=0;
	$HelpBuffer[$i++]="Help Summary";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]="Function Key Commands and command line equivalents:";
	$HelpBuffer[$i++]="Note: In \"Terminal Mode\", prefix the commands with a \"!\":";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]=" F1/\"w1\"    - Display main window          F2/\"w2\"     - Extended Information Window";
	$HelpBuffer[$i++]=" F3/\"w3\"    - Unprocessed Commands Window";
	$HelpBuffer[$i++]=" F7/\"log\"   - System Log Window            F8/\"term\"   - Terminal Mode Window";
	$HelpBuffer[$i++]=" F9/\"help\"  - Help(This Screen)";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]=" F11/Lock/Unlock  - Toggle System Lock     F12/\"Quit\"  - Quit";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]="Sending GPS Receiver commands in \"Terminal Mode\":";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]=" In \"Normal Mode\", prefix the commands with a \"!\":";
	$HelpBuffer[$i++]=" to send a command to the receiver.";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]=" Motorola commands can be prefixed with @@ but they don't have to be.";
	$HelpBuffer[$i++]=" The checksum will be computed automatically before the command is sent to";
	$HelpBuffer[$i++]=" the receiver.";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]=" So a \@\@Cj and Cj are the same.";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]=" To send a hex code to the receiver put a '#' after the command.";
	$HelpBuffer[$i++]=" e.x. \@\@Az#ffffffff will send the proper command to get the antenna delay.";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]=" Note: HP Receiver commands do not require a checksum.";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]=" Note: If the receiver is network connected, \"Terminal Mode\" is used only";
	$HelpBuffer[$i++]="       for display purposes";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]="Other Commands:";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]=" blink a|e     - Blink the Active or Enabled LED";
	$HelpBuffer[$i++]=" lock          - Lock all keyboard input";
	$HelpBuffer[$i++]=" sleep n       - Sleep for n*(1/100) second increments";
	$HelpBuffer[$i++]=" unlock/Ctrl-L - Unlock keyboard";
	$HelpBuffer[$i++]=" Ctrl-W        - Suspend Data display";
	$HelpBuffer[$i++]=" Ctrl-V        - Toggles \"Show more receiver data\". In HP Mode, the terminal screen";
	$HelpBuffer[$i++]="                 will be very busy!";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]="Command line movement uses Standard Keys and \"Emacs Mode\" keys";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]=" Standard Keys:";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]="   Home      - Beginning of line, End        - End of line";
	$HelpBuffer[$i++]="   LeftArrow - Move to the left,  RightArrow - Move to the right";
	$HelpBuffer[$i++]="   UpArrow   - Previous Command,  DownArrow  - Next Command";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]="   Ctrl-D    - Delete Char Right, Bksp       - Delete Char Left";
	$HelpBuffer[$i++]="   Ctrl-U    - Delete line,       Ctrl-K     - Delete to EOL";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]=" \"Emacs Mode\" key combinations:";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]="   Ctrl-A - Beginning of line, Ctrl-E - End of line";
	$HelpBuffer[$i++]="   Ctrl-B - Move to the left,  Ctrl-F - Move to the right";
	$HelpBuffer[$i++]="   Ctrl-P - Previous Command,  Ctrl-N - Next Command";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]="Time Formats. Specify with the -f option or the \"TimeFormat\" init file option.";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]=" USA1 - Default - Display Date as mm/dd/yyyy, Time as: hh:mm:ss.frac {UTC|GPS}";
	$HelpBuffer[$i++]=" USA2 - Display Date as mm/dd/yyyy, Time as: ddd-hh:mm:ss.frac {UT|GPS}";
	$HelpBuffer[$i++]=" USA3 - Display Date as mm/dd/yyyy, Time as: yyyy.ddd.hh:mm:ss {UT|GPS}";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]=" EUR1 - Display Date as dd/mm/yyyy, Time as: hh:mm:ss.frac {UTC|GPS}";
	$HelpBuffer[$i++]=" EUR2 - Display Date as dd/mm/yyyy, Time as: ddd-hh:mm:ss.frac {UT|GPS}";
	$HelpBuffer[$i++]=" EUR3 - Display Date as dd/mm/yyyy, Time as: yyyy.ddd.hh:mm:ss.frac {UT|GPS}";
	$HelpBuffer[$i++]="";
	$HelpBuffer[$i++]=" ISO1 - Display Date as yyyy-mm-<DD>, Time as: hh:mm:ss.frac {UTC|GPS}";
	$HelpBuffer[$i++]=" ISO2 - Display Date as yyyy-mm-<DD>, Time as: yyyy-mm-ddThh:mm:ss.frac {UTC|GPS}";
	$HelpBuffer[$i++]="";
	$HelpBufMax=$i;
}
