Annotation of embedaddon/ntp/scripts/monitoring/ntploopwatch, revision 1.1.1.1

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>