154359Sroberto#!/usr/bin/perl -w
254359Sroberto;# --*-perl-*--
354359Sroberto;#
4182007Sroberto;# /src/NTP/ntp4-dev/scripts/monitoring/ntploopwatch,v 4.7 2004/11/14 16:11:05 kardel RELEASE_20050508_A
554359Sroberto;#
654359Sroberto;# process loop filter statistics file and either
754359Sroberto;#     - show statistics periodically using gnuplot
854359Sroberto;#     - or print a single plot
954359Sroberto;#
1054359Sroberto;#  Copyright (c) 1992-1998 
1154359Sroberto;#  Rainer Pruy, Friedrich-Alexander Universit�t Erlangen-N�rnberg
1254359Sroberto;#
1354359Sroberto;#
1454359Sroberto;#############################################################
1554359Sroberto$0 =~ s!^.*/([^/]+)$!$1!;
1654359Sroberto$F = ' ' x length($0);
1754359Sroberto$|=1;
1854359Sroberto
1954359Sroberto$ENV{'SHELL'} = '/bin/sh'; # use bourne shell
2054359Sroberto
2154359Srobertoundef($config);
2254359Srobertoundef($workdir);
2354359Srobertoundef($PrintIt);
2454359Srobertoundef($samples);
2554359Srobertoundef($StartTime);
2654359Srobertoundef($EndTime);
2754359Sroberto($a,$b) if 0;			# keep -w happy
2854359Sroberto$usage = <<"E-O-P";
2954359Srobertousage:
3054359Sroberto  to watch statistics permanently:
3154359Sroberto     $0 [-v[<level>]] [-c <config-file>] [-d <working-dir>]
3254359Sroberto     $F [-h <hostname>]
3354359Sroberto
3454359Sroberto  to get a single print out specify also
3554359Sroberto     $F -P[<printer>] [-s<samples>]
3654359Sroberto     $F               [-S <start-time>] [-E <end-time>]
3754359Sroberto     $F               [-Y <MaxOffs>] [-y <MinOffs>]
3854359Sroberto
3954359SrobertoIf You like long option names, You can use:
4054359Sroberto    -help
4154359Sroberto    -c    +config
4254359Sroberto    -d    +directory
4354359Sroberto    -h    +host
4454359Sroberto    -v    +verbose[=<level>]
4554359Sroberto    -P    +printer[=<printer>]
4654359Sroberto    -s    +samples[=<samples>]
4754359Sroberto    -S    +starttime
4854359Sroberto    -E    +endtime
4954359Sroberto    -Y    +maxy
5054359Sroberto    -y    +miny
5154359Sroberto
5254359SrobertoIf <printer> contains a '/' (slash character) output is directed to 
5354359Srobertoa file of this name instead of delivered to a printer.
5454359SrobertoE-O-P
5554359Sroberto
5654359Sroberto;# add directory to look for lr.pl and timelocal.pl (in front of current list)
57182007Srobertounshift(@INC,".");
5854359Sroberto
5954359Srobertorequire "lr.pl";	# linear regresion routines
6054359Sroberto
6154359Sroberto$MJD_1970 = 40587;		# from ntp.h (V3)
6254359Sroberto$RecordSize = 48;		# usually a line fits into 42 bytes
6354359Sroberto$MinClip = 1;		# clip Y scales with greater range than this
6454359Sroberto
6554359Sroberto;# largest extension of Y scale from mean value, factor for standart deviation
6654359Sroberto$FuzzLow = 2.2;			# for side closer to zero
6754359Sroberto$FuzzBig = 1.8;			# for side farther from zero
6854359Sroberto
6954359Srobertorequire "ctime.pl";
7054359Srobertorequire "timelocal.pl";
7154359Sroberto;# early distributions of ctime.pl had a bug
7254359Sroberto$ENV{'TZ'} = 'MET' unless defined $ENV{'TZ'} || $[ > 4.010;
7354359Srobertoif (defined(@ctime'MoY))
7454359Sroberto{
7554359Sroberto  *Month=*ctime'MoY;
7654359Sroberto  *Day=*ctime'DoW;
7754359Sroberto} 					# ' re-sync emacs fontification
7854359Srobertoelse
7954359Sroberto{
8054359Sroberto  @Month = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
8154359Sroberto  @Day   = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
8254359Sroberto}
8354359Srobertoprint @ctime'DoW if 0; # ' re-sync emacs fontification
8454359Sroberto
8554359Sroberto;# max number of days per month
8654359Sroberto@MaxNumDaysPerMonth = (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
8754359Sroberto
8854359Sroberto;# config settable parameters
8954359Sroberto$delay = 60;
9054359Sroberto$srcprefix = "./var\@\$STATHOST/loopstats.";
9154359Sroberto$showoffs = 1;
9254359Sroberto$showfreq = 1;
9354359Sroberto$showcmpl = 0;
9454359Sroberto$showoreg = 0;
9554359Sroberto$showfreg = 0;
9654359Srobertoundef($timebase);
9754359Srobertoundef($freqbase);
9854359Srobertoundef($cmplscale);
9954359Srobertoundef($MaxY);
10054359Srobertoundef($MinY);
10154359Sroberto$deltaT  = 512; # indicate sample data gaps greater than $deltaT seconds
10254359Sroberto$verbose = 1;
10354359Sroberto
10454359Srobertowhile($_ = shift(@ARGV))
10554359Sroberto{
10654359Sroberto    (/^[+-]help$/) && die($usage);
10754359Sroberto    
10854359Sroberto    (/^-c$/ || /^\+config$/) &&
10954359Sroberto	(@ARGV || die($usage), $config = shift(@ARGV), next);
11054359Sroberto
11154359Sroberto    (/^-d$/ || /^\+directory$/) &&
11254359Sroberto	(@ARGV || die($usage), $workdir = shift(@ARGV), next);
11354359Sroberto
11454359Sroberto    (/^-h$/ || /^\+host$/) &&
11554359Sroberto	(@ARGV || die($usage), $STATHOST = shift, next);
11654359Sroberto    
11754359Sroberto    (/^-v(\d*)$/ || /^\+verbose=?(\d*)$/) &&
11854359Sroberto	($verbose=($1 eq "") ? 1 : $1, next);
11954359Sroberto
12054359Sroberto    (/^-P(\S*)$/ || /^\+[Pp]rinter=?(\S*)$/) &&
12154359Sroberto	($PrintIt = $1, $verbose==1 && ($verbose = 0), next);
12254359Sroberto
12354359Sroberto    (/^-s(\d*)$/ || /^\+samples=?(\d*)$/) &&
12454359Sroberto	(($samples = ($1 eq "") ? (shift || die($usage)): $1), next);
12554359Sroberto    
12654359Sroberto    (/^-S$/ || /^\+[Ss]tart[Tt]ime$/) &&
12754359Sroberto	(@ARGV || die($usage), $StartTime=&date_time_spec2seconds(shift),next);
12854359Sroberto
12954359Sroberto    (/^-E$/ || /^\+[Ee]nd[Tt]ime$/) &&
13054359Sroberto	(@ARGV || die($usage), $EndTime = &date_time_spec2seconds(shift),next);
13154359Sroberto    
13254359Sroberto    (/^-Y$/ || /^\+[Mm]ax[Yy]$/) &&
13354359Sroberto	(@ARGV || die($usage), $MaxY = shift, next);
13454359Sroberto    
13554359Sroberto    (/^-y$/ || /^\+[Mm]in[Yy]$/) &&
13654359Sroberto	(@ARGV || die($usage), $MinY = shift, next);
13754359Sroberto    
13854359Sroberto    die("$0: unexpected argument \"$_\"\n$usage");
13954359Sroberto}
14054359Sroberto
14154359Srobertoif (defined($workdir))
14254359Sroberto{
14354359Sroberto  chdir($workdir) ||
14454359Sroberto      die("$0: failed to change working dir to \"$workdir\": $!\n");
14554359Sroberto}
14654359Sroberto
14754359Sroberto$PrintIt = "ps" if defined($PrintIt) && $PrintIt eq "";
14854359Sroberto
14954359Srobertoif (!defined($PrintIt))
15054359Sroberto{
15154359Sroberto    defined($samples) &&
15254359Sroberto	print "WARNING: your samples value may be shadowed by config file settings\n";
15354359Sroberto    defined($StartTime) &&
15454359Sroberto	print "WARNING: your StartTime value may be shadowed by config file settings\n";
15554359Sroberto    defined($EndTime) &&
15654359Sroberto	print "WARNING: your EndTime value may be shadowed by config file settings\n";
15754359Sroberto    defined($MaxY) &&
15854359Sroberto	print "WARNING: your MaxY value may be shadowed by config file settings\n";
15954359Sroberto    defined($MinY) &&
16054359Sroberto	print "WARNING: your MinY value may be shadowed by config file settings\n";
16154359Sroberto	
16254359Sroberto    ;# check operating environment
16354359Sroberto    ;# 
16454359Sroberto    ;# gnuplot usually has X support
16554359Sroberto    ;# I vaguely remember there was one with sunview support
16654359Sroberto    ;#
16754359Sroberto    ;# If Your plotcmd can display graphics using some other method
16854359Sroberto    ;# (Tek window,..) fix the following test
16954359Sroberto    ;# (or may be, just disable it)
17054359Sroberto    ;#
17154359Sroberto    !(defined($ENV{'DISPLAY'}) || defined($ENV{'WINDOW_PARENT'})) &&
17254359Sroberto	die("Need window system to monitor statistics\n");
17354359Sroberto}
17454359Sroberto
17554359Sroberto;# configuration file
17654359Sroberto$config = "loopwatch.config" unless defined($config);
17754359Sroberto($STATHOST = $config) =~ s!.*loopwatch\.config.([^/\.]*)$!$1!
17854359Sroberto    unless defined($STATHOST);
17954359Sroberto($STATTAG = $STATHOST) =~ s/^([^\.\*\s]+)\..*$/$1/;
18054359Sroberto
18154359Sroberto$srcprefix =~ s/\$STATHOST/$STATHOST/g;
18254359Sroberto
18354359Sroberto;# plot command 
18454359Sroberto@plotcmd=("gnuplot",
18554359Sroberto	  '-title', "Ntp loop filter statistics $STATHOST",
18654359Sroberto	  '-name', "NtpLoopWatch_$STATTAG");
18754359Sroberto$tmpfile = "/tmp/ntpstat.$$";
18854359Sroberto
18954359Sroberto;# other variables
19054359Sroberto$doplot = "";	# assembled command for @plotcmd to display plot
19154359Srobertoundef($laststat);
19254359Sroberto
19354359Sroberto;# plot value ranges
19454359Srobertoundef($mintime);
19554359Srobertoundef($maxtime);
19654359Srobertoundef($minoffs);
19754359Srobertoundef($maxoffs);
19854359Srobertoundef($minfreq);
19954359Srobertoundef($maxfreq);
20054359Srobertoundef($mincmpl);
20154359Srobertoundef($maxcmpl);
20254359Srobertoundef($miny);
20354359Srobertoundef($maxy);
20454359Sroberto
20554359Sroberto;# stop operation if plot command dies
20654359Srobertosub sigchld
20754359Sroberto{
20854359Sroberto  local($pid) = wait;
20954359Sroberto  unlink($tmpfile);
21054359Sroberto  warn(sprintf("%s: %s died: exit status: %d signal %d\n",
21154359Sroberto	      $0,
21254359Sroberto	       (defined($Plotpid) && $Plotpid == $pid)
21354359Sroberto	       ? "plotcmd" : "unknown child $pid",
21454359Sroberto	       $?>>8,$? & 0xff)) if $?;
21554359Sroberto  exit(1) if $? && defined($Plotpid) && $pid == $Plotpid;
21654359Sroberto}
21754359Sroberto&sigchld if 0;
21854359Sroberto$SIG{'CHLD'} = "sigchld";
21954359Sroberto$SIG{'CLD'} = "sigchld";
22054359Sroberto
22154359Srobertosub abort
22254359Sroberto{
22354359Sroberto  unlink($tmpfile);
22454359Sroberto  defined($Plotpid) && kill('TERM',$Plotpid);
22554359Sroberto  die("$0: received signal SIG$_[$[] - exiting\n");
22654359Sroberto}
22754359Sroberto&abort if 0;	# make -w happy - &abort IS used
22854359Sroberto$SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = $SIG{'PIPE'} = "abort";
22954359Sroberto
23054359Sroberto;#
23154359Srobertosub abs
23254359Sroberto{
23354359Sroberto  ($_[$[] < 0) ? -($_[$[]) : $_[$[];
23454359Sroberto}
23554359Sroberto
23654359Srobertosub boolval
23754359Sroberto{
23854359Sroberto  local($v) = ($_[$[]);
23954359Sroberto
24054359Sroberto  return 1 if ($v eq 'yes') || ($v eq 'y');
24154359Sroberto  return 1 if ($v =~ /^[0-9]*$/) && ($v != 0);
24254359Sroberto  return 0;
24354359Sroberto}
24454359Sroberto
24554359Sroberto;#####################
24654359Sroberto;# start of real work 
24754359Sroberto
24854359Srobertoprint "starting plot command (" . join(" ",@plotcmd) . ")\n" if $verbose > 1;
24954359Sroberto
25054359Sroberto$Plotpid = open(PLOT,"|-");
25154359Srobertoselect((select(PLOT),$|=1)[$[]);	# make PLOT line bufferd
25254359Sroberto
25354359Srobertodefined($Plotpid) ||
25454359Sroberto    die("$0: failed to start plot command: $!\n");
25554359Sroberto
25654359Srobertounless ($Plotpid)
25754359Sroberto{
25854359Sroberto   ;# child == plot command
25954359Sroberto   close(STDOUT);
26054359Sroberto   open(STDOUT,">&STDERR") ||
26154359Sroberto       die("$0: failed to redirect STDOUT of plot command: $!\n");
26254359Sroberto   
26354359Sroberto   print STDOUT "plot command running as $$\n";
26454359Sroberto
26554359Sroberto   exec @plotcmd;
26654359Sroberto   die("$0: failed to exec (@plotcmd): $!\n");
26754359Sroberto   exit(1); # in case ...
26854359Sroberto}
26954359Sroberto
27054359Srobertosub read_config
27154359Sroberto{
27254359Sroberto  local($at) = (stat($config))[$[+9];
27354359Sroberto  local($_,$c,$v);
27454359Sroberto
27554359Sroberto  (undef($laststat),(print("stat $config failed: $!\n")),return) if ! defined($at);
27654359Sroberto  return if (defined($laststat) && ($laststat == $at));
27754359Sroberto  $laststat = $at;
27854359Sroberto
27954359Sroberto  print "reading configuration from \"$config\"\n" if $verbose;
28054359Sroberto
28154359Sroberto  open(CF,"<$config") ||
28254359Sroberto      (warn("$0: failed to read \"$config\" - using old settings ($!)\n"),
28354359Sroberto       return);
28454359Sroberto  while(<CF>)
28554359Sroberto  {
28654359Sroberto    chop;
28754359Sroberto    s/^([^\#]*[^\#\s]?)\s*\#.*$//;
28854359Sroberto    next if /^\s*$/;
28954359Sroberto
29054359Sroberto    s/^\s*([^=\s]*)\s*=\s*(.*\S)\s*$/$1=$2/;
29154359Sroberto
29254359Sroberto    ($c,$v) = split(/=/,$_,2);
29354359Sroberto    print "processing \"$c=$v\"\n" if $verbose > 3;
29454359Sroberto    ($c eq "delay") && ($delay = $v,1) && next;
29554359Sroberto    ($c eq 'samples') && (!defined($PrintIt) || !defined($samples)) &&
29654359Sroberto	($samples = $v,1) && next;
29754359Sroberto    ($c eq 'srcprefix') && (($srcprefix=$v)=~s/\$STATHOST/$STATHOST/g,1)
29854359Sroberto	&& next;
29954359Sroberto    ($c eq 'showoffs') &&
30054359Sroberto	($showoffs = boolval($v),1) && next;
30154359Sroberto    ($c eq 'showfreq') &&
30254359Sroberto	($showfreq = boolval($v),1) && next;
30354359Sroberto    ($c eq 'showcmpl') &&
30454359Sroberto	($showcmpl = boolval($v),1) && next;
30554359Sroberto    ($c eq 'showoreg') &&
30654359Sroberto	($showoreg = boolval($v),1) && next;
30754359Sroberto    ($c eq 'showfreg') &&
30854359Sroberto	($showfreg = boolval($v),1) && next;
30954359Sroberto
31054359Sroberto    ($c eq 'exit') && (unlink($tmpfile),die("$0: exit by config request\n"));
31154359Sroberto
31254359Sroberto    ($c eq 'freqbase' ||
31354359Sroberto     $c eq 'cmplscale') &&
31454359Sroberto	do {
31554359Sroberto	    if (! defined($v) || $v eq "" || $v eq 'dynamic')
31654359Sroberto	    {
31754359Sroberto	      eval "undef(\$$c);";
31854359Sroberto	    }
31954359Sroberto	    else
32054359Sroberto	    {
32154359Sroberto	      eval "\$$c = \$v;";
32254359Sroberto	    }
32354359Sroberto	    next;
32454359Sroberto	};
32554359Sroberto    ($c eq 'timebase') &&
32654359Sroberto	do {
32754359Sroberto	    if (! defined($v) || $v eq "" || $v eq "dynamic")
32854359Sroberto	    {
32954359Sroberto	      undef($timebase);
33054359Sroberto	    }
33154359Sroberto	    else
33254359Sroberto	    {
33354359Sroberto	      $timebase=&date_time_spec2seconds($v);
33454359Sroberto	    }
33554359Sroberto	};
33654359Sroberto    ($c eq 'EndTime') &&
33754359Sroberto	do {
33854359Sroberto	    next if defined($EndTime) && defined($PrintIt);
33954359Sroberto	    if (! defined($v) || $v eq "" || $v eq "none")
34054359Sroberto	    {
34154359Sroberto	      undef($EndTime);
34254359Sroberto	    }
34354359Sroberto	    else
34454359Sroberto	    {
34554359Sroberto	      $EndTime=&date_time_spec2seconds($v);
34654359Sroberto	    }
34754359Sroberto	};
34854359Sroberto    ($c eq 'StartTime') &&
34954359Sroberto	do {
35054359Sroberto	    next if defined($StartTime) && defined($PrintIt);
35154359Sroberto	    if (! defined($v) || $v eq "" || $v eq "none")
35254359Sroberto	    {
35354359Sroberto	      undef($StartTime);
35454359Sroberto	    }
35554359Sroberto	    else
35654359Sroberto	    {
35754359Sroberto	      $StartTime=&date_time_spec2seconds($v);
35854359Sroberto	    }
35954359Sroberto	};
36054359Sroberto
36154359Sroberto    ($c eq 'MaxY') &&
36254359Sroberto	do {
36354359Sroberto	    next if defined($MaxY) && defined($PrintIt);
36454359Sroberto	    if (! defined($v) || $v eq "" || $v eq "none")
36554359Sroberto	    {
36654359Sroberto	      undef($MaxY);
36754359Sroberto	    }
36854359Sroberto	    else
36954359Sroberto	    {
37054359Sroberto	      $MaxY=$v;
37154359Sroberto	    }
37254359Sroberto	};
37354359Sroberto
37454359Sroberto    ($c eq 'MinY') &&
37554359Sroberto	do {
37654359Sroberto	    next if defined($MinY) && defined($PrintIt);
37754359Sroberto	    if (! defined($v) || $v eq "" || $v eq "none")
37854359Sroberto	    {
37954359Sroberto	      undef($MinY);
38054359Sroberto	    }
38154359Sroberto	    else
38254359Sroberto	    {
38354359Sroberto	      $MinY=$v;
38454359Sroberto	    }
38554359Sroberto	};
38654359Sroberto
38754359Sroberto    ($c eq 'deltaT') &&
38854359Sroberto	do {
38954359Sroberto	    if (!defined($v) || $v eq "")
39054359Sroberto	    {
39154359Sroberto	      undef($deltaT);
39254359Sroberto	    }
39354359Sroberto	    else
39454359Sroberto	    {
39554359Sroberto	      $deltaT = $v;
39654359Sroberto	    }
39754359Sroberto	    next;
39854359Sroberto	};
39954359Sroberto    ($c eq 'verbose') && ! defined($PrintIt) &&
40054359Sroberto	do {
40154359Sroberto	     if (!defined($v) || $v == 0)
40254359Sroberto	     {
40354359Sroberto	       $verbose = 0;
40454359Sroberto	     }
40554359Sroberto	     else
40654359Sroberto	     {
40754359Sroberto	       $verbose = $v;
40854359Sroberto	     }
40954359Sroberto	     next;
41054359Sroberto	};
41154359Sroberto    ;# otherwise: silently ignore unrecognized config line
41254359Sroberto  }
41354359Sroberto  close(CF);
41454359Sroberto  ;# set show defaults when nothing selected
41554359Sroberto  $showoffs = $showfreq = $showcmpl = 1
41654359Sroberto      unless $showoffs || $showfreq || $showcmpl;
41754359Sroberto  if ($verbose > 3)
41854359Sroberto  {
41954359Sroberto    print  "new configuration:\n";
42054359Sroberto    print  "   delay\t= $delay\n";
42154359Sroberto    print  "   samples\t= $samples\n";
42254359Sroberto    print  "   srcprefix\t= $srcprefix\n";
42354359Sroberto    print  "   showoffs\t= $showoffs\n";
42454359Sroberto    print  "   showfreq\t= $showfreq\n";
42554359Sroberto    print  "   showcmpl\t= $showcmpl\n";
42654359Sroberto    print  "   showoreg\t= $showoreg\n";
42754359Sroberto    print  "   showfreg\t= $showfreg\n";
42854359Sroberto    printf "   timebase\t= %s",defined($timebase)?&ctime($timebase):"dynamic\n";
42954359Sroberto    printf "   freqbase\t= %s\n",defined($freqbase)  ?"$freqbase":"dynamic";
43054359Sroberto    printf "   cmplscale\t= %s\n",defined($cmplscale)?"$cmplscale":"dynamic";
43154359Sroberto    printf "   StartTime\t= %s",defined($StartTime)?&ctime($StartTime):"none\n";
43254359Sroberto    printf "   EndTime\t= %s",  defined($EndTime) ?  &ctime($EndTime):"none\n";
43354359Sroberto    printf "   MaxY\t= %s",defined($MaxY)? $MaxY      :"none\n";
43454359Sroberto    printf "   MinY\t= %s",defined($MinY)? $MinY      :"none\n";
43554359Sroberto    print  "   verbose\t= $verbose\n";
43654359Sroberto  }
43754359Srobertoprint "configuration file read\n" if $verbose > 2;
43854359Sroberto}
43954359Sroberto
440182007Srobertosub make_doplot($$)
44154359Sroberto{
442182007Sroberto    my($lo, $lf) = @_;
44354359Sroberto    local($c) = ("");
44454359Sroberto    local($fmt)
44554359Sroberto	= ("%s \"%s\" using 1:%d title '%s <%lf %lf> %6s' with lines");
44654359Sroberto    local($regfmt)
44754359Sroberto	= ("%s ((%lf * x) + %lf) title 'lin. approx. %s (%f t[h]) %s %f <%f> %6s' with lines");
44854359Sroberto    
44954359Sroberto    $doplot = "    set title 'NTP loopfilter statistics for $STATHOST  " .
45054359Sroberto	"(last $LastCnt samples from $srcprefix*)'\n";
45154359Sroberto    
45254359Sroberto    local($xts,$xte,$i,$t);
45354359Sroberto    
45454359Sroberto    local($s,$c) = ("");
45554359Sroberto
45654359Sroberto    ;# number of integral seconds to get at least 12 tic marks on x axis
45754359Sroberto    $t = int(($maxtime - $mintime) / 12 + 0.5);
45854359Sroberto    $t = 1 unless $t;		# prevent $t to be zero
45954359Sroberto    foreach $i (30,
46054359Sroberto		60,5*60,15*60,30*60,
46154359Sroberto		60*60,2*60*60,6*60*60,12*60*60,
46254359Sroberto		24*60*60,48*60*60)
46354359Sroberto    {
46454359Sroberto	last if $t < $i;
46554359Sroberto	$t = $t - ($t % $i);
46654359Sroberto    }
46754359Sroberto    print "time label resolution: $t seconds\n" if $verbose > 1;
46854359Sroberto    
46954359Sroberto    ;# make gnuplot use wall clock time labels instead of NTP seconds
47054359Sroberto    for ($c="", $i = $mintime - ($mintime % $t);
47154359Sroberto	 $i <= $maxtime + $t;
47254359Sroberto	 $i += $t, $c=",")
47354359Sroberto    {
47454359Sroberto	$s .= $c;
47554359Sroberto	((int($i / $t) % 2) &&
47654359Sroberto	 ($s .= sprintf("'' %lf",($i - $LastTimeBase)/3600))) ||
47754359Sroberto	     (($t <= 60) &&
47854359Sroberto	      ($s .= sprintf("'%d:%02d:%02d' %lf",
47954359Sroberto			     (localtime($i))[$[+2,$[+1,$[+0],
48054359Sroberto			     ($i - $LastTimeBase)/3600))) 
48154359Sroberto		 || (($t <= 2*60*60) &&
48254359Sroberto		     ($s .= sprintf("'%d:%02d' %lf",
48354359Sroberto				    (localtime($i))[$[+2,$[+1],
48454359Sroberto				    ($i - $LastTimeBase)/3600)))
48554359Sroberto		     || (($t <= 12*60*60) &&
48654359Sroberto			 ($s .= sprintf("'%s %d:00' %lf",
48754359Sroberto					$Day[(localtime($i))[$[+6]],
48854359Sroberto					(localtime($i))[$[+2],
48954359Sroberto					($i - $LastTimeBase)/3600)))
49054359Sroberto			 || ($s .= sprintf("'%d.%d-%d:00' %lf",
49154359Sroberto					   (localtime($i))[$[+3,$[+4,$[+2],
49254359Sroberto					   ($i - $LastTimeBase)/3600));
49354359Sroberto    }
49454359Sroberto    $doplot .= "set xtics ($s)\n";
49554359Sroberto    
49654359Sroberto    chop($xts = &ctime($mintime));
49754359Sroberto    chop($xte = &ctime($maxtime));
49854359Sroberto    $doplot .= "set xlabel 'Start:  $xts    --   Time Scale   --    End:  $xte'\n";
49954359Sroberto    $doplot .= "set yrange [" ;
50054359Sroberto    $doplot .= defined($MinY) ? sprintf("%lf", $MinY) : $miny;
50154359Sroberto    $doplot .= ':';
50254359Sroberto    $doplot .= defined($MaxY) ? sprintf("%lf", $MaxY) : $maxy;
50354359Sroberto    $doplot .= "]\n";
50454359Sroberto    
50554359Sroberto    $doplot .= "   plot";
50654359Sroberto    $c = "";
50754359Sroberto    $showoffs &&
50854359Sroberto	($doplot .= sprintf($fmt,$c,$tmpfile,2,
50954359Sroberto			    "offset",
51054359Sroberto			    $minoffs,$maxoffs,
51154359Sroberto			    "[ms]"),
51254359Sroberto	 $c = ",");
51354359Sroberto    $LastCmplScale = 1 if ! defined($LastCmplScale);
51454359Sroberto    $showcmpl &&
51554359Sroberto	($doplot .= sprintf($fmt,$c,$tmpfile,4,
51654359Sroberto			    "compliance" .
51754359Sroberto			    (&abs($LastCmplScale) > 1
51854359Sroberto			     ? " / $LastCmplScale"
51954359Sroberto			     : (&abs($LastCmplScale) == 1 ? "" : " * ".(1/$LastCmplScale))),
52054359Sroberto			    $mincmpl/$LastCmplScale,$maxcmpl/$LastCmplScale,
52154359Sroberto			    ""),
52254359Sroberto	 $c = ",");
52354359Sroberto    $LastFreqBase = 0 if ! defined($LastFreqBase);
52454359Sroberto    $LastFreqBaseString = "?" if ! defined($LastFreqBaseString);
52554359Sroberto    $FreqScale = 1 if ! defined($FreqScale);
52654359Sroberto    $FreqScaleInv = 1 if ! defined($FreqScaleInv);
52754359Sroberto    $showfreq &&
52854359Sroberto	($doplot .= sprintf($fmt,$c,$tmpfile,3,
52954359Sroberto			    "frequency" .
53054359Sroberto			    ($LastFreqBase > 0
53154359Sroberto			     ? " - $LastFreqBaseString" 
53254359Sroberto			     : ($LastFreqBase == 0 ? "" : " + $LastFreqBaseString")),
53354359Sroberto			    $minfreq * $FreqScale - $LastFreqBase,
53454359Sroberto			    $maxfreq * $FreqScale - $LastFreqBase,
53554359Sroberto			    "[${FreqScaleInv}ppm]"),
53654359Sroberto	 $c = ",");
53754359Sroberto    $showoreg && $showoffs &&
53854359Sroberto	($doplot .= sprintf($regfmt, $c,
539182007Sroberto			    $lo->B(),$lo->A(),
54054359Sroberto			    "offset   ",
541182007Sroberto			    $lo->B(),
542182007Sroberto			    (($lo->A()) < 0 ? '-' : '+'),
543182007Sroberto			    &abs($lo->A()), $lo->r(),
54454359Sroberto			    "[ms]"),
54554359Sroberto	 $c = ",");
54654359Sroberto    $showfreg && $showfreq &&
54754359Sroberto	($doplot .= sprintf($regfmt, $c,
548182007Sroberto			    $lf->B() * $FreqScale,
549182007Sroberto			    ($lf->A() + $minfreq) * $FreqScale - $LastFreqBase,
55054359Sroberto			    "frequency",
551182007Sroberto			    $lf->B() * $FreqScale,
552182007Sroberto			    (($lf->A() + $minfreq) * $FreqScale - $LastFreqBase) < 0 ? '-' : '+',
553182007Sroberto			    &abs(($lf->A() + $minfreq) * $FreqScale - $LastFreqBase),
554182007Sroberto			    $lf->r(),
55554359Sroberto			    "[${FreqScaleInv}ppm]"),
55654359Sroberto	 $c = ",");
55754359Sroberto    $doplot .= "\n";
55854359Sroberto}
55954359Sroberto
56054359Sroberto%F_key   = ();
56154359Sroberto%F_name  = ();
56254359Sroberto%F_size  = ();
56354359Sroberto%F_mtime = ();
56454359Sroberto%F_first = ();
56554359Sroberto%F_last  = ();
56654359Sroberto
56754359Srobertosub genfile
56854359Sroberto{
569182007Sroberto    local($cnt,$in,$out,$lo,$lf,@fpos) = @_;
57054359Sroberto    
57154359Sroberto    local(@F,@t,$t,$lastT) = ();
57254359Sroberto    local(@break,@time,@offs,@freq,@cmpl,@loffset,@filekey) = ();
57354359Sroberto    local($lm,$l,@f);
57454359Sroberto    
57554359Sroberto    local($sdir,$sname);
57654359Sroberto    
57754359Sroberto    ;# allocate some storage for the tables
57854359Sroberto    ;# otherwise realloc may get into troubles
57954359Sroberto    if (defined($StartTime) && defined($EndTime))
58054359Sroberto    {
58154359Sroberto	$l = ($EndTime-$StartTime) -$[+1 +1; # worst case: 1 sample per second
58254359Sroberto    }
58354359Sroberto    else
58454359Sroberto    {
58554359Sroberto	$l = $cnt + 10;
58654359Sroberto    }
58754359Sroberto    print "preextending arrays to $l entries\n" if $verbose > 2;
58854359Sroberto    $#break =   $l; for ($i=$[; $i<=$l;$i++) { $break[$i] = 0; }
58954359Sroberto    $#time =    $l; for ($i=$[; $i<=$l;$i++) { $time[$i] = 0; }
59054359Sroberto    $#offs =    $l; for ($i=$[; $i<=$l;$i++) { $offs[$i] = 0; }
59154359Sroberto    $#freq =    $l; for ($i=$[; $i<=$l;$i++) { $freq[$i] = 0; }
59254359Sroberto    $#cmpl =    $l; for ($i=$[; $i<=$l;$i++) { $cmpl[$i] = 0; }
59354359Sroberto    $#loffset = $l; for ($i=$[; $i<=$l;$i++) { $loffset[$i] = 0; }
59454359Sroberto    $#filekey = $l; for ($i=$[; $i<=$l;$i++) { $filekey[$i] = 0; }
59554359Sroberto    ;# now reduce size again
59654359Sroberto    $#break =   $[ - 1;
59754359Sroberto    $#time =    $[ - 1;
59854359Sroberto    $#offs =    $[ - 1;
59954359Sroberto    $#freq =    $[ - 1;
60054359Sroberto    $#cmpl =    $[ - 1;
60154359Sroberto    $#loffset = $[ - 1;
60254359Sroberto    $#filekey = $[ - 1;
60354359Sroberto    print "memory allocation ready\n" if $verbose > 2;
60454359Sroberto    sleep(3) if $verbose > 1;
60554359Sroberto
60654359Sroberto    $fpos[$[] = '' if !defined($fpos[$[]);
60754359Sroberto
60854359Sroberto    if (index($in,"/") < $[)
60954359Sroberto    {
61054359Sroberto	$sdir = ".";
61154359Sroberto	$sname = $in;
61254359Sroberto    }
61354359Sroberto    else
61454359Sroberto    {
61554359Sroberto	($sdir,$sname) = ($in =~ m!^(.*)/([^/]*)!);
61654359Sroberto	$sname = "" unless defined($sname);
61754359Sroberto    }
61854359Sroberto    
61954359Sroberto    $Ltime = -1 if ! defined($Ltime);
62054359Sroberto    if (!defined($Lsdir) || $Lsdir ne $sdir || $Ltime != (stat($sdir))[$[+9] ||
62154359Sroberto	grep($F_mtime{$_} != (stat($F_name{$_}))[$[+9], @F_files))
62254359Sroberto	
62354359Sroberto    {
62454359Sroberto	print "rescanning directory \"$sdir\" for files \"$sname*\"\n"
62554359Sroberto	    if $verbose > 1;
62654359Sroberto
62754359Sroberto	;# rescan directory on changes
62854359Sroberto	$Lsdir = $sdir;
62954359Sroberto	$Ltime = (stat($sdir))[$[+9];
63054359Sroberto	</X{> if 0;		# dummy line - calm down my formatter
63154359Sroberto	local(@newfiles) = < ${in}*[0-9] >;
63254359Sroberto	local($st_dev,$st_ino,$st_mtime,$st_size,$name,$key,$modified);
63354359Sroberto
63454359Sroberto	foreach $name (@newfiles)
63554359Sroberto	{
63654359Sroberto	    ($st_dev,$st_ino,$st_size,$st_mtime) =
63754359Sroberto		(stat($name))[$[,$[+1,$[+7,$[+9];
63854359Sroberto	    $modified = 0;
63954359Sroberto	    $key = sprintf("%lx|%lu", $st_dev, $st_ino);
64054359Sroberto	    
64154359Sroberto	    print "candidate file \"$name\"",
64254359Sroberto                  (defined($st_dev) ? "" : " failed: $!"),"\n"
64354359Sroberto		      if $verbose > 2;
64454359Sroberto	    
64554359Sroberto	    if (! defined($F_key{$name}) || $F_key{$name} ne $key)
64654359Sroberto	    {
64754359Sroberto		$F_key{$name} = $key;
64854359Sroberto		$modified++;
64954359Sroberto	    }
65054359Sroberto	    if (!defined($F_name{$key}) || $F_name{$key} ne $name)
65154359Sroberto	    {
65254359Sroberto		$F_name{$key} = $name;
65354359Sroberto		$modified++;
65454359Sroberto	    }
65554359Sroberto	    if (!defined($F_size{$key}) || $F_size{$key} != $st_size)
65654359Sroberto	    {
65754359Sroberto		$F_size{$key} = $st_size;
65854359Sroberto		$modified++;
65954359Sroberto	    }
66054359Sroberto	    if (!defined($F_mtime{$key}) || $F_mtime{$key} != $st_mtime)
66154359Sroberto	    {
66254359Sroberto		$F_mtime{$key} = $st_mtime;
66354359Sroberto		$modified++;
66454359Sroberto	    }
66554359Sroberto	    if ($modified)
66654359Sroberto	    {
66754359Sroberto		print "new data \"$name\" key: $key;\n" if $verbose > 1;
66854359Sroberto	        print "             size: $st_size; mtime: $st_mtime;\n"
66954359Sroberto		    if $verbose > 1;
67054359Sroberto		$F_last{$key} = $F_first{$key} = $st_mtime;
67154359Sroberto		$F_first{$key}--; # prevent zero divide later on
67254359Sroberto		;# now compute derivated attributes
67354359Sroberto		open(IN, "<$name") ||
67454359Sroberto		    do {
67554359Sroberto			warn "$0: failed to open \"$name\": $!";
67654359Sroberto			next;
67754359Sroberto		    };
67854359Sroberto
67954359Sroberto		while(<IN>)
68054359Sroberto		{
68154359Sroberto		    @F = split;
68254359Sroberto		    next if @F < 5;
68354359Sroberto		    next if $F[$[] eq "";
68454359Sroberto		    $t = ($F[$[] - $MJD_1970) * 24 * 60 * 60;
68554359Sroberto		    $t += $F[$[+1];
68654359Sroberto		    $F_first{$key} = $t;
68754359Sroberto		    print "\tfound first entry: $t ",&ctime($t)
68854359Sroberto			if $verbose > 4;
68954359Sroberto		    last;
69054359Sroberto		}
69154359Sroberto		seek(IN,
69254359Sroberto		     ($st_size > 4*$RecordSize) ? $st_size - 4*$RecordSize : 0,
69354359Sroberto		     0);
69454359Sroberto		while(<IN>)
69554359Sroberto		{
69654359Sroberto		    @F = split;
69754359Sroberto		    next if @F < 5;
69854359Sroberto		    next if $F[$[] eq "";
69954359Sroberto		    $t = ($F[$[] - $MJD_1970) * 24 * 60 * 60;
70054359Sroberto		    $t += $F[$[+1];
70154359Sroberto		    $F_last{$key} = $t;
70254359Sroberto		    $_ = <IN>;
70354359Sroberto		    print "\tfound last entry: $t ", &ctime($t)
70454359Sroberto			if $verbose > 4 && ! defined($_);
70554359Sroberto		    last unless defined($_);
70654359Sroberto		    redo;
70754359Sroberto		    ;# Ok, calm down...
70854359Sroberto		    ;# using $_ = <IN> in conjunction with redo
70954359Sroberto		    ;# is semantically equivalent to the while loop, but
71054359Sroberto		    ;# I needed a one line look ahead and this solution
71154359Sroberto		    ;# was what I thought of first
71254359Sroberto		    ;# and.. If you do not like it dont look
71354359Sroberto		}
71454359Sroberto		close(IN);
71554359Sroberto		print("             first: ",$F_first{$key},
71654359Sroberto		      " last: ",$F_last{$key},"\n") if $verbose > 1;
71754359Sroberto	    }
71854359Sroberto	}
71954359Sroberto	;# now reclaim memory used for files no longer referenced ...
72054359Sroberto	local(%Names);
72154359Sroberto	grep($Names{$_} = 1,@newfiles);
72254359Sroberto	foreach (keys %F_key)
72354359Sroberto	{
72454359Sroberto	    next if defined($Names{$_});
72554359Sroberto	    delete $F_key{$_};
72654359Sroberto	    $verbose > 2 && print "no longer referenced: \"$_\"\n";
72754359Sroberto	}
72854359Sroberto	%Names = ();
72954359Sroberto	
73054359Sroberto	grep($Names{$_} = 1,values(%F_key));
73154359Sroberto	foreach (keys %F_name)
73254359Sroberto	{
73354359Sroberto	    next if defined($Names{$_});
73454359Sroberto	    delete $F_name{$_};
73554359Sroberto	    $verbose > 2 && print "unref name($_)= $F_name{$_}\n";
73654359Sroberto	}
73754359Sroberto	foreach (keys %F_size)
73854359Sroberto	{
73954359Sroberto	    next if defined($Names{$_});
74054359Sroberto	    delete $F_size{$_};
74154359Sroberto	    $verbose > 2 && print "unref size($_)\n";
74254359Sroberto	}
74354359Sroberto	foreach (keys %F_mtime)
74454359Sroberto	{
74554359Sroberto	    next if defined($Names{$_});
74654359Sroberto	    delete $F_mtime{$_};
74754359Sroberto	    $verbose > 2 && print "unref mtime($_)\n";
74854359Sroberto	}
74954359Sroberto	foreach (keys %F_first)
75054359Sroberto	{
75154359Sroberto	    next if defined($Names{$_});
75254359Sroberto	    delete $F_first{$_};
75354359Sroberto	    $verbose > 2 && print "unref first($_)\n";
75454359Sroberto	}
75554359Sroberto	foreach (keys %F_last)
75654359Sroberto	{
75754359Sroberto	    next if defined($Names{$_});
75854359Sroberto	    delete $F_last{$_};
75954359Sroberto	    $verbose > 2 && print "unref last($_)\n";
76054359Sroberto	}
76154359Sroberto	;# create list sorted by time
76254359Sroberto	@F_files = sort {$F_first{$a} <=> $F_first{$b}; } keys(%F_name);
76354359Sroberto	if ($verbose > 1)
76454359Sroberto	{
76554359Sroberto	    print "Resulting file list:\n";
76654359Sroberto	    foreach (@F_files)
76754359Sroberto	    {
76854359Sroberto		print "\t$_\t$F_name{$_}\n";
76954359Sroberto	    }
77054359Sroberto	}
77154359Sroberto    }
77254359Sroberto    
77354359Sroberto    printf("processing %s; output \"$out\" (%d input files)\n",
77454359Sroberto	   ((defined($StartTime) && defined($EndTime))
77554359Sroberto	    ? "time range"
77654359Sroberto	    : (defined($StartTime) ? "$cnt samples from StartTime" :
77754359Sroberto	      (defined($EndTime) ? "$cnt samples to EndTime" :
77854359Sroberto		 "last $cnt samples"))),
77954359Sroberto	    scalar(@F_files))
78054359Sroberto	if $verbose > 1;
78154359Sroberto    
78254359Sroberto    ;# open output file - will be input for plotcmd
78354359Sroberto    open(OUT,">$out") || 
78454359Sroberto	do {
78554359Sroberto	    warn("$0: cannot create \"$out\": $!\n");
78654359Sroberto	};
78754359Sroberto    
78854359Sroberto    @f = @F_files;
78954359Sroberto    if (defined($StartTime))
79054359Sroberto    {
79154359Sroberto	while (@f && ($F_last{$f[$[]} < $StartTime))
79254359Sroberto	{
79354359Sroberto	    print("shifting ", $F_name{$f[$[]},
79454359Sroberto		  " last: ", $F_last{$f[$[]},
79554359Sroberto		  " < StartTime: $StartTime\n")
79654359Sroberto		if $verbose > 3;
79754359Sroberto	    shift(@f);
79854359Sroberto	}
79954359Sroberto
80054359Sroberto
80154359Sroberto    }
80254359Sroberto    if (defined($EndTime))
80354359Sroberto    {
80454359Sroberto	while (@f && ($F_first{$f[$#f]} > $EndTime))
80554359Sroberto	{
80654359Sroberto	    print("popping  ", $F_name{$f[$#f]},
80754359Sroberto		  " first: ", $F_first{$f[$#f]},
80854359Sroberto		  " > EndTime: $EndTime\n")
80954359Sroberto		if $verbose > 3;
81054359Sroberto	    pop(@f);
81154359Sroberto	}
81254359Sroberto    }
81354359Sroberto    
81454359Sroberto    if (@f)
81554359Sroberto    {
81654359Sroberto	if (defined($StartTime))
81754359Sroberto	{
81854359Sroberto	    print "guess start according to StartTime ($StartTime)\n"
81954359Sroberto		if $verbose > 3;
82054359Sroberto
82154359Sroberto	    if ($fpos[$[] eq 'start')
82254359Sroberto	    {
82354359Sroberto		if (grep($_ eq $fpos[$[+1],@f))
82454359Sroberto		{
82554359Sroberto		    shift(@f) while @f && $f[$[] ne $fpos[$[+1];
82654359Sroberto		}
82754359Sroberto		else
82854359Sroberto		{
82954359Sroberto		    @fpos = ('start', $f[$[], undef);
83054359Sroberto		}
83154359Sroberto	    }
83254359Sroberto	    else
83354359Sroberto	    {
83454359Sroberto		@fpos = ('start' , $f[$[], undef);
83554359Sroberto	    }
83654359Sroberto	    
83754359Sroberto	    if (!defined($fpos[$[+2]))
83854359Sroberto	    {
83954359Sroberto		if ($StartTime <= $F_first{$f[$[]})
84054359Sroberto		{
84154359Sroberto		    $fpos[$[+2] = 0;
84254359Sroberto		}
84354359Sroberto		else
84454359Sroberto		{
84554359Sroberto		    $fpos[$[+2] =
84654359Sroberto			int($F_size{$f[$[]} *
84754359Sroberto			    (($StartTime - $F_first{$f[$[]})/
84854359Sroberto			     ($F_last{$f[$[]} - $F_first{$f[$[]})));
84954359Sroberto		    $fpos[$[+2] = ($fpos[$[+2] <= 2 * $RecordSize)
85054359Sroberto			? 0 : $fpos[$[+2] - 2 * $RecordSize;
85154359Sroberto		    ;# anyway  as the data may contain "time holes" 
85254359Sroberto		    ;# our heuristics may baldly fail
85354359Sroberto		    ;# so just start at 0
85454359Sroberto		    $fpos[$[+2] = 0;
85554359Sroberto		}
85654359Sroberto	    }
85754359Sroberto	}
85854359Sroberto	elsif (defined($EndTime))
85954359Sroberto	{
86054359Sroberto	    print "guess starting point according to EndTime ($EndTime)\n"
86154359Sroberto		if $verbose > 3;
86254359Sroberto	    
86354359Sroberto	    if ($fpos[$[] eq 'end')
86454359Sroberto	    {
86554359Sroberto		if (grep($_ eq $fpos[$[+1],@f))
86654359Sroberto		{
86754359Sroberto		    shift(@f) while @f && $f[$[] ne $fpos[$[+1];
86854359Sroberto		}
86954359Sroberto		else
87054359Sroberto		{
87154359Sroberto		    @fpos = ('end', $f[$[], undef);
87254359Sroberto		}
87354359Sroberto	    }
87454359Sroberto	    else
87554359Sroberto	    {
87654359Sroberto		@fpos = ('end', $f[$[], undef);
87754359Sroberto	    }
87854359Sroberto	    
87954359Sroberto	    if (!defined($fpos[$[+2]))
88054359Sroberto	    {
88154359Sroberto		local(@x) = reverse(@f);
88254359Sroberto		local($s,$c) = (0,$cnt);
88354359Sroberto		if ($EndTime < $F_last{$x[$[]})
88454359Sroberto		{
88554359Sroberto		    ;# last file will only be used partially
88654359Sroberto		    $s = int($F_size{$x[$[]} *
88754359Sroberto			     (($EndTime - $F_first{$x[$[]}) /
88854359Sroberto			      ($F_last{$x[$[]} - $F_first{$x[$[]})));
88954359Sroberto		    $s = int($s/$RecordSize);
89054359Sroberto		    $c -= $s - 1;
89154359Sroberto		    if ($c <= 0)
89254359Sroberto		    {
89354359Sroberto			;# start is in the same file
89454359Sroberto			$fpos[$[+1] = $x[$[];
89554359Sroberto			$fpos[$[+2] = ($c >=-2) ? 0 : (-$c - 2) * $RecordSize;
89654359Sroberto			shift(@f) while @f && ($f[$[] ne $x[$[]);
89754359Sroberto		    }
89854359Sroberto		    else
89954359Sroberto		    {
90054359Sroberto			shift(@x);
90154359Sroberto		    }
90254359Sroberto		}
90354359Sroberto		
90454359Sroberto		if (!defined($fpos[$[+2]))
90554359Sroberto		{
90654359Sroberto		    local($_);
90754359Sroberto		    while($_ = shift(@x))
90854359Sroberto		    {
90954359Sroberto			$s = int($F_size{$_}/$RecordSize);
91054359Sroberto			$c -= $s - 1;
91154359Sroberto			if ($c <= 0)
91254359Sroberto			{
91354359Sroberto			    $fpos[$[+1] = $_;
91454359Sroberto			    $fpos[$[+2] = ($c>-2) ? 0 : (-$c - 2) * $RecordSize;
91554359Sroberto			    shift(@f) while @f && ($f[$[] ne $_);
91654359Sroberto			    last;
91754359Sroberto			}
91854359Sroberto		    }
91954359Sroberto		}
92054359Sroberto	    }
92154359Sroberto	}
92254359Sroberto	else
92354359Sroberto	{
92454359Sroberto	    print "guessing starting point according to count ($cnt)\n"
92554359Sroberto		if $verbose > 3;
92654359Sroberto	    ;# guess offset to get last available $cnt samples
92754359Sroberto	    if ($fpos[$[] eq 'cnt')
92854359Sroberto	    {
92954359Sroberto		if (grep($_ eq $fpos[$[+1],@f))
93054359Sroberto		{
93154359Sroberto		    print "old positioning applies\n" if $verbose > 3;
93254359Sroberto		    shift(@f) while @f && $f[$[] ne $fpos[$[+1];
93354359Sroberto		}
93454359Sroberto		else
93554359Sroberto		{
93654359Sroberto		    @fpos = ('cnt', $f[$[], undef);
93754359Sroberto		}
93854359Sroberto	    }
93954359Sroberto	    else
94054359Sroberto	    {
94154359Sroberto		@fpos = ('cnt', $f[$[], undef);
94254359Sroberto	    }
94354359Sroberto	    
94454359Sroberto	    if (!defined($fpos[$[+2]))
94554359Sroberto	    {
94654359Sroberto		local(@x) = reverse(@f);
94754359Sroberto		local($s,$c) = (0,$cnt);
94854359Sroberto		
94954359Sroberto		local($_);
95054359Sroberto		while($_ = shift(@x))
95154359Sroberto		{
95254359Sroberto		    print "examing \"$_\" $c samples still needed\n"
95354359Sroberto			if $verbose > 4;
95454359Sroberto		    $s = int($F_size{$_}/$RecordSize);
95554359Sroberto		    $c -= $s - 1;
95654359Sroberto		    if ($c <= 0)
95754359Sroberto		    {
95854359Sroberto			$fpos[$[+1] = $_;
95954359Sroberto			$fpos[$[+2] = ($c>-2) ? 0 : (-$c - 2) * $RecordSize;
96054359Sroberto			shift(@f) while @f && ($f[$[] ne $_);
96154359Sroberto			last;
96254359Sroberto		    }
96354359Sroberto		}
96454359Sroberto		if (!defined($fpos[$[+2]))
96554359Sroberto		{
96654359Sroberto		    print "no starting point yet - using start of data\n"
96754359Sroberto			if $verbose > 2;
96854359Sroberto		    $fpos[$[+2] = 0;
96954359Sroberto		}
97054359Sroberto	    }
97154359Sroberto	}
97254359Sroberto    }
97354359Sroberto    print "Ooops, no suitable input file ??\n"
97454359Sroberto	if $verbose > 1 && @f <= 0;
97554359Sroberto
97654359Sroberto    printf("Starting at (%s) \"%s\" offset %ld using %d files\n",
97754359Sroberto	   $fpos[$[+1],
97854359Sroberto	   $F_name{$fpos[$[+1]},
97954359Sroberto	   $fpos[$[+2],
98054359Sroberto	   scalar(@f))
98154359Sroberto	if $verbose > 2;
98254359Sroberto
98354359Sroberto    $lm = 1;
98454359Sroberto    $l = 0;    
98554359Sroberto    foreach $key (@f)
98654359Sroberto    {
98754359Sroberto	$file = $F_name{$key};
98854359Sroberto	print "processing file \"$file\"\n" if $verbose > 2;
98954359Sroberto	
99054359Sroberto	open(IN,"<$file") ||
99154359Sroberto	    (warn("$0: cannot read \"$file\": $!\n"), next);
99254359Sroberto	
99354359Sroberto	;# try to seek to a position nearer to the start of the interesting lines
99454359Sroberto	;# should always affect only first item in @f
99554359Sroberto	($key eq $fpos[$[+1]) &&
99654359Sroberto	    (($verbose > 1) &&
99754359Sroberto	     print("Seeking to offset $fpos[$[+2]\n"),
99854359Sroberto		seek(IN,$fpos[$[+2],0) ||
99954359Sroberto		    warn("$0: seek(\"$F_name{$key}\" failed: $|\n"));
100054359Sroberto	
100154359Sroberto	while(<IN>)
100254359Sroberto	{
100354359Sroberto	    $l++;
100454359Sroberto	    ($verbose > 3) &&
100554359Sroberto		(($l % $lm) == 0 && print("\t$l lines read\n") &&
100654359Sroberto		 (($l ==     2) && ($lm =    10) ||
100754359Sroberto		  ($l ==   100) && ($lm =   100) ||
100854359Sroberto		  ($l ==   500) && ($lm =   500) ||
100954359Sroberto		  ($l ==  1000) && ($lm =  1000) ||
101054359Sroberto		  ($l ==  5000) && ($lm =  5000) ||
101154359Sroberto		  ($l == 10000) && ($lm = 10000)));
101254359Sroberto	    
101354359Sroberto	    @F = split;
101454359Sroberto	    
101554359Sroberto	    next if @F < 6;	# no valid input line is this short
101654359Sroberto	    next if $F[$[] eq "";
101754359Sroberto	    next if ($F[$[] !~ /^\d+$/);
101854359Sroberto	    ($F[$[] !~ /^\d+$/) && # A 'never should have happend' error
101954359Sroberto		die("$0: unexpected input line: >$_<\n");
102054359Sroberto	    
102154359Sroberto	    ;# modified Julian to UNIX epoch
102254359Sroberto	    $t = ($F[$[] - $MJD_1970) * 24 * 60 * 60;
102354359Sroberto	    $t += $F[$[+1];	# add seconds + fraction
102454359Sroberto	    
102554359Sroberto	    ;# multiply offset by 1000 to get ms - try to avoid float op
102654359Sroberto	    (($F[$[+2] =~ s/(\d*)\.(\d{3})(\d*)/$1$2.$3/) &&
102754359Sroberto	     $F[$[+2] =~ s/0+([\d\.])/($1 eq '.') ? '0.' : $1/e) # strip leading zeros
102854359Sroberto		|| ($F[$[+2] *= 1000);
102954359Sroberto
103054359Sroberto	    
103154359Sroberto	    ;# skip samples out of specified time range
103254359Sroberto	    next if (defined($StartTime) && $StartTime > $t);
103354359Sroberto	    next if (defined($EndTime) && $EndTime < $t);
103454359Sroberto	    
103554359Sroberto	    next if defined($lastT) && $t < $lastT; # backward in time ??
103654359Sroberto	    
103754359Sroberto	    push(@offs,$F[$[+2]);
103854359Sroberto	    push(@freq,$F[$[+3] * (2**20/10**6));
103954359Sroberto	    push(@cmpl,$F[$[+5]);
104054359Sroberto	    
104154359Sroberto	    push(@break, (defined($lastT) && ($t - $lastT > $deltaT))); 
104254359Sroberto	    $lastT = $t;
104354359Sroberto	    push(@time,$t);
104454359Sroberto	    push(@loffset, tell(IN) - length($_));
104554359Sroberto	    push(@filekey, $key);
104654359Sroberto	    
104754359Sroberto	    shift(@break),shift(@time),shift(@offs),
104854359Sroberto	    shift(@freq), shift(@cmpl),shift(@loffset),
104954359Sroberto	    shift(@filekey)
105054359Sroberto		if @time > $cnt &&
105154359Sroberto		    ! (defined($StartTime) && defined($EndTime));
105254359Sroberto
105354359Sroberto	    last if @time >= $cnt && defined($StartTime) && !defined($EndTime);
105454359Sroberto	}
105554359Sroberto	close(IN);
105654359Sroberto	last if @time >= $cnt && defined($StartTime) && !defined($EndTime);
105754359Sroberto    }
105854359Sroberto    print "input scanned ($l lines/",scalar(@time)," samples)\n"
105954359Sroberto	if $verbose > 1;
106054359Sroberto    
106154359Sroberto    if (@time)
106254359Sroberto    {
106354359Sroberto	local($_,@F);
106454359Sroberto	
106554359Sroberto	local($timebase) unless defined($timebase);
106654359Sroberto	local($freqbase) unless defined($freqbase);
106754359Sroberto	local($cmplscale) unless defined($cmplscale);
106854359Sroberto	
106954359Sroberto	undef $mintime;
107054359Sroberto	undef $maxtime;
107154359Sroberto	undef $minoffs;
107254359Sroberto	undef $maxoffs;
107354359Sroberto	undef $minfreq;
107454359Sroberto	undef $maxfreq;
107554359Sroberto	undef $mincmpl;
107654359Sroberto	undef $maxcmpl;
107754359Sroberto	undef $miny;
107854359Sroberto	undef $maxy ;
107954359Sroberto	
108054359Sroberto	print "computing ranges\n" if $verbose > 2;
108154359Sroberto	
108254359Sroberto	$LastCnt = @time;
108354359Sroberto
108454359Sroberto	;# @time is in ascending order (;-)
108554359Sroberto	$mintime = $time[$[];
108654359Sroberto	$maxtime = $time[$#time];
108754359Sroberto	unless (defined($timebase))
108854359Sroberto	{
108954359Sroberto	    local($time,@X) = (time);
109054359Sroberto	    @X = localtime($time);
109154359Sroberto	    
109254359Sroberto	    ;# compute today 00:00:00
109354359Sroberto	    $timebase = $time - ((($X[$[+2]*60)+$X[$[+1])*60+$X[$[]);
109454359Sroberto
109554359Sroberto	}
109654359Sroberto	$LastTimeBase = $timebase;
109754359Sroberto
109854359Sroberto	if ($showoffs)
109954359Sroberto	{
110054359Sroberto	    local($i,$m,$f);
110154359Sroberto	    
110254359Sroberto	    $minoffs = &min(@offs);
110354359Sroberto	    $maxoffs = &max(@offs);
110454359Sroberto	    
110554359Sroberto	    ;# I know, it is not perl style using indices to access arrays,
110654359Sroberto	    ;# but I have to proccess two arrays in sync, non-destructively
110754359Sroberto	    ;# (otherwise a (shift(@a1),shift(a2)) would do),
110854359Sroberto	    ;# I dont like to make copies of these arrays as they may be huge
110954359Sroberto	    $i = $[;
1110182007Sroberto	    $lo->sample(($time[$i]-$timebase)/3600,$offs[$i]),$i++
111154359Sroberto		while $i <= $#time;
111254359Sroberto
111354359Sroberto	    ($minoffs == $maxoffs) && ($minoffs -= 0.1,$maxoffs += 0.1);
111454359Sroberto
1115182007Sroberto	    $i = $lo->sigma();
1116182007Sroberto	    $m = $lo->mean();
111754359Sroberto
111854359Sroberto	    print "mean offset: $m sigma: $i\n" if $verbose > 2;
111954359Sroberto
112054359Sroberto	    if (($maxoffs - $minoffs) > $MinClip)
112154359Sroberto	    {
112254359Sroberto		$f = (&abs($minoffs) < &abs($maxoffs)) ? $FuzzLow : $FuzzBig;
112354359Sroberto		$miny = (($m - $minoffs) <= ($f * $i))
112454359Sroberto		    ? $minoffs : ($m - $f * $i);
112554359Sroberto		$f = ($f == $FuzzLow) ? $FuzzBig : $FuzzLow;
112654359Sroberto		$maxy = (($maxoffs - $m) <= ($f * $i))
112754359Sroberto		    ? $maxoffs : ($m + $f * $i);
112854359Sroberto	    }
112954359Sroberto	    else
113054359Sroberto	    {
113154359Sroberto		$miny = $minoffs;
113254359Sroberto		$maxy = $maxoffs;
113354359Sroberto	    }
113454359Sroberto	    ($maxy-$miny) == 0 &&
113554359Sroberto		(($maxy,$miny)
113654359Sroberto		 = (($maxoffs - $minoffs) > 0)
113754359Sroberto		 ? ($maxoffs,$minoffs) : ($MinClip,-$MinClip));
113854359Sroberto
113954359Sroberto	    $maxy = $MaxY if defined($MaxY) && $MaxY < $maxy;
114054359Sroberto	    $miny = $MinY if defined($MinY) && $MinY > $miny;
114154359Sroberto
114254359Sroberto	    print  "offset min clipped from $minoffs to $miny\n"
114354359Sroberto		if $verbose > 2 && $minoffs != $miny;
114454359Sroberto	    print  "offset max clipped from $maxoffs to $maxy\n"
114554359Sroberto		if $verbose > 2 && $maxoffs != $maxy;
114654359Sroberto	}
114754359Sroberto	
114854359Sroberto	if ($showfreq)
114954359Sroberto	{
115054359Sroberto	    local($i,$m);
115154359Sroberto	    
115254359Sroberto	    $minfreq = &min(@freq);
115354359Sroberto	    $maxfreq = &max(@freq);
115454359Sroberto	    
115554359Sroberto	    $i = $[;
1156182007Sroberto	    $lf->sample(($time[$i]-$timebase)/3600,$freq[$i]-$minfreq),
115754359Sroberto	    $i++
115854359Sroberto		while $i <= $#time;
115954359Sroberto	    
1160182007Sroberto	    $i = $lf->sigma();
1161182007Sroberto	    $m = $lf->mean() + $minfreq;
116254359Sroberto
116354359Sroberto	    print "mean frequency: $m sigma: $i\n" if $verbose > 2;
116454359Sroberto
116554359Sroberto	    if (defined($maxy))
116654359Sroberto	    {
116754359Sroberto		local($s) =
116854359Sroberto		    ($maxfreq - $minfreq)
116954359Sroberto			? ($maxy - $miny) / ($maxfreq - $minfreq) : 1;
117054359Sroberto
117154359Sroberto		if (defined($freqbase))
117254359Sroberto		{
117354359Sroberto		    $FreqScale = 1;
117454359Sroberto		    $FreqScaleInv = "";
117554359Sroberto		}
117654359Sroberto		else
117754359Sroberto		{
117854359Sroberto		    $FreqScale = 1;
117954359Sroberto		    $FreqScale = 10 ** int(log($s)/log(10) - 0.9999);
118054359Sroberto		    $FreqScaleInv =
118154359Sroberto			("$FreqScale" =~ /^10(0*)$/) ? "0.${1}1" : 
118254359Sroberto			 ($FreqScale == 1 ? "" : (1/$FreqScale));
118354359Sroberto		    
118454359Sroberto		    $freqbase = ($maxfreq + $minfreq)/ 2 * $FreqScale; #$m * $FreqScale;
1185182007Sroberto		    $freqbase -= ($maxy + $miny) / 2; #$lf->mean();
118654359Sroberto
118754359Sroberto		    ;# round resulting freqbase
118854359Sroberto		    ;# to precision of min max difference
118954359Sroberto		    $s = -12;
119054359Sroberto		    $s = int(log(($maxfreq-$minfreq)*$FreqScale)/log(10))-1
119154359Sroberto			unless ($maxfreq-$minfreq) < 1e-12;
119254359Sroberto		    $s = 10 ** $s;
119354359Sroberto		    $freqbase = int($freqbase / $s) * $s;
119454359Sroberto		}
119554359Sroberto	    }
119654359Sroberto	    else
119754359Sroberto	    {
119854359Sroberto		$FreqScale = 1;
119954359Sroberto		$FreqScaleInv = "";
120054359Sroberto		$freqbase = $m unless defined($freqbase);
120154359Sroberto		if (($maxfreq - $minfreq) > $MinClip)
120254359Sroberto		{
120354359Sroberto		    $f = (&abs($minfreq) < &abs($maxfreq))
120454359Sroberto			? $FuzzLow : $FuzzBig;
120554359Sroberto		    $miny = (($freqbase - $minfreq) <= ($f * $i))
120654359Sroberto			? ($minfreq-$freqbase) : (- $f * $i);
120754359Sroberto		    $f = ($f == $FuzzLow) ? $FuzzBig : $FuzzLow;
120854359Sroberto		    $maxy = (($maxfreq - $freqbase) <= ($f * $i))
120954359Sroberto			? ($maxfreq-$freqbase) : ($f * $i);
121054359Sroberto		}
121154359Sroberto		else
121254359Sroberto		{
121354359Sroberto		    $miny = $minfreq - $freqbase;
121454359Sroberto		    $maxy = $maxfreq - $freqbase;
121554359Sroberto		}
121654359Sroberto		($maxy - $miny) == 0 &&
121754359Sroberto		    (($maxy,$miny) =
121854359Sroberto		     (($maxfreq - $minfreq) > 0)
121954359Sroberto		     ? ($maxfreq-$freqbase,$minfreq-$freqbase) : (0.5,-0.5));
122054359Sroberto		
122154359Sroberto		$maxy = $MaxY if defined($MaxY) && $MaxY < $maxy;
122254359Sroberto		$miny = $MinY if defined($MinY) && $MinY > $miny;
122354359Sroberto
122454359Sroberto		print("frequency min clipped from ",$minfreq-$freqbase,
122554359Sroberto		      " to $miny\n")
122654359Sroberto		    if $verbose > 2 && $miny != ($minfreq - $freqbase);
122754359Sroberto		print("frequency max clipped from ",$maxfreq-$freqbase,
122854359Sroberto		      " to $maxy\n")
122954359Sroberto		    if $verbose > 2 && $maxy != ($maxfreq - $freqbase);
123054359Sroberto	    }
123154359Sroberto	    $LastFreqBaseString =
123254359Sroberto		sprintf("%g",$freqbase >= 0 ? $freqbase : -$freqbase);
123354359Sroberto	    $LastFreqBase = $freqbase;
123454359Sroberto	    print "LastFreqBaseString now \"$LastFreqBaseString\"\n"
123554359Sroberto		if $verbose > 5;
123654359Sroberto	}
123754359Sroberto	else
123854359Sroberto	{
123954359Sroberto	    $FreqScale = 1;
124054359Sroberto	    $FreqScaleInv = "";
124154359Sroberto	    $LastFreqBase = 0;
124254359Sroberto	    $LastFreqBaseString = "";
124354359Sroberto	}
124454359Sroberto		
124554359Sroberto	if ($showcmpl)
124654359Sroberto	{
124754359Sroberto	    $mincmpl = &min(@cmpl);
124854359Sroberto	    $maxcmpl = &max(@cmpl);
124954359Sroberto
125054359Sroberto	    if (!defined($cmplscale))
125154359Sroberto	    {
125254359Sroberto		if (defined($maxy))
125354359Sroberto		{
125454359Sroberto		    local($cmp)
125554359Sroberto			= (&abs($miny) > &abs($maxy)) ? &abs($miny) : $maxy;
125654359Sroberto		    $cmplscale = $cmp == $maxy ? 1 : -1;
125754359Sroberto
125854359Sroberto		    foreach (0.01, 0.02, 0.05,
125954359Sroberto			     0.1, 0.2, 0.25, 0.4, 0.5,
126054359Sroberto			     1, 2, 4, 5,
126154359Sroberto			     10, 20, 25, 50,
126254359Sroberto			     100, 200, 250, 500, 1000)
126354359Sroberto		    {
126454359Sroberto			$cmplscale *= $_, last if $maxcmpl/$_ <= $cmp;
126554359Sroberto		    }
126654359Sroberto		}
126754359Sroberto		else
126854359Sroberto		{
126954359Sroberto		    $cmplscale = 1;
127054359Sroberto		    $miny = $mincmpl ? 0 : -$MinClip;
127154359Sroberto		    $maxy = $maxcmpl+$MinClip;
127254359Sroberto		}
127354359Sroberto	    }
127454359Sroberto	    $LastCmplScale = $cmplscale;
127554359Sroberto	}
127654359Sroberto	else
127754359Sroberto	{
127854359Sroberto	    $LastCmplScale = 1;
127954359Sroberto	}
128054359Sroberto	
128154359Sroberto	print "creating plot command input file\n" if $verbose > 2;
128254359Sroberto	
128354359Sroberto	
128454359Sroberto	print OUT ("# preprocessed NTP statistics file for $STATHOST\n");
128554359Sroberto	print OUT ("#    timebase is: ",&ctime($LastTimeBase))
128654359Sroberto	    if defined($LastTimeBase);
128754359Sroberto	print OUT ("#    frequency is offset by  ",
128854359Sroberto		   ($LastFreqBase >= 0 ? "+" : "-"),
128954359Sroberto		   "$LastFreqBaseString [${FreqScaleInv}ppm]\n");
129054359Sroberto	print OUT ("#    compliance is scaled by $LastCmplScale\n");
129154359Sroberto	print OUT ("# time [h]\toffset [ms]\tfrequency [${FreqScaleInv}ppm]\tcompliance\n");
129254359Sroberto	
129354359Sroberto	printf OUT ("%s%lf\t%lf\t%lf\t%lf\n",
129454359Sroberto		    (shift(@break) ? "\n" : ""),
129554359Sroberto		    (shift(@time) - $LastTimeBase)/3600,
129654359Sroberto		    shift(@offs),
129754359Sroberto		    shift(@freq) * $FreqScale - $LastFreqBase,
129854359Sroberto		    shift(@cmpl) / $LastCmplScale)
129954359Sroberto	    while(@time);
130054359Sroberto    }
130154359Sroberto    else
130254359Sroberto    {
130354359Sroberto	;# prevent plotcmd from processing empty file
130454359Sroberto	print "Creating plot command dummy...\n" if $verbose > 2;
130554359Sroberto	print OUT "# dummy samples\n0 1 2 3\n1 1 2 3\n";
1306182007Sroberto	$lo->sample(0,1);
1307182007Sroberto	$lo->sample(1,1);
1308182007Sroberto	$lf->sample(0,2);
1309182007Sroberto	$lf->sample(1,2);
131054359Sroberto	@time = (0, 1); $maxtime = 1; $mintime = 0;
131154359Sroberto	@offs = (1, 1); $maxoffs = 1; $minoffs = 1;
131254359Sroberto	@freq = (2, 2); $maxfreq = 2; $minfreq = 2;
131354359Sroberto	@cmpl = (3, 3); $maxcmpl = 3; $mincmpl = 3;
131454359Sroberto	$LastCnt = 2;
131554359Sroberto	$LastFreqBase = 0;
131654359Sroberto	$LastCmplScale = 1;
131754359Sroberto	$LastTimeBase = 0;
131854359Sroberto	$miny = -$MinClip;
131954359Sroberto	$maxy = 3 + $MinClip;
132054359Sroberto    }
132154359Sroberto    close(OUT);
132254359Sroberto    
132354359Sroberto    print "plot command input file created\n"
132454359Sroberto	if $verbose > 2;
132554359Sroberto	
132654359Sroberto	
132754359Sroberto    if (($fpos[$[] eq 'cnt' && scalar(@loffset) >= $cnt) ||
132854359Sroberto	($fpos[$[] eq 'start' && $mintime <= $StartTime) ||
132954359Sroberto	($fpos[$[] eq 'end'))
133054359Sroberto    {
133154359Sroberto	return ($fpos[$[],$filekey[$[],$loffset[$[]);
133254359Sroberto    }
133354359Sroberto    else			# found to few lines - next time start search earlier in file
133454359Sroberto    {
133554359Sroberto	if ($fpos[$[] eq 'start')
133654359Sroberto	{
133754359Sroberto	    ;# the timestamps we got for F_first and F_last guaranteed
133854359Sroberto	    ;# that no file is left out
133954359Sroberto	    ;# the only thing that could happen is:
134054359Sroberto	    ;# we guessed the starting point wrong
134154359Sroberto	    ;# compute a new guess from the first record found
134254359Sroberto	    ;# if this equals our last guess use data of first record
134354359Sroberto	    ;# otherwise try new guess
134454359Sroberto	    
134554359Sroberto	    if ($fpos[$[+1] eq $filekey[$[] && $loffset[$[] > $fpos[$[+2])
134654359Sroberto	    {
134754359Sroberto		local($noff);
134854359Sroberto		$noff = $loffset[$[] - ($cnt - @loffset + 1) * $RecordSize;
134954359Sroberto		$noff = 0 if $noff < 0;
135054359Sroberto		
135154359Sroberto		return (@fpos[$[,$[+1], ($noff == $fpos[$[+2]) ? $loffset[$[] : $noff);
135254359Sroberto	    }
135354359Sroberto	    return ($fpos[$[],$filekey[$[],$loffset[$[]);
135454359Sroberto	}
135554359Sroberto	elsif ($fpos[$[] eq 'end' || $fpos[$[] eq 'cnt')
135654359Sroberto	{
135754359Sroberto	    ;# try to start earlier in file
135854359Sroberto	    ;# if we already started at the beginning
135954359Sroberto	    ;# try to use previous file
136054359Sroberto	    ;# this assumes distance to better starting point is at most one file
136154359Sroberto	    ;# the primary guess at top of genfile() should usually allow this
136254359Sroberto	    ;# assumption
136354359Sroberto	    ;# if the offset of the first sample used is within 
136454359Sroberto	    ;# a different file than we guessed it must have occurred later
136554359Sroberto	    ;# in the sequence of files
136654359Sroberto	    ;# this only can happen if our starting file did not contain
136754359Sroberto	    ;# a valid sample from the starting point we guessed
136854359Sroberto	    ;# however this does not invalidate our assumption, no check needed
136954359Sroberto	    local($noff,$key);
137054359Sroberto	    if ($fpos[$[+2] > 0)
137154359Sroberto	    {
137254359Sroberto		$noff = $fpos[$[+2] - $RecordSize * ($cnt - @loffset + 1);
137354359Sroberto		$noff = 0 if $noff < 0;
137454359Sroberto		return (@fpos[$[,$[+1],$noff);
137554359Sroberto	    }
137654359Sroberto	    else
137754359Sroberto	    {
137854359Sroberto		if ($fpos[$[+1] eq $F_files[$[])
137954359Sroberto		{
138054359Sroberto		    ;# first file - and not enough samples
138154359Sroberto		    ;# use data of first sample
138254359Sroberto		    return ($fpos[$[], $filekey[$[], $loffset[$[]);
138354359Sroberto		}
138454359Sroberto		else
138554359Sroberto		{
138654359Sroberto		    ;# search key of previous file
138754359Sroberto		    $key = $F_files[$[];
138854359Sroberto		    @F = reverse(@F_files);
138954359Sroberto		    while ($_ = shift(@F))
139054359Sroberto		    {
139154359Sroberto			if ($_ eq $fpos[$[+1])
139254359Sroberto			{
139354359Sroberto			    $key = shift(@F) if @F;
139454359Sroberto			    last;
139554359Sroberto			}
139654359Sroberto		    }
139754359Sroberto		    $noff = int($F_size{$key} / $RecordSize);
139854359Sroberto		    $noff -= $cnt - @loffset;
139954359Sroberto		    $noff = 0 if $noff < 0;
140054359Sroberto		    $noff *= $RecordSize;
140154359Sroberto		    return ($fpos[$[], $key, $noff);
140254359Sroberto		}
140354359Sroberto	    }
140454359Sroberto	}
140554359Sroberto	else
140654359Sroberto	{
140754359Sroberto	    return ();
140854359Sroberto	}
140954359Sroberto	
141054359Sroberto	return 0 if @loffset <= 1 || ($loffset[$#loffset] - $loffset[$[]) <= 1;
141154359Sroberto	
141254359Sroberto	;# EOF - 1.1 * avg(line) * $cnt
141354359Sroberto	local($val) =  $loffset[$#loffset]
141454359Sroberto	    - $cnt * 11 * (($loffset[$#loffset] - $loffset[$[]) / @loffset) / 10;
141554359Sroberto	return ($val < 0) ? 0 : $val;
141654359Sroberto    }
141754359Sroberto}
141854359Sroberto
141954359Sroberto$Ltime = -1 if ! defined($Ltime);
142054359Sroberto$LastFreqBase = 0;
142154359Sroberto$LastFreqBaseString = "??";
142254359Sroberto
142354359Sroberto;# initial setup of plot
142454359Srobertoprint "initialize plotting\n" if $verbose;
142554359Srobertoif (defined($PrintIt))
142654359Sroberto{
142754359Sroberto  if ($PrintIt =~ m,/,)
142854359Sroberto  {
142954359Sroberto    print "Saving plot to file $PrintIt\n";
143054359Sroberto    print PLOT "set output '$PrintIt'\n";
143154359Sroberto  }
143254359Sroberto  else
143354359Sroberto  {
143454359Sroberto    print "Printing plot on printer $PrintIt\n";
143554359Sroberto    print PLOT "set output '| lpr -P$PrintIt -h'\n";
143654359Sroberto  }
143754359Sroberto  print PLOT "set terminal postscript landscape color solid 'Helvetica' 10\n";
143854359Sroberto}
143954359Srobertoprint PLOT "set grid\n";
144054359Srobertoprint PLOT "set tics out\n";
144154359Srobertoprint PLOT "set format y '%g '\n";
144254359Srobertoprintf PLOT "set time 47\n" unless defined($PrintIt);
144354359Sroberto
144454359Sroberto@filepos =();
144554359Srobertowhile(1)
144654359Sroberto{
144754359Sroberto  print &ctime(time) if $verbose;
144854359Sroberto
144954359Sroberto  ;# update diplay characteristics
145054359Sroberto  &read_config;# unless defined($PrintIt);
145154359Sroberto
145254359Sroberto  unlink($tmpfile);
1453182007Sroberto  my $lo = lr->new();
1454182007Sroberto  my $lf = lr->new();
1455182007Sroberto    
1456182007Sroberto  @filepos = &genfile($samples,$srcprefix,$tmpfile,$lo,$lf,@filepos);
145754359Sroberto
145854359Sroberto  ;# make plotcmd display samples
1459182007Sroberto  make_doplot($lo, $lf);
146054359Sroberto  print "Displaying plot...\n" if $verbose > 1;
146154359Sroberto  print "command for plot sub process:\n$doplot----\n" if $verbose > 3;
146254359Sroberto  print PLOT $doplot;
146354359Sroberto}
146454359Srobertocontinue
146554359Sroberto{
146654359Sroberto  if (defined($PrintIt))
146754359Sroberto  {
146854359Sroberto    delete $SIG{'CHLD'};
146954359Sroberto    print PLOT "quit\n";
147054359Sroberto    close(PLOT);
147154359Sroberto    if ($PrintIt =~ m,/,)
147254359Sroberto    {
147354359Sroberto      print "Plot saved to file $PrintIt\n";
147454359Sroberto    }
147554359Sroberto    else
147654359Sroberto    {
147754359Sroberto      print "Plot spooled to printer $PrintIt\n";
147854359Sroberto    }
147954359Sroberto    unlink($tmpfile);
148054359Sroberto    exit(0);
148154359Sroberto  }
148254359Sroberto  ;# wait $delay seconds
148354359Sroberto  print "waiting $delay seconds ..." if $verbose > 2;
148454359Sroberto  sleep($delay);
148554359Sroberto  print " continuing\n" if $verbose > 2;
148654359Sroberto  undef($LastFreqBaseString);
148754359Sroberto}
148854359Sroberto
148954359Sroberto
149054359Srobertosub date_time_spec2seconds
149154359Sroberto{
149254359Sroberto    local($_) = @_;
149354359Sroberto    ;# a date_time_spec consistes of:
149454359Sroberto    ;#  YYYY-MM-DD_HH:MM:SS.ms
149554359Sroberto    ;# values can be omitted from the beginning and default than to
149654359Sroberto    ;# values of current date
149754359Sroberto    ;# values omitted from the end default to lowest possible values
149854359Sroberto
149954359Sroberto    local($time) = time;
150054359Sroberto    local($sec,$min,$hour,$mday,$mon,$year)
150154359Sroberto	= localtime($time);
150254359Sroberto
150354359Sroberto    local($last) = ();
150454359Sroberto
150554359Sroberto    s/^\D*(.*\d)\D*/$1/;	# strip off garbage
150654359Sroberto
150754359Sroberto  PARSE:
150854359Sroberto    {
150954359Sroberto	if (s/^(\d{4})(-|$)//)
151054359Sroberto	{
151154359Sroberto	    if ($1 < 1970)
151254359Sroberto	    {
151354359Sroberto		warn("$0: can not handle years before 1970 - year $1 ignored\n");
151454359Sroberto		return undef;
151554359Sroberto	    }
151654359Sroberto	    elsif ( $1 >= 2070)
151754359Sroberto	    {
151854359Sroberto		warn("$0: can not handle years past 2070 - year $1 ignored\n");
151954359Sroberto		return undef;
152054359Sroberto	    }
152154359Sroberto	    else
152254359Sroberto	    {
152354359Sroberto		$year = $1 % 100; # 0<= $year < 100
152454359Sroberto				 ;# - interpreted 70 .. 99,00 .. 69
152554359Sroberto	    }
152654359Sroberto	    $last = $[ + 5;
152754359Sroberto	    last PARSE if $_ eq '';
152854359Sroberto	    warn("$0: bad date_time_spec: \"$_\" found after YEAR\n"),
152954359Sroberto	    return(undef)
153054359Sroberto		if $2 eq '';
153154359Sroberto	}
153254359Sroberto
153354359Sroberto	if (s/^(\d{1,2})(-|$)//)
153454359Sroberto	{
153554359Sroberto	    warn("$0: implausible month $1\n"),return(undef)
153654359Sroberto		if $1 < 1 || $1 > 12;
153754359Sroberto	    $mon = $1 - 1;
153854359Sroberto	    $last = $[ + 4;
153954359Sroberto	    last PARSE if $_ eq '';
154054359Sroberto	    warn("$0: bad date_time_spec: \"$_\" found after MONTH\n"),
154154359Sroberto	    return(undef)
154254359Sroberto		if $2 eq '';
154354359Sroberto	}
154454359Sroberto	else
154554359Sroberto	{
154654359Sroberto	    warn("$0: bad date_time_spec \"$_\"\n"),return(undef)
154754359Sroberto		if defined($last);
154854359Sroberto	    
154954359Sroberto	}
155054359Sroberto
155154359Sroberto	if (s/^(\d{1,2})([_ ]|$)//)
155254359Sroberto	{
155354359Sroberto	    warn("$0: implausible month day $1 for month ".($mon+1)." (".
155454359Sroberto		 $MaxNumDaysPerMonth[$mon].")$mon\n"),
155554359Sroberto	    return(undef)
155654359Sroberto		if $1 < 1 || $1 > $MaxNumDaysPerMonth[$mon];
155754359Sroberto	    $mday = $1;
155854359Sroberto	    $last = $[ + 3;
155954359Sroberto	    last PARSE if $_ eq '';
156054359Sroberto	    warn("$0: bad date_time_spec \"$_\" found after MDAY\n"),
156154359Sroberto	    return(undef)
156254359Sroberto		if $2 eq '';
156354359Sroberto	}
156454359Sroberto	else
156554359Sroberto	{
156654359Sroberto	    warn("$0: bad date_time_spec \"$_\"\n"), return undef
156754359Sroberto		if defined($last);
156854359Sroberto	}
156954359Sroberto
157054359Sroberto	;# now we face a problem:
157154359Sroberto 	;# if ! defined($last) a prefix of "07:"
157254359Sroberto	;# can be either 07:MM or 07:ss
157354359Sroberto	;# to get the second interpretation make the user add
157454359Sroberto 	;# a msec fraction part and check for this special case
157554359Sroberto	if (! defined($last) && s/^(\d{1,2}):(\d{1,2}\.\d+)//)
157654359Sroberto	{
157754359Sroberto	    warn("$0: implausible minute $1\n"), return undef
157854359Sroberto		if $1 < 0 || $1 >= 60;
157954359Sroberto	    warn("$0: implausible second $1\n"), return undef
158054359Sroberto		if $2 < 0 || $2 >= 60;
158154359Sroberto	    $min = $1;
158254359Sroberto	    $sec = $2;
158354359Sroberto	    $last = $[ + 1;
158454359Sroberto	    last PARSE if $_ eq '';
158554359Sroberto	    warn("$0: bad date_time_spec \"$_\" after SECONDS\n");
158654359Sroberto	    return undef;
158754359Sroberto	}
158854359Sroberto	
158954359Sroberto	if (s/^(\d{1,2})(:|$)//)
159054359Sroberto	{
159154359Sroberto	    warn("$0: implausible hour $1\n"), return undef
159254359Sroberto		if $1 < 0 || $1 > 24;
159354359Sroberto	    $hour = $1;
159454359Sroberto	    $last = $[ + 2;
159554359Sroberto	    last PARSE if $_ eq '';
159654359Sroberto	    warn("$0: bad date_time_spec found \"$_\" after HOUR\n"),
159754359Sroberto	    return undef
159854359Sroberto		if $2 eq '';
159954359Sroberto	}
160054359Sroberto	else
160154359Sroberto	{
160254359Sroberto	    warn("$0: bad date_time_spec \"$_\"\n"), return undef
160354359Sroberto		if defined($last);
160454359Sroberto	}
160554359Sroberto
160654359Sroberto	if (s/^(\d{1,2})(:|$)//)
160754359Sroberto	{
160854359Sroberto	    warn("$0: implausible minute $1\n"), return undef
160954359Sroberto		if $1 < 0 || $1 >=60;
161054359Sroberto	    $min = $1;
161154359Sroberto	    $last = $[ + 1;
161254359Sroberto	    last PARSE if $_ eq '';
161354359Sroberto	    warn("$0: bad date_time_spec found \"$_\" after MINUTE\n"),
161454359Sroberto	    return undef
161554359Sroberto		if $2 eq '';
161654359Sroberto	}
161754359Sroberto	else
161854359Sroberto	{
161954359Sroberto	    warn("$0: bad date_time_spec \"$_\"\n"), return undef
162054359Sroberto		if defined($last);
162154359Sroberto	}
162254359Sroberto
162354359Sroberto	if (s/^(\d{1,2}(\.\d+)?)//)
162454359Sroberto	{
162554359Sroberto	    warn("$0: implausible second $1\n"), return undef
162654359Sroberto		if $1 < 0 || $1 >=60;
162754359Sroberto	    $sec = $1;
162854359Sroberto	    $last = $[;
162954359Sroberto	    last PARSE if $_ eq '';
163054359Sroberto	    warn("$0: bad date_time_spec found \"$_\" after SECOND\n");
163154359Sroberto	    return undef;
163254359Sroberto	}
163354359Sroberto    }
163454359Sroberto
163554359Sroberto    return $time unless defined($last);
163654359Sroberto
163754359Sroberto    $sec  = 0 if $last > $[;
163854359Sroberto    $min  = 0 if $last > $[ + 1;
163954359Sroberto    $hour = 0 if $last > $[ + 2;
164054359Sroberto    $mday = 1 if $last > $[ + 3;
164154359Sroberto    $mon  = 0 if $last > $[ + 4;
164254359Sroberto    local($rtime) = &timelocal($sec,$min,$hour,$mday,$mon,$year, 0,0, 0);
164354359Sroberto
164454359Sroberto    ;# $rtime may be off if daylight savings time is in effect at given date
164554359Sroberto    return $rtime + ($sec - int($sec))
164654359Sroberto	if $hour == (localtime($rtime))[$[+2];
164754359Sroberto    return
164854359Sroberto	&timelocal($sec,$min,$hour,$mday,$mon,$year, 0,0, 1)
164954359Sroberto	    + ($sec - int($sec));
165054359Sroberto}
165154359Sroberto
165254359Sroberto
165354359Srobertosub min
165454359Sroberto{
165554359Sroberto  local($m) = shift;
165654359Sroberto
165754359Sroberto  grep((($m > $_) && ($m = $_),0),@_);
165854359Sroberto  $m;
165954359Sroberto}
166054359Sroberto
166154359Srobertosub max
166254359Sroberto{
166354359Sroberto  local($m) = shift;
166454359Sroberto
166554359Sroberto  grep((($m < $_) && ($m = $_),0),@_);
166654359Sroberto  $m;
166754359Sroberto}
1668