home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
nisttime.carsoncity.k12.mi.us
/
nisttime.carsoncity.k12.mi.us.tar
/
nisttime.carsoncity.k12.mi.us
/
pub
/
lockclock
/
lockclock.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-11-18
|
34KB
|
946 lines
main(argc,argv)
int argc;
char *argv[];
{
#include <stdio.h>
#include <math.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/wait.h>
#include <sys/time.h>
#include "timex.h"
extern int errno;
/*
this program locks the local clock using calibration
data received from some external source. the actual
calibration process is implemented in a separate
subroutine and the details are not known to this
program. the subroutine must return the current
time as a day number and time in seconds, followed
by the measured time difference, where a positive value
means that the local clock is fast.
*/
FILE *lop; /*used to open file lock.dat*/
FILE *pop; /*used to open file param.dat*/
FILE *fopen();
#include "lock.h" /*get data structure definitions*/
#include "sizint.h"
struct lockst lock[NUMSTATES]; /*push-down stack of the system state*/
struct lockpm param; /*lock parameter structure */
char *rootdir="/local/etc"; /*directory for lockclock files*/
char *sname="lock.dat"; /*name of state file */
char *pname="param.dat"; /*name of parameter file*/
void getlock(); /*reads state file into structure*/
void pushlock(); /*push down lock structure by 1*/
void lockerr(); /*prints message to file and exits*/
void putleap(); /*writes leap second file for other tasks*/
int adjt(); /*adjusts local time, returns 0 if ok*/
int sett(); /*steps local time, returns 0 if ok*/
LONG gettck(); /*returns usec when tick received */
LONG jadj; /*value returned by gettck*/
LONG ladj = 0; /*last value of jadj*/
int i;
LONG daym,fdaym; /*time of calibration, MJD and seconds*/
double delta; /*time difference local-remote in s*/
int lsflag; /*leap second flag from getdif*/
int lsdue; /*leap second is due this month*/
double sigmam; /*sigma of differences returned by getdif*/
int getdif(); /*returns time difference local-remote*/
int j;
unsigned int iwait; /*nominal delay until next cycle*/
unsigned int jwait; /*nominal delay between time adjustments*/
unsigned int kwait; /*sleep interval for big adj. in mode 7*/
double tcor,lptcor; /*time adjustments in seconds */
int jloop; /*number of adjustments for each cycle*/
double xavgt; /*y-time constant normalized by delay*/
char msg[80]; /*message to environment showing status*/
char *s; /*message pointer */
double ddum;
int conserr = 0; /*number of consecutive errors from getdif*/
int conterr; /*number of consecutive errors from gettck*/
int filefull = NUMSTATES; /*counter to see when file is getting full*/
int nloops = 0; /*number of loops since dead-start */
char filenam[50]; /*holds new file name copy command*/
int pid; /*return value from wait call*/
union wait *status; /*return status from wait call*/
int fstep; /*reset counter in freq-step detector*/
int fsteplim; /*max value of fstep */
struct timex tmx; /*used for David Mills kernel calls */
/*
parameter slew gives the time in seconds necessary to
correct the local clock by 1 second when subroutine
adjtime is used. For ULTRIX/RISC, for example, adjtime
moves the clock 15 usec/tick. Since there are 256
ticks/second in this system, the clock moves 256X15 =
3840 usec/sec, so that it takes about 260 seconds to
move 1 second. This parameter is needed to know how
long it will take to adjust the clock by a big amount,
which is usually necessary only at startup.
*/
int slew;
/*
the first job is to convert ourselves into a daemon.
We enter the phone booth as mild-mannered Clark Kent,
we make a copy of ourselves, the original disappears,
the child emerges and then ....
*/
signal(SIGTTOU,SIG_IGN); /*the keyboard can't stop us*/
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
if( (i=fork()) < 0) abort(); /*fork of child failed*/
if(i > 0)
{
printf("\n pid of process is %d",i);
exit(0); /*the parent exits */
}
/*
make ourselves into a unique process group so that we
can't re-acquire a control terminal that might be able
to stop us.
Then close our control terminal.
Nothing can stop us now ...
*/
if( setpgrp(0,getpid() ) == -1) abort();
if( (i= open("/dev/tty",O_RDWR) ) >= 0)
{
ioctl(i,TIOCNOTTY,(char *) NULL); /*bye bye terminal*/
close(i);
}
for(i=0; i< NOFILE; i++) close(i); /*close all files*/
errno=0; /*reset errors, if any*/
chdir(rootdir); /*set default directory*/
/*
open parameter file and get system parameters
*/
if( (pop=fopen(pname,"r")) == NULL) /*opened readonly*/
lockerr("Cannot open param.dat in lockclock.");
if(fscanf(pop," %f %d %d %d %f %d",¶m.xsigma,¶m.flags,
¶m.wait0,¶m.iavgt,¶m.xoffst,&slew) !=6)
{
fclose(pop);
lockerr(" error reading parm.dat in lockclock.");
}
fclose(pop);
/*
initialize leap second coming flag to off
*/
lsdue=0;
/*
If David Mills' kernel routines are present and will be
used to set leap seconds then make sure that they are
fully disabled now.
*/
if( (param.flags & 16) != 0)
{
tmx.modes=MOD_STATUS; /*change status variable*/
tmx.status=STA_UNSYNC; /*we start out un synched*/
syscall(SYS_ntp_adjtime,&tmx); /*tell it to the kernel */
}
/*
open system state file and get previous state
*/
if( (lop=fopen(sname,"r")) == NULL) /*open read only */
lockerr("Cannot open file lock.dat in lockclock.");
for(i=1; i< NUMSTATES; i++)
{
getlock(&lop,&lock[i]); /*read structure from file via lop*/
if(lock[i].day == 0) break; /*EOF on input */
}
if(fclose(lop) == EOF)
lockerr("Error closing lock.dat at startup");
while(i < NUMSTATES) lock[i++].day = 0; /*zero remaining records*/
/*
if the previous mode read from the file is 4, 5, or 6, then
set the current mode to 4, which will adjust the time if
necessary and then switch back to the locked modes after
the time is adjusted. if the previous mode was anything
else, then start over again with mode 0
*/
if(lock[1].mode >= 4)
{
lock[0].mode=4;
lock[0].y=lock[1].y;
lock[0].d=lock[1].d;
lock[0].flags=lock[1].flags;
lock[0].waitn=lock[1].waitn=param.wait0;
lock[0].waitx=lock[1].waitx;
lock[0].errx=lock[1].errx;
lock[0].ybar=lock[1].ybar;
lock[0].sumerr=lock[1].sumerr;
}
else lock[0].mode=0;
loop: /*come back here when we wake up each time*/
/*
now get the current measurement of the time difference.
on return, daym and fdaym are the day and time of the
measurement and delta is the time difference.
daym is the MJD as a long int;
fdaym is the time since midnight in seconds as a long int;
delta is the time difference in s as a double;
if delta > 0, the local clock is fast.
the next parameter is the rejection threshold for the
time difference data. The subroutine computes three
consecutive time differences which must differ among
themselves by less than this value. See getdif
for what happens if they don't.
This rejection criterion is set more or less arbitrarily
at 3 X the measurement noise estimate read from the
parameter file. If the measurement noise is white and
if param.xsigma is a good estimate of its std. deviation,
then the rejection criterion should operate about 1% of
the time or less.
The next parameter (lsflag) is the leap second flag as read from
the ACTS transmissions
the next paramater (sigmam) is an estimate of the measurement
noise found by getdif. It is related to the variation among
the successive time differences found by getdif.
*/
j=getdif(&daym,&fdaym,&delta,(double)(3*param.xsigma),&lsflag,
&sigmam);
/*
if j == 1 then the three time difference measurements agreed
among themselves to within 3 sigma. Use the returned value
of sigmam to adjust the value of sigma from the value first
read from the file.
if j > 1 then the time difference data were too noisy based
on the current value of sigma which either means that one
of the points had a glitch or that sigma is a bit too small.
The returned value of sigmam will be larger than sigma in
this case so that sigma will grow.
*/
if(j >= 1) param.xsigma= 0.8*param.xsigma + 0.2*sigmam;
/*
if j<=0 then getdif had a problem connecting to acts
and the return values are not valid. wait a bit and
try again.
if j ==3 then getdif connected to ACTS but the three
time-difference estimates disagree with each other
and there is something very wrong with the ACTS system
or with the local clock.
*/
if( (j <= 0) || (j == 3) )
{
conserr++; /*increment consecutive error counter */
if(conserr < 5) /*wait 15 sec and retry up to 5 times*/
{
sleep(15);
goto loop;
}
else /*retries don't fix the problem */
{ /*copy previous state to this one and wait*/
lock[0].day=lock[1].day;
lock[0].fday=lock[1].fday;
lock[0].mode=lock[1].mode;
lock[0].x=0;
lock[0].y=lock[1].y;
lock[0].d=lock[1].d;
lock[0].flags=j;
iwait=lock[0].waitn=lock[1].waitn;
lock[0].waitx=lock[1].waitx;
lock[0].errx=lock[1].errx;
lock[0].ybar=lock[1].ybar;
lock[0].sumerr=lock[1].sumerr;
goto finis; /*hope for better times */
}
} /*end of negative return status from getdif*/
/*
test the returned leap-second status and compare it
to the value stored in lsdue to see if the status
this time is different from the last value that we
knew about.
if the status just received is > than the stored status
then either this is the first notification for an event
that is still a month away or the leap second is today.
if the status just received is < than the stored status
then either an event was scheduled and is now over or
lsflag < 0 which means that the consecutive values of
this flag did not agree as received from ACTS. Either
situation should not happen normally since it implies
either that we slept through the leap second, or the
consecutive values read from ACTS did not agree. Both
of these situations are errors.
if the current and stored status values are the same then
nothing happens here.
*/
if(lsflag > lsdue)
{
if(lsdue == 0) putleap(lsflag); /*first notification */
lsdue=lsflag;
/*
if the new status is >= 10 then this is a notice that
the leap second will happen today. if David Mills'
kernel routines have been installed and enabled, then
leap seconds are handled by letting the kernel routine
do it. This leap second mode will be enabled by setting
the 5th bit of the flag word
*/
if( (lsdue >= 10) /*leap second today */
&& ( (param.flags & 16) != 0) ) /*use kernel routines*/
{
tmx.modes= MOD_STATUS; /*set leap second flag*/
if(lsdue == 10) tmx.status=STA_INS; /*insert leap second */
else tmx.status=STA_DEL; /*delete leap second */
syscall(SYS_ntp_adjtime,&tmx);
}
}
else if( (lsflag >= 0) && /*no error in lsflag*/
(lsflag < lsdue) ) lsdue=0; /*event time is over*/
/*
continue if return status from getdif is +1 or +2:
store this time difference in the state structure.
The time difference value returned from getdif is
adjusted by subtracting a possible time offset read
from the parameter file. This offset could be used
to correct for some systematic bias but is used on
the Sun version to move the time away from 0 to avoid
a possible race condition in the driver where the driver
latency varies as the time moves by small amounts near
a tick interrupt. This race condition results in the
time jumping back and forth by 1 tick as the actual
difference either comes just before or just after a
tick.
*/
lock[0].day=daym; /*day in MJD */
lock[0].fday=fdaym; /*time in seconds since 00:00*/
lock[0].x=delta - param.xoffst; /* corrected time difference*/
lock[0].flags=j; /*status of comparison*/
/*
mode 0 is used for a totally cold start. store
the time difference in the state structure and
advance to mode 1 for the next cycle.
the delay between cycles is set to the initial
value read from the parameter structure
*/
if(lock[1].mode == 0) /*mode 0 is cold start */
{
lock[0].y=lock[0].d=lock[0].errx=lock[0].ybar=
lock[0].sumerr=0;
lock[0].mode=1;
iwait=lock[0].waitn=param.wait0;
goto finis;
} /*end of mode 0*/
/*
if this is not the first time of a cold start then
compute the actual time difference since the last
calibration in seconds.
xavgt is the time constant for frequency averaging
normalized by the current delay. It is used in
the exponential filter for the frequency estimator.
if the specified averaging time is longer than
3 samples times, then it is limited to 3 sample
times following a cold start to speed up the
time needed to get to equilibrium following a
re-start of the program.
*/
lock[0].waitx=86400*(lock[0].day - lock[1].day) +
(lock[0].fday - lock[1].fday);
xavgt=lock[0].waitx/( (double) param.iavgt);
if( (nloops < 10) && (xavgt < 0.33)) xavgt=0.33;
/*
mode 1 is used to make an initial estimate for the
frequency during a cold start. it is the mode used
for the second cycle during the cold start process
if the time difference since the last loop is not
much bigger than the estimate of the measurement noise
xsigma, then we did not wait long enough given the
rate of the local clock. increase the wait time in
this case and do not advance to mode 2. if this
happens a second time then the rate is really very
small and it may take forever to lock up. the true
rate is almost 0 in this case.
*/
if(lock[1].mode == 1) /*make initial frequency estimate*/
{
lock[0].y=(lock[0].x - lock[1].x)/lock[0].waitx;
lock[0].d=lock[0].errx=lock[0].ybar=lock[0].sumerr=0;
if(fabs(lock[0].x - lock[1].x) < 2*param.xsigma)
{
if(lock[2].mode == 1) /*if this is second time*/
{
lock[0].y=(lock[0].y + lock[1].y)/2;
lock[0].mode=2;
iwait=lock[0].waitn=lock[1].waitn;
}
else /*this is only the first time here*/
{
lock[0].mode=1;
iwait=lock[0].waitn=1.5*lock[1].waitn;
}
} /*end of first difference small */
else
{
lock[0].mode=2;
iwait=lock[0].waitn=lock[1].waitn;
} /*end of first difference not small*/
goto finis;
} /*end of mode 1 */
/*
mode 2 is used for the third cycle of a cold start.
we make a second frequency estimate and see if it
is consistent with the previous one by checking
how the first one would have worked as a predictor
for this round.
if the prediction error is large then either the
clock has lots of frequency noise, the channel has
lots of time noise or the drift is appreciable.
these questions should be addressed by measuring
the Allan Variance of the free running system.
the program tries to fix this problem by shortening
the interval between measurements. Depending on what
is going on, this may not be the optimum strategy.
The initial estimate of ybar is simply the average
of the two frequency estimates that we have made
so far.
*/
if(lock[1].mode == 2) /*check frequency estimate*/
{
lock[0].y=(lock[0].x - lock[1].x)/lock[0].waitx;
lock[0].d=lock[0].sumerr=0;
lock[0].ybar=(lock[1].y + lock[0].y)/2;
lock[0].errx=(lock[1].y-lock[0].y)*lock[0].waitx;
if(fabs(lock[0].errx) > 3*param.xsigma)
{
iwait=lock[0].waitn=0.9*(float)lock[1].waitn;
lock[0].mode=2;
goto finis;
} /*end of large prediction error */
else
{
iwait=lock[0].waitn=lock[1].waitn;
lock[0].mode=3;
goto finis;
} /*end of small prediction error */
} /* end of mode 2*/
/*
in mode 3, the exponential frequency estimate ybar
is used to predict the time difference between this
cycle and the last one. The averaging time for this
filter is set by xavgt, which is determined from the
free-running Allan variance measurements. The prediction
error is stored in errx and integrated in sumerr.
if the least significant bit of param.flags is set
the advance to mode 4 and set the local time after
4 cycles in mode 3. If the least significant bit of
param.flags is not set, then stay in mode 3 forever.
This is the last mode that can run non-privileged and
is used for characterizing the local clock.
*/
if(lock[1].mode == 3) /*begin using avg. freq for prediction*/
{
lock[0].y=(lock[0].x - lock[1].x)/lock[0].waitx;
lock[0].d=0;
lock[0].ybar=(lock[1].ybar + xavgt*lock[0].y)/(1+xavgt);
lock[0].errx=(lock[0].x - lock[1].x)
- lock[0].waitx*lock[1].ybar;
lock[0].sumerr=lock[1].sumerr + lock[0].errx;
iwait=lock[0].waitn=lock[1].waitn;
lock[0].mode=3;
if( (param.flags & 1) == 0)
goto finis; /*never advance to mode 4*/
if( (lock[2].mode != 3) ||
(lock[3].mode != 3) ) goto finis; /*wait longer*/
lock[0].mode=4; /* advance immediately to set mode */
lock[0].sumerr=0; /* start with clean slate */
lock[0].errx=fabs(lock[0].errx); /*value must be pos. def.*/
} /*end of mode 3*/
/*
mode 4 is used to set the clock. If the third bit of
param.flags is set, then the clock is set to within 1
second in a single step using subroutine sett. If the
third bit of param.flags is not set, then the clock is
adjusted gradually using adjtime. Since subroutine
adjtime makes time adjustments slowly it may take
an appreciable time to set the clock correctly if it is
far off.
the integer parameter slew (defined in the header)
is the number of seconds that are needed to adjust
the local clock by 1 second. The absolute value of
iwait/slew is therefore the maximum adjustment that
is possible in the nominal delay of iwait seconds.
note that all of the parameters are frozen during
this time adjustment.
when the time offset has been reduced so that it
can be done in one step, then perform this adjustment
and calculate how long it will take. Set the delay
for this interval and switch to mode 5, which is not
a real mode, but just indicates that the final time
adjustment is in progress. This mode is automatically
converted to mode 6 on the next round.
*/
if( (lock[1].mode == 4) || (lock[0].mode == 4) )
{
if( (param.flags & 4) != 0) /*then set time in 1 step*/
{
if(fabs(lock[0].x) >= 1) /*step time if big */
{
tcor= -rint(lock[0].x); /* set to nearest second*/
sett(tcor);
lock[0].waitn=lock[1].waitn;
iwait=120;
goto finis; /*wait for a bit*/
}
else goto fnladj; /* error is less than 1 s */
} /* end of third bit not zero */
/*
if we are still here then the time will be set by
gradual adjustments rather than in a single step
*/
iwait=lock[0].waitn=lock[1].waitn;
if(lock[0].x <= -lock[0].waitn/slew) /*error is large and neg.*/
{
tcor=lock[0].waitn/slew;
adjt(tcor);
goto finis;
}
if(lock[0].x >= lock[0].waitn/slew) /*error is large and pos.*/
{
tcor= -lock[0].waitn/slew;
adjt(tcor);
goto finis;
}
/*
if we get here, then the residual error can be
removed in one more step. perform this adjustment
and wait until it is finished, but not less than 10
minutes. switch to the lock mode on the next round.
this intermediate step is called mode 5, but it is
not a true mode, since it will be converted to mode
6 on the next round.
note that in addition to the time adjustment remaining
to be done, we must add the amount by which the clock
will drift due to its frequency offset during the
time we will be waiting.
*/
fnladj: /*final adjustment is always a slew*/
tcor= -lock[0].x;
iwait=fabs(tcor)*slew;
if(iwait < 600) iwait = 600;
tcor -= iwait*lock[0].ybar;
adjt(tcor);
fstep=0; /* reset freq. step detector*/
lock[0].mode=5; /*see note above*/
/*
if David Mills kernel stuff is enabled, tell it that
the clock is now okay.
*/
if( (param.flags & 16) != 0)
{
tmx.modes=MOD_STATUS | MOD_MAXERROR | MOD_ESTERROR;
tmx.status=0; /*everything okay, pll off*/
tmx.maxerror=tmx.esterror=0;
syscall(SYS_ntp_adjtime,&tmx);
}
goto finis;
} /* end of mode 4*/
/*
mode 6 is used to keep the local clock on time. the
average time offset should be 0 and any residual time
therefore means that the rate is not quite right.
this mode assumes that any time error last time was
removed then and that we have been making periodic
adjustments to compensate for the average frequency
offset. Any time difference this time is therefore an
indication that the average frequency is wrong.
There are two exceptions to this. if the time error is
very small (currently if it is less than xsigma/2 in
absolute value), then it is treated as noise that is
not significantly different from 0 and is not used to
update the frequency estimator here and is not removed
below. This will produce a step in what happens when
the time error is near xsigma/2 -- values slightly
smaller are ignored and those slightly larger are used
at full strength.
The second exception is if the time error is very large
(currently if it is greater than 4 errx). This is
treated as a time step error and the response is a reset --
the frequency estimator is not changed since the time
is treated as a glitch, but the time error will be removed
below. if the problem really is a time step, then this was
the right thing to do, but it won't work if there has been
a frequency step. from observation, these are quite rare
although they sometimes happen on baldwin. the parameter
fstep is set to the approximate value of the time constant
for frequency updates, and the reset algorithm is suspended
for that number of following cycles so that the update of
the frequency will be allowed to proceed. this will allow
the frequency to adjust in case of a frequency step.
note that the test for mode 5 will only be satisfied
once since it is the transition mode from 4.
*/
if( (lock[1].mode == 5) || (lock[1].mode == 6) )
{
lock[0].y=lock[1].ybar +lock[0].x/lock[0].waitx;
lock[0].d=0;
if(fabs(lock[0].x) <= 4.0*lock[1].errx)
fstep=0; /*error is small -- no reset*/
else
{
if(fstep <= 0) /*first time it is large*/
/*
initialize fstep to time constant for
frequency updates and set fsteplim to this
same value. This is used below to show that
this is the first round of a reset
*/
fstep = fsteplim= 1/xavgt + 1;
else
fstep--; /*count number of consec. resets*/
} /* end of error is large */
/*
update frequency if error is not too small to be noise
and not too large to be reset or if this is not first
reset so that we are going to assume it is a frequency
step rather than a time step
*/
if( ( (fabs(lock[0].x) >= 0.5*param.xsigma) &&
(fabs(lock[0].x) <= 4.0*lock[1].errx) ) ||
( (fstep > 0) && (fstep < fsteplim) ) )
lock[0].ybar=(lock[1].ybar + xavgt*lock[0].y)/(1+xavgt);
else lock[0].ybar=lock[1].ybar;
/*
errx is the average prediction error over the last
10 cycles and sumerr is simply the integral of the error.
*/
lock[0].errx=0.9*lock[1].errx + 0.1*fabs(lock[0].x);
lock[0].sumerr=lock[1].sumerr + lock[0].x;
iwait=lock[0].waitn=lock[1].waitn;
/*
if the 4th bit of param.flags is set then switch
to mode 7 after 10 rounds. this will synchronize
the system using the hardware tick via the CTS line
rather than the data from ACTS. revert back to
mode 6 if this system doesn't work.
*/
if( ( (param.flags & 8) != 0) && (nloops >= 10) )
{
lock[0].mode=7;
conterr= 0;
}
else lock[0].mode=6;
goto finis;
} /* end of mode 5/6 */
/*
mode 7 is used if the local machine has a direct 1 pps
input via the CTS line on a serial port. It is like mode
6 in that the local clock is continuously adjusted, but
the adjustments use the external tick and are set to whatever
it takes to bring the local clock in coincidence with the
hardware tick.
the periodic acts connections in this situation are used
only to be sure that we have not lost a whole second since
the hardware system cannot detect this.
the frequency and average frequency are simply propagated
across without change in this mode. Parameters errx and
sumerr are handled the same as in mode 6.
if the error is less than 50 msec, then everything is
probably okay. if it is larger than this value, then
something has gone wrong. switch back to mode 6 to try
and see what is happening. Also reset nloops to 0 so
that the program will be forced to stay in mode 6
for at least 10 loops to reacquire the frequency
if it has changed.
*/
if(lock[1].mode == 7)
{
lock[0].y=lock[1].y;
lock[0].ybar=lock[1].ybar;
lock[0].d=0;
lock[0].errx= 0.9*lock[1].errx + 0.1*fabs(lock[0].x);
lock[0].sumerr=lock[1].sumerr + lock[0].x;
iwait=lock[0].waitn= lock[1].waitn;
if(fabs(lock[0].x) < 0.05) lock[0].mode=7;
else
{
nloops=0;
lock[0].mode=6;
}
goto finis; /*finished with mode 7*/
} /* end of lock mode 7*/
finis: /*finished with this round*/
conserr = 0; /*reset consecutive error counter*/
/*
if the nominal delay is longer than 3000 seconds, then
limit the delay to 3000 seconds during the first 10 loops
following a re-start so that the program settles down
pretty quickly. if iwait is already < 3000 then it is
short enough and need not be reduced.
*/
if( (nloops++ < 10) && (iwait > 3000) ) iwait = 3000;
if( (lop=fopen(sname,"r+")) == NULL)
lockerr("Cannot open lock.dat for update in lockclock.");
for(i=0; i< NUMSTATES; i++) putlock(&lop,&lock[i]);
if(fclose(lop) == EOF)
lockerr("Error close lock.dat after update.");
pushlock(lock,0,NUMSTATES); /*push down lock stack by 1*/
/*
if generate new files names has been selected and if this
file is getting full, then copy it to anoter name.
the new name will be the same as the root name as stored
in string sname with the latest MJD added on to the end
to make it unique.
*/
if( (param.flags & 2) != 0) /*generate new files enabled*/
{
if(filefull-- < 8) /*file is almost full*/
{
#if ALPHA == 1
sprintf(filenam,"cp %s %s_%d",sname,sname,lock[0].day);
#else
sprintf(filenam,"cp %s %s_%ld",sname,sname,lock[0].day);
#endif
system(filenam);
pid=wait(status); /*wait for copy to finish*/
filefull = NUMSTATES; /*reset for next round */
}
}
if(argc > 1) /*store time of next update*/
{
i=(fdaym+iwait)/3600; /*compute hr and min. of next cycle*/
j=((fdaym+iwait)%3600)/60;
sprintf(msg,"-> %d:%.2d >%.1f",i,j,1000*param.xsigma);
/*
if a leap second is due today then announce it in the
argument list
*/
if(lsdue >= 10) msg[1]='!';
s=argv[1];
for(i=0; msg[i] != 0; i++) *s++ = msg[i];
*s='\0'; /*add trailing 0 byte */
}
if(lock[0].mode == 6) /*this is continuous adjust mode*/
{
/*
first remove the static time error if it is >= than
the measurement noise estimate. if it is less than
the measurement noise then don't do the correction since
that is just as likely to make things worse.
*/
if(fabs(lock[0].x) >= 0.5*param.xsigma)
{
tcor= -lock[0].x;
jwait= (int)(fabs(tcor)*slew) + 1;
adjt(tcor);
sleep(jwait);
fdaym += jwait;
}
} /* end of static time correction in mode 6*/
/*
make a static adjustment in mode 7 if we have slipped an
entire second.
*/
if( (lock[0].mode == 7) &&
(fabs(lock[0].x) >= 1) )
{
tcor= -lock[0].x;
jwait= (int)(fabs(tcor)*slew) +1;
adjt(tcor);
sleep(jwait);
fdaym += jwait;
}
if( (lock[0].mode == 6) || (lock[0].mode == 7) )
{
/*
if a leap second adjustment is due today and if it is
near the end of the day, then do it now. Since it
takes slew seconds to make the adjustment, if we are
within about slew seconds of the end of the day, then
we might just as well do it now.
if bit 4 of the flag word is set then leap second
adjustments are made by calling David Mills' kernel
routines and no leap second adjustments are ever
made here.
*/
if( ( (param.flags & 16) == 0) && (lsdue >= 10) ) /*if due today */
{
if( (fdaym + slew) > 86200l) /*do it now*/
{
if(lsdue == 10) lptcor= -1; /* ls = 1 */
else lptcor= 1; /* ls = 2 */
adjt(lptcor);
sleep(slew);
fdaym += slew;
lsdue= 0;
} /*end of do correction now*/
} /* end of currection due today*/
} /* end of mode is 6 or 7 */
if(lock[0].mode == 6) /*begin periodic corrections in mode 6*/
{
/*
find out how long we must wait before a correction
of 1 ms will be needed. Then adjust this interval
so that there will be an integral number of them in
the calibration delay and then compute the exact
correction needed each time.
if the offset frequency is very small, then the
correction needed in the full iwait seconds may
be less than 1 ms. Compute the correction needed
in this case and apply it in two steps.
if the offset frequency is very large, then the
delay between 1 ms corrections may be less than
1 sec. Since the maximum slew rate is on the order
of milliseconds/second, the best we can do is to
delay for 1 second and then adjust at the maximum
slew rate.
*/
jwait=(LONG) fabs(0.001/lock[0].ybar);
if(jwait < 1) jwait = 1;
if(jwait > iwait)
{
jwait=iwait/2;
jloop=2;
} /*end of jwait too big */
else /*jwait is not too big */
{
jloop=iwait/jwait; /*number of adjustments per cycle*/
jwait=iwait/jloop; /*actual delay between adjustments*/
}
tcor= -((double)jwait)*lock[0].ybar;
for(i=0; i<jloop; i++)
{
/*
if a leap second is due today then do it when
we get within slew seconds of the end of the
day, since it takes slew seconds to do it. we
will make the correction on the last sleep cycle
before the end of the day.
*/
if( ( (param.flags & 16) == 0) /*leap sec via slew*/
&& (lsdue >= 10) && /*leap second today*/
(fdaym + slew + jwait >=86400l) ) /*next cycle is too late*/
{
if(lsdue == 10) lptcor = -1;
else lptcor = 1;
adjt(lptcor); /*leap second adjustment*/
sleep(slew);
lsdue = 0;
}
adjt(tcor);
sleep(jwait);
fdaym += jwait;
} /*end of periodic adjustment loop */
goto loop; /*go around again */
} /*end of incremental corrections in mode 6*/
if(lock[0].mode == 7) /*incremental adjustments in mode 7*/
{
/*
proceed as with mode 6. find out how long between
corrections assuming that the error is to be no more
than 0.2 msec -- about the jitter in consecutive ACTS
measurements. then schedule a loop with this interval
don't let the loop interval be less than 10 seconds
and don't let there be less than 2 tests per acts call.
The acts calls are really just to be sure that we have
not jumped an entire second.
*/
jwait= (LONG) fabs(0.0002/lock[0].ybar);
if(jwait < 10) jwait = 10;
if(jwait > iwait/2) jwait=iwait/2;
jloop= iwait/jwait; /*number of adjustments per ACTS cycle*/
jwait=iwait/jloop; /*actual delay between adjustments */
for(i=0; i<jloop; i++)
{
if( ( (param.flags & 16) == 0) && /*leap sec via slew*/
(lsdue >= 10) && /*if leap second today */
(fdaym + slew +jwait >= 86400l) )
{
if(lsdue == 10) lptcor = -1;
else lptcor = 1;
adjt(lptcor);
sleep(slew);
lsdue = 0;
}
/*
gettck returns microseconds portion of time when
tick is received. if returned value is negative
then system could not catch the tick. Use the
previous correction in this case and increment
the error flag. if we miss several in a row, then
something may be wrong with the tick system. try
to fix that by going back to mode 6.
*/
for(j=0; j<5; j++)
if( (jadj = gettck() ) > 0) break; /*got it*/
if(jadj < 0) /*5 errors in a row */
{
jadj= ladj;
if(conterr++ > 5)
{
lock[0].mode = 6; /*go back to mode 6*/
nloops= 0; /*may have to remeasure freq*/
goto loop; /*get an acts calibration*/
}
}
else /*no error from gettck */
{
conterr = 0;
ladj=jadj; /*save this one for next time */
}
/*
adjust time towards nearest second. if microsecond
value is < 0.5 second, retard clock, otherwise advance
it to the next second. This algorithm will fail if
the interval between calibrations is long enough that
the clock has drifted by 0.5 second or more, in which
case we will convert this error to a full second.
if the time slew is not too large, then it will
complete in less than jwait seconds, so then wait
jwait seconds and loop again.
if the measured time error is very large so that it
will not complete in jwait seconds, then wait as long
as it will take. This is almost certainly an error of
some kind. Nevertheless, it is important not to clobber
the adjustment loop by going around too quickly.
*/
if(jadj < 500000l) tcor = -jadj;
else tcor= 1000000l - jadj;
tcor *= 1e-6; /*convert usec to sec */
adjt(tcor);
/*
if default loop time is greater than the time needed to
make the adjustment then all is well; set the sleep time
to the default loop time. otherwise use a sleep time of
however long it will take based on the slew parameter
*/
if(jwait >
(kwait=(int)(fabs(tcor)*slew + 1)) ) kwait=jwait;
sleep(kwait); /* wait for next one */
fdaym += kwait;
}
} /* end of correction in mode 7 */
else /*mode is anything other than 6 or 7*/
{
sleep(iwait); /*wait for next round*/
}
goto loop; /*go around again*/
}