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