xref: /netbsd-src/external/bsd/ntp/dist/scripts/monitoring/ntploopstat (revision abb0f93cd77b67f080613360c65701f85e5f5cfe)
1*abb0f93cSkardel#!/usr/bin/perl -w
2*abb0f93cSkardel# --*-perl-*-
3*abb0f93cSkardel;#
4*abb0f93cSkardel;# ntploopstat,v 3.1 1993/07/06 01:09:11 jbj Exp
5*abb0f93cSkardel;#
6*abb0f93cSkardel;# Poll NTP server using NTP mode 7 loopinfo request.
7*abb0f93cSkardel;# Log info and timestamp to file for processing by ntploopwatch.
8*abb0f93cSkardel;#
9*abb0f93cSkardel;#
10*abb0f93cSkardel;# Copyright (c) 1992
11*abb0f93cSkardel;# Rainer Pruy Friedrich-Alexander Universitaet Erlangen-Nuernberg
12*abb0f93cSkardel;#
13*abb0f93cSkardel;#################################################################
14*abb0f93cSkardel;#
15*abb0f93cSkardel;# The format written to the logfile is the same as used by xntpd
16*abb0f93cSkardel;# for the loopstats file.
17*abb0f93cSkardel;# This script however allows to gather loop filter statistics from
18*abb0f93cSkardel;# remote servers where you do not have access to the loopstats logfile.
19*abb0f93cSkardel;#
20*abb0f93cSkardel;# Please note: Communication delays affect the accuracy of the
21*abb0f93cSkardel;#              timestamps recorded. Effects from these delays will probably
22*abb0f93cSkardel;#              not show up, as timestamps are recorded to the second only.
23*abb0f93cSkardel;#              (Should have implemented &gettimeofday()..)
24*abb0f93cSkardel;#
25*abb0f93cSkardel
26*abb0f93cSkardel$0 =~ s!^.*/([^/]+)$!$1!;		# beautify script name
27*abb0f93cSkardel
28*abb0f93cSkardel$ntpserver = 'localhost';		# default host to poll
29*abb0f93cSkardel$delay = 60;				# default sampling rate
30*abb0f93cSkardel				       ;# keep it shorter than minpoll (=64)
31*abb0f93cSkardel				       ;# to get all values
32*abb0f93cSkardel
33*abb0f93cSkardelrequire "ctime.pl";
34*abb0f93cSkardel;# handle bug in early ctime distributions
35*abb0f93cSkardel$ENV{'TZ'} = 'MET' unless defined($ENV{'TZ'}) || $] > 4.010;
36*abb0f93cSkardel
37*abb0f93cSkardelif (defined(@ctime'MoY))
38*abb0f93cSkardel{
39*abb0f93cSkardel    *MonthName = *ctime'MoY;
40*abb0f93cSkardel}
41*abb0f93cSkardelelse
42*abb0f93cSkardel{
43*abb0f93cSkardel    @MonthName = ('Jan','Feb','Mar','Apr','May','Jun',
44*abb0f93cSkardel		  'Jul','Aug','Sep','Oct','Nov','Dec');
45*abb0f93cSkardel}
46*abb0f93cSkardel
47*abb0f93cSkardel;# this routine can be redefined to point to syslog if necessary
48*abb0f93cSkardelsub msg
49*abb0f93cSkardel{
50*abb0f93cSkardel    return unless $verbose;
51*abb0f93cSkardel
52*abb0f93cSkardel    print  STDERR "$0: ";
53*abb0f93cSkardel    printf STDERR @_;
54*abb0f93cSkardel}
55*abb0f93cSkardel
56*abb0f93cSkardel;#############################################################
57*abb0f93cSkardel;#
58*abb0f93cSkardel;# process command line
59*abb0f93cSkardel$usage = <<"E-O-S";
60*abb0f93cSkardel
61*abb0f93cSkardelusage:
62*abb0f93cSkardel  $0 [-d<delay>] [-t<timeout>] [-l <logfile>] [-v] [ntpserver]
63*abb0f93cSkardelE-O-S
64*abb0f93cSkardel
65*abb0f93cSkardelwhile($_ = shift)
66*abb0f93cSkardel{
67*abb0f93cSkardel    /^-v(\d*)$/ && ($verbose=($1 eq '') ? 1 : $1,1) && next;
68*abb0f93cSkardel    /^-d(\d*)$/ &&
69*abb0f93cSkardel	do {
70*abb0f93cSkardel	    ($1 ne '') && ($delay = $1,1) && next;
71*abb0f93cSkardel	    @ARGV || die("$0: delay value missing after -d\n$usage");
72*abb0f93cSkardel	    $delay = shift;
73*abb0f93cSkardel	    ($delay  >= 0) || die("$0: bad delay value \"$delay\"\n$usage");
74*abb0f93cSkardel	    next;
75*abb0f93cSkardel	};
76*abb0f93cSkardel    /^-l$/ &&
77*abb0f93cSkardel	do {
78*abb0f93cSkardel	    @ARGV || die("$0: logfile missing after -l\n$usage");
79*abb0f93cSkardel	    $logfile = shift;
80*abb0f93cSkardel	    next;
81*abb0f93cSkardel	};
82*abb0f93cSkardel    /^-t(\d*(\.\d*)?)$/ &&
83*abb0f93cSkardel	do {
84*abb0f93cSkardel	    ($1 ne '') && ($timeout = $1,1) && next;
85*abb0f93cSkardel	    @ARGV || die("$0: timeout value missing after -t\n$usage\n");
86*abb0f93cSkardel	    $timeout = shift;
87*abb0f93cSkardel	    ($timeout > 0) ||
88*abb0f93cSkardel		die("$0: bad timeout value \"$timeout\"\n$usage");
89*abb0f93cSkardel	    next;
90*abb0f93cSkardel	};
91*abb0f93cSkardel
92*abb0f93cSkardel    /^-/ && die("$0: unknown option \"$_\"\n$usage");
93*abb0f93cSkardel
94*abb0f93cSkardel    ;# any other argument is server to poll
95*abb0f93cSkardel    $ntpserver = $_;
96*abb0f93cSkardel    last;
97*abb0f93cSkardel}
98*abb0f93cSkardel
99*abb0f93cSkardelif (@ARGV)
100*abb0f93cSkardel{
101*abb0f93cSkardel    warn("unexpected arguments: ".join(" ",@ARGV).".\n");
102*abb0f93cSkardel    die("$0: too many servers specified\n$usage");
103*abb0f93cSkardel}
104*abb0f93cSkardel
105*abb0f93cSkardel;# logfile defaults to include server name
106*abb0f93cSkardel;# The name of the current month is appended and
107*abb0f93cSkardel;# the file is opened and closed for each sample.
108*abb0f93cSkardel;#
109*abb0f93cSkardel$logfile = "loopstats:$ntpserver." unless defined($logfile);
110*abb0f93cSkardel$timeout = 12.0 unless defined($timeout); # wait $timeout seconds for reply
111*abb0f93cSkardel
112*abb0f93cSkardel$MAX_FAIL = 60;				# give up after $MAX_FAIL failed polls
113*abb0f93cSkardel
114*abb0f93cSkardel
115*abb0f93cSkardel$MJD_1970 = 40587;
116*abb0f93cSkardel
117*abb0f93cSkardelif (eval 'require "syscall.ph";')
118*abb0f93cSkardel{
119*abb0f93cSkardel    if (defined(&SYS_gettimeofday))
120*abb0f93cSkardel    {
121*abb0f93cSkardel	;# assume standard
122*abb0f93cSkardel 	;# gettimeofday(struct timeval *tp,struct timezone *tzp)
123*abb0f93cSkardel	;# syntax for gettimeofday syscall
124*abb0f93cSkardel 	;# tzp = NULL -> undef
125*abb0f93cSkardel	;# tp = (long,long)
126*abb0f93cSkardel	eval 'sub time { local($tz) = pack("LL",0,0);
127*abb0f93cSkardel              (&msg("gettimeofday failed: $!\n"),
128*abb0f93cSkardel	      return (time))
129*abb0f93cSkardel	      unless syscall(&SYS_gettimeofday,$tz,undef) == 0;
130*abb0f93cSkardel              local($s,$us) = unpack("LL",$tz);
131*abb0f93cSkardel              return $s + $us/1000000; }';
132*abb0f93cSkardel	local($t1,$t2,$t3);
133*abb0f93cSkardel	$t1 = time;
134*abb0f93cSkardel	eval '$t2 = &time;';
135*abb0f93cSkardel	$t3 = time;
136*abb0f93cSkardel	die("$0: gettimeofday failed: $@.\n") if defined($@) && $@;
137*abb0f93cSkardel	die("$0: gettimeofday inconsistency time=$t1,gettimeofday=$t2,time=$t2\n")
138*abb0f93cSkardel	    if (int($t1) != int($t2) && int($t3) != int($t2));
139*abb0f93cSkardel	&msg("Using gettimeofday for timestamps\n");
140*abb0f93cSkardel    }
141*abb0f93cSkardel    else
142*abb0f93cSkardel    {
143*abb0f93cSkardel	warn("No gettimeofday syscall found - using time builtin for timestamps\n");
144*abb0f93cSkardel        eval 'sub time { return time; }';
145*abb0f93cSkardel    }
146*abb0f93cSkardel}
147*abb0f93cSkardelelse
148*abb0f93cSkardel{
149*abb0f93cSkardel    warn("No syscall.ph file found - using time builtin for timestamps\n");
150*abb0f93cSkardel    eval 'sub time { return time; }';
151*abb0f93cSkardel}
152*abb0f93cSkardel
153*abb0f93cSkardel
154*abb0f93cSkardel;#------------------+
155*abb0f93cSkardel;# from ntp_request.h
156*abb0f93cSkardel;#------------------+
157*abb0f93cSkardel
158*abb0f93cSkardel;# NTP mode 7 packet format:
159*abb0f93cSkardel;#	Byte 1:     ResponseBit MoreBit Version(3bit) Mode(3bit)==7
160*abb0f93cSkardel;#      Byte 2:     AuthBit Sequence #   - 0 - 127 see MoreBit
161*abb0f93cSkardel;#      Byte 3:     Implementation #
162*abb0f93cSkardel;#      Byte 4:     Request Code
163*abb0f93cSkardel;#
164*abb0f93cSkardel;#      Short 1:    Err(3bit) NumItems(12bit)
165*abb0f93cSkardel;#      Short 2:    MBZ(3bit)=0 DataItemSize(12bit)
166*abb0f93cSkardel;#      0 - 500 byte Data
167*abb0f93cSkardel;#  if AuthBit is set:
168*abb0f93cSkardel;#      Long:       KeyId
169*abb0f93cSkardel;#      2xLong:     AuthCode
170*abb0f93cSkardel
171*abb0f93cSkardel;#
172*abb0f93cSkardel$IMPL_XNTPD  = 2;
173*abb0f93cSkardel$REQ_LOOP_INFO = 8;
174*abb0f93cSkardel
175*abb0f93cSkardel
176*abb0f93cSkardel;# request packet for REQ_LOOP_INFO:
177*abb0f93cSkardel;#     B1:  RB=0 MB=0 V=2 M=7
178*abb0f93cSkardel;#     B2:  S# = 0
179*abb0f93cSkardel;#     B3:  I# = IMPL_XNTPD
180*abb0f93cSkardel;#     B4:  RC = REQ_LOOP_INFO
181*abb0f93cSkardel;#     S1:  E=0 NI=0
182*abb0f93cSkardel;#     S2:  MBZ=0 DIS=0
183*abb0f93cSkardel;#     data:  32 byte 0 padding
184*abb0f93cSkardel;#            8byte timestamp if encryption, 0 padding otherwise
185*abb0f93cSkardel$loopinfo_reqpkt =
186*abb0f93cSkardel    pack("CCCC nn x32 x8", 0x17, 0, $IMPL_XNTPD, $REQ_LOOP_INFO, 0, 0);
187*abb0f93cSkardel
188*abb0f93cSkardel;# ignore any auth data in packets
189*abb0f93cSkardel$loopinfo_response_size =
190*abb0f93cSkardel    1+1+1+1+2+2			# header size like request pkt
191*abb0f93cSkardel    + 8				# l_fp last_offset
192*abb0f93cSkardel    + 8				# l_fp drift_comp
193*abb0f93cSkardel    + 4				# u_long compliance
194*abb0f93cSkardel    + 4				# u_long watchdog_timer
195*abb0f93cSkardel    ;
196*abb0f93cSkardel$loopinfo_response_fmt    = "C4n2N2N2NN";
197*abb0f93cSkardel$loopinfo_response_fmt_v2 = "C4n2N2N2N2N";
198*abb0f93cSkardel
199*abb0f93cSkardel;#
200*abb0f93cSkardel;# prepare connection to server
201*abb0f93cSkardel;#
202*abb0f93cSkardel
203*abb0f93cSkardel;# workaround for broken socket.ph on dynix_ptx
204*abb0f93cSkardeleval 'sub INTEL {1;}' unless defined(&INTEL);
205*abb0f93cSkardeleval 'sub ATT {1;}'  unless defined(&ATT);
206*abb0f93cSkardel
207*abb0f93cSkardelrequire "sys/socket.ph";
208*abb0f93cSkardel
209*abb0f93cSkardelrequire 'netinet/in.ph';
210*abb0f93cSkardel
211*abb0f93cSkardel;# if you do not have netinet/in.ph enable the following lines
212*abb0f93cSkardel;#eval 'sub INADDR_ANY { 0x00000000; }' unless defined(&INADDR_ANY);
213*abb0f93cSkardel;#eval 'sub IPPRORO_UDP { 17; }' unless defined(&IPPROTO_UDP);
214*abb0f93cSkardel
215*abb0f93cSkardelif ($ntpserver =~ /^((0x?)?\w+)\.((0x?)?\w+)\.((0x?)?\w+)\.((0x?)?\w+)$/)
216*abb0f93cSkardel{
217*abb0f93cSkardel    local($a,$b,$c,$d) = ($1,$3,$5,$7);
218*abb0f93cSkardel    $a = oct($a) if defined($2);
219*abb0f93cSkardel    $b = oct($b) if defined($4);
220*abb0f93cSkardel    $c = oct($c) if defined($6);
221*abb0f93cSkardel    $d = oct($d) if defined($8);
222*abb0f93cSkardel    $server_addr = pack("C4", $a,$b,$c,$d);
223*abb0f93cSkardel
224*abb0f93cSkardel    $server_mainname
225*abb0f93cSkardel	= (gethostbyaddr($server_addr,&AF_INET))[$[] || $ntpserver;
226*abb0f93cSkardel}
227*abb0f93cSkardelelse
228*abb0f93cSkardel{
229*abb0f93cSkardel    ($server_mainname,$server_addr)
230*abb0f93cSkardel	= (gethostbyname($ntpserver))[$[,$[+4];
231*abb0f93cSkardel
232*abb0f93cSkardel    die("$0: host \"$ntpserver\" is unknown\n")
233*abb0f93cSkardel	unless defined($server_addr);
234*abb0f93cSkardel}
235*abb0f93cSkardel&msg ("Address of server \"$ntpserver\" is \"%d.%d.%d.%d\"\n",
236*abb0f93cSkardel      unpack("C4",$server_addr));
237*abb0f93cSkardel
238*abb0f93cSkardel$proto_udp = (getprotobyname('udp'))[$[+2] || &IPPROTO_UDP;
239*abb0f93cSkardel
240*abb0f93cSkardel$ntp_port =
241*abb0f93cSkardel    (getservbyname('ntp','udp'))[$[+2] ||
242*abb0f93cSkardel    (warn "Could not get port number for service \"ntp/udp\" using 123\n"),
243*abb0f93cSkardel    ($ntp_port=123);
244*abb0f93cSkardel
245*abb0f93cSkardel;#
246*abb0f93cSkardel0 && &SOCK_DGRAM;		# satisfy perl -w ...
247*abb0f93cSkardelsocket(S, &AF_INET, &SOCK_DGRAM, $proto_udp) ||
248*abb0f93cSkardel    die("Cannot open socket: $!\n");
249*abb0f93cSkardel
250*abb0f93cSkardelbind(S, pack("S n N x8", &AF_INET, 0, &INADDR_ANY)) ||
251*abb0f93cSkardel    die("Cannot bind: $!\n");
252*abb0f93cSkardel
253*abb0f93cSkardel($my_port, $my_addr) = (unpack("S n a4 x8",getsockname(S)))[$[+1,$[+2];
254*abb0f93cSkardel
255*abb0f93cSkardel&msg("Listening at address %d.%d.%d.%d port %d\n",
256*abb0f93cSkardel     unpack("C4",$my_addr), $my_port);
257*abb0f93cSkardel
258*abb0f93cSkardel$server_inaddr = pack("Sna4x8", &AF_INET, $ntp_port, $server_addr);
259*abb0f93cSkardel
260*abb0f93cSkardel;############################################################
261*abb0f93cSkardel;#
262*abb0f93cSkardel;# the main loop:
263*abb0f93cSkardel;#	send request
264*abb0f93cSkardel;#      get reply
265*abb0f93cSkardel;#      wait til next sample time
266*abb0f93cSkardel
267*abb0f93cSkardelundef($lasttime);
268*abb0f93cSkardel$lostpacket = 0;
269*abb0f93cSkardel
270*abb0f93cSkardelwhile(1)
271*abb0f93cSkardel{
272*abb0f93cSkardel    $stime = &time;
273*abb0f93cSkardel
274*abb0f93cSkardel    &msg("Sending request $stime...\n");
275*abb0f93cSkardel
276*abb0f93cSkardel    $ret = send(S,$loopinfo_reqpkt,0,$server_inaddr);
277*abb0f93cSkardel
278*abb0f93cSkardel    if (! defined($ret) || $ret < length($loopinfo_reqpkt))
279*abb0f93cSkardel    {
280*abb0f93cSkardel	warn("$0: send failed ret=($ret): $!\n");
281*abb0f93cSkardel	$fail++;
282*abb0f93cSkardel	next;
283*abb0f93cSkardel    }
284*abb0f93cSkardel
285*abb0f93cSkardel    &msg("Waiting for reply...\n");
286*abb0f93cSkardel
287*abb0f93cSkardel    $mask = ""; vec($mask,fileno(S),1) = 1;
288*abb0f93cSkardel    $ret = select($mask,undef,undef,$timeout);
289*abb0f93cSkardel
290*abb0f93cSkardel    if (! defined($ret))
291*abb0f93cSkardel    {
292*abb0f93cSkardel	warn("$0: select failed: $!\n");
293*abb0f93cSkardel	$fail++;
294*abb0f93cSkardel	next;
295*abb0f93cSkardel    }
296*abb0f93cSkardel    elsif ($ret == 0)
297*abb0f93cSkardel    {
298*abb0f93cSkardel	warn("$0: request to $ntpserver timed out ($timeout seconds)\n");
299*abb0f93cSkardel	;# do not count this event as failure
300*abb0f93cSkardel	;# it usually this happens due to dropped udp packets on noisy and
301*abb0f93cSkardel	;# havily loaded lines, so just try again;
302*abb0f93cSkardel	$lostpacket = 1;
303*abb0f93cSkardel	next;
304*abb0f93cSkardel    }
305*abb0f93cSkardel
306*abb0f93cSkardel    &msg("Receiving reply...\n");
307*abb0f93cSkardel
308*abb0f93cSkardel    $len = 520;				# max size of a mode 7 packet
309*abb0f93cSkardel    $reply = "";			# just make it defined for -w
310*abb0f93cSkardel    $ret = recv(S,$reply,$len,0);
311*abb0f93cSkardel
312*abb0f93cSkardel    if (!defined($ret))
313*abb0f93cSkardel    {
314*abb0f93cSkardel	warn("$0: recv failed: $!\n");
315*abb0f93cSkardel	$fail++;
316*abb0f93cSkardel	next;
317*abb0f93cSkardel    }
318*abb0f93cSkardel
319*abb0f93cSkardel    $etime = &time;
320*abb0f93cSkardel    &msg("Received at\t$etime\n");
321*abb0f93cSkardel
322*abb0f93cSkardel    ;#$time = ($stime + $etime) / 2; # symmetric delay assumed
323*abb0f93cSkardel    $time = $etime;		# the above assumption breaks for X25
324*abb0f93cSkardel			       ;# so taking etime makes timestamps be a
325*abb0f93cSkardel			       ;# little late, but keeps them increasing
326*abb0f93cSkardel			       ;# monotonously
327*abb0f93cSkardel
328*abb0f93cSkardel    &msg(sprintf("Reply from %d.%d.%d.%d took %f seconds\n",
329*abb0f93cSkardel		 (unpack("SnC4",$ret))[$[+2 .. $[+5], ($etime - $stime)));
330*abb0f93cSkardel
331*abb0f93cSkardel    if ($len < $loopinfo_response_size)
332*abb0f93cSkardel    {
333*abb0f93cSkardel	warn("$0: short packet ($len bytes) received ($loopinfo_response_size bytes expected\n");
334*abb0f93cSkardel	$fail++;
335*abb0f93cSkardel	next;
336*abb0f93cSkardel    }
337*abb0f93cSkardel
338*abb0f93cSkardel    ($b1,$b2,$b3,$b4,$s1,$s2,
339*abb0f93cSkardel     $offset_i,$offset_f,$drift_i,$drift_f,$compl,$watchdog)
340*abb0f93cSkardel	= unpack($loopinfo_response_fmt,$reply);
341*abb0f93cSkardel
342*abb0f93cSkardel    ;# check reply
343*abb0f93cSkardel    if (($s1 >> 12) != 0)	      # error !
344*abb0f93cSkardel    {
345*abb0f93cSkardel	die("$0: got error reply ".($s1>>12)."\n");
346*abb0f93cSkardel    }
347*abb0f93cSkardel    if (($b1 != 0x97 && $b1 != 0x9f) || # Reply NotMore V=2 M=7
348*abb0f93cSkardel	($b2 != 0 && $b2 != 0x80) ||	# S=0 Auth no/yes
349*abb0f93cSkardel	$b3 != $IMPL_XNTPD ||		# ! IMPL_XNTPD
350*abb0f93cSkardel	$b4 != $REQ_LOOP_INFO ||	# Ehh.. not loopinfo reply ?
351*abb0f93cSkardel	$s1 != 1 ||			# ????
352*abb0f93cSkardel	($s2 != 24 && $s2 != 28)	#
353*abb0f93cSkardel	)
354*abb0f93cSkardel    {
355*abb0f93cSkardel	warn("$0: Bad/unexpected reply from server:\n");
356*abb0f93cSkardel	warn("  \"".unpack("H*",$reply)."\"\n");
357*abb0f93cSkardel	warn("   ".sprintf("b1=%x b2=%x b3=%x b4=%x s1=%d s2=%d\n",
358*abb0f93cSkardel			   $b1,$b2,$b3,$b4,$s1,$s2));
359*abb0f93cSkardel	$fail++;
360*abb0f93cSkardel	next;
361*abb0f93cSkardel    }
362*abb0f93cSkardel    elsif ($s2 == 28)
363*abb0f93cSkardel    {
364*abb0f93cSkardel      ;# seems to be a version 2 xntpd
365*abb0f93cSkardel      ($b1,$b2,$b3,$b4,$s1,$s2,
366*abb0f93cSkardel       $offset_i,$offset_f,$drift_i,$drift_f,$compl_i,$compl_f,$watchdog)
367*abb0f93cSkardel	  = unpack($loopinfo_response_fmt_v2,$reply);
368*abb0f93cSkardel      $compl = &lfptoa($compl_i, $compl_f);
369*abb0f93cSkardel    }
370*abb0f93cSkardel
371*abb0f93cSkardel    $time -= $watchdog;
372*abb0f93cSkardel
373*abb0f93cSkardel    $offset = &lfptoa($offset_i, $offset_f);
374*abb0f93cSkardel    $drift  = &lfptoa($drift_i, $drift_f);
375*abb0f93cSkardel
376*abb0f93cSkardel    &log($time,$offset,$drift,$compl) && ($fail = 0);;
377*abb0f93cSkardel}
378*abb0f93cSkardelcontinue
379*abb0f93cSkardel{
380*abb0f93cSkardel    die("$0: Too many failures - terminating\n") if $fail > $MAX_FAIL;
381*abb0f93cSkardel    &msg("Sleeping " . ($lostpacket ? ($delay / 2) : $delay) . " seconds...\n");
382*abb0f93cSkardel
383*abb0f93cSkardel    sleep($lostpacket ? ($delay / 2) : $delay);
384*abb0f93cSkardel    $lostpacket = 0;
385*abb0f93cSkardel}
386*abb0f93cSkardel
387*abb0f93cSkardelsub log
388*abb0f93cSkardel{
389*abb0f93cSkardel    local($time,$offs,$freq,$cmpl) = @_;
390*abb0f93cSkardel    local($y,$m,$d);
391*abb0f93cSkardel    local($fname,$suff) = ($logfile);
392*abb0f93cSkardel
393*abb0f93cSkardel
394*abb0f93cSkardel    ;# silently drop sample if distance to last sample is too low
395*abb0f93cSkardel    if (defined($lasttime) && ($lasttime + 2) >= $time)
396*abb0f93cSkardel    {
397*abb0f93cSkardel      &msg("Dropped packet - old sample\n");
398*abb0f93cSkardel      return 1;
399*abb0f93cSkardel    }
400*abb0f93cSkardel
401*abb0f93cSkardel    ;# $suff determines which samples end up in the same file
402*abb0f93cSkardel    ;# could have used $year (;-) or WeekOfYear, DayOfYear,....
403*abb0f93cSkardel    ;# Change it to your suit...
404*abb0f93cSkardel
405*abb0f93cSkardel    ($d,$m,$y) = (localtime($time))[$[+3 .. $[+5];
406*abb0f93cSkardel    $suff = sprintf("%04d%02d%02d",$y+1900,$m+1,$d);
407*abb0f93cSkardel    $fname .= $suff;
408*abb0f93cSkardel    if (!open(LOG,">>$fname"))
409*abb0f93cSkardel    {
410*abb0f93cSkardel	warn("$0: open($fname) failed: $!\n");
411*abb0f93cSkardel	$fail++;
412*abb0f93cSkardel	return 0;
413*abb0f93cSkardel    }
414*abb0f93cSkardel    else
415*abb0f93cSkardel    {
416*abb0f93cSkardel	;# file format
417*abb0f93cSkardel	;#          MJD seconds offset drift compliance
418*abb0f93cSkardel	printf LOG ("%d %.3lf %.8lf %.7lf %d\n",
419*abb0f93cSkardel		    int($time/86400)+$MJD_1970,
420*abb0f93cSkardel		    $time - int($time/86400) * 86400,
421*abb0f93cSkardel		    $offs,$freq,$cmpl);
422*abb0f93cSkardel	close(LOG);
423*abb0f93cSkardel	$lasttime = $time;
424*abb0f93cSkardel    }
425*abb0f93cSkardel    return 1;
426*abb0f93cSkardel}
427*abb0f93cSkardel
428*abb0f93cSkardel;# see ntp_fp.h to understand this
429*abb0f93cSkardelsub lfptoa
430*abb0f93cSkardel{
431*abb0f93cSkardel    local($i,$f) = @_;
432*abb0f93cSkardel    local($sign) = 1;
433*abb0f93cSkardel
434*abb0f93cSkardel
435*abb0f93cSkardel    if ($i & 0x80000000)
436*abb0f93cSkardel    {
437*abb0f93cSkardel	if ($f == 0)
438*abb0f93cSkardel	{
439*abb0f93cSkardel	    $i = -$i;
440*abb0f93cSkardel	}
441*abb0f93cSkardel	else
442*abb0f93cSkardel	{
443*abb0f93cSkardel	    $f = -$f;
444*abb0f93cSkardel	    $i = ~$i;
445*abb0f93cSkardel	    $i += 1;			# 2s complement
446*abb0f93cSkardel	}
447*abb0f93cSkardel	$sign = -1;
448*abb0f93cSkardel	;#print "NEG: $i $f\n";
449*abb0f93cSkardel    }
450*abb0f93cSkardel    else
451*abb0f93cSkardel    {
452*abb0f93cSkardel	;#print "POS: $i $f\n";
453*abb0f93cSkardel    }
454*abb0f93cSkardel    ;# unlike xntpd I have perl do the dirty work.
455*abb0f93cSkardel    ;# Using floats here may affect precision, but
456*abb0f93cSkardel    ;# currently these bits aren't significant anyway
457*abb0f93cSkardel    return $sign * ($i + $f/2**32);
458*abb0f93cSkardel}
459