home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / x / xntp3.zip / xntpd / ntp_loopfilter < prev    next >
Text File  |  1992-08-29  |  18KB  |  675 lines

  1. /*
  2.  * ntp_loopfilter.c - implements the NTP loop filter algorithm
  3.  */
  4.  
  5. #include <stdio.h>
  6. #include <sys/types.h>
  7. #include <netinet/in.h>
  8.  
  9. #include "ntp_syslog.h"
  10. #include "ntp_fp.h"
  11. #include "ntp.h"
  12.  
  13. #ifdef PPS
  14. #include <sys/file.h>
  15. #include <sys/ioctl.h>
  16. #include <sgtty.h>
  17. #include <sys/time.h>
  18. #include <sys/stat.h>
  19. #include <fcntl.h>
  20.  
  21. #include "refclock.h"
  22. #endif /* PPS */
  23.  
  24. #ifdef STREAM
  25. #include <stropts.h>
  26. #include <sys/clkdefs.h>
  27. #endif /* STREAMS */
  28.  
  29. /*
  30.  * The loop filter is implemented in slavish adherence to the
  31.  * specification (Section 5), except that for consistency we
  32.  * mostly carry the quantities in the same units as appendix G.
  33.  *
  34.  * Note that the long values below are the fractional portion of
  35.  * a long fixed-point value.  This limits these values to +-0.5
  36.  * seconds.  When adjustments are capped inside this range (see
  37.  * CLOCK_MAX_{I,F}) both the clock_adjust and the compliance
  38.  * registers should be fine. (When the compliance is above 16, it
  39.  * will at most accumulate 2**CLOCK_MULT times the maximum offset,
  40.  * which means it fits in a s_fp.)
  41.  *
  42.  * The skew compensation is a special case. In version 2, it was
  43.  * kept in ms/4s (i.e., CLOCK_FREQ was 10). In version 3 (Section 5)
  44.  * it seems to be 2**-16ms/4s in a s_fp for a maximum of +-125ppm
  45.  * (stated maximum 100ppm). Since this seems about to change to a
  46.  * larger range, it will be kept in units of 2**-20 (CLOCK_DSCALE)
  47.  * in an s_fp (mainly because that's nearly the same as parts per
  48.  * million). Note that this is ``seconds per second'', whereas a
  49.  * clock adjustment is a 32-bit fraction of a second to be applied
  50.  * every 2**CLOCK_ADJ seconds; to find it, shift the drift right by
  51.  * (CLOCK_DSCALE-16-CLOCK_ADJ). When updating the drift, on the other
  52.  * hand, the CLOCK_FREQ factor from the spec assumes the value to be
  53.  * in ``seconds per 4 seconds''; to get our units, CLOCK_ADJ must be
  54.  * added to the shift.
  55.  */
  56.  
  57. #define RSH_DRIFT_TO_FRAC (CLOCK_DSCALE - 16)
  58. #define RSH_DRIFT_TO_ADJ (RSH_DRIFT_TO_FRAC - CLOCK_ADJ)
  59. #define RSH_FRAC_TO_FREQ (CLOCK_FREQ + CLOCK_ADJ - RSH_DRIFT_TO_FRAC)
  60.  
  61. l_fp last_offset;    /* last adjustment done */
  62. long clock_adjust;    /* clock adjust register (fraction only) */
  63.  
  64. s_fp drift_comp;    /* drift compensation register */
  65. s_fp max_comp;        /* drift limit imposed by max host clock slew */
  66.  
  67. int time_constant;    /* log2 of time constant (0 .. 4) */
  68. u_long tcadj_time;    /* last time-constant adjust time */
  69.  
  70. u_long watchdog_timer;    /* watchdog timer, in seconds */
  71. int first_adjustment;    /* set to 1 if waiting for first adjustment */
  72. int tc_counter;        /* time-constant hold counter */
  73.  
  74. int pps_control;    /* set to one if pps signal valid */
  75. l_fp pps_fudge;        /* pps tuning offset */
  76. u_long pps_update;    /* last pps update time */
  77.  
  78. #ifdef PPS
  79. /*
  80.  * This module has support for a 1-pps signal to fine-tune the local
  81.  * clock. The signal is optional; when present and operating within
  82.  * given tolerances in frequency and jitter, it is used to discipline
  83.  * the local clock. In order for this to work, the local clock must be
  84.  * set to within +-500 ms by another means, such as a radio clock or
  85.  * NTP itself. The 1-pps signal is connected via a serial port and
  86.  * gadget box consisting of a one-shot and EIA level-converter. When
  87.  * operated at 38.4 kbps with a SPARCstation IPC, this arrangement has a
  88.  * worst-case jitter less than 26 us.
  89. */
  90. #ifdef B38400
  91. #define PPS_BAUD B38400        /* serial port speed */
  92. #else
  93. #define PPS_BAUD EXTB
  94. #endif
  95. #define PPS_MAXAGE 120        /* seconds after which we disbelieve pps */
  96. #define PPS_MAXUPDATE 600    /* seconds after which we disbelieve timecode */
  97. #define    PPS_DEV    "/dev/pps"    /* pps port */
  98. #define PPS_FAC 5        /* pps shift (log2 trimmed samples) */
  99. #define    NPPS 42            /* pps filter size (1<<PPS_FAC+2*PPS_TRIM) */
  100. #define PPS_TRIM 5        /* samples trimmed from median filter */
  101. #define    PPS_DELAY 0x00103000    /* uart character time (about 247 us) */ 
  102. #define PPS_XCPT "\377"        /* intercept character */
  103.  
  104. struct refclockio io;        /* given to the I/O handler */
  105. l_fp pps_offset;        /* filtered pps offset */
  106. u_long pps_time;        /* last pps sample time */
  107. u_long nsamples;        /* number of pps samples collected */
  108. long samples[NPPS];        /* median filter for pps samples */
  109. void pps_sample();
  110. /*
  111.  * Imported from ntp_io.c
  112.  */
  113. extern struct interface *loopback_interface;
  114.  
  115. #endif /* PPS */
  116.  
  117. /*
  118.  * Debug flag importation
  119.  */
  120. extern int debug;
  121.  
  122. /*
  123.  * Imported from timer module
  124.  */
  125. extern u_long current_time;    /* like it says, in seconds */
  126.  
  127. /*
  128.  * sys_poll and sys_refskew are set here
  129.  */
  130. extern int sys_poll;        /* log2 of system poll interval */
  131. extern l_fp sys_refskew;    /* accumulated skew since last update */
  132. extern u_fp sys_maxd;        /* max dispersion of survivor list */
  133.  
  134. /*
  135.  * init_loopfilter - initialize loop filter data
  136.  */
  137. void
  138. init_loopfilter()
  139. {
  140.     extern unsigned long tsf_maxslew;
  141.     unsigned long tsf_limit;
  142. #if defined(PPS) && defined(PPSDEV)
  143.     int fdpps;
  144. #if !defined(HPUXGADGET)
  145.     struct sgttyb ttyb;
  146. #endif
  147.     void pps_receive();
  148. #if defined(HPUXGADGET)
  149.     extern int io_addclock_simple();
  150. #else
  151.     extern int io_addclock();
  152. #endif
  153. #endif /* PPS && PPSDEV */
  154.  
  155.     clock_adjust = 0;
  156.     drift_comp = 0;
  157.     time_constant = 0;
  158.     tcadj_time = 0;
  159.     sys_poll = NTP_MINPOLL;
  160.     watchdog_timer = 0;
  161.     tc_counter = 0;
  162.     last_offset.l_i = 0;
  163.     last_offset.l_f = 0;
  164.     first_adjustment = 1;
  165.  
  166. /*
  167.  * Limit for drift_comp, minimum of two values. The first is to avoid
  168.  * signed overflow, the second to keep within 75% of the maximum
  169.  * adjustment possible in adj_systime().
  170.  */
  171.     max_comp = 0x7fff0000;
  172.     tsf_limit = ((tsf_maxslew >> 1) + (tsf_maxslew >> 2));
  173.     if ((max_comp >> RSH_DRIFT_TO_ADJ) > tsf_limit)
  174.         max_comp = tsf_limit << RSH_DRIFT_TO_ADJ;
  175.  
  176.     pps_control = 0;
  177. #if defined(PPS) && defined(PPSDEV)
  178.     pps_fudge.l_i = 0;
  179.     pps_fudge.l_f = PPS_DELAY;
  180.     pps_time = pps_update = 0;
  181.     nsamples = 0;
  182.  
  183.     /*
  184.      * Open pps serial port, set for exclusive use, set line speed
  185.      * and raw mode. We don't really care if the pps device comes
  186.      * up; if not, we  just use the timecode. Therefore, if anything
  187.      * goes wrong, just reclaim the resources and continue.
  188.      */
  189.     fdpps = open(PPS_DEV, O_RDONLY);
  190.     if (fdpps == -1) {
  191.         syslog(LOG_ERR,
  192.             "init_loopfilter: open %s fails", PPS_DEV);
  193.         return;
  194.     }
  195. #if !defined(HPUXGADGET)
  196.         if (ioctl(fdpps, TIOCEXCL, (char *)0) < 0) {
  197.                 syslog(LOG_ERR,
  198.             "init_loopfilter: ioctl(%s, TIOCEXCL) fails", PPS_DEV);
  199.         (void) close(fdpps);
  200.         return;
  201.     }
  202.         ttyb.sg_ispeed = ttyb.sg_ospeed = PPS_BAUD;
  203.     ttyb.sg_erase = ttyb.sg_kill = 0;
  204.         ttyb.sg_flags = RAW;
  205.         if (ioctl(fdpps, TIOCSETP, (char *)&ttyb) < 0) {
  206.                 syslog(LOG_ERR,
  207.             "init_loopfilter: ioctl(%s, TIOCSETP) fails", PPS_DEV);
  208.         (void) close(fdpps);
  209.         return;
  210.     }
  211. #endif
  212.  
  213. #ifdef STREAM
  214.     /*
  215.      * Pop off existing streams modules and push on clk module
  216.      */
  217.     while (ioctl(fdpps, I_POP, 0 ) >= 0) ;
  218.     if (ioctl(fdpps, I_PUSH, "clk" ) < 0) {
  219.         syslog(LOG_ERR,
  220.             "init_loopfilter: ioctl(%s, I_PUSH) fails", PPS_DEV);
  221.         (void) close(fdpps);
  222.         return;
  223.     }
  224.     /*
  225.      * Tickle the tty_clk streams module to timestamp the intercept
  226.      * character.
  227.      */
  228.     if (ioctl(fdpps, CLK_SETSTR, PPS_XCPT) < 0) {
  229.                 syslog(LOG_ERR,
  230.                     "init_loopfilter: ioctl(%s, CLK_SETSTR) fails", PPS_DEV);
  231.                 (void) close(fdpps);
  232.                 return;
  233.     }
  234. #else
  235. /* Line-discipline folks invited to hack here */
  236. #endif /* STREAM */
  237.  
  238.     /*
  239.      * Insert in device list.
  240.      */
  241.     io.clock_recv = pps_receive;
  242.     io.srcclock = (caddr_t)NULL;
  243.     io.datalen = 0;
  244.     io.fd = fdpps;
  245. #if defined(HPUXGADGET)
  246.     if (!io_addclock_simple(&io)) {
  247. #else
  248.     if (!io_addclock(&io)) {
  249. #endif
  250.         syslog(LOG_ERR, "init_loopfilter: addclock %s fails", PPS_DEV);
  251.         (void) close(fdpps);
  252.         return;
  253.     }
  254. #endif /* PPS && PPSDEV */
  255. }
  256.  
  257. /*
  258.  * local_clock - the NTP logical clock loop filter.  Returns 1 if the
  259.  *         clock was stepped, 0 if it was slewed and -1 if it is
  260.  *         hopeless.
  261.  */
  262. int
  263. local_clock(fp_offset, from)
  264.     l_fp *fp_offset;        /* best offset estimate */
  265.     struct sockaddr_in *from;    /* who offset is from, for messages */
  266. {
  267.     register long offset;
  268.     register u_long dispersion;
  269.     register u_long tmp_ui;
  270.     register u_long tmp_uf;
  271.     register long tmp;
  272.     int isneg;
  273.     extern void step_systime();
  274.     extern char *ntoa();
  275.     extern char *lfptoa();
  276.     extern char *mfptoa();
  277.  
  278. #ifdef DEBUG
  279.     if (debug > 1)
  280.         printf("local_clock(%s, %s)\n", lfptoa(fp_offset, 9),
  281.             ntoa(from));
  282. #endif
  283.  
  284.     /*
  285.      * Take the absolute value of the offset
  286.      */
  287.     tmp_ui = fp_offset->l_ui;
  288.     tmp_uf = fp_offset->l_uf;
  289.     if (M_ISNEG(tmp_ui, tmp_uf)) {
  290.         M_NEG(tmp_ui, tmp_uf);
  291.         isneg = 1;
  292.     } else
  293.         isneg = 0;
  294.  
  295.     /*
  296.      * If the clock is way off, don't tempt fate by correcting it.
  297.      */
  298.     if (tmp_ui >= CLOCK_WAYTOOBIG) {
  299.         syslog(LOG_ERR,
  300.            "Clock appears to be %u seconds %s, something may be wrong",
  301.             tmp_ui, isneg>0?"fast":"slow");
  302. #ifndef BIGTIMESTEP
  303.         return (-1);
  304. #endif BIGTIMESTEP
  305.     }
  306.  
  307.     /*
  308.      * Save this offset for later perusal
  309.      */
  310.     last_offset = *fp_offset;
  311.  
  312.     /*
  313.      * If the magnitude of the offset is greater than CLOCK.MAX, step
  314.      * the time and reset the registers.
  315.      */
  316.     if (tmp_ui > CLOCK_MAX_I || (tmp_ui == CLOCK_MAX_I
  317.         && (u_long)tmp_uf >= (u_long)CLOCK_MAX_F)) {
  318.         if (watchdog_timer < CLOCK_MINSTEP) {
  319.             /* Mustn't step yet, pretend we adjusted. */
  320.             syslog(LOG_INFO,
  321.                    "adjust: STEP dropped (%s offset %s)\n",
  322.                    ntoa(from), lfptoa(fp_offset, 9));
  323.             return (0);
  324.         }
  325.         syslog(LOG_INFO, "adjust: STEP %s offset %s\n",
  326.             ntoa(from), lfptoa(fp_offset, 9));
  327.         step_systime(fp_offset);
  328.  
  329.         clock_adjust = 0;
  330.         watchdog_timer = 0;
  331.         first_adjustment = 1;
  332.         pps_update = 0;
  333.         return (1);
  334.     }
  335.  
  336.     /*
  337.      * Here we've got an offset small enough to slew.  Note that
  338.      * since the offset is small we don't have to carry the damned
  339.      * high order longword in our calculations.
  340.      *
  341.      * The time constant and sample interval are approximated with
  342.      * shifts, as in Section 5 of the v3 spec. The spec procedure
  343.      * looks strange, as an interval of 64 to 127 seconds seems to
  344.      * cause multiplication with 128 (and so on). This code lowers
  345.      * the multiplier by one bit.
  346.      *
  347.      * The time constant update goes after adjust and skew updates,
  348.      * as in appendix G.
  349.      */
  350. #ifdef PPS
  351.     /*
  352.      * If pps samples are valid, update offset, root delay and
  353.      * root dispersion. This may be a dramatic surprise to high-
  354.      * stratum clients, since all of a sudden this server looks
  355.      * like a stratum-1 clock.
  356.      */
  357.     if (pps_control)
  358.         last_offset = pps_offset;
  359. #endif
  360.     offset = last_offset.l_f;
  361.     clock_adjust = offset >> time_constant;
  362.  
  363.     /*
  364.      * Calculate the new frequency error. The factor given in the
  365.      * spec gives the adjustment per 2**CLOCK_ADJ seconds, but we
  366.      * want it as a (scaled) pure ratio, so we include that factor
  367.      * now and remove it later.
  368.      */
  369.     if (first_adjustment) {
  370.         first_adjustment = 0;
  371.     } else {
  372.         /*
  373.          * Clamp the integration interval to NTP_MAXPOLL.
  374.          * The bitcounting in Section 5 gives (n+1)-6 for 2**n,
  375.          * but has a factor 2**6 missing from CLOCK_FREQ.
  376.          * We make 2**n give n instead. If watchdog_timer is zero,
  377.          * pretend it's one.
  378.          */
  379.         tmp = NTP_MAXPOLL;
  380.         tmp_uf = watchdog_timer;
  381.         if (tmp_uf == 0)
  382.             tmp_uf = 1;
  383.         while (tmp_uf < (1<<NTP_MAXPOLL)) {
  384.             tmp--;
  385.             tmp_uf <<= 1;
  386.         }
  387.         /*
  388.          * We apply the frequency scaling at the same time as
  389.          * the sample interval; this ensures a safe right-shift.
  390.          * (as long as it keeps below 31 bits, which current
  391.          *  parameters should ensure.
  392.          */
  393.         tmp = (RSH_FRAC_TO_FREQ - tmp) + time_constant + time_constant;
  394.         if (offset < 0)
  395.             tmp = -((-offset) >> tmp);
  396.         else
  397.             tmp = offset >> tmp;
  398. #ifdef DEBUG
  399.         if (debug > 2)
  400.             printf("watchdog %u, gain %u, change %s\n",
  401.                 watchdog_timer, 1<<time_constant,
  402.                 fptoa(tmp, 5));
  403. #endif
  404.         drift_comp += tmp;
  405.         /*
  406.          * Check that the result is in the possible interval
  407.          */
  408.         if (drift_comp >= max_comp || drift_comp <= -max_comp) {
  409.             syslog(LOG_ERR,
  410.                    "Drift exceeds %s%sppm, cannot cope",
  411.                    drift_comp>0?"+":"-", fptoa(max_comp, 0));
  412.             exit(1);
  413.         }
  414.     }
  415.     watchdog_timer = 0;
  416.  
  417.     /*
  418.      * Determine when to adjust the time constant.
  419.      */
  420.     if (current_time - tcadj_time >= (1 << sys_poll)) {
  421.         tmp = offset;
  422.         if (tmp < 0) tmp = -tmp;
  423.         tmp = tmp >> (16 + CLOCK_WEIGHTTC - time_constant);
  424.         tcadj_time = current_time;
  425.         if (tmp > sys_maxd) {
  426.             tc_counter = 0;
  427.             if (time_constant > 0) time_constant--;
  428.         }
  429.         else {
  430.             tc_counter++;
  431.             if (tc_counter > CLOCK_HOLDTC) {
  432.                 tc_counter = 0;
  433.                 if (time_constant < CLOCK_MAXTC)
  434.                     time_constant++;
  435.             }
  436.         }
  437.     }
  438.     sys_poll = NTP_MINPOLL + time_constant;
  439.  
  440. #ifdef DEBUG
  441.     if (debug > 1)
  442.         printf("adj %s, drft %s, tau %3i\n",
  443.             mfptoa((clock_adjust<0?-1:0), clock_adjust, 9),
  444.             fptoa(drift_comp, 9), time_constant);
  445. #endif
  446.  
  447.     (void) record_loop_stats(&last_offset, &drift_comp, time_constant);
  448.     
  449.     /*
  450.      * Whew.  I've had enough.
  451.      */
  452.     return (0);
  453. }
  454.  
  455.  
  456. /*
  457.  * adj_host_clock - Called every 2**CLOCK_ADJ seconds to update host clock
  458.  */
  459. void
  460. adj_host_clock()
  461. {
  462.     register long adjustment;
  463.     extern void adj_systime();
  464.  
  465. #ifdef PPS
  466.     if (pps_time != 0 && current_time - pps_time > PPS_MAXAGE)
  467.         pps_time = 0;
  468.     if (pps_update != 0 && current_time - pps_update > PPS_MAXUPDATE)
  469.         pps_update = 0;
  470.     if (pps_time != 0 && pps_update != 0) {
  471.         if (!pps_control)
  472.             syslog(LOG_INFO, "pps synch");
  473.         pps_control = 1;
  474.     } else {
  475.         if (pps_control)
  476.             syslog(LOG_INFO, "pps synch lost");
  477.         pps_control = 0;
  478.     }
  479. #endif
  480.     if (sys_refskew.l_i >= NTP_MAXSKEW)
  481.         sys_refskew.l_f = 0;    /* clamp it */
  482.     else
  483.         L_ADDUF(&sys_refskew, NTP_SKEWINC);
  484.     adjustment = clock_adjust;
  485.     if (adjustment < 0)
  486.         adjustment = -((-adjustment) >> CLOCK_PHASE);
  487.     else
  488.         adjustment >>= CLOCK_PHASE;
  489.  
  490.     clock_adjust -= adjustment;
  491.     if (drift_comp < 0)
  492.         adjustment -= ((-drift_comp) >> RSH_DRIFT_TO_ADJ);
  493.     else
  494.         adjustment += drift_comp >> RSH_DRIFT_TO_ADJ;
  495.     adj_systime(adjustment);
  496.  
  497.     watchdog_timer += (1<<CLOCK_ADJ);
  498.     if (watchdog_timer >= NTP_MAXAGE) {
  499.         first_adjustment = 1;    /* don't use next offset for freq */
  500.     }
  501. }
  502.  
  503.  
  504. /*
  505.  * loop_config - configure the loop filter
  506.  */
  507. void
  508. loop_config(item, value)
  509.     int item;
  510.     l_fp *value;
  511. {
  512.     s_fp tmp;
  513.  
  514.     switch (item) {
  515.     case LOOP_DRIFTCOMP:
  516.         tmp = LFPTOFP(value);
  517.         if (tmp >= max_comp || tmp <= -max_comp) {
  518.             syslog(LOG_ERR,
  519.                 "loop_config: skew compensation %s too large",
  520.                 fptoa(tmp, 5));
  521.         } else {
  522.             drift_comp = tmp;
  523.         }
  524.         break;
  525.     
  526.     case LOOP_PPSDELAY:
  527.         pps_fudge = *value;
  528.         break;
  529.  
  530.     default:
  531.         /* sigh */
  532.         break;
  533.     }
  534. }
  535.  
  536. #if defined(PPS) && defined(PPSDEV)
  537.  
  538. /*
  539.  * pps_receive - compute and store 1-pps signal offset
  540.  * 
  541.  * This routine is called once per second when the 1-pps signal is 
  542.  * present. It calculates the offset of the local clock relative to the
  543.  * 1-pps signal and saves in a circular buffer for later use.
  544.  */
  545. void pps_receive(rbufp)
  546.     struct recvbuf *rbufp;
  547. {
  548.     u_char *dpt;        /* buffer pointer */
  549.     u_char ctmp;        /* intercept char */
  550.     l_fp ts;            /* l_fp temps */
  551.  
  552.     /*
  553.      * Set up pointers, check the buffer length, discard intercept
  554.      * character and convert unix timeval to timestamp format.
  555.      */
  556.     dpt = (u_char *)&rbufp->recv_space;
  557. #if defined(HPUXGADGET)
  558.     if (rbufp->recv_length != sizeof(struct timeval)) {
  559. #else
  560.         ctmp = *dpt++;
  561.     if (rbufp->recv_length != sizeof(struct timeval) + 1) {
  562. #endif
  563. #ifdef DEBUG
  564.         if (debug)
  565.             printf("pps_receive: bad length %d %c\n",
  566.             rbufp->recv_length, ctmp);
  567. #endif
  568.         return;
  569.     }
  570.     if (!buftvtots(dpt, &ts)) {
  571. #ifdef DEBUG
  572.         if (debug)
  573.             printf("pps_receive: buftvtots failed\n");
  574. #endif
  575.         return;
  576.     }
  577.         /*
  578.      * Correct for uart delay and process sample offset.
  579.      */
  580.     L_SUB(&ts, &pps_fudge);
  581.     pps_sample(&ts);
  582. }    
  583. #endif
  584.  
  585. #ifdef PPS
  586. /*
  587.  * pps_sample - process pps sample offset
  588.  */
  589. void pps_sample(tsr)
  590.     l_fp *tsr;
  591. {
  592.     int i, j;        /* temp ints */
  593.     long sort[NPPS];    /* temp array for sorting */
  594.     l_fp lftemp, ts;    /* l_fp temps */
  595.     long ltemp;        /* long temp */
  596.     extern void record_stats();
  597.     extern u_short ctlsysstatus();
  598.     extern u_char sys_stratum;
  599.     extern s_fp sys_offset;
  600.     extern s_fp sys_rootdelay;
  601.     extern u_fp sys_rootdispersion;
  602.       
  603.     /*
  604.      * Note the seconds offset is already in the low-order timestamp
  605.      * doubleword, so all we have to do is sign-extend and invert it.
  606.      * The resulting offset is believed only if within CLOCK_MAX.
  607.      */
  608.     pps_time = current_time;
  609.     ts = *tsr;
  610.     lftemp.l_i = lftemp.l_f = 0;
  611.     M_ADDF(lftemp.l_i, lftemp.l_f, ts.l_f);
  612.     L_NEG(&lftemp);
  613.     if (ts.l_f <= -CLOCK_MAX_F || ts.l_f >= CLOCK_MAX_F)
  614.         return;
  615.  
  616.     /*
  617.      * Save the sample in a circular buffer for later processing.
  618.      */
  619.     nsamples++;
  620.     i = ((int)(nsamples)) % NPPS;
  621.     samples[i] = lftemp.l_f;
  622.     if (i != NPPS-1)
  623.         return;
  624.  
  625.     /*
  626.      * When the buffer fills up, construct an array of sorted
  627.      * samples.
  628.      */
  629.     for (i = 0; i < NPPS; i++) {
  630.         sort[i] = samples[i];
  631.         for (j = 0; j < i; j++) {
  632.             if (sort[j] > sort[i]) {
  633.                 ltemp = sort[j];
  634.                 sort[j] = sort[i];
  635.                 sort[i] = ltemp;
  636.             }
  637.         }
  638.     }
  639.  
  640.     /*
  641.      * Compute offset as the average of all samples in the filter
  642.      * less PPS_TRIM samples trimmed from the beginning and end,
  643.      * dispersion as the difference between max and min of samples
  644.      * retained. The system stratum, root delay and root dispersion
  645.      * are also set here.
  646.      */
  647.     pps_offset.l_i = pps_offset.l_f = 0;
  648.     for (i = PPS_TRIM; i < NPPS - PPS_TRIM; i++)
  649.         M_ADDF(pps_offset.l_i, pps_offset.l_f, sort[i]);
  650.     if (L_ISNEG(&pps_offset)) {
  651.         L_NEG(&pps_offset);
  652.         for (i = 0; i < PPS_FAC; i++)
  653.             L_RSHIFT(&pps_offset);
  654.         L_NEG(&pps_offset);
  655.     } else {
  656.         for (i = 0; i < PPS_FAC; i++)
  657.             L_RSHIFT(&pps_offset);
  658.     }
  659.     sys_stratum = 1;
  660.     sys_rootdelay = 0;
  661.     lftemp.l_i = 0;
  662.     lftemp.l_f = sort[NPPS-1-PPS_TRIM] - sort[PPS_TRIM];
  663.     sys_maxd = LFPTOFP(&lftemp);
  664.     sys_rootdispersion = sys_maxd;
  665. #ifdef DEBUG
  666.     if (debug)
  667.         printf("pps_filter: %s %s %s\n", lfptoa(&pps_fudge, 6),
  668.         lfptoa(&pps_offset, 6), lfptoa(&lftemp, 5));
  669. #endif
  670.     record_stats(loopback_interface, ctlsysstatus(), &pps_offset,
  671.         sys_rootdelay, sys_rootdispersion);
  672.     return;
  673. }
  674. #endif /* PPS */
  675.