1*c0b746e5SOllivier Robert#!/usr/bin/perl -w
2*c0b746e5SOllivier Robert;# --*-perl-*--
3*c0b746e5SOllivier Robert;#
4*c0b746e5SOllivier Robert;# /src/NTP/ntp4-dev/scripts/monitoring/ntploopwatch,v 4.7 2004/11/14 16:11:05 kardel RELEASE_20050508_A
5*c0b746e5SOllivier Robert;#
6*c0b746e5SOllivier Robert;# process loop filter statistics file and either
7*c0b746e5SOllivier Robert;#     - show statistics periodically using gnuplot
8*c0b746e5SOllivier Robert;#     - or print a single plot
9*c0b746e5SOllivier Robert;#
10*c0b746e5SOllivier Robert;#  Copyright (c) 1992-1998
11*c0b746e5SOllivier Robert;#  Rainer Pruy, Friedrich-Alexander Universit�t Erlangen-N�rnberg
12*c0b746e5SOllivier Robert;#
13*c0b746e5SOllivier Robert;#
14*c0b746e5SOllivier Robert;#############################################################
15*c0b746e5SOllivier Robert$0 =~ s!^.*/([^/]+)$!$1!;
16*c0b746e5SOllivier Robert$F = ' ' x length($0);
17*c0b746e5SOllivier Robert$|=1;
18*c0b746e5SOllivier Robert
19*c0b746e5SOllivier Robert$ENV{'SHELL'} = '/bin/sh'; # use bourne shell
20*c0b746e5SOllivier Robert
21*c0b746e5SOllivier Robertundef($config);
22*c0b746e5SOllivier Robertundef($workdir);
23*c0b746e5SOllivier Robertundef($PrintIt);
24*c0b746e5SOllivier Robertundef($samples);
25*c0b746e5SOllivier Robertundef($StartTime);
26*c0b746e5SOllivier Robertundef($EndTime);
27*c0b746e5SOllivier Robert($a,$b) if 0;			# keep -w happy
28*c0b746e5SOllivier Robert$usage = <<"E-O-P";
29*c0b746e5SOllivier Robertusage:
30*c0b746e5SOllivier Robert  to watch statistics permanently:
31*c0b746e5SOllivier Robert     $0 [-v[<level>]] [-c <config-file>] [-d <working-dir>]
32*c0b746e5SOllivier Robert     $F [-h <hostname>]
33*c0b746e5SOllivier Robert
34*c0b746e5SOllivier Robert  to get a single print out specify also
35*c0b746e5SOllivier Robert     $F -P[<printer>] [-s<samples>]
36*c0b746e5SOllivier Robert     $F               [-S <start-time>] [-E <end-time>]
37*c0b746e5SOllivier Robert     $F               [-Y <MaxOffs>] [-y <MinOffs>]
38*c0b746e5SOllivier Robert
39*c0b746e5SOllivier RobertIf You like long option names, You can use:
40*c0b746e5SOllivier Robert    -help
41*c0b746e5SOllivier Robert    -c    +config
42*c0b746e5SOllivier Robert    -d    +directory
43*c0b746e5SOllivier Robert    -h    +host
44*c0b746e5SOllivier Robert    -v    +verbose[=<level>]
45*c0b746e5SOllivier Robert    -P    +printer[=<printer>]
46*c0b746e5SOllivier Robert    -s    +samples[=<samples>]
47*c0b746e5SOllivier Robert    -S    +starttime
48*c0b746e5SOllivier Robert    -E    +endtime
49*c0b746e5SOllivier Robert    -Y    +maxy
50*c0b746e5SOllivier Robert    -y    +miny
51*c0b746e5SOllivier Robert
52*c0b746e5SOllivier RobertIf <printer> contains a '/' (slash character) output is directed to
53*c0b746e5SOllivier Roberta file of this name instead of delivered to a printer.
54*c0b746e5SOllivier RobertE-O-P
55*c0b746e5SOllivier Robert
56*c0b746e5SOllivier Robert;# add directory to look for lr.pl and timelocal.pl (in front of current list)
57*c0b746e5SOllivier Robertunshift(@INC,".");
58*c0b746e5SOllivier Robert
59*c0b746e5SOllivier Robertrequire "lr.pl";	# linear regresion routines
60*c0b746e5SOllivier Robert
61*c0b746e5SOllivier Robert$MJD_1970 = 40587;		# from ntp.h (V3)
62*c0b746e5SOllivier Robert$RecordSize = 48;		# usually a line fits into 42 bytes
63*c0b746e5SOllivier Robert$MinClip = 1;		# clip Y scales with greater range than this
64*c0b746e5SOllivier Robert
65*c0b746e5SOllivier Robert;# largest extension of Y scale from mean value, factor for standart deviation
66*c0b746e5SOllivier Robert$FuzzLow = 2.2;			# for side closer to zero
67*c0b746e5SOllivier Robert$FuzzBig = 1.8;			# for side farther from zero
68*c0b746e5SOllivier Robert
69*c0b746e5SOllivier Robertrequire "ctime.pl";
70*c0b746e5SOllivier Robertrequire "timelocal.pl";
71*c0b746e5SOllivier Robert;# early distributions of ctime.pl had a bug
72*c0b746e5SOllivier Robert$ENV{'TZ'} = 'MET' unless defined $ENV{'TZ'} || $[ > 4.010;
73*c0b746e5SOllivier Robertif (defined(@ctime'MoY))
74*c0b746e5SOllivier Robert{
75*c0b746e5SOllivier Robert  *Month=*ctime'MoY;
76*c0b746e5SOllivier Robert  *Day=*ctime'DoW;
77*c0b746e5SOllivier Robert} 					# ' re-sync emacs fontification
78*c0b746e5SOllivier Robertelse
79*c0b746e5SOllivier Robert{
80*c0b746e5SOllivier Robert  @Month = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
81*c0b746e5SOllivier Robert  @Day   = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
82*c0b746e5SOllivier Robert}
83*c0b746e5SOllivier Robertprint @ctime'DoW if 0; # ' re-sync emacs fontification
84*c0b746e5SOllivier Robert
85*c0b746e5SOllivier Robert;# max number of days per month
86*c0b746e5SOllivier Robert@MaxNumDaysPerMonth = (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
87*c0b746e5SOllivier Robert
88*c0b746e5SOllivier Robert;# config settable parameters
89*c0b746e5SOllivier Robert$delay = 60;
90*c0b746e5SOllivier Robert$srcprefix = "./var\@\$STATHOST/loopstats.";
91*c0b746e5SOllivier Robert$showoffs = 1;
92*c0b746e5SOllivier Robert$showfreq = 1;
93*c0b746e5SOllivier Robert$showcmpl = 0;
94*c0b746e5SOllivier Robert$showoreg = 0;
95*c0b746e5SOllivier Robert$showfreg = 0;
96*c0b746e5SOllivier Robertundef($timebase);
97*c0b746e5SOllivier Robertundef($freqbase);
98*c0b746e5SOllivier Robertundef($cmplscale);
99*c0b746e5SOllivier Robertundef($MaxY);
100*c0b746e5SOllivier Robertundef($MinY);
101*c0b746e5SOllivier Robert$deltaT  = 512; # indicate sample data gaps greater than $deltaT seconds
102*c0b746e5SOllivier Robert$verbose = 1;
103*c0b746e5SOllivier Robert
104*c0b746e5SOllivier Robertwhile($_ = shift(@ARGV))
105*c0b746e5SOllivier Robert{
106*c0b746e5SOllivier Robert    (/^[+-]help$/) && die($usage);
107*c0b746e5SOllivier Robert
108*c0b746e5SOllivier Robert    (/^-c$/ || /^\+config$/) &&
109*c0b746e5SOllivier Robert	(@ARGV || die($usage), $config = shift(@ARGV), next);
110*c0b746e5SOllivier Robert
111*c0b746e5SOllivier Robert    (/^-d$/ || /^\+directory$/) &&
112*c0b746e5SOllivier Robert	(@ARGV || die($usage), $workdir = shift(@ARGV), next);
113*c0b746e5SOllivier Robert
114*c0b746e5SOllivier Robert    (/^-h$/ || /^\+host$/) &&
115*c0b746e5SOllivier Robert	(@ARGV || die($usage), $STATHOST = shift, next);
116*c0b746e5SOllivier Robert
117*c0b746e5SOllivier Robert    (/^-v(\d*)$/ || /^\+verbose=?(\d*)$/) &&
118*c0b746e5SOllivier Robert	($verbose=($1 eq "") ? 1 : $1, next);
119*c0b746e5SOllivier Robert
120*c0b746e5SOllivier Robert    (/^-P(\S*)$/ || /^\+[Pp]rinter=?(\S*)$/) &&
121*c0b746e5SOllivier Robert	($PrintIt = $1, $verbose==1 && ($verbose = 0), next);
122*c0b746e5SOllivier Robert
123*c0b746e5SOllivier Robert    (/^-s(\d*)$/ || /^\+samples=?(\d*)$/) &&
124*c0b746e5SOllivier Robert	(($samples = ($1 eq "") ? (shift || die($usage)): $1), next);
125*c0b746e5SOllivier Robert
126*c0b746e5SOllivier Robert    (/^-S$/ || /^\+[Ss]tart[Tt]ime$/) &&
127*c0b746e5SOllivier Robert	(@ARGV || die($usage), $StartTime=&date_time_spec2seconds(shift),next);
128*c0b746e5SOllivier Robert
129*c0b746e5SOllivier Robert    (/^-E$/ || /^\+[Ee]nd[Tt]ime$/) &&
130*c0b746e5SOllivier Robert	(@ARGV || die($usage), $EndTime = &date_time_spec2seconds(shift),next);
131*c0b746e5SOllivier Robert
132*c0b746e5SOllivier Robert    (/^-Y$/ || /^\+[Mm]ax[Yy]$/) &&
133*c0b746e5SOllivier Robert	(@ARGV || die($usage), $MaxY = shift, next);
134*c0b746e5SOllivier Robert
135*c0b746e5SOllivier Robert    (/^-y$/ || /^\+[Mm]in[Yy]$/) &&
136*c0b746e5SOllivier Robert	(@ARGV || die($usage), $MinY = shift, next);
137*c0b746e5SOllivier Robert
138*c0b746e5SOllivier Robert    die("$0: unexpected argument \"$_\"\n$usage");
139*c0b746e5SOllivier Robert}
140*c0b746e5SOllivier Robert
141*c0b746e5SOllivier Robertif (defined($workdir))
142*c0b746e5SOllivier Robert{
143*c0b746e5SOllivier Robert  chdir($workdir) ||
144*c0b746e5SOllivier Robert      die("$0: failed to change working dir to \"$workdir\": $!\n");
145*c0b746e5SOllivier Robert}
146*c0b746e5SOllivier Robert
147*c0b746e5SOllivier Robert$PrintIt = "ps" if defined($PrintIt) && $PrintIt eq "";
148*c0b746e5SOllivier Robert
149*c0b746e5SOllivier Robertif (!defined($PrintIt))
150*c0b746e5SOllivier Robert{
151*c0b746e5SOllivier Robert    defined($samples) &&
152*c0b746e5SOllivier Robert	print "WARNING: your samples value may be shadowed by config file settings\n";
153*c0b746e5SOllivier Robert    defined($StartTime) &&
154*c0b746e5SOllivier Robert	print "WARNING: your StartTime value may be shadowed by config file settings\n";
155*c0b746e5SOllivier Robert    defined($EndTime) &&
156*c0b746e5SOllivier Robert	print "WARNING: your EndTime value may be shadowed by config file settings\n";
157*c0b746e5SOllivier Robert    defined($MaxY) &&
158*c0b746e5SOllivier Robert	print "WARNING: your MaxY value may be shadowed by config file settings\n";
159*c0b746e5SOllivier Robert    defined($MinY) &&
160*c0b746e5SOllivier Robert	print "WARNING: your MinY value may be shadowed by config file settings\n";
161*c0b746e5SOllivier Robert
162*c0b746e5SOllivier Robert    ;# check operating environment
163*c0b746e5SOllivier Robert    ;#
164*c0b746e5SOllivier Robert    ;# gnuplot usually has X support
165*c0b746e5SOllivier Robert    ;# I vaguely remember there was one with sunview support
166*c0b746e5SOllivier Robert    ;#
167*c0b746e5SOllivier Robert    ;# If Your plotcmd can display graphics using some other method
168*c0b746e5SOllivier Robert    ;# (Tek window,..) fix the following test
169*c0b746e5SOllivier Robert    ;# (or may be, just disable it)
170*c0b746e5SOllivier Robert    ;#
171*c0b746e5SOllivier Robert    !(defined($ENV{'DISPLAY'}) || defined($ENV{'WINDOW_PARENT'})) &&
172*c0b746e5SOllivier Robert	die("Need window system to monitor statistics\n");
173*c0b746e5SOllivier Robert}
174*c0b746e5SOllivier Robert
175*c0b746e5SOllivier Robert;# configuration file
176*c0b746e5SOllivier Robert$config = "loopwatch.config" unless defined($config);
177*c0b746e5SOllivier Robert($STATHOST = $config) =~ s!.*loopwatch\.config.([^/\.]*)$!$1!
178*c0b746e5SOllivier Robert    unless defined($STATHOST);
179*c0b746e5SOllivier Robert($STATTAG = $STATHOST) =~ s/^([^\.\*\s]+)\..*$/$1/;
180*c0b746e5SOllivier Robert
181*c0b746e5SOllivier Robert$srcprefix =~ s/\$STATHOST/$STATHOST/g;
182*c0b746e5SOllivier Robert
183*c0b746e5SOllivier Robert;# plot command
184*c0b746e5SOllivier Robert@plotcmd=("gnuplot",
185*c0b746e5SOllivier Robert	  '-title', "Ntp loop filter statistics $STATHOST",
186*c0b746e5SOllivier Robert	  '-name', "NtpLoopWatch_$STATTAG");
187*c0b746e5SOllivier Robert$tmpfile = "/tmp/ntpstat.$$";
188*c0b746e5SOllivier Robert
189*c0b746e5SOllivier Robert;# other variables
190*c0b746e5SOllivier Robert$doplot = "";	# assembled command for @plotcmd to display plot
191*c0b746e5SOllivier Robertundef($laststat);
192*c0b746e5SOllivier Robert
193*c0b746e5SOllivier Robert;# plot value ranges
194*c0b746e5SOllivier Robertundef($mintime);
195*c0b746e5SOllivier Robertundef($maxtime);
196*c0b746e5SOllivier Robertundef($minoffs);
197*c0b746e5SOllivier Robertundef($maxoffs);
198*c0b746e5SOllivier Robertundef($minfreq);
199*c0b746e5SOllivier Robertundef($maxfreq);
200*c0b746e5SOllivier Robertundef($mincmpl);
201*c0b746e5SOllivier Robertundef($maxcmpl);
202*c0b746e5SOllivier Robertundef($miny);
203*c0b746e5SOllivier Robertundef($maxy);
204*c0b746e5SOllivier Robert
205*c0b746e5SOllivier Robert;# stop operation if plot command dies
206*c0b746e5SOllivier Robertsub sigchld
207*c0b746e5SOllivier Robert{
208*c0b746e5SOllivier Robert  local($pid) = wait;
209*c0b746e5SOllivier Robert  unlink($tmpfile);
210*c0b746e5SOllivier Robert  warn(sprintf("%s: %s died: exit status: %d signal %d\n",
211*c0b746e5SOllivier Robert	      $0,
212*c0b746e5SOllivier Robert	       (defined($Plotpid) && $Plotpid == $pid)
213*c0b746e5SOllivier Robert	       ? "plotcmd" : "unknown child $pid",
214*c0b746e5SOllivier Robert	       $?>>8,$? & 0xff)) if $?;
215*c0b746e5SOllivier Robert  exit(1) if $? && defined($Plotpid) && $pid == $Plotpid;
216*c0b746e5SOllivier Robert}
217*c0b746e5SOllivier Robert&sigchld if 0;
218*c0b746e5SOllivier Robert$SIG{'CHLD'} = "sigchld";
219*c0b746e5SOllivier Robert$SIG{'CLD'} = "sigchld";
220*c0b746e5SOllivier Robert
221*c0b746e5SOllivier Robertsub abort
222*c0b746e5SOllivier Robert{
223*c0b746e5SOllivier Robert  unlink($tmpfile);
224*c0b746e5SOllivier Robert  defined($Plotpid) && kill('TERM',$Plotpid);
225*c0b746e5SOllivier Robert  die("$0: received signal SIG$_[$[] - exiting\n");
226*c0b746e5SOllivier Robert}
227*c0b746e5SOllivier Robert&abort if 0;	# make -w happy - &abort IS used
228*c0b746e5SOllivier Robert$SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = $SIG{'PIPE'} = "abort";
229*c0b746e5SOllivier Robert
230*c0b746e5SOllivier Robert;#
231*c0b746e5SOllivier Robertsub abs
232*c0b746e5SOllivier Robert{
233*c0b746e5SOllivier Robert  ($_[$[] < 0) ? -($_[$[]) : $_[$[];
234*c0b746e5SOllivier Robert}
235*c0b746e5SOllivier Robert
236*c0b746e5SOllivier Robertsub boolval
237*c0b746e5SOllivier Robert{
238*c0b746e5SOllivier Robert  local($v) = ($_[$[]);
239*c0b746e5SOllivier Robert
240*c0b746e5SOllivier Robert  return 1 if ($v eq 'yes') || ($v eq 'y');
241*c0b746e5SOllivier Robert  return 1 if ($v =~ /^[0-9]*$/) && ($v != 0);
242*c0b746e5SOllivier Robert  return 0;
243*c0b746e5SOllivier Robert}
244*c0b746e5SOllivier Robert
245*c0b746e5SOllivier Robert;#####################
246*c0b746e5SOllivier Robert;# start of real work
247*c0b746e5SOllivier Robert
248*c0b746e5SOllivier Robertprint "starting plot command (" . join(" ",@plotcmd) . ")\n" if $verbose > 1;
249*c0b746e5SOllivier Robert
250*c0b746e5SOllivier Robert$Plotpid = open(PLOT,"|-");
251*c0b746e5SOllivier Robertselect((select(PLOT),$|=1)[$[]);	# make PLOT line bufferd
252*c0b746e5SOllivier Robert
253*c0b746e5SOllivier Robertdefined($Plotpid) ||
254*c0b746e5SOllivier Robert    die("$0: failed to start plot command: $!\n");
255*c0b746e5SOllivier Robert
256*c0b746e5SOllivier Robertunless ($Plotpid)
257*c0b746e5SOllivier Robert{
258*c0b746e5SOllivier Robert   ;# child == plot command
259*c0b746e5SOllivier Robert   close(STDOUT);
260*c0b746e5SOllivier Robert   open(STDOUT,">&STDERR") ||
261*c0b746e5SOllivier Robert       die("$0: failed to redirect STDOUT of plot command: $!\n");
262*c0b746e5SOllivier Robert
263*c0b746e5SOllivier Robert   print STDOUT "plot command running as $$\n";
264*c0b746e5SOllivier Robert
265*c0b746e5SOllivier Robert   exec @plotcmd;
266*c0b746e5SOllivier Robert   die("$0: failed to exec (@plotcmd): $!\n");
267*c0b746e5SOllivier Robert   exit(1); # in case ...
268*c0b746e5SOllivier Robert}
269*c0b746e5SOllivier Robert
270*c0b746e5SOllivier Robertsub read_config
271*c0b746e5SOllivier Robert{
272*c0b746e5SOllivier Robert  local($at) = (stat($config))[$[+9];
273*c0b746e5SOllivier Robert  local($_,$c,$v);
274*c0b746e5SOllivier Robert
275*c0b746e5SOllivier Robert  (undef($laststat),(print("stat $config failed: $!\n")),return) if ! defined($at);
276*c0b746e5SOllivier Robert  return if (defined($laststat) && ($laststat == $at));
277*c0b746e5SOllivier Robert  $laststat = $at;
278*c0b746e5SOllivier Robert
279*c0b746e5SOllivier Robert  print "reading configuration from \"$config\"\n" if $verbose;
280*c0b746e5SOllivier Robert
281*c0b746e5SOllivier Robert  open(CF,"<$config") ||
282*c0b746e5SOllivier Robert      (warn("$0: failed to read \"$config\" - using old settings ($!)\n"),
283*c0b746e5SOllivier Robert       return);
284*c0b746e5SOllivier Robert  while(<CF>)
285*c0b746e5SOllivier Robert  {
286*c0b746e5SOllivier Robert    chop;
287*c0b746e5SOllivier Robert    s/^([^\#]*[^\#\s]?)\s*\#.*$//;
288*c0b746e5SOllivier Robert    next if /^\s*$/;
289*c0b746e5SOllivier Robert
290*c0b746e5SOllivier Robert    s/^\s*([^=\s]*)\s*=\s*(.*\S)\s*$/$1=$2/;
291*c0b746e5SOllivier Robert
292*c0b746e5SOllivier Robert    ($c,$v) = split(/=/,$_,2);
293*c0b746e5SOllivier Robert    print "processing \"$c=$v\"\n" if $verbose > 3;
294*c0b746e5SOllivier Robert    ($c eq "delay") && ($delay = $v,1) && next;
295*c0b746e5SOllivier Robert    ($c eq 'samples') && (!defined($PrintIt) || !defined($samples)) &&
296*c0b746e5SOllivier Robert	($samples = $v,1) && next;
297*c0b746e5SOllivier Robert    ($c eq 'srcprefix') && (($srcprefix=$v)=~s/\$STATHOST/$STATHOST/g,1)
298*c0b746e5SOllivier Robert	&& next;
299*c0b746e5SOllivier Robert    ($c eq 'showoffs') &&
300*c0b746e5SOllivier Robert	($showoffs = boolval($v),1) && next;
301*c0b746e5SOllivier Robert    ($c eq 'showfreq') &&
302*c0b746e5SOllivier Robert	($showfreq = boolval($v),1) && next;
303*c0b746e5SOllivier Robert    ($c eq 'showcmpl') &&
304*c0b746e5SOllivier Robert	($showcmpl = boolval($v),1) && next;
305*c0b746e5SOllivier Robert    ($c eq 'showoreg') &&
306*c0b746e5SOllivier Robert	($showoreg = boolval($v),1) && next;
307*c0b746e5SOllivier Robert    ($c eq 'showfreg') &&
308*c0b746e5SOllivier Robert	($showfreg = boolval($v),1) && next;
309*c0b746e5SOllivier Robert
310*c0b746e5SOllivier Robert    ($c eq 'exit') && (unlink($tmpfile),die("$0: exit by config request\n"));
311*c0b746e5SOllivier Robert
312*c0b746e5SOllivier Robert    ($c eq 'freqbase' ||
313*c0b746e5SOllivier Robert     $c eq 'cmplscale') &&
314*c0b746e5SOllivier Robert	do {
315*c0b746e5SOllivier Robert	    if (! defined($v) || $v eq "" || $v eq 'dynamic')
316*c0b746e5SOllivier Robert	    {
317*c0b746e5SOllivier Robert	      eval "undef(\$$c);";
318*c0b746e5SOllivier Robert	    }
319*c0b746e5SOllivier Robert	    else
320*c0b746e5SOllivier Robert	    {
321*c0b746e5SOllivier Robert	      eval "\$$c = \$v;";
322*c0b746e5SOllivier Robert	    }
323*c0b746e5SOllivier Robert	    next;
324*c0b746e5SOllivier Robert	};
325*c0b746e5SOllivier Robert    ($c eq 'timebase') &&
326*c0b746e5SOllivier Robert	do {
327*c0b746e5SOllivier Robert	    if (! defined($v) || $v eq "" || $v eq "dynamic")
328*c0b746e5SOllivier Robert	    {
329*c0b746e5SOllivier Robert	      undef($timebase);
330*c0b746e5SOllivier Robert	    }
331*c0b746e5SOllivier Robert	    else
332*c0b746e5SOllivier Robert	    {
333*c0b746e5SOllivier Robert	      $timebase=&date_time_spec2seconds($v);
334*c0b746e5SOllivier Robert	    }
335*c0b746e5SOllivier Robert	};
336*c0b746e5SOllivier Robert    ($c eq 'EndTime') &&
337*c0b746e5SOllivier Robert	do {
338*c0b746e5SOllivier Robert	    next if defined($EndTime) && defined($PrintIt);
339*c0b746e5SOllivier Robert	    if (! defined($v) || $v eq "" || $v eq "none")
340*c0b746e5SOllivier Robert	    {
341*c0b746e5SOllivier Robert	      undef($EndTime);
342*c0b746e5SOllivier Robert	    }
343*c0b746e5SOllivier Robert	    else
344*c0b746e5SOllivier Robert	    {
345*c0b746e5SOllivier Robert	      $EndTime=&date_time_spec2seconds($v);
346*c0b746e5SOllivier Robert	    }
347*c0b746e5SOllivier Robert	};
348*c0b746e5SOllivier Robert    ($c eq 'StartTime') &&
349*c0b746e5SOllivier Robert	do {
350*c0b746e5SOllivier Robert	    next if defined($StartTime) && defined($PrintIt);
351*c0b746e5SOllivier Robert	    if (! defined($v) || $v eq "" || $v eq "none")
352*c0b746e5SOllivier Robert	    {
353*c0b746e5SOllivier Robert	      undef($StartTime);
354*c0b746e5SOllivier Robert	    }
355*c0b746e5SOllivier Robert	    else
356*c0b746e5SOllivier Robert	    {
357*c0b746e5SOllivier Robert	      $StartTime=&date_time_spec2seconds($v);
358*c0b746e5SOllivier Robert	    }
359*c0b746e5SOllivier Robert	};
360*c0b746e5SOllivier Robert
361*c0b746e5SOllivier Robert    ($c eq 'MaxY') &&
362*c0b746e5SOllivier Robert	do {
363*c0b746e5SOllivier Robert	    next if defined($MaxY) && defined($PrintIt);
364*c0b746e5SOllivier Robert	    if (! defined($v) || $v eq "" || $v eq "none")
365*c0b746e5SOllivier Robert	    {
366*c0b746e5SOllivier Robert	      undef($MaxY);
367*c0b746e5SOllivier Robert	    }
368*c0b746e5SOllivier Robert	    else
369*c0b746e5SOllivier Robert	    {
370*c0b746e5SOllivier Robert	      $MaxY=$v;
371*c0b746e5SOllivier Robert	    }
372*c0b746e5SOllivier Robert	};
373*c0b746e5SOllivier Robert
374*c0b746e5SOllivier Robert    ($c eq 'MinY') &&
375*c0b746e5SOllivier Robert	do {
376*c0b746e5SOllivier Robert	    next if defined($MinY) && defined($PrintIt);
377*c0b746e5SOllivier Robert	    if (! defined($v) || $v eq "" || $v eq "none")
378*c0b746e5SOllivier Robert	    {
379*c0b746e5SOllivier Robert	      undef($MinY);
380*c0b746e5SOllivier Robert	    }
381*c0b746e5SOllivier Robert	    else
382*c0b746e5SOllivier Robert	    {
383*c0b746e5SOllivier Robert	      $MinY=$v;
384*c0b746e5SOllivier Robert	    }
385*c0b746e5SOllivier Robert	};
386*c0b746e5SOllivier Robert
387*c0b746e5SOllivier Robert    ($c eq 'deltaT') &&
388*c0b746e5SOllivier Robert	do {
389*c0b746e5SOllivier Robert	    if (!defined($v) || $v eq "")
390*c0b746e5SOllivier Robert	    {
391*c0b746e5SOllivier Robert	      undef($deltaT);
392*c0b746e5SOllivier Robert	    }
393*c0b746e5SOllivier Robert	    else
394*c0b746e5SOllivier Robert	    {
395*c0b746e5SOllivier Robert	      $deltaT = $v;
396*c0b746e5SOllivier Robert	    }
397*c0b746e5SOllivier Robert	    next;
398*c0b746e5SOllivier Robert	};
399*c0b746e5SOllivier Robert    ($c eq 'verbose') && ! defined($PrintIt) &&
400*c0b746e5SOllivier Robert	do {
401*c0b746e5SOllivier Robert	     if (!defined($v) || $v == 0)
402*c0b746e5SOllivier Robert	     {
403*c0b746e5SOllivier Robert	       $verbose = 0;
404*c0b746e5SOllivier Robert	     }
405*c0b746e5SOllivier Robert	     else
406*c0b746e5SOllivier Robert	     {
407*c0b746e5SOllivier Robert	       $verbose = $v;
408*c0b746e5SOllivier Robert	     }
409*c0b746e5SOllivier Robert	     next;
410*c0b746e5SOllivier Robert	};
411*c0b746e5SOllivier Robert    ;# otherwise: silently ignore unrecognized config line
412*c0b746e5SOllivier Robert  }
413*c0b746e5SOllivier Robert  close(CF);
414*c0b746e5SOllivier Robert  ;# set show defaults when nothing selected
415*c0b746e5SOllivier Robert  $showoffs = $showfreq = $showcmpl = 1
416*c0b746e5SOllivier Robert      unless $showoffs || $showfreq || $showcmpl;
417*c0b746e5SOllivier Robert  if ($verbose > 3)
418*c0b746e5SOllivier Robert  {
419*c0b746e5SOllivier Robert    print  "new configuration:\n";
420*c0b746e5SOllivier Robert    print  "   delay\t= $delay\n";
421*c0b746e5SOllivier Robert    print  "   samples\t= $samples\n";
422*c0b746e5SOllivier Robert    print  "   srcprefix\t= $srcprefix\n";
423*c0b746e5SOllivier Robert    print  "   showoffs\t= $showoffs\n";
424*c0b746e5SOllivier Robert    print  "   showfreq\t= $showfreq\n";
425*c0b746e5SOllivier Robert    print  "   showcmpl\t= $showcmpl\n";
426*c0b746e5SOllivier Robert    print  "   showoreg\t= $showoreg\n";
427*c0b746e5SOllivier Robert    print  "   showfreg\t= $showfreg\n";
428*c0b746e5SOllivier Robert    printf "   timebase\t= %s",defined($timebase)?&ctime($timebase):"dynamic\n";
429*c0b746e5SOllivier Robert    printf "   freqbase\t= %s\n",defined($freqbase)  ?"$freqbase":"dynamic";
430*c0b746e5SOllivier Robert    printf "   cmplscale\t= %s\n",defined($cmplscale)?"$cmplscale":"dynamic";
431*c0b746e5SOllivier Robert    printf "   StartTime\t= %s",defined($StartTime)?&ctime($StartTime):"none\n";
432*c0b746e5SOllivier Robert    printf "   EndTime\t= %s",  defined($EndTime) ?  &ctime($EndTime):"none\n";
433*c0b746e5SOllivier Robert    printf "   MaxY\t= %s",defined($MaxY)? $MaxY      :"none\n";
434*c0b746e5SOllivier Robert    printf "   MinY\t= %s",defined($MinY)? $MinY      :"none\n";
435*c0b746e5SOllivier Robert    print  "   verbose\t= $verbose\n";
436*c0b746e5SOllivier Robert  }
437*c0b746e5SOllivier Robertprint "configuration file read\n" if $verbose > 2;
438*c0b746e5SOllivier Robert}
439*c0b746e5SOllivier Robert
440*c0b746e5SOllivier Robertsub make_doplot($$)
441*c0b746e5SOllivier Robert{
442*c0b746e5SOllivier Robert    my($lo, $lf) = @_;
443*c0b746e5SOllivier Robert    local($c) = ("");
444*c0b746e5SOllivier Robert    local($fmt)
445*c0b746e5SOllivier Robert	= ("%s \"%s\" using 1:%d title '%s <%lf %lf> %6s' with lines");
446*c0b746e5SOllivier Robert    local($regfmt)
447*c0b746e5SOllivier Robert	= ("%s ((%lf * x) + %lf) title 'lin. approx. %s (%f t[h]) %s %f <%f> %6s' with lines");
448*c0b746e5SOllivier Robert
449*c0b746e5SOllivier Robert    $doplot = "    set title 'NTP loopfilter statistics for $STATHOST  " .
450*c0b746e5SOllivier Robert	"(last $LastCnt samples from $srcprefix*)'\n";
451*c0b746e5SOllivier Robert
452*c0b746e5SOllivier Robert    local($xts,$xte,$i,$t);
453*c0b746e5SOllivier Robert
454*c0b746e5SOllivier Robert    local($s,$c) = ("");
455*c0b746e5SOllivier Robert
456*c0b746e5SOllivier Robert    ;# number of integral seconds to get at least 12 tic marks on x axis
457*c0b746e5SOllivier Robert    $t = int(($maxtime - $mintime) / 12 + 0.5);
458*c0b746e5SOllivier Robert    $t = 1 unless $t;		# prevent $t to be zero
459*c0b746e5SOllivier Robert    foreach $i (30,
460*c0b746e5SOllivier Robert		60,5*60,15*60,30*60,
461*c0b746e5SOllivier Robert		60*60,2*60*60,6*60*60,12*60*60,
462*c0b746e5SOllivier Robert		24*60*60,48*60*60)
463*c0b746e5SOllivier Robert    {
464*c0b746e5SOllivier Robert	last if $t < $i;
465*c0b746e5SOllivier Robert	$t = $t - ($t % $i);
466*c0b746e5SOllivier Robert    }
467*c0b746e5SOllivier Robert    print "time label resolution: $t seconds\n" if $verbose > 1;
468*c0b746e5SOllivier Robert
469*c0b746e5SOllivier Robert    ;# make gnuplot use wall clock time labels instead of NTP seconds
470*c0b746e5SOllivier Robert    for ($c="", $i = $mintime - ($mintime % $t);
471*c0b746e5SOllivier Robert	 $i <= $maxtime + $t;
472*c0b746e5SOllivier Robert	 $i += $t, $c=",")
473*c0b746e5SOllivier Robert    {
474*c0b746e5SOllivier Robert	$s .= $c;
475*c0b746e5SOllivier Robert	((int($i / $t) % 2) &&
476*c0b746e5SOllivier Robert	 ($s .= sprintf("'' %lf",($i - $LastTimeBase)/3600))) ||
477*c0b746e5SOllivier Robert	     (($t <= 60) &&
478*c0b746e5SOllivier Robert	      ($s .= sprintf("'%d:%02d:%02d' %lf",
479*c0b746e5SOllivier Robert			     (localtime($i))[$[+2,$[+1,$[+0],
480*c0b746e5SOllivier Robert			     ($i - $LastTimeBase)/3600)))
481*c0b746e5SOllivier Robert		 || (($t <= 2*60*60) &&
482*c0b746e5SOllivier Robert		     ($s .= sprintf("'%d:%02d' %lf",
483*c0b746e5SOllivier Robert				    (localtime($i))[$[+2,$[+1],
484*c0b746e5SOllivier Robert				    ($i - $LastTimeBase)/3600)))
485*c0b746e5SOllivier Robert		     || (($t <= 12*60*60) &&
486*c0b746e5SOllivier Robert			 ($s .= sprintf("'%s %d:00' %lf",
487*c0b746e5SOllivier Robert					$Day[(localtime($i))[$[+6]],
488*c0b746e5SOllivier Robert					(localtime($i))[$[+2],
489*c0b746e5SOllivier Robert					($i - $LastTimeBase)/3600)))
490*c0b746e5SOllivier Robert			 || ($s .= sprintf("'%d.%d-%d:00' %lf",
491*c0b746e5SOllivier Robert					   (localtime($i))[$[+3,$[+4,$[+2],
492*c0b746e5SOllivier Robert					   ($i - $LastTimeBase)/3600));
493*c0b746e5SOllivier Robert    }
494*c0b746e5SOllivier Robert    $doplot .= "set xtics ($s)\n";
495*c0b746e5SOllivier Robert
496*c0b746e5SOllivier Robert    chop($xts = &ctime($mintime));
497*c0b746e5SOllivier Robert    chop($xte = &ctime($maxtime));
498*c0b746e5SOllivier Robert    $doplot .= "set xlabel 'Start:  $xts    --   Time Scale   --    End:  $xte'\n";
499*c0b746e5SOllivier Robert    $doplot .= "set yrange [" ;
500*c0b746e5SOllivier Robert    $doplot .= defined($MinY) ? sprintf("%lf", $MinY) : $miny;
501*c0b746e5SOllivier Robert    $doplot .= ':';
502*c0b746e5SOllivier Robert    $doplot .= defined($MaxY) ? sprintf("%lf", $MaxY) : $maxy;
503*c0b746e5SOllivier Robert    $doplot .= "]\n";
504*c0b746e5SOllivier Robert
505*c0b746e5SOllivier Robert    $doplot .= "   plot";
506*c0b746e5SOllivier Robert    $c = "";
507*c0b746e5SOllivier Robert    $showoffs &&
508*c0b746e5SOllivier Robert	($doplot .= sprintf($fmt,$c,$tmpfile,2,
509*c0b746e5SOllivier Robert			    "offset",
510*c0b746e5SOllivier Robert			    $minoffs,$maxoffs,
511*c0b746e5SOllivier Robert			    "[ms]"),
512*c0b746e5SOllivier Robert	 $c = ",");
513*c0b746e5SOllivier Robert    $LastCmplScale = 1 if ! defined($LastCmplScale);
514*c0b746e5SOllivier Robert    $showcmpl &&
515*c0b746e5SOllivier Robert	($doplot .= sprintf($fmt,$c,$tmpfile,4,
516*c0b746e5SOllivier Robert			    "compliance" .
517*c0b746e5SOllivier Robert			    (&abs($LastCmplScale) > 1
518*c0b746e5SOllivier Robert			     ? " / $LastCmplScale"
519*c0b746e5SOllivier Robert			     : (&abs($LastCmplScale) == 1 ? "" : " * ".(1/$LastCmplScale))),
520*c0b746e5SOllivier Robert			    $mincmpl/$LastCmplScale,$maxcmpl/$LastCmplScale,
521*c0b746e5SOllivier Robert			    ""),
522*c0b746e5SOllivier Robert	 $c = ",");
523*c0b746e5SOllivier Robert    $LastFreqBase = 0 if ! defined($LastFreqBase);
524*c0b746e5SOllivier Robert    $LastFreqBaseString = "?" if ! defined($LastFreqBaseString);
525*c0b746e5SOllivier Robert    $FreqScale = 1 if ! defined($FreqScale);
526*c0b746e5SOllivier Robert    $FreqScaleInv = 1 if ! defined($FreqScaleInv);
527*c0b746e5SOllivier Robert    $showfreq &&
528*c0b746e5SOllivier Robert	($doplot .= sprintf($fmt,$c,$tmpfile,3,
529*c0b746e5SOllivier Robert			    "frequency" .
530*c0b746e5SOllivier Robert			    ($LastFreqBase > 0
531*c0b746e5SOllivier Robert			     ? " - $LastFreqBaseString"
532*c0b746e5SOllivier Robert			     : ($LastFreqBase == 0 ? "" : " + $LastFreqBaseString")),
533*c0b746e5SOllivier Robert			    $minfreq * $FreqScale - $LastFreqBase,
534*c0b746e5SOllivier Robert			    $maxfreq * $FreqScale - $LastFreqBase,
535*c0b746e5SOllivier Robert			    "[${FreqScaleInv}ppm]"),
536*c0b746e5SOllivier Robert	 $c = ",");
537*c0b746e5SOllivier Robert    $showoreg && $showoffs &&
538*c0b746e5SOllivier Robert	($doplot .= sprintf($regfmt, $c,
539*c0b746e5SOllivier Robert			    $lo->B(),$lo->A(),
540*c0b746e5SOllivier Robert			    "offset   ",
541*c0b746e5SOllivier Robert			    $lo->B(),
542*c0b746e5SOllivier Robert			    (($lo->A()) < 0 ? '-' : '+'),
543*c0b746e5SOllivier Robert			    &abs($lo->A()), $lo->r(),
544*c0b746e5SOllivier Robert			    "[ms]"),
545*c0b746e5SOllivier Robert	 $c = ",");
546*c0b746e5SOllivier Robert    $showfreg && $showfreq &&
547*c0b746e5SOllivier Robert	($doplot .= sprintf($regfmt, $c,
548*c0b746e5SOllivier Robert			    $lf->B() * $FreqScale,
549*c0b746e5SOllivier Robert			    ($lf->A() + $minfreq) * $FreqScale - $LastFreqBase,
550*c0b746e5SOllivier Robert			    "frequency",
551*c0b746e5SOllivier Robert			    $lf->B() * $FreqScale,
552*c0b746e5SOllivier Robert			    (($lf->A() + $minfreq) * $FreqScale - $LastFreqBase) < 0 ? '-' : '+',
553*c0b746e5SOllivier Robert			    &abs(($lf->A() + $minfreq) * $FreqScale - $LastFreqBase),
554*c0b746e5SOllivier Robert			    $lf->r(),
555*c0b746e5SOllivier Robert			    "[${FreqScaleInv}ppm]"),
556*c0b746e5SOllivier Robert	 $c = ",");
557*c0b746e5SOllivier Robert    $doplot .= "\n";
558*c0b746e5SOllivier Robert}
559*c0b746e5SOllivier Robert
560*c0b746e5SOllivier Robert%F_key   = ();
561*c0b746e5SOllivier Robert%F_name  = ();
562*c0b746e5SOllivier Robert%F_size  = ();
563*c0b746e5SOllivier Robert%F_mtime = ();
564*c0b746e5SOllivier Robert%F_first = ();
565*c0b746e5SOllivier Robert%F_last  = ();
566*c0b746e5SOllivier Robert
567*c0b746e5SOllivier Robertsub genfile
568*c0b746e5SOllivier Robert{
569*c0b746e5SOllivier Robert    local($cnt,$in,$out,$lo,$lf,@fpos) = @_;
570*c0b746e5SOllivier Robert
571*c0b746e5SOllivier Robert    local(@F,@t,$t,$lastT) = ();
572*c0b746e5SOllivier Robert    local(@break,@time,@offs,@freq,@cmpl,@loffset,@filekey) = ();
573*c0b746e5SOllivier Robert    local($lm,$l,@f);
574*c0b746e5SOllivier Robert
575*c0b746e5SOllivier Robert    local($sdir,$sname);
576*c0b746e5SOllivier Robert
577*c0b746e5SOllivier Robert    ;# allocate some storage for the tables
578*c0b746e5SOllivier Robert    ;# otherwise realloc may get into troubles
579*c0b746e5SOllivier Robert    if (defined($StartTime) && defined($EndTime))
580*c0b746e5SOllivier Robert    {
581*c0b746e5SOllivier Robert	$l = ($EndTime-$StartTime) -$[+1 +1; # worst case: 1 sample per second
582*c0b746e5SOllivier Robert    }
583*c0b746e5SOllivier Robert    else
584*c0b746e5SOllivier Robert    {
585*c0b746e5SOllivier Robert	$l = $cnt + 10;
586*c0b746e5SOllivier Robert    }
587*c0b746e5SOllivier Robert    print "preextending arrays to $l entries\n" if $verbose > 2;
588*c0b746e5SOllivier Robert    $#break =   $l; for ($i=$[; $i<=$l;$i++) { $break[$i] = 0; }
589*c0b746e5SOllivier Robert    $#time =    $l; for ($i=$[; $i<=$l;$i++) { $time[$i] = 0; }
590*c0b746e5SOllivier Robert    $#offs =    $l; for ($i=$[; $i<=$l;$i++) { $offs[$i] = 0; }
591*c0b746e5SOllivier Robert    $#freq =    $l; for ($i=$[; $i<=$l;$i++) { $freq[$i] = 0; }
592*c0b746e5SOllivier Robert    $#cmpl =    $l; for ($i=$[; $i<=$l;$i++) { $cmpl[$i] = 0; }
593*c0b746e5SOllivier Robert    $#loffset = $l; for ($i=$[; $i<=$l;$i++) { $loffset[$i] = 0; }
594*c0b746e5SOllivier Robert    $#filekey = $l; for ($i=$[; $i<=$l;$i++) { $filekey[$i] = 0; }
595*c0b746e5SOllivier Robert    ;# now reduce size again
596*c0b746e5SOllivier Robert    $#break =   $[ - 1;
597*c0b746e5SOllivier Robert    $#time =    $[ - 1;
598*c0b746e5SOllivier Robert    $#offs =    $[ - 1;
599*c0b746e5SOllivier Robert    $#freq =    $[ - 1;
600*c0b746e5SOllivier Robert    $#cmpl =    $[ - 1;
601*c0b746e5SOllivier Robert    $#loffset = $[ - 1;
602*c0b746e5SOllivier Robert    $#filekey = $[ - 1;
603*c0b746e5SOllivier Robert    print "memory allocation ready\n" if $verbose > 2;
604*c0b746e5SOllivier Robert    sleep(3) if $verbose > 1;
605*c0b746e5SOllivier Robert
606*c0b746e5SOllivier Robert    $fpos[$[] = '' if !defined($fpos[$[]);
607*c0b746e5SOllivier Robert
608*c0b746e5SOllivier Robert    if (index($in,"/") < $[)
609*c0b746e5SOllivier Robert    {
610*c0b746e5SOllivier Robert	$sdir = ".";
611*c0b746e5SOllivier Robert	$sname = $in;
612*c0b746e5SOllivier Robert    }
613*c0b746e5SOllivier Robert    else
614*c0b746e5SOllivier Robert    {
615*c0b746e5SOllivier Robert	($sdir,$sname) = ($in =~ m!^(.*)/([^/]*)!);
616*c0b746e5SOllivier Robert	$sname = "" unless defined($sname);
617*c0b746e5SOllivier Robert    }
618*c0b746e5SOllivier Robert
619*c0b746e5SOllivier Robert    $Ltime = -1 if ! defined($Ltime);
620*c0b746e5SOllivier Robert    if (!defined($Lsdir) || $Lsdir ne $sdir || $Ltime != (stat($sdir))[$[+9] ||
621*c0b746e5SOllivier Robert	grep($F_mtime{$_} != (stat($F_name{$_}))[$[+9], @F_files))
622*c0b746e5SOllivier Robert
623*c0b746e5SOllivier Robert    {
624*c0b746e5SOllivier Robert	print "rescanning directory \"$sdir\" for files \"$sname*\"\n"
625*c0b746e5SOllivier Robert	    if $verbose > 1;
626*c0b746e5SOllivier Robert
627*c0b746e5SOllivier Robert	;# rescan directory on changes
628*c0b746e5SOllivier Robert	$Lsdir = $sdir;
629*c0b746e5SOllivier Robert	$Ltime = (stat($sdir))[$[+9];
630*c0b746e5SOllivier Robert	</X{> if 0;		# dummy line - calm down my formatter
631*c0b746e5SOllivier Robert	local(@newfiles) = < ${in}*[0-9] >;
632*c0b746e5SOllivier Robert	local($st_dev,$st_ino,$st_mtime,$st_size,$name,$key,$modified);
633*c0b746e5SOllivier Robert
634*c0b746e5SOllivier Robert	foreach $name (@newfiles)
635*c0b746e5SOllivier Robert	{
636*c0b746e5SOllivier Robert	    ($st_dev,$st_ino,$st_size,$st_mtime) =
637*c0b746e5SOllivier Robert		(stat($name))[$[,$[+1,$[+7,$[+9];
638*c0b746e5SOllivier Robert	    $modified = 0;
639*c0b746e5SOllivier Robert	    $key = sprintf("%lx|%lu", $st_dev, $st_ino);
640*c0b746e5SOllivier Robert
641*c0b746e5SOllivier Robert	    print "candidate file \"$name\"",
642*c0b746e5SOllivier Robert                  (defined($st_dev) ? "" : " failed: $!"),"\n"
643*c0b746e5SOllivier Robert		      if $verbose > 2;
644*c0b746e5SOllivier Robert
645*c0b746e5SOllivier Robert	    if (! defined($F_key{$name}) || $F_key{$name} ne $key)
646*c0b746e5SOllivier Robert	    {
647*c0b746e5SOllivier Robert		$F_key{$name} = $key;
648*c0b746e5SOllivier Robert		$modified++;
649*c0b746e5SOllivier Robert	    }
650*c0b746e5SOllivier Robert	    if (!defined($F_name{$key}) || $F_name{$key} ne $name)
651*c0b746e5SOllivier Robert	    {
652*c0b746e5SOllivier Robert		$F_name{$key} = $name;
653*c0b746e5SOllivier Robert		$modified++;
654*c0b746e5SOllivier Robert	    }
655*c0b746e5SOllivier Robert	    if (!defined($F_size{$key}) || $F_size{$key} != $st_size)
656*c0b746e5SOllivier Robert	    {
657*c0b746e5SOllivier Robert		$F_size{$key} = $st_size;
658*c0b746e5SOllivier Robert		$modified++;
659*c0b746e5SOllivier Robert	    }
660*c0b746e5SOllivier Robert	    if (!defined($F_mtime{$key}) || $F_mtime{$key} != $st_mtime)
661*c0b746e5SOllivier Robert	    {
662*c0b746e5SOllivier Robert		$F_mtime{$key} = $st_mtime;
663*c0b746e5SOllivier Robert		$modified++;
664*c0b746e5SOllivier Robert	    }
665*c0b746e5SOllivier Robert	    if ($modified)
666*c0b746e5SOllivier Robert	    {
667*c0b746e5SOllivier Robert		print "new data \"$name\" key: $key;\n" if $verbose > 1;
668*c0b746e5SOllivier Robert	        print "             size: $st_size; mtime: $st_mtime;\n"
669*c0b746e5SOllivier Robert		    if $verbose > 1;
670*c0b746e5SOllivier Robert		$F_last{$key} = $F_first{$key} = $st_mtime;
671*c0b746e5SOllivier Robert		$F_first{$key}--; # prevent zero divide later on
672*c0b746e5SOllivier Robert		;# now compute derivated attributes
673*c0b746e5SOllivier Robert		open(IN, "<$name") ||
674*c0b746e5SOllivier Robert		    do {
675*c0b746e5SOllivier Robert			warn "$0: failed to open \"$name\": $!";
676*c0b746e5SOllivier Robert			next;
677*c0b746e5SOllivier Robert		    };
678*c0b746e5SOllivier Robert
679*c0b746e5SOllivier Robert		while(<IN>)
680*c0b746e5SOllivier Robert		{
681*c0b746e5SOllivier Robert		    @F = split;
682*c0b746e5SOllivier Robert		    next if @F < 5;
683*c0b746e5SOllivier Robert		    next if $F[$[] eq "";
684*c0b746e5SOllivier Robert		    $t = ($F[$[] - $MJD_1970) * 24 * 60 * 60;
685*c0b746e5SOllivier Robert		    $t += $F[$[+1];
686*c0b746e5SOllivier Robert		    $F_first{$key} = $t;
687*c0b746e5SOllivier Robert		    print "\tfound first entry: $t ",&ctime($t)
688*c0b746e5SOllivier Robert			if $verbose > 4;
689*c0b746e5SOllivier Robert		    last;
690*c0b746e5SOllivier Robert		}
691*c0b746e5SOllivier Robert		seek(IN,
692*c0b746e5SOllivier Robert		     ($st_size > 4*$RecordSize) ? $st_size - 4*$RecordSize : 0,
693*c0b746e5SOllivier Robert		     0);
694*c0b746e5SOllivier Robert		while(<IN>)
695*c0b746e5SOllivier Robert		{
696*c0b746e5SOllivier Robert		    @F = split;
697*c0b746e5SOllivier Robert		    next if @F < 5;
698*c0b746e5SOllivier Robert		    next if $F[$[] eq "";
699*c0b746e5SOllivier Robert		    $t = ($F[$[] - $MJD_1970) * 24 * 60 * 60;
700*c0b746e5SOllivier Robert		    $t += $F[$[+1];
701*c0b746e5SOllivier Robert		    $F_last{$key} = $t;
702*c0b746e5SOllivier Robert		    $_ = <IN>;
703*c0b746e5SOllivier Robert		    print "\tfound last entry: $t ", &ctime($t)
704*c0b746e5SOllivier Robert			if $verbose > 4 && ! defined($_);
705*c0b746e5SOllivier Robert		    last unless defined($_);
706*c0b746e5SOllivier Robert		    redo;
707*c0b746e5SOllivier Robert		    ;# Ok, calm down...
708*c0b746e5SOllivier Robert		    ;# using $_ = <IN> in conjunction with redo
709*c0b746e5SOllivier Robert		    ;# is semantically equivalent to the while loop, but
710*c0b746e5SOllivier Robert		    ;# I needed a one line look ahead and this solution
711*c0b746e5SOllivier Robert		    ;# was what I thought of first
712*c0b746e5SOllivier Robert		    ;# and.. If you do not like it dont look
713*c0b746e5SOllivier Robert		}
714*c0b746e5SOllivier Robert		close(IN);
715*c0b746e5SOllivier Robert		print("             first: ",$F_first{$key},
716*c0b746e5SOllivier Robert		      " last: ",$F_last{$key},"\n") if $verbose > 1;
717*c0b746e5SOllivier Robert	    }
718*c0b746e5SOllivier Robert	}
719*c0b746e5SOllivier Robert	;# now reclaim memory used for files no longer referenced ...
720*c0b746e5SOllivier Robert	local(%Names);
721*c0b746e5SOllivier Robert	grep($Names{$_} = 1,@newfiles);
722*c0b746e5SOllivier Robert	foreach (keys %F_key)
723*c0b746e5SOllivier Robert	{
724*c0b746e5SOllivier Robert	    next if defined($Names{$_});
725*c0b746e5SOllivier Robert	    delete $F_key{$_};
726*c0b746e5SOllivier Robert	    $verbose > 2 && print "no longer referenced: \"$_\"\n";
727*c0b746e5SOllivier Robert	}
728*c0b746e5SOllivier Robert	%Names = ();
729*c0b746e5SOllivier Robert
730*c0b746e5SOllivier Robert	grep($Names{$_} = 1,values(%F_key));
731*c0b746e5SOllivier Robert	foreach (keys %F_name)
732*c0b746e5SOllivier Robert	{
733*c0b746e5SOllivier Robert	    next if defined($Names{$_});
734*c0b746e5SOllivier Robert	    delete $F_name{$_};
735*c0b746e5SOllivier Robert	    $verbose > 2 && print "unref name($_)= $F_name{$_}\n";
736*c0b746e5SOllivier Robert	}
737*c0b746e5SOllivier Robert	foreach (keys %F_size)
738*c0b746e5SOllivier Robert	{
739*c0b746e5SOllivier Robert	    next if defined($Names{$_});
740*c0b746e5SOllivier Robert	    delete $F_size{$_};
741*c0b746e5SOllivier Robert	    $verbose > 2 && print "unref size($_)\n";
742*c0b746e5SOllivier Robert	}
743*c0b746e5SOllivier Robert	foreach (keys %F_mtime)
744*c0b746e5SOllivier Robert	{
745*c0b746e5SOllivier Robert	    next if defined($Names{$_});
746*c0b746e5SOllivier Robert	    delete $F_mtime{$_};
747*c0b746e5SOllivier Robert	    $verbose > 2 && print "unref mtime($_)\n";
748*c0b746e5SOllivier Robert	}
749*c0b746e5SOllivier Robert	foreach (keys %F_first)
750*c0b746e5SOllivier Robert	{
751*c0b746e5SOllivier Robert	    next if defined($Names{$_});
752*c0b746e5SOllivier Robert	    delete $F_first{$_};
753*c0b746e5SOllivier Robert	    $verbose > 2 && print "unref first($_)\n";
754*c0b746e5SOllivier Robert	}
755*c0b746e5SOllivier Robert	foreach (keys %F_last)
756*c0b746e5SOllivier Robert	{
757*c0b746e5SOllivier Robert	    next if defined($Names{$_});
758*c0b746e5SOllivier Robert	    delete $F_last{$_};
759*c0b746e5SOllivier Robert	    $verbose > 2 && print "unref last($_)\n";
760*c0b746e5SOllivier Robert	}
761*c0b746e5SOllivier Robert	;# create list sorted by time
762*c0b746e5SOllivier Robert	@F_files = sort {$F_first{$a} <=> $F_first{$b}; } keys(%F_name);
763*c0b746e5SOllivier Robert	if ($verbose > 1)
764*c0b746e5SOllivier Robert	{
765*c0b746e5SOllivier Robert	    print "Resulting file list:\n";
766*c0b746e5SOllivier Robert	    foreach (@F_files)
767*c0b746e5SOllivier Robert	    {
768*c0b746e5SOllivier Robert		print "\t$_\t$F_name{$_}\n";
769*c0b746e5SOllivier Robert	    }
770*c0b746e5SOllivier Robert	}
771*c0b746e5SOllivier Robert    }
772*c0b746e5SOllivier Robert
773*c0b746e5SOllivier Robert    printf("processing %s; output \"$out\" (%d input files)\n",
774*c0b746e5SOllivier Robert	   ((defined($StartTime) && defined($EndTime))
775*c0b746e5SOllivier Robert	    ? "time range"
776*c0b746e5SOllivier Robert	    : (defined($StartTime) ? "$cnt samples from StartTime" :
777*c0b746e5SOllivier Robert	      (defined($EndTime) ? "$cnt samples to EndTime" :
778*c0b746e5SOllivier Robert		 "last $cnt samples"))),
779*c0b746e5SOllivier Robert	    scalar(@F_files))
780*c0b746e5SOllivier Robert	if $verbose > 1;
781*c0b746e5SOllivier Robert
782*c0b746e5SOllivier Robert    ;# open output file - will be input for plotcmd
783*c0b746e5SOllivier Robert    open(OUT,">$out") ||
784*c0b746e5SOllivier Robert	do {
785*c0b746e5SOllivier Robert	    warn("$0: cannot create \"$out\": $!\n");
786*c0b746e5SOllivier Robert	};
787*c0b746e5SOllivier Robert
788*c0b746e5SOllivier Robert    @f = @F_files;
789*c0b746e5SOllivier Robert    if (defined($StartTime))
790*c0b746e5SOllivier Robert    {
791*c0b746e5SOllivier Robert	while (@f && ($F_last{$f[$[]} < $StartTime))
792*c0b746e5SOllivier Robert	{
793*c0b746e5SOllivier Robert	    print("shifting ", $F_name{$f[$[]},
794*c0b746e5SOllivier Robert		  " last: ", $F_last{$f[$[]},
795*c0b746e5SOllivier Robert		  " < StartTime: $StartTime\n")
796*c0b746e5SOllivier Robert		if $verbose > 3;
797*c0b746e5SOllivier Robert	    shift(@f);
798*c0b746e5SOllivier Robert	}
799*c0b746e5SOllivier Robert
800*c0b746e5SOllivier Robert
801*c0b746e5SOllivier Robert    }
802*c0b746e5SOllivier Robert    if (defined($EndTime))
803*c0b746e5SOllivier Robert    {
804*c0b746e5SOllivier Robert	while (@f && ($F_first{$f[$#f]} > $EndTime))
805*c0b746e5SOllivier Robert	{
806*c0b746e5SOllivier Robert	    print("popping  ", $F_name{$f[$#f]},
807*c0b746e5SOllivier Robert		  " first: ", $F_first{$f[$#f]},
808*c0b746e5SOllivier Robert		  " > EndTime: $EndTime\n")
809*c0b746e5SOllivier Robert		if $verbose > 3;
810*c0b746e5SOllivier Robert	    pop(@f);
811*c0b746e5SOllivier Robert	}
812*c0b746e5SOllivier Robert    }
813*c0b746e5SOllivier Robert
814*c0b746e5SOllivier Robert    if (@f)
815*c0b746e5SOllivier Robert    {
816*c0b746e5SOllivier Robert	if (defined($StartTime))
817*c0b746e5SOllivier Robert	{
818*c0b746e5SOllivier Robert	    print "guess start according to StartTime ($StartTime)\n"
819*c0b746e5SOllivier Robert		if $verbose > 3;
820*c0b746e5SOllivier Robert
821*c0b746e5SOllivier Robert	    if ($fpos[$[] eq 'start')
822*c0b746e5SOllivier Robert	    {
823*c0b746e5SOllivier Robert		if (grep($_ eq $fpos[$[+1],@f))
824*c0b746e5SOllivier Robert		{
825*c0b746e5SOllivier Robert		    shift(@f) while @f && $f[$[] ne $fpos[$[+1];
826*c0b746e5SOllivier Robert		}
827*c0b746e5SOllivier Robert		else
828*c0b746e5SOllivier Robert		{
829*c0b746e5SOllivier Robert		    @fpos = ('start', $f[$[], undef);
830*c0b746e5SOllivier Robert		}
831*c0b746e5SOllivier Robert	    }
832*c0b746e5SOllivier Robert	    else
833*c0b746e5SOllivier Robert	    {
834*c0b746e5SOllivier Robert		@fpos = ('start' , $f[$[], undef);
835*c0b746e5SOllivier Robert	    }
836*c0b746e5SOllivier Robert
837*c0b746e5SOllivier Robert	    if (!defined($fpos[$[+2]))
838*c0b746e5SOllivier Robert	    {
839*c0b746e5SOllivier Robert		if ($StartTime <= $F_first{$f[$[]})
840*c0b746e5SOllivier Robert		{
841*c0b746e5SOllivier Robert		    $fpos[$[+2] = 0;
842*c0b746e5SOllivier Robert		}
843*c0b746e5SOllivier Robert		else
844*c0b746e5SOllivier Robert		{
845*c0b746e5SOllivier Robert		    $fpos[$[+2] =
846*c0b746e5SOllivier Robert			int($F_size{$f[$[]} *
847*c0b746e5SOllivier Robert			    (($StartTime - $F_first{$f[$[]})/
848*c0b746e5SOllivier Robert			     ($F_last{$f[$[]} - $F_first{$f[$[]})));
849*c0b746e5SOllivier Robert		    $fpos[$[+2] = ($fpos[$[+2] <= 2 * $RecordSize)
850*c0b746e5SOllivier Robert			? 0 : $fpos[$[+2] - 2 * $RecordSize;
851*c0b746e5SOllivier Robert		    ;# anyway  as the data may contain "time holes"
852*c0b746e5SOllivier Robert		    ;# our heuristics may baldly fail
853*c0b746e5SOllivier Robert		    ;# so just start at 0
854*c0b746e5SOllivier Robert		    $fpos[$[+2] = 0;
855*c0b746e5SOllivier Robert		}
856*c0b746e5SOllivier Robert	    }
857*c0b746e5SOllivier Robert	}
858*c0b746e5SOllivier Robert	elsif (defined($EndTime))
859*c0b746e5SOllivier Robert	{
860*c0b746e5SOllivier Robert	    print "guess starting point according to EndTime ($EndTime)\n"
861*c0b746e5SOllivier Robert		if $verbose > 3;
862*c0b746e5SOllivier Robert
863*c0b746e5SOllivier Robert	    if ($fpos[$[] eq 'end')
864*c0b746e5SOllivier Robert	    {
865*c0b746e5SOllivier Robert		if (grep($_ eq $fpos[$[+1],@f))
866*c0b746e5SOllivier Robert		{
867*c0b746e5SOllivier Robert		    shift(@f) while @f && $f[$[] ne $fpos[$[+1];
868*c0b746e5SOllivier Robert		}
869*c0b746e5SOllivier Robert		else
870*c0b746e5SOllivier Robert		{
871*c0b746e5SOllivier Robert		    @fpos = ('end', $f[$[], undef);
872*c0b746e5SOllivier Robert		}
873*c0b746e5SOllivier Robert	    }
874*c0b746e5SOllivier Robert	    else
875*c0b746e5SOllivier Robert	    {
876*c0b746e5SOllivier Robert		@fpos = ('end', $f[$[], undef);
877*c0b746e5SOllivier Robert	    }
878*c0b746e5SOllivier Robert
879*c0b746e5SOllivier Robert	    if (!defined($fpos[$[+2]))
880*c0b746e5SOllivier Robert	    {
881*c0b746e5SOllivier Robert		local(@x) = reverse(@f);
882*c0b746e5SOllivier Robert		local($s,$c) = (0,$cnt);
883*c0b746e5SOllivier Robert		if ($EndTime < $F_last{$x[$[]})
884*c0b746e5SOllivier Robert		{
885*c0b746e5SOllivier Robert		    ;# last file will only be used partially
886*c0b746e5SOllivier Robert		    $s = int($F_size{$x[$[]} *
887*c0b746e5SOllivier Robert			     (($EndTime - $F_first{$x[$[]}) /
888*c0b746e5SOllivier Robert			      ($F_last{$x[$[]} - $F_first{$x[$[]})));
889*c0b746e5SOllivier Robert		    $s = int($s/$RecordSize);
890*c0b746e5SOllivier Robert		    $c -= $s - 1;
891*c0b746e5SOllivier Robert		    if ($c <= 0)
892*c0b746e5SOllivier Robert		    {
893*c0b746e5SOllivier Robert			;# start is in the same file
894*c0b746e5SOllivier Robert			$fpos[$[+1] = $x[$[];
895*c0b746e5SOllivier Robert			$fpos[$[+2] = ($c >=-2) ? 0 : (-$c - 2) * $RecordSize;
896*c0b746e5SOllivier Robert			shift(@f) while @f && ($f[$[] ne $x[$[]);
897*c0b746e5SOllivier Robert		    }
898*c0b746e5SOllivier Robert		    else
899*c0b746e5SOllivier Robert		    {
900*c0b746e5SOllivier Robert			shift(@x);
901*c0b746e5SOllivier Robert		    }
902*c0b746e5SOllivier Robert		}
903*c0b746e5SOllivier Robert
904*c0b746e5SOllivier Robert		if (!defined($fpos[$[+2]))
905*c0b746e5SOllivier Robert		{
906*c0b746e5SOllivier Robert		    local($_);
907*c0b746e5SOllivier Robert		    while($_ = shift(@x))
908*c0b746e5SOllivier Robert		    {
909*c0b746e5SOllivier Robert			$s = int($F_size{$_}/$RecordSize);
910*c0b746e5SOllivier Robert			$c -= $s - 1;
911*c0b746e5SOllivier Robert			if ($c <= 0)
912*c0b746e5SOllivier Robert			{
913*c0b746e5SOllivier Robert			    $fpos[$[+1] = $_;
914*c0b746e5SOllivier Robert			    $fpos[$[+2] = ($c>-2) ? 0 : (-$c - 2) * $RecordSize;
915*c0b746e5SOllivier Robert			    shift(@f) while @f && ($f[$[] ne $_);
916*c0b746e5SOllivier Robert			    last;
917*c0b746e5SOllivier Robert			}
918*c0b746e5SOllivier Robert		    }
919*c0b746e5SOllivier Robert		}
920*c0b746e5SOllivier Robert	    }
921*c0b746e5SOllivier Robert	}
922*c0b746e5SOllivier Robert	else
923*c0b746e5SOllivier Robert	{
924*c0b746e5SOllivier Robert	    print "guessing starting point according to count ($cnt)\n"
925*c0b746e5SOllivier Robert		if $verbose > 3;
926*c0b746e5SOllivier Robert	    ;# guess offset to get last available $cnt samples
927*c0b746e5SOllivier Robert	    if ($fpos[$[] eq 'cnt')
928*c0b746e5SOllivier Robert	    {
929*c0b746e5SOllivier Robert		if (grep($_ eq $fpos[$[+1],@f))
930*c0b746e5SOllivier Robert		{
931*c0b746e5SOllivier Robert		    print "old positioning applies\n" if $verbose > 3;
932*c0b746e5SOllivier Robert		    shift(@f) while @f && $f[$[] ne $fpos[$[+1];
933*c0b746e5SOllivier Robert		}
934*c0b746e5SOllivier Robert		else
935*c0b746e5SOllivier Robert		{
936*c0b746e5SOllivier Robert		    @fpos = ('cnt', $f[$[], undef);
937*c0b746e5SOllivier Robert		}
938*c0b746e5SOllivier Robert	    }
939*c0b746e5SOllivier Robert	    else
940*c0b746e5SOllivier Robert	    {
941*c0b746e5SOllivier Robert		@fpos = ('cnt', $f[$[], undef);
942*c0b746e5SOllivier Robert	    }
943*c0b746e5SOllivier Robert
944*c0b746e5SOllivier Robert	    if (!defined($fpos[$[+2]))
945*c0b746e5SOllivier Robert	    {
946*c0b746e5SOllivier Robert		local(@x) = reverse(@f);
947*c0b746e5SOllivier Robert		local($s,$c) = (0,$cnt);
948*c0b746e5SOllivier Robert
949*c0b746e5SOllivier Robert		local($_);
950*c0b746e5SOllivier Robert		while($_ = shift(@x))
951*c0b746e5SOllivier Robert		{
952*c0b746e5SOllivier Robert		    print "examing \"$_\" $c samples still needed\n"
953*c0b746e5SOllivier Robert			if $verbose > 4;
954*c0b746e5SOllivier Robert		    $s = int($F_size{$_}/$RecordSize);
955*c0b746e5SOllivier Robert		    $c -= $s - 1;
956*c0b746e5SOllivier Robert		    if ($c <= 0)
957*c0b746e5SOllivier Robert		    {
958*c0b746e5SOllivier Robert			$fpos[$[+1] = $_;
959*c0b746e5SOllivier Robert			$fpos[$[+2] = ($c>-2) ? 0 : (-$c - 2) * $RecordSize;
960*c0b746e5SOllivier Robert			shift(@f) while @f && ($f[$[] ne $_);
961*c0b746e5SOllivier Robert			last;
962*c0b746e5SOllivier Robert		    }
963*c0b746e5SOllivier Robert		}
964*c0b746e5SOllivier Robert		if (!defined($fpos[$[+2]))
965*c0b746e5SOllivier Robert		{
966*c0b746e5SOllivier Robert		    print "no starting point yet - using start of data\n"
967*c0b746e5SOllivier Robert			if $verbose > 2;
968*c0b746e5SOllivier Robert		    $fpos[$[+2] = 0;
969*c0b746e5SOllivier Robert		}
970*c0b746e5SOllivier Robert	    }
971*c0b746e5SOllivier Robert	}
972*c0b746e5SOllivier Robert    }
973*c0b746e5SOllivier Robert    print "Ooops, no suitable input file ??\n"
974*c0b746e5SOllivier Robert	if $verbose > 1 && @f <= 0;
975*c0b746e5SOllivier Robert
976*c0b746e5SOllivier Robert    printf("Starting at (%s) \"%s\" offset %ld using %d files\n",
977*c0b746e5SOllivier Robert	   $fpos[$[+1],
978*c0b746e5SOllivier Robert	   $F_name{$fpos[$[+1]},
979*c0b746e5SOllivier Robert	   $fpos[$[+2],
980*c0b746e5SOllivier Robert	   scalar(@f))
981*c0b746e5SOllivier Robert	if $verbose > 2;
982*c0b746e5SOllivier Robert
983*c0b746e5SOllivier Robert    $lm = 1;
984*c0b746e5SOllivier Robert    $l = 0;
985*c0b746e5SOllivier Robert    foreach $key (@f)
986*c0b746e5SOllivier Robert    {
987*c0b746e5SOllivier Robert	$file = $F_name{$key};
988*c0b746e5SOllivier Robert	print "processing file \"$file\"\n" if $verbose > 2;
989*c0b746e5SOllivier Robert
990*c0b746e5SOllivier Robert	open(IN,"<$file") ||
991*c0b746e5SOllivier Robert	    (warn("$0: cannot read \"$file\": $!\n"), next);
992*c0b746e5SOllivier Robert
993*c0b746e5SOllivier Robert	;# try to seek to a position nearer to the start of the interesting lines
994*c0b746e5SOllivier Robert	;# should always affect only first item in @f
995*c0b746e5SOllivier Robert	($key eq $fpos[$[+1]) &&
996*c0b746e5SOllivier Robert	    (($verbose > 1) &&
997*c0b746e5SOllivier Robert	     print("Seeking to offset $fpos[$[+2]\n"),
998*c0b746e5SOllivier Robert		seek(IN,$fpos[$[+2],0) ||
999*c0b746e5SOllivier Robert		    warn("$0: seek(\"$F_name{$key}\" failed: $|\n"));
1000*c0b746e5SOllivier Robert
1001*c0b746e5SOllivier Robert	while(<IN>)
1002*c0b746e5SOllivier Robert	{
1003*c0b746e5SOllivier Robert	    $l++;
1004*c0b746e5SOllivier Robert	    ($verbose > 3) &&
1005*c0b746e5SOllivier Robert		(($l % $lm) == 0 && print("\t$l lines read\n") &&
1006*c0b746e5SOllivier Robert		 (($l ==     2) && ($lm =    10) ||
1007*c0b746e5SOllivier Robert		  ($l ==   100) && ($lm =   100) ||
1008*c0b746e5SOllivier Robert		  ($l ==   500) && ($lm =   500) ||
1009*c0b746e5SOllivier Robert		  ($l ==  1000) && ($lm =  1000) ||
1010*c0b746e5SOllivier Robert		  ($l ==  5000) && ($lm =  5000) ||
1011*c0b746e5SOllivier Robert		  ($l == 10000) && ($lm = 10000)));
1012*c0b746e5SOllivier Robert
1013*c0b746e5SOllivier Robert	    @F = split;
1014*c0b746e5SOllivier Robert
1015*c0b746e5SOllivier Robert	    next if @F < 6;	# no valid input line is this short
1016*c0b746e5SOllivier Robert	    next if $F[$[] eq "";
1017*c0b746e5SOllivier Robert	    next if ($F[$[] !~ /^\d+$/);
1018*c0b746e5SOllivier Robert	    ($F[$[] !~ /^\d+$/) && # A 'never should have happend' error
1019*c0b746e5SOllivier Robert		die("$0: unexpected input line: >$_<\n");
1020*c0b746e5SOllivier Robert
1021*c0b746e5SOllivier Robert	    ;# modified Julian to UNIX epoch
1022*c0b746e5SOllivier Robert	    $t = ($F[$[] - $MJD_1970) * 24 * 60 * 60;
1023*c0b746e5SOllivier Robert	    $t += $F[$[+1];	# add seconds + fraction
1024*c0b746e5SOllivier Robert
1025*c0b746e5SOllivier Robert	    ;# multiply offset by 1000 to get ms - try to avoid float op
1026*c0b746e5SOllivier Robert	    (($F[$[+2] =~ s/(\d*)\.(\d{3})(\d*)/$1$2.$3/) &&
1027*c0b746e5SOllivier Robert	     $F[$[+2] =~ s/0+([\d\.])/($1 eq '.') ? '0.' : $1/e) # strip leading zeros
1028*c0b746e5SOllivier Robert		|| ($F[$[+2] *= 1000);
1029*c0b746e5SOllivier Robert
1030*c0b746e5SOllivier Robert
1031*c0b746e5SOllivier Robert	    ;# skip samples out of specified time range
1032*c0b746e5SOllivier Robert	    next if (defined($StartTime) && $StartTime > $t);
1033*c0b746e5SOllivier Robert	    next if (defined($EndTime) && $EndTime < $t);
1034*c0b746e5SOllivier Robert
1035*c0b746e5SOllivier Robert	    next if defined($lastT) && $t < $lastT; # backward in time ??
1036*c0b746e5SOllivier Robert
1037*c0b746e5SOllivier Robert	    push(@offs,$F[$[+2]);
1038*c0b746e5SOllivier Robert	    push(@freq,$F[$[+3] * (2**20/10**6));
1039*c0b746e5SOllivier Robert	    push(@cmpl,$F[$[+5]);
1040*c0b746e5SOllivier Robert
1041*c0b746e5SOllivier Robert	    push(@break, (defined($lastT) && ($t - $lastT > $deltaT)));
1042*c0b746e5SOllivier Robert	    $lastT = $t;
1043*c0b746e5SOllivier Robert	    push(@time,$t);
1044*c0b746e5SOllivier Robert	    push(@loffset, tell(IN) - length($_));
1045*c0b746e5SOllivier Robert	    push(@filekey, $key);
1046*c0b746e5SOllivier Robert
1047*c0b746e5SOllivier Robert	    shift(@break),shift(@time),shift(@offs),
1048*c0b746e5SOllivier Robert	    shift(@freq), shift(@cmpl),shift(@loffset),
1049*c0b746e5SOllivier Robert	    shift(@filekey)
1050*c0b746e5SOllivier Robert		if @time > $cnt &&
1051*c0b746e5SOllivier Robert		    ! (defined($StartTime) && defined($EndTime));
1052*c0b746e5SOllivier Robert
1053*c0b746e5SOllivier Robert	    last if @time >= $cnt && defined($StartTime) && !defined($EndTime);
1054*c0b746e5SOllivier Robert	}
1055*c0b746e5SOllivier Robert	close(IN);
1056*c0b746e5SOllivier Robert	last if @time >= $cnt && defined($StartTime) && !defined($EndTime);
1057*c0b746e5SOllivier Robert    }
1058*c0b746e5SOllivier Robert    print "input scanned ($l lines/",scalar(@time)," samples)\n"
1059*c0b746e5SOllivier Robert	if $verbose > 1;
1060*c0b746e5SOllivier Robert
1061*c0b746e5SOllivier Robert    if (@time)
1062*c0b746e5SOllivier Robert    {
1063*c0b746e5SOllivier Robert	local($_,@F);
1064*c0b746e5SOllivier Robert
1065*c0b746e5SOllivier Robert	local($timebase) unless defined($timebase);
1066*c0b746e5SOllivier Robert	local($freqbase) unless defined($freqbase);
1067*c0b746e5SOllivier Robert	local($cmplscale) unless defined($cmplscale);
1068*c0b746e5SOllivier Robert
1069*c0b746e5SOllivier Robert	undef $mintime;
1070*c0b746e5SOllivier Robert	undef $maxtime;
1071*c0b746e5SOllivier Robert	undef $minoffs;
1072*c0b746e5SOllivier Robert	undef $maxoffs;
1073*c0b746e5SOllivier Robert	undef $minfreq;
1074*c0b746e5SOllivier Robert	undef $maxfreq;
1075*c0b746e5SOllivier Robert	undef $mincmpl;
1076*c0b746e5SOllivier Robert	undef $maxcmpl;
1077*c0b746e5SOllivier Robert	undef $miny;
1078*c0b746e5SOllivier Robert	undef $maxy ;
1079*c0b746e5SOllivier Robert
1080*c0b746e5SOllivier Robert	print "computing ranges\n" if $verbose > 2;
1081*c0b746e5SOllivier Robert
1082*c0b746e5SOllivier Robert	$LastCnt = @time;
1083*c0b746e5SOllivier Robert
1084*c0b746e5SOllivier Robert	;# @time is in ascending order (;-)
1085*c0b746e5SOllivier Robert	$mintime = $time[$[];
1086*c0b746e5SOllivier Robert	$maxtime = $time[$#time];
1087*c0b746e5SOllivier Robert	unless (defined($timebase))
1088*c0b746e5SOllivier Robert	{
1089*c0b746e5SOllivier Robert	    local($time,@X) = (time);
1090*c0b746e5SOllivier Robert	    @X = localtime($time);
1091*c0b746e5SOllivier Robert
1092*c0b746e5SOllivier Robert	    ;# compute today 00:00:00
1093*c0b746e5SOllivier Robert	    $timebase = $time - ((($X[$[+2]*60)+$X[$[+1])*60+$X[$[]);
1094*c0b746e5SOllivier Robert
1095*c0b746e5SOllivier Robert	}
1096*c0b746e5SOllivier Robert	$LastTimeBase = $timebase;
1097*c0b746e5SOllivier Robert
1098*c0b746e5SOllivier Robert	if ($showoffs)
1099*c0b746e5SOllivier Robert	{
1100*c0b746e5SOllivier Robert	    local($i,$m,$f);
1101*c0b746e5SOllivier Robert
1102*c0b746e5SOllivier Robert	    $minoffs = &min(@offs);
1103*c0b746e5SOllivier Robert	    $maxoffs = &max(@offs);
1104*c0b746e5SOllivier Robert
1105*c0b746e5SOllivier Robert	    ;# I know, it is not perl style using indices to access arrays,
1106*c0b746e5SOllivier Robert	    ;# but I have to proccess two arrays in sync, non-destructively
1107*c0b746e5SOllivier Robert	    ;# (otherwise a (shift(@a1),shift(a2)) would do),
1108*c0b746e5SOllivier Robert	    ;# I dont like to make copies of these arrays as they may be huge
1109*c0b746e5SOllivier Robert	    $i = $[;
1110*c0b746e5SOllivier Robert	    $lo->sample(($time[$i]-$timebase)/3600,$offs[$i]),$i++
1111*c0b746e5SOllivier Robert		while $i <= $#time;
1112*c0b746e5SOllivier Robert
1113*c0b746e5SOllivier Robert	    ($minoffs == $maxoffs) && ($minoffs -= 0.1,$maxoffs += 0.1);
1114*c0b746e5SOllivier Robert
1115*c0b746e5SOllivier Robert	    $i = $lo->sigma();
1116*c0b746e5SOllivier Robert	    $m = $lo->mean();
1117*c0b746e5SOllivier Robert
1118*c0b746e5SOllivier Robert	    print "mean offset: $m sigma: $i\n" if $verbose > 2;
1119*c0b746e5SOllivier Robert
1120*c0b746e5SOllivier Robert	    if (($maxoffs - $minoffs) > $MinClip)
1121*c0b746e5SOllivier Robert	    {
1122*c0b746e5SOllivier Robert		$f = (&abs($minoffs) < &abs($maxoffs)) ? $FuzzLow : $FuzzBig;
1123*c0b746e5SOllivier Robert		$miny = (($m - $minoffs) <= ($f * $i))
1124*c0b746e5SOllivier Robert		    ? $minoffs : ($m - $f * $i);
1125*c0b746e5SOllivier Robert		$f = ($f == $FuzzLow) ? $FuzzBig : $FuzzLow;
1126*c0b746e5SOllivier Robert		$maxy = (($maxoffs - $m) <= ($f * $i))
1127*c0b746e5SOllivier Robert		    ? $maxoffs : ($m + $f * $i);
1128*c0b746e5SOllivier Robert	    }
1129*c0b746e5SOllivier Robert	    else
1130*c0b746e5SOllivier Robert	    {
1131*c0b746e5SOllivier Robert		$miny = $minoffs;
1132*c0b746e5SOllivier Robert		$maxy = $maxoffs;
1133*c0b746e5SOllivier Robert	    }
1134*c0b746e5SOllivier Robert	    ($maxy-$miny) == 0 &&
1135*c0b746e5SOllivier Robert		(($maxy,$miny)
1136*c0b746e5SOllivier Robert		 = (($maxoffs - $minoffs) > 0)
1137*c0b746e5SOllivier Robert		 ? ($maxoffs,$minoffs) : ($MinClip,-$MinClip));
1138*c0b746e5SOllivier Robert
1139*c0b746e5SOllivier Robert	    $maxy = $MaxY if defined($MaxY) && $MaxY < $maxy;
1140*c0b746e5SOllivier Robert	    $miny = $MinY if defined($MinY) && $MinY > $miny;
1141*c0b746e5SOllivier Robert
1142*c0b746e5SOllivier Robert	    print  "offset min clipped from $minoffs to $miny\n"
1143*c0b746e5SOllivier Robert		if $verbose > 2 && $minoffs != $miny;
1144*c0b746e5SOllivier Robert	    print  "offset max clipped from $maxoffs to $maxy\n"
1145*c0b746e5SOllivier Robert		if $verbose > 2 && $maxoffs != $maxy;
1146*c0b746e5SOllivier Robert	}
1147*c0b746e5SOllivier Robert
1148*c0b746e5SOllivier Robert	if ($showfreq)
1149*c0b746e5SOllivier Robert	{
1150*c0b746e5SOllivier Robert	    local($i,$m);
1151*c0b746e5SOllivier Robert
1152*c0b746e5SOllivier Robert	    $minfreq = &min(@freq);
1153*c0b746e5SOllivier Robert	    $maxfreq = &max(@freq);
1154*c0b746e5SOllivier Robert
1155*c0b746e5SOllivier Robert	    $i = $[;
1156*c0b746e5SOllivier Robert	    $lf->sample(($time[$i]-$timebase)/3600,$freq[$i]-$minfreq),
1157*c0b746e5SOllivier Robert	    $i++
1158*c0b746e5SOllivier Robert		while $i <= $#time;
1159*c0b746e5SOllivier Robert
1160*c0b746e5SOllivier Robert	    $i = $lf->sigma();
1161*c0b746e5SOllivier Robert	    $m = $lf->mean() + $minfreq;
1162*c0b746e5SOllivier Robert
1163*c0b746e5SOllivier Robert	    print "mean frequency: $m sigma: $i\n" if $verbose > 2;
1164*c0b746e5SOllivier Robert
1165*c0b746e5SOllivier Robert	    if (defined($maxy))
1166*c0b746e5SOllivier Robert	    {
1167*c0b746e5SOllivier Robert		local($s) =
1168*c0b746e5SOllivier Robert		    ($maxfreq - $minfreq)
1169*c0b746e5SOllivier Robert			? ($maxy - $miny) / ($maxfreq - $minfreq) : 1;
1170*c0b746e5SOllivier Robert
1171*c0b746e5SOllivier Robert		if (defined($freqbase))
1172*c0b746e5SOllivier Robert		{
1173*c0b746e5SOllivier Robert		    $FreqScale = 1;
1174*c0b746e5SOllivier Robert		    $FreqScaleInv = "";
1175*c0b746e5SOllivier Robert		}
1176*c0b746e5SOllivier Robert		else
1177*c0b746e5SOllivier Robert		{
1178*c0b746e5SOllivier Robert		    $FreqScale = 1;
1179*c0b746e5SOllivier Robert		    $FreqScale = 10 ** int(log($s)/log(10) - 0.9999);
1180*c0b746e5SOllivier Robert		    $FreqScaleInv =
1181*c0b746e5SOllivier Robert			("$FreqScale" =~ /^10(0*)$/) ? "0.${1}1" :
1182*c0b746e5SOllivier Robert			 ($FreqScale == 1 ? "" : (1/$FreqScale));
1183*c0b746e5SOllivier Robert
1184*c0b746e5SOllivier Robert		    $freqbase = ($maxfreq + $minfreq)/ 2 * $FreqScale; #$m * $FreqScale;
1185*c0b746e5SOllivier Robert		    $freqbase -= ($maxy + $miny) / 2; #$lf->mean();
1186*c0b746e5SOllivier Robert
1187*c0b746e5SOllivier Robert		    ;# round resulting freqbase
1188*c0b746e5SOllivier Robert		    ;# to precision of min max difference
1189*c0b746e5SOllivier Robert		    $s = -12;
1190*c0b746e5SOllivier Robert		    $s = int(log(($maxfreq-$minfreq)*$FreqScale)/log(10))-1
1191*c0b746e5SOllivier Robert			unless ($maxfreq-$minfreq) < 1e-12;
1192*c0b746e5SOllivier Robert		    $s = 10 ** $s;
1193*c0b746e5SOllivier Robert		    $freqbase = int($freqbase / $s) * $s;
1194*c0b746e5SOllivier Robert		}
1195*c0b746e5SOllivier Robert	    }
1196*c0b746e5SOllivier Robert	    else
1197*c0b746e5SOllivier Robert	    {
1198*c0b746e5SOllivier Robert		$FreqScale = 1;
1199*c0b746e5SOllivier Robert		$FreqScaleInv = "";
1200*c0b746e5SOllivier Robert		$freqbase = $m unless defined($freqbase);
1201*c0b746e5SOllivier Robert		if (($maxfreq - $minfreq) > $MinClip)
1202*c0b746e5SOllivier Robert		{
1203*c0b746e5SOllivier Robert		    $f = (&abs($minfreq) < &abs($maxfreq))
1204*c0b746e5SOllivier Robert			? $FuzzLow : $FuzzBig;
1205*c0b746e5SOllivier Robert		    $miny = (($freqbase - $minfreq) <= ($f * $i))
1206*c0b746e5SOllivier Robert			? ($minfreq-$freqbase) : (- $f * $i);
1207*c0b746e5SOllivier Robert		    $f = ($f == $FuzzLow) ? $FuzzBig : $FuzzLow;
1208*c0b746e5SOllivier Robert		    $maxy = (($maxfreq - $freqbase) <= ($f * $i))
1209*c0b746e5SOllivier Robert			? ($maxfreq-$freqbase) : ($f * $i);
1210*c0b746e5SOllivier Robert		}
1211*c0b746e5SOllivier Robert		else
1212*c0b746e5SOllivier Robert		{
1213*c0b746e5SOllivier Robert		    $miny = $minfreq - $freqbase;
1214*c0b746e5SOllivier Robert		    $maxy = $maxfreq - $freqbase;
1215*c0b746e5SOllivier Robert		}
1216*c0b746e5SOllivier Robert		($maxy - $miny) == 0 &&
1217*c0b746e5SOllivier Robert		    (($maxy,$miny) =
1218*c0b746e5SOllivier Robert		     (($maxfreq - $minfreq) > 0)
1219*c0b746e5SOllivier Robert		     ? ($maxfreq-$freqbase,$minfreq-$freqbase) : (0.5,-0.5));
1220*c0b746e5SOllivier Robert
1221*c0b746e5SOllivier Robert		$maxy = $MaxY if defined($MaxY) && $MaxY < $maxy;
1222*c0b746e5SOllivier Robert		$miny = $MinY if defined($MinY) && $MinY > $miny;
1223*c0b746e5SOllivier Robert
1224*c0b746e5SOllivier Robert		print("frequency min clipped from ",$minfreq-$freqbase,
1225*c0b746e5SOllivier Robert		      " to $miny\n")
1226*c0b746e5SOllivier Robert		    if $verbose > 2 && $miny != ($minfreq - $freqbase);
1227*c0b746e5SOllivier Robert		print("frequency max clipped from ",$maxfreq-$freqbase,
1228*c0b746e5SOllivier Robert		      " to $maxy\n")
1229*c0b746e5SOllivier Robert		    if $verbose > 2 && $maxy != ($maxfreq - $freqbase);
1230*c0b746e5SOllivier Robert	    }
1231*c0b746e5SOllivier Robert	    $LastFreqBaseString =
1232*c0b746e5SOllivier Robert		sprintf("%g",$freqbase >= 0 ? $freqbase : -$freqbase);
1233*c0b746e5SOllivier Robert	    $LastFreqBase = $freqbase;
1234*c0b746e5SOllivier Robert	    print "LastFreqBaseString now \"$LastFreqBaseString\"\n"
1235*c0b746e5SOllivier Robert		if $verbose > 5;
1236*c0b746e5SOllivier Robert	}
1237*c0b746e5SOllivier Robert	else
1238*c0b746e5SOllivier Robert	{
1239*c0b746e5SOllivier Robert	    $FreqScale = 1;
1240*c0b746e5SOllivier Robert	    $FreqScaleInv = "";
1241*c0b746e5SOllivier Robert	    $LastFreqBase = 0;
1242*c0b746e5SOllivier Robert	    $LastFreqBaseString = "";
1243*c0b746e5SOllivier Robert	}
1244*c0b746e5SOllivier Robert
1245*c0b746e5SOllivier Robert	if ($showcmpl)
1246*c0b746e5SOllivier Robert	{
1247*c0b746e5SOllivier Robert	    $mincmpl = &min(@cmpl);
1248*c0b746e5SOllivier Robert	    $maxcmpl = &max(@cmpl);
1249*c0b746e5SOllivier Robert
1250*c0b746e5SOllivier Robert	    if (!defined($cmplscale))
1251*c0b746e5SOllivier Robert	    {
1252*c0b746e5SOllivier Robert		if (defined($maxy))
1253*c0b746e5SOllivier Robert		{
1254*c0b746e5SOllivier Robert		    local($cmp)
1255*c0b746e5SOllivier Robert			= (&abs($miny) > &abs($maxy)) ? &abs($miny) : $maxy;
1256*c0b746e5SOllivier Robert		    $cmplscale = $cmp == $maxy ? 1 : -1;
1257*c0b746e5SOllivier Robert
1258*c0b746e5SOllivier Robert		    foreach (0.01, 0.02, 0.05,
1259*c0b746e5SOllivier Robert			     0.1, 0.2, 0.25, 0.4, 0.5,
1260*c0b746e5SOllivier Robert			     1, 2, 4, 5,
1261*c0b746e5SOllivier Robert			     10, 20, 25, 50,
1262*c0b746e5SOllivier Robert			     100, 200, 250, 500, 1000)
1263*c0b746e5SOllivier Robert		    {
1264*c0b746e5SOllivier Robert			$cmplscale *= $_, last if $maxcmpl/$_ <= $cmp;
1265*c0b746e5SOllivier Robert		    }
1266*c0b746e5SOllivier Robert		}
1267*c0b746e5SOllivier Robert		else
1268*c0b746e5SOllivier Robert		{
1269*c0b746e5SOllivier Robert		    $cmplscale = 1;
1270*c0b746e5SOllivier Robert		    $miny = $mincmpl ? 0 : -$MinClip;
1271*c0b746e5SOllivier Robert		    $maxy = $maxcmpl+$MinClip;
1272*c0b746e5SOllivier Robert		}
1273*c0b746e5SOllivier Robert	    }
1274*c0b746e5SOllivier Robert	    $LastCmplScale = $cmplscale;
1275*c0b746e5SOllivier Robert	}
1276*c0b746e5SOllivier Robert	else
1277*c0b746e5SOllivier Robert	{
1278*c0b746e5SOllivier Robert	    $LastCmplScale = 1;
1279*c0b746e5SOllivier Robert	}
1280*c0b746e5SOllivier Robert
1281*c0b746e5SOllivier Robert	print "creating plot command input file\n" if $verbose > 2;
1282*c0b746e5SOllivier Robert
1283*c0b746e5SOllivier Robert
1284*c0b746e5SOllivier Robert	print OUT ("# preprocessed NTP statistics file for $STATHOST\n");
1285*c0b746e5SOllivier Robert	print OUT ("#    timebase is: ",&ctime($LastTimeBase))
1286*c0b746e5SOllivier Robert	    if defined($LastTimeBase);
1287*c0b746e5SOllivier Robert	print OUT ("#    frequency is offset by  ",
1288*c0b746e5SOllivier Robert		   ($LastFreqBase >= 0 ? "+" : "-"),
1289*c0b746e5SOllivier Robert		   "$LastFreqBaseString [${FreqScaleInv}ppm]\n");
1290*c0b746e5SOllivier Robert	print OUT ("#    compliance is scaled by $LastCmplScale\n");
1291*c0b746e5SOllivier Robert	print OUT ("# time [h]\toffset [ms]\tfrequency [${FreqScaleInv}ppm]\tcompliance\n");
1292*c0b746e5SOllivier Robert
1293*c0b746e5SOllivier Robert	printf OUT ("%s%lf\t%lf\t%lf\t%lf\n",
1294*c0b746e5SOllivier Robert		    (shift(@break) ? "\n" : ""),
1295*c0b746e5SOllivier Robert		    (shift(@time) - $LastTimeBase)/3600,
1296*c0b746e5SOllivier Robert		    shift(@offs),
1297*c0b746e5SOllivier Robert		    shift(@freq) * $FreqScale - $LastFreqBase,
1298*c0b746e5SOllivier Robert		    shift(@cmpl) / $LastCmplScale)
1299*c0b746e5SOllivier Robert	    while(@time);
1300*c0b746e5SOllivier Robert    }
1301*c0b746e5SOllivier Robert    else
1302*c0b746e5SOllivier Robert    {
1303*c0b746e5SOllivier Robert	;# prevent plotcmd from processing empty file
1304*c0b746e5SOllivier Robert	print "Creating plot command dummy...\n" if $verbose > 2;
1305*c0b746e5SOllivier Robert	print OUT "# dummy samples\n0 1 2 3\n1 1 2 3\n";
1306*c0b746e5SOllivier Robert	$lo->sample(0,1);
1307*c0b746e5SOllivier Robert	$lo->sample(1,1);
1308*c0b746e5SOllivier Robert	$lf->sample(0,2);
1309*c0b746e5SOllivier Robert	$lf->sample(1,2);
1310*c0b746e5SOllivier Robert	@time = (0, 1); $maxtime = 1; $mintime = 0;
1311*c0b746e5SOllivier Robert	@offs = (1, 1); $maxoffs = 1; $minoffs = 1;
1312*c0b746e5SOllivier Robert	@freq = (2, 2); $maxfreq = 2; $minfreq = 2;
1313*c0b746e5SOllivier Robert	@cmpl = (3, 3); $maxcmpl = 3; $mincmpl = 3;
1314*c0b746e5SOllivier Robert	$LastCnt = 2;
1315*c0b746e5SOllivier Robert	$LastFreqBase = 0;
1316*c0b746e5SOllivier Robert	$LastCmplScale = 1;
1317*c0b746e5SOllivier Robert	$LastTimeBase = 0;
1318*c0b746e5SOllivier Robert	$miny = -$MinClip;
1319*c0b746e5SOllivier Robert	$maxy = 3 + $MinClip;
1320*c0b746e5SOllivier Robert    }
1321*c0b746e5SOllivier Robert    close(OUT);
1322*c0b746e5SOllivier Robert
1323*c0b746e5SOllivier Robert    print "plot command input file created\n"
1324*c0b746e5SOllivier Robert	if $verbose > 2;
1325*c0b746e5SOllivier Robert
1326*c0b746e5SOllivier Robert
1327*c0b746e5SOllivier Robert    if (($fpos[$[] eq 'cnt' && scalar(@loffset) >= $cnt) ||
1328*c0b746e5SOllivier Robert	($fpos[$[] eq 'start' && $mintime <= $StartTime) ||
1329*c0b746e5SOllivier Robert	($fpos[$[] eq 'end'))
1330*c0b746e5SOllivier Robert    {
1331*c0b746e5SOllivier Robert	return ($fpos[$[],$filekey[$[],$loffset[$[]);
1332*c0b746e5SOllivier Robert    }
1333*c0b746e5SOllivier Robert    else			# found to few lines - next time start search earlier in file
1334*c0b746e5SOllivier Robert    {
1335*c0b746e5SOllivier Robert	if ($fpos[$[] eq 'start')
1336*c0b746e5SOllivier Robert	{
1337*c0b746e5SOllivier Robert	    ;# the timestamps we got for F_first and F_last guaranteed
1338*c0b746e5SOllivier Robert	    ;# that no file is left out
1339*c0b746e5SOllivier Robert	    ;# the only thing that could happen is:
1340*c0b746e5SOllivier Robert	    ;# we guessed the starting point wrong
1341*c0b746e5SOllivier Robert	    ;# compute a new guess from the first record found
1342*c0b746e5SOllivier Robert	    ;# if this equals our last guess use data of first record
1343*c0b746e5SOllivier Robert	    ;# otherwise try new guess
1344*c0b746e5SOllivier Robert
1345*c0b746e5SOllivier Robert	    if ($fpos[$[+1] eq $filekey[$[] && $loffset[$[] > $fpos[$[+2])
1346*c0b746e5SOllivier Robert	    {
1347*c0b746e5SOllivier Robert		local($noff);
1348*c0b746e5SOllivier Robert		$noff = $loffset[$[] - ($cnt - @loffset + 1) * $RecordSize;
1349*c0b746e5SOllivier Robert		$noff = 0 if $noff < 0;
1350*c0b746e5SOllivier Robert
1351*c0b746e5SOllivier Robert		return (@fpos[$[,$[+1], ($noff == $fpos[$[+2]) ? $loffset[$[] : $noff);
1352*c0b746e5SOllivier Robert	    }
1353*c0b746e5SOllivier Robert	    return ($fpos[$[],$filekey[$[],$loffset[$[]);
1354*c0b746e5SOllivier Robert	}
1355*c0b746e5SOllivier Robert	elsif ($fpos[$[] eq 'end' || $fpos[$[] eq 'cnt')
1356*c0b746e5SOllivier Robert	{
1357*c0b746e5SOllivier Robert	    ;# try to start earlier in file
1358*c0b746e5SOllivier Robert	    ;# if we already started at the beginning
1359*c0b746e5SOllivier Robert	    ;# try to use previous file
1360*c0b746e5SOllivier Robert	    ;# this assumes distance to better starting point is at most one file
1361*c0b746e5SOllivier Robert	    ;# the primary guess at top of genfile() should usually allow this
1362*c0b746e5SOllivier Robert	    ;# assumption
1363*c0b746e5SOllivier Robert	    ;# if the offset of the first sample used is within
1364*c0b746e5SOllivier Robert	    ;# a different file than we guessed it must have occurred later
1365*c0b746e5SOllivier Robert	    ;# in the sequence of files
1366*c0b746e5SOllivier Robert	    ;# this only can happen if our starting file did not contain
1367*c0b746e5SOllivier Robert	    ;# a valid sample from the starting point we guessed
1368*c0b746e5SOllivier Robert	    ;# however this does not invalidate our assumption, no check needed
1369*c0b746e5SOllivier Robert	    local($noff,$key);
1370*c0b746e5SOllivier Robert	    if ($fpos[$[+2] > 0)
1371*c0b746e5SOllivier Robert	    {
1372*c0b746e5SOllivier Robert		$noff = $fpos[$[+2] - $RecordSize * ($cnt - @loffset + 1);
1373*c0b746e5SOllivier Robert		$noff = 0 if $noff < 0;
1374*c0b746e5SOllivier Robert		return (@fpos[$[,$[+1],$noff);
1375*c0b746e5SOllivier Robert	    }
1376*c0b746e5SOllivier Robert	    else
1377*c0b746e5SOllivier Robert	    {
1378*c0b746e5SOllivier Robert		if ($fpos[$[+1] eq $F_files[$[])
1379*c0b746e5SOllivier Robert		{
1380*c0b746e5SOllivier Robert		    ;# first file - and not enough samples
1381*c0b746e5SOllivier Robert		    ;# use data of first sample
1382*c0b746e5SOllivier Robert		    return ($fpos[$[], $filekey[$[], $loffset[$[]);
1383*c0b746e5SOllivier Robert		}
1384*c0b746e5SOllivier Robert		else
1385*c0b746e5SOllivier Robert		{
1386*c0b746e5SOllivier Robert		    ;# search key of previous file
1387*c0b746e5SOllivier Robert		    $key = $F_files[$[];
1388*c0b746e5SOllivier Robert		    @F = reverse(@F_files);
1389*c0b746e5SOllivier Robert		    while ($_ = shift(@F))
1390*c0b746e5SOllivier Robert		    {
1391*c0b746e5SOllivier Robert			if ($_ eq $fpos[$[+1])
1392*c0b746e5SOllivier Robert			{
1393*c0b746e5SOllivier Robert			    $key = shift(@F) if @F;
1394*c0b746e5SOllivier Robert			    last;
1395*c0b746e5SOllivier Robert			}
1396*c0b746e5SOllivier Robert		    }
1397*c0b746e5SOllivier Robert		    $noff = int($F_size{$key} / $RecordSize);
1398*c0b746e5SOllivier Robert		    $noff -= $cnt - @loffset;
1399*c0b746e5SOllivier Robert		    $noff = 0 if $noff < 0;
1400*c0b746e5SOllivier Robert		    $noff *= $RecordSize;
1401*c0b746e5SOllivier Robert		    return ($fpos[$[], $key, $noff);
1402*c0b746e5SOllivier Robert		}
1403*c0b746e5SOllivier Robert	    }
1404*c0b746e5SOllivier Robert	}
1405*c0b746e5SOllivier Robert	else
1406*c0b746e5SOllivier Robert	{
1407*c0b746e5SOllivier Robert	    return ();
1408*c0b746e5SOllivier Robert	}
1409*c0b746e5SOllivier Robert
1410*c0b746e5SOllivier Robert	return 0 if @loffset <= 1 || ($loffset[$#loffset] - $loffset[$[]) <= 1;
1411*c0b746e5SOllivier Robert
1412*c0b746e5SOllivier Robert	;# EOF - 1.1 * avg(line) * $cnt
1413*c0b746e5SOllivier Robert	local($val) =  $loffset[$#loffset]
1414*c0b746e5SOllivier Robert	    - $cnt * 11 * (($loffset[$#loffset] - $loffset[$[]) / @loffset) / 10;
1415*c0b746e5SOllivier Robert	return ($val < 0) ? 0 : $val;
1416*c0b746e5SOllivier Robert    }
1417*c0b746e5SOllivier Robert}
1418*c0b746e5SOllivier Robert
1419*c0b746e5SOllivier Robert$Ltime = -1 if ! defined($Ltime);
1420*c0b746e5SOllivier Robert$LastFreqBase = 0;
1421*c0b746e5SOllivier Robert$LastFreqBaseString = "??";
1422*c0b746e5SOllivier Robert
1423*c0b746e5SOllivier Robert;# initial setup of plot
1424*c0b746e5SOllivier Robertprint "initialize plotting\n" if $verbose;
1425*c0b746e5SOllivier Robertif (defined($PrintIt))
1426*c0b746e5SOllivier Robert{
1427*c0b746e5SOllivier Robert  if ($PrintIt =~ m,/,)
1428*c0b746e5SOllivier Robert  {
1429*c0b746e5SOllivier Robert    print "Saving plot to file $PrintIt\n";
1430*c0b746e5SOllivier Robert    print PLOT "set output '$PrintIt'\n";
1431*c0b746e5SOllivier Robert  }
1432*c0b746e5SOllivier Robert  else
1433*c0b746e5SOllivier Robert  {
1434*c0b746e5SOllivier Robert    print "Printing plot on printer $PrintIt\n";
1435*c0b746e5SOllivier Robert    print PLOT "set output '| lpr -P$PrintIt -h'\n";
1436*c0b746e5SOllivier Robert  }
1437*c0b746e5SOllivier Robert  print PLOT "set terminal postscript landscape color solid 'Helvetica' 10\n";
1438*c0b746e5SOllivier Robert}
1439*c0b746e5SOllivier Robertprint PLOT "set grid\n";
1440*c0b746e5SOllivier Robertprint PLOT "set tics out\n";
1441*c0b746e5SOllivier Robertprint PLOT "set format y '%g '\n";
1442*c0b746e5SOllivier Robertprintf PLOT "set time 47\n" unless defined($PrintIt);
1443*c0b746e5SOllivier Robert
1444*c0b746e5SOllivier Robert@filepos =();
1445*c0b746e5SOllivier Robertwhile(1)
1446*c0b746e5SOllivier Robert{
1447*c0b746e5SOllivier Robert  print &ctime(time) if $verbose;
1448*c0b746e5SOllivier Robert
1449*c0b746e5SOllivier Robert  ;# update diplay characteristics
1450*c0b746e5SOllivier Robert  &read_config;# unless defined($PrintIt);
1451*c0b746e5SOllivier Robert
1452*c0b746e5SOllivier Robert  unlink($tmpfile);
1453*c0b746e5SOllivier Robert  my $lo = lr->new();
1454*c0b746e5SOllivier Robert  my $lf = lr->new();
1455*c0b746e5SOllivier Robert
1456*c0b746e5SOllivier Robert  @filepos = &genfile($samples,$srcprefix,$tmpfile,$lo,$lf,@filepos);
1457*c0b746e5SOllivier Robert
1458*c0b746e5SOllivier Robert  ;# make plotcmd display samples
1459*c0b746e5SOllivier Robert  make_doplot($lo, $lf);
1460*c0b746e5SOllivier Robert  print "Displaying plot...\n" if $verbose > 1;
1461*c0b746e5SOllivier Robert  print "command for plot sub process:\n$doplot----\n" if $verbose > 3;
1462*c0b746e5SOllivier Robert  print PLOT $doplot;
1463*c0b746e5SOllivier Robert}
1464*c0b746e5SOllivier Robertcontinue
1465*c0b746e5SOllivier Robert{
1466*c0b746e5SOllivier Robert  if (defined($PrintIt))
1467*c0b746e5SOllivier Robert  {
1468*c0b746e5SOllivier Robert    delete $SIG{'CHLD'};
1469*c0b746e5SOllivier Robert    print PLOT "quit\n";
1470*c0b746e5SOllivier Robert    close(PLOT);
1471*c0b746e5SOllivier Robert    if ($PrintIt =~ m,/,)
1472*c0b746e5SOllivier Robert    {
1473*c0b746e5SOllivier Robert      print "Plot saved to file $PrintIt\n";
1474*c0b746e5SOllivier Robert    }
1475*c0b746e5SOllivier Robert    else
1476*c0b746e5SOllivier Robert    {
1477*c0b746e5SOllivier Robert      print "Plot spooled to printer $PrintIt\n";
1478*c0b746e5SOllivier Robert    }
1479*c0b746e5SOllivier Robert    unlink($tmpfile);
1480*c0b746e5SOllivier Robert    exit(0);
1481*c0b746e5SOllivier Robert  }
1482*c0b746e5SOllivier Robert  ;# wait $delay seconds
1483*c0b746e5SOllivier Robert  print "waiting $delay seconds ..." if $verbose > 2;
1484*c0b746e5SOllivier Robert  sleep($delay);
1485*c0b746e5SOllivier Robert  print " continuing\n" if $verbose > 2;
1486*c0b746e5SOllivier Robert  undef($LastFreqBaseString);
1487*c0b746e5SOllivier Robert}
1488*c0b746e5SOllivier Robert
1489*c0b746e5SOllivier Robert
1490*c0b746e5SOllivier Robertsub date_time_spec2seconds
1491*c0b746e5SOllivier Robert{
1492*c0b746e5SOllivier Robert    local($_) = @_;
1493*c0b746e5SOllivier Robert    ;# a date_time_spec consistes of:
1494*c0b746e5SOllivier Robert    ;#  YYYY-MM-DD_HH:MM:SS.ms
1495*c0b746e5SOllivier Robert    ;# values can be omitted from the beginning and default than to
1496*c0b746e5SOllivier Robert    ;# values of current date
1497*c0b746e5SOllivier Robert    ;# values omitted from the end default to lowest possible values
1498*c0b746e5SOllivier Robert
1499*c0b746e5SOllivier Robert    local($time) = time;
1500*c0b746e5SOllivier Robert    local($sec,$min,$hour,$mday,$mon,$year)
1501*c0b746e5SOllivier Robert	= localtime($time);
1502*c0b746e5SOllivier Robert
1503*c0b746e5SOllivier Robert    local($last) = ();
1504*c0b746e5SOllivier Robert
1505*c0b746e5SOllivier Robert    s/^\D*(.*\d)\D*/$1/;	# strip off garbage
1506*c0b746e5SOllivier Robert
1507*c0b746e5SOllivier Robert  PARSE:
1508*c0b746e5SOllivier Robert    {
1509*c0b746e5SOllivier Robert	if (s/^(\d{4})(-|$)//)
1510*c0b746e5SOllivier Robert	{
1511*c0b746e5SOllivier Robert	    if ($1 < 1970)
1512*c0b746e5SOllivier Robert	    {
1513*c0b746e5SOllivier Robert		warn("$0: can not handle years before 1970 - year $1 ignored\n");
1514*c0b746e5SOllivier Robert		return undef;
1515*c0b746e5SOllivier Robert	    }
1516*c0b746e5SOllivier Robert	    elsif ( $1 >= 2070)
1517*c0b746e5SOllivier Robert	    {
1518*c0b746e5SOllivier Robert		warn("$0: can not handle years past 2070 - year $1 ignored\n");
1519*c0b746e5SOllivier Robert		return undef;
1520*c0b746e5SOllivier Robert	    }
1521*c0b746e5SOllivier Robert	    else
1522*c0b746e5SOllivier Robert	    {
1523*c0b746e5SOllivier Robert		$year = $1 % 100; # 0<= $year < 100
1524*c0b746e5SOllivier Robert				 ;# - interpreted 70 .. 99,00 .. 69
1525*c0b746e5SOllivier Robert	    }
1526*c0b746e5SOllivier Robert	    $last = $[ + 5;
1527*c0b746e5SOllivier Robert	    last PARSE if $_ eq '';
1528*c0b746e5SOllivier Robert	    warn("$0: bad date_time_spec: \"$_\" found after YEAR\n"),
1529*c0b746e5SOllivier Robert	    return(undef)
1530*c0b746e5SOllivier Robert		if $2 eq '';
1531*c0b746e5SOllivier Robert	}
1532*c0b746e5SOllivier Robert
1533*c0b746e5SOllivier Robert	if (s/^(\d{1,2})(-|$)//)
1534*c0b746e5SOllivier Robert	{
1535*c0b746e5SOllivier Robert	    warn("$0: implausible month $1\n"),return(undef)
1536*c0b746e5SOllivier Robert		if $1 < 1 || $1 > 12;
1537*c0b746e5SOllivier Robert	    $mon = $1 - 1;
1538*c0b746e5SOllivier Robert	    $last = $[ + 4;
1539*c0b746e5SOllivier Robert	    last PARSE if $_ eq '';
1540*c0b746e5SOllivier Robert	    warn("$0: bad date_time_spec: \"$_\" found after MONTH\n"),
1541*c0b746e5SOllivier Robert	    return(undef)
1542*c0b746e5SOllivier Robert		if $2 eq '';
1543*c0b746e5SOllivier Robert	}
1544*c0b746e5SOllivier Robert	else
1545*c0b746e5SOllivier Robert	{
1546*c0b746e5SOllivier Robert	    warn("$0: bad date_time_spec \"$_\"\n"),return(undef)
1547*c0b746e5SOllivier Robert		if defined($last);
1548*c0b746e5SOllivier Robert
1549*c0b746e5SOllivier Robert	}
1550*c0b746e5SOllivier Robert
1551*c0b746e5SOllivier Robert	if (s/^(\d{1,2})([_ ]|$)//)
1552*c0b746e5SOllivier Robert	{
1553*c0b746e5SOllivier Robert	    warn("$0: implausible month day $1 for month ".($mon+1)." (".
1554*c0b746e5SOllivier Robert		 $MaxNumDaysPerMonth[$mon].")$mon\n"),
1555*c0b746e5SOllivier Robert	    return(undef)
1556*c0b746e5SOllivier Robert		if $1 < 1 || $1 > $MaxNumDaysPerMonth[$mon];
1557*c0b746e5SOllivier Robert	    $mday = $1;
1558*c0b746e5SOllivier Robert	    $last = $[ + 3;
1559*c0b746e5SOllivier Robert	    last PARSE if $_ eq '';
1560*c0b746e5SOllivier Robert	    warn("$0: bad date_time_spec \"$_\" found after MDAY\n"),
1561*c0b746e5SOllivier Robert	    return(undef)
1562*c0b746e5SOllivier Robert		if $2 eq '';
1563*c0b746e5SOllivier Robert	}
1564*c0b746e5SOllivier Robert	else
1565*c0b746e5SOllivier Robert	{
1566*c0b746e5SOllivier Robert	    warn("$0: bad date_time_spec \"$_\"\n"), return undef
1567*c0b746e5SOllivier Robert		if defined($last);
1568*c0b746e5SOllivier Robert	}
1569*c0b746e5SOllivier Robert
1570*c0b746e5SOllivier Robert	;# now we face a problem:
1571*c0b746e5SOllivier Robert 	;# if ! defined($last) a prefix of "07:"
1572*c0b746e5SOllivier Robert	;# can be either 07:MM or 07:ss
1573*c0b746e5SOllivier Robert	;# to get the second interpretation make the user add
1574*c0b746e5SOllivier Robert 	;# a msec fraction part and check for this special case
1575*c0b746e5SOllivier Robert	if (! defined($last) && s/^(\d{1,2}):(\d{1,2}\.\d+)//)
1576*c0b746e5SOllivier Robert	{
1577*c0b746e5SOllivier Robert	    warn("$0: implausible minute $1\n"), return undef
1578*c0b746e5SOllivier Robert		if $1 < 0 || $1 >= 60;
1579*c0b746e5SOllivier Robert	    warn("$0: implausible second $1\n"), return undef
1580*c0b746e5SOllivier Robert		if $2 < 0 || $2 >= 60;
1581*c0b746e5SOllivier Robert	    $min = $1;
1582*c0b746e5SOllivier Robert	    $sec = $2;
1583*c0b746e5SOllivier Robert	    $last = $[ + 1;
1584*c0b746e5SOllivier Robert	    last PARSE if $_ eq '';
1585*c0b746e5SOllivier Robert	    warn("$0: bad date_time_spec \"$_\" after SECONDS\n");
1586*c0b746e5SOllivier Robert	    return undef;
1587*c0b746e5SOllivier Robert	}
1588*c0b746e5SOllivier Robert
1589*c0b746e5SOllivier Robert	if (s/^(\d{1,2})(:|$)//)
1590*c0b746e5SOllivier Robert	{
1591*c0b746e5SOllivier Robert	    warn("$0: implausible hour $1\n"), return undef
1592*c0b746e5SOllivier Robert		if $1 < 0 || $1 > 24;
1593*c0b746e5SOllivier Robert	    $hour = $1;
1594*c0b746e5SOllivier Robert	    $last = $[ + 2;
1595*c0b746e5SOllivier Robert	    last PARSE if $_ eq '';
1596*c0b746e5SOllivier Robert	    warn("$0: bad date_time_spec found \"$_\" after HOUR\n"),
1597*c0b746e5SOllivier Robert	    return undef
1598*c0b746e5SOllivier Robert		if $2 eq '';
1599*c0b746e5SOllivier Robert	}
1600*c0b746e5SOllivier Robert	else
1601*c0b746e5SOllivier Robert	{
1602*c0b746e5SOllivier Robert	    warn("$0: bad date_time_spec \"$_\"\n"), return undef
1603*c0b746e5SOllivier Robert		if defined($last);
1604*c0b746e5SOllivier Robert	}
1605*c0b746e5SOllivier Robert
1606*c0b746e5SOllivier Robert	if (s/^(\d{1,2})(:|$)//)
1607*c0b746e5SOllivier Robert	{
1608*c0b746e5SOllivier Robert	    warn("$0: implausible minute $1\n"), return undef
1609*c0b746e5SOllivier Robert		if $1 < 0 || $1 >=60;
1610*c0b746e5SOllivier Robert	    $min = $1;
1611*c0b746e5SOllivier Robert	    $last = $[ + 1;
1612*c0b746e5SOllivier Robert	    last PARSE if $_ eq '';
1613*c0b746e5SOllivier Robert	    warn("$0: bad date_time_spec found \"$_\" after MINUTE\n"),
1614*c0b746e5SOllivier Robert	    return undef
1615*c0b746e5SOllivier Robert		if $2 eq '';
1616*c0b746e5SOllivier Robert	}
1617*c0b746e5SOllivier Robert	else
1618*c0b746e5SOllivier Robert	{
1619*c0b746e5SOllivier Robert	    warn("$0: bad date_time_spec \"$_\"\n"), return undef
1620*c0b746e5SOllivier Robert		if defined($last);
1621*c0b746e5SOllivier Robert	}
1622*c0b746e5SOllivier Robert
1623*c0b746e5SOllivier Robert	if (s/^(\d{1,2}(\.\d+)?)//)
1624*c0b746e5SOllivier Robert	{
1625*c0b746e5SOllivier Robert	    warn("$0: implausible second $1\n"), return undef
1626*c0b746e5SOllivier Robert		if $1 < 0 || $1 >=60;
1627*c0b746e5SOllivier Robert	    $sec = $1;
1628*c0b746e5SOllivier Robert	    $last = $[;
1629*c0b746e5SOllivier Robert	    last PARSE if $_ eq '';
1630*c0b746e5SOllivier Robert	    warn("$0: bad date_time_spec found \"$_\" after SECOND\n");
1631*c0b746e5SOllivier Robert	    return undef;
1632*c0b746e5SOllivier Robert	}
1633*c0b746e5SOllivier Robert    }
1634*c0b746e5SOllivier Robert
1635*c0b746e5SOllivier Robert    return $time unless defined($last);
1636*c0b746e5SOllivier Robert
1637*c0b746e5SOllivier Robert    $sec  = 0 if $last > $[;
1638*c0b746e5SOllivier Robert    $min  = 0 if $last > $[ + 1;
1639*c0b746e5SOllivier Robert    $hour = 0 if $last > $[ + 2;
1640*c0b746e5SOllivier Robert    $mday = 1 if $last > $[ + 3;
1641*c0b746e5SOllivier Robert    $mon  = 0 if $last > $[ + 4;
1642*c0b746e5SOllivier Robert    local($rtime) = &timelocal($sec,$min,$hour,$mday,$mon,$year, 0,0, 0);
1643*c0b746e5SOllivier Robert
1644*c0b746e5SOllivier Robert    ;# $rtime may be off if daylight savings time is in effect at given date
1645*c0b746e5SOllivier Robert    return $rtime + ($sec - int($sec))
1646*c0b746e5SOllivier Robert	if $hour == (localtime($rtime))[$[+2];
1647*c0b746e5SOllivier Robert    return
1648*c0b746e5SOllivier Robert	&timelocal($sec,$min,$hour,$mday,$mon,$year, 0,0, 1)
1649*c0b746e5SOllivier Robert	    + ($sec - int($sec));
1650*c0b746e5SOllivier Robert}
1651*c0b746e5SOllivier Robert
1652*c0b746e5SOllivier Robert
1653*c0b746e5SOllivier Robertsub min
1654*c0b746e5SOllivier Robert{
1655*c0b746e5SOllivier Robert  local($m) = shift;
1656*c0b746e5SOllivier Robert
1657*c0b746e5SOllivier Robert  grep((($m > $_) && ($m = $_),0),@_);
1658*c0b746e5SOllivier Robert  $m;
1659*c0b746e5SOllivier Robert}
1660*c0b746e5SOllivier Robert
1661*c0b746e5SOllivier Robertsub max
1662*c0b746e5SOllivier Robert{
1663*c0b746e5SOllivier Robert  local($m) = shift;
1664*c0b746e5SOllivier Robert
1665*c0b746e5SOllivier Robert  grep((($m < $_) && ($m = $_),0),@_);
1666*c0b746e5SOllivier Robert  $m;
1667*c0b746e5SOllivier Robert}
1668