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 / refclock_wwvb. < prev    next >
Text File  |  1992-08-29  |  25KB  |  916 lines

  1. /*
  2.  * refclock_wwvb - clock driver for the Spectracom WWVB receiver
  3.  */
  4. #include <stdio.h>
  5. #include <ctype.h>
  6. #include <sys/types.h>
  7. #include <sys/socket.h>
  8. #include <netinet/in.h>
  9. #include <sys/time.h>
  10. #include <sys/file.h>
  11. #include <sys/ioctl.h>
  12. #if defined(HPUX)
  13. #include <termio.h>
  14. #else
  15. #include <sgtty.h>
  16. #endif
  17.  
  18. #include "ntp_syslog.h"
  19. #include "ntp_fp.h"
  20. #include "ntp.h"
  21. #include "ntp_refclock.h"
  22. #include "ntp_unixtime.h"
  23.  
  24. #if defined(REFCLOCK) && defined(WWVB)
  25. /*
  26.  * This driver supports the Spectracom Model 8170 and Netclock/2 WWVB
  27.  * Synchronized Clock under Unix and on a Gizmo board. There are two
  28.  * formats used by these clocks. Format 0 (zero), which is available
  29.  * with both the Netclock/2 and 8170, is in the following
  30.  * format:
  31.  *
  32.  * <cr><lf>I<sp><sp>ddd<sp>hh:mm:ss<sp><sp>TZ=nn<cr><lf>
  33.  *
  34.  * The ddd, hh, mm and ss fields show the day of year, hours, minutes
  35.  * and seconds, respectively. The nn field shows the local hour offset
  36.  * relative to UTC and should always be set to 00. The I is normally
  37.  * <sp> when the clock is synchronized and '?' when it isn't (it could
  38.  * also be a '*' if we set the time manually, but this is forbidden.
  39.  *
  40.  * Format 2 (two), which is available only with the Netclock/2 and
  41.  * specially modified 8170, is in the following format:
  42.  *
  43.  * <cr><lf>IQyy<sp>ddd<sp>hh:mm:ss.mmm<sp>LD
  44.  *
  45.  * The ddd, hh and ss fields and I are as in format 0. The yy field
  46.  * shows the year and mmm the milliseconds, respectively. The Q is
  47.  * normally <sp> when the time error is less than 1 ms and and a
  48.  * character in the set 'A'...'D' when the time error is less than 10,
  49.  * 100, 500 and greater than 500 ms respectively. The L is normally
  50.  * <sp>, but is set to 'L' early in the month of an upcoming UTC
  51.  * leap second and reset to <sp> on the first day of the following
  52.  * month. The D is set to 'S' for standard time 'I' on the day
  53.  * preceding a switch to daylight time, 'D' for daylight time and 'O'
  54.  * on the day preceding a switch to standard time. The start bit of the
  55.  * first <cr> is supposed to be synchronized to the on-time second.
  56.  *
  57.  * This driver does not need to be told which format is in use - it
  58.  * figures out which one from the length of the message. It also
  59.  * corrects for the UART delay by measuring the delay between the <cr>
  60.  * and <lf> characters. A three-stage median filter is used to reduce
  61.  * jitter and provide a dispersion measure. The driver makes no attempt
  62.  * to correct for the intrinsic jitter of the radio itself, which is a
  63.  * known problem with the older 8170 radios. When necessary, jitter
  64.  * correction can be provided by a 1-pps signal using facilities built
  65.  * into the ntp_loopfilter module.
  66.  *
  67.  * Bugs:
  68.  * The year indication so carefully provided in format 2 is not used.
  69.  */
  70.  
  71. /*
  72.  * Definitions
  73.  */
  74. #define    MAXUNITS    4    /* max number of WWVB units */
  75. #ifdef GIZMO
  76. #define    WWVB232    "/dev/tty%d"    /* %d is the unit number */
  77. #else
  78. #define    WWVB232    "/dev/wwvb%d"
  79. #define    SPEED232    B9600    /* 9600 baud */
  80. #endif
  81.  
  82. /*
  83.  * Radio interface parameters
  84.  */
  85. #define    WWVBMAXDISPERSE    (FP_SECOND>>1) /* max error for synchronized clock (0.5 s as an u_fp) */
  86. #define    WWVBSKEWFACTOR    17    /* skew factor (for about 32 ppm) */
  87. #define    WWVBPRECISION    (-13)    /* precision assumed (about 100 us) */
  88. #define    WWVBREFID    "WWVB"    /* reference id */
  89. #define    WWVBDESCRIPTION    "Spectracom WWVB Receiver" /* who we are */
  90. #define    WWVBHSREFID    0x7f7f040a /* 127.127.4.10 refid hi strata */
  91. #define GMT        0    /* hour offset from Greenwich */
  92. #define    NCODES        3    /* stages of median filter */
  93. #define    LENWWVB0    22    /* format 0 timecode length */
  94. #define    LENWWVB2    24    /* format 2 timecode length */
  95. #define FMTWWVBU    0    /* unknown format timecode id */
  96. #define FMTWWVB0    1    /* format 0 timecode id */
  97. #define FMTWWVB2    2    /* format 2 timecode id */
  98. #define    DEFFUDGETIME    0    /* default fudge time (ms) */
  99. #define BMAX        50    /* timecode buffer length */
  100.  
  101. /*
  102.  * Hack to avoid excercising the multiplier.  I have no pride.
  103.  */
  104. #define    MULBY10(x)    (((x)<<3) + ((x)<<1))
  105.  
  106. #define    CODEDIFF    0x20000000    /* 0.125 seconds as an l_fp fraction */
  107.  
  108. /*
  109.  * Time conversion tables imported from the library
  110.  */
  111. extern u_long msutotsflo[];
  112. extern u_long msutotsfhi[];
  113. extern u_long ustotslo[];
  114. extern u_long ustotsmid[];
  115. extern u_long ustotshi[];
  116.  
  117. /*
  118.  * Imported from the timer module
  119.  */
  120. extern u_long current_time;
  121. extern struct event timerqueue[];
  122. /*
  123.  * Debug flag
  124.  */
  125. extern int debug;
  126.  
  127. /*
  128.  * WWVB unit control structure.
  129.  */
  130. struct wwvbunit {
  131.     struct peer *peer;        /* associated peer structure */
  132.     struct refclockio io;        /* given to the I/O handler */
  133.     l_fp lastrec;            /* last receive time */
  134.     l_fp lastref;            /* last timecode time */
  135.     l_fp offset[NCODES];        /* recent sample offsets */
  136.     char lastcode[BMAX];        /* last timecode received */
  137.     u_char format;            /* timecode format */
  138.     u_char tcswitch;        /* timecode switch */
  139.     u_char lencode;            /* length of last timecode */
  140.     u_long lasttime;        /* last time clock heard from */
  141.     u_char unit;            /* unit number for this guy */
  142.     u_char status;            /* clock status */
  143.     u_char lastevent;        /* last clock event */
  144.     u_char reason;            /* reason for last abort */
  145.     u_char year;            /* year of eternity */
  146.     u_short day;            /* day of year */
  147.     u_char hour;            /* hour of day */
  148.     u_char minute;            /* minute of hour */
  149.     u_char second;            /* seconds of minute */
  150.     u_char leap;            /* leap indicators */
  151.     u_short msec;            /* millisecond of second */
  152.     u_char quality;            /* quality char from format 2 */
  153.     u_long yearstart;        /* start of current year */
  154.     /*
  155.      * Status tallies
  156.       */
  157.     u_long polls;            /* polls sent */
  158.     u_long noreply;            /* no replies to polls */
  159.     u_long coderecv;        /* timecodes received */
  160.     u_long badformat;        /* bad format */
  161.     u_long baddata;            /* bad data */
  162.     u_long timestarted;        /* time we started this */
  163. };
  164.  
  165. /*
  166.  * Data space for the unit structures.  Note that we allocate these on
  167.  * the fly, but never give them back.
  168.  */
  169. static struct wwvbunit *wwvbunits[MAXUNITS];
  170. static u_char unitinuse[MAXUNITS];
  171.  
  172. /*
  173.  * Keep the fudge factors separately so they can be set even
  174.  * when no clock is configured.
  175.  */
  176. static l_fp fudgefactor[MAXUNITS];
  177. static u_char stratumtouse[MAXUNITS];
  178. static u_char sloppyclockflag[MAXUNITS];
  179.  
  180. /*
  181.  * wwvb_init - initialize internal wwvb driver data
  182.  */
  183. void
  184. wwvb_init()
  185. {
  186.     register int i;
  187.     /*
  188.      * Just zero the data arrays
  189.      */
  190.     bzero((char *)wwvbunits, sizeof wwvbunits);
  191.     bzero((char *)unitinuse, sizeof unitinuse);
  192.  
  193.     /*
  194.      * Initialize fudge factors to default.
  195.      */
  196.     for (i = 0; i < MAXUNITS; i++) {
  197.         fudgefactor[i].l_ui = 0;
  198.         fudgefactor[i].l_uf = DEFFUDGETIME;
  199.         stratumtouse[i] = 0;
  200.         sloppyclockflag[i] = 0;
  201.     }
  202. }
  203.  
  204.  
  205. /*
  206.  * wwvb_start - open the WWVB devices and initialize data for processing
  207.  */
  208. int
  209. wwvb_start(unit, peer)
  210.     u_int unit;
  211.     struct peer *peer;
  212. {
  213.     register struct wwvbunit *wwvb;
  214.     register int i;
  215.     int fd232;
  216.     char wwvbdev[20];
  217.     unsigned int ldisc;
  218. #if defined(HPUX)
  219.     struct termio ttyb;
  220. #else
  221.     struct sgttyb ttyb;
  222. #endif
  223.     void wwvb_receive();
  224.     extern int io_addclock();
  225.     extern void io_closeclock();
  226.     extern char *emalloc();
  227.  
  228.     /*
  229.      * Check configuration info.
  230.      */
  231.     if (unit >= MAXUNITS) {
  232.         syslog(LOG_ERR,
  233.             "wwvb clock: unit number %d invalid (max 3)", unit);
  234.         return 0;
  235.     }
  236.     if (unitinuse[unit]) {
  237.         syslog(LOG_ERR,
  238.             "wwvb clock: unit number %d in use", unit);
  239.         return 0;
  240.     }
  241.  
  242.     /*
  243.      * Open serial port.
  244.      */
  245.     (void) sprintf(wwvbdev, WWVB232, unit);
  246.     fd232 = open(wwvbdev, O_RDWR, 0777);
  247.     if (fd232 == -1) {
  248.         syslog(LOG_ERR,
  249.             "wwvb clock: open of %s failed: %m", wwvbdev);
  250.         return 0;
  251.     }
  252.  
  253. #if defined(HPUX)
  254.     /*
  255.      * Don't have exclusive use, but we can do cooked mode and baud rate.
  256.      */
  257.     if (ioctl(fd232, TCGETA, (char *)&ttyb) < 0) {
  258.                 syslog(LOG_ERR, "wwvb clock: ioctl(%s, TCGETA): %m", wwvbdev);
  259.                 return 0;
  260.         }
  261.         ttyb.c_iflag = (IGNBRK|ICRNL);
  262.         ttyb.c_oflag = (OPOST|ONLCR);
  263.         ttyb.c_cflag = (SPEED232|CS8|CLOCAL|CREAD|CSTOPB);
  264.         ttyb.c_lflag = ICANON;
  265.     ttyb.c_cc[VEOF] = '\0';
  266.     ttyb.c_cc[VERASE] = '\0';
  267.     ttyb.c_cc[VKILL] = '\0';
  268.         if (ioctl(fd232, TCSETA, (char *)&ttyb) < 0) {
  269.                 syslog(LOG_ERR, "wwvb clock: ioctl(%s, TCSETA): %m", wwvbdev);
  270.                 return 0;
  271.         }
  272. #else
  273.     /*
  274.      * Set for exclusive use, cooked mode and baud rate.
  275.      */
  276.     if (ioctl(fd232, TIOCEXCL, (char *)0) < 0) {
  277.         syslog(LOG_ERR,
  278.             "wwvb clock: ioctl(%s, TIOCEXCL): %m", wwvbdev);
  279.         goto screwed;
  280.     }
  281.     ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
  282.     ttyb.sg_flags = EVENP|ODDP|CRMOD|NOHANG;
  283.     if (ioctl(fd232, TIOCSETP, (char *)&ttyb) < 0) {
  284.         syslog(LOG_ERR,
  285.             "wwvb clock: ioctl(%s, TIOCSETP): %m", wwvbdev);
  286.         goto screwed;
  287.     }
  288. #endif
  289.  
  290.     /*
  291.      * Looks like this might succeed.  Find memory for the
  292.      * structure. Look to see if there are any unused ones, if not
  293.      * we malloc() one.
  294.      */
  295.     if (wwvbunits[unit] != 0) {
  296.         wwvb = wwvbunits[unit];    /* The one we want is okay */
  297.     } else {
  298.         for (i = 0; i < MAXUNITS; i++) {
  299.             if (!unitinuse[i] && wwvbunits[i] != 0)
  300.                 break;
  301.         }
  302.         if (i < MAXUNITS) {
  303.             /*
  304.              * Reclaim this one
  305.              */
  306.             wwvb = wwvbunits[i];
  307.             wwvbunits[i] = 0;
  308.         } else {
  309.             wwvb = (struct wwvbunit *)
  310.                 emalloc(sizeof(struct wwvbunit));
  311.         }
  312.     }
  313.     bzero((char *)wwvb, sizeof(struct wwvbunit));
  314.     wwvbunits[unit] = wwvb;
  315.  
  316.     /*
  317.      * Set up the structures
  318.      */
  319.     wwvb->peer = peer;
  320.     wwvb->unit = (u_char)unit;
  321.     wwvb->timestarted = current_time;
  322.     wwvb->tcswitch = 0;
  323.  
  324.     wwvb->io.clock_recv = wwvb_receive;
  325.     wwvb->io.srcclock = (caddr_t)wwvb;
  326.     wwvb->io.datalen = 0;
  327.     wwvb->io.fd = fd232;
  328.  
  329.     /*
  330.      * Flush buffers and link in I/O list.
  331.      */
  332.     ldisc = 0;
  333. #if defined(HPUX)
  334.     if (ioctl(fd232, TCFLSH, 2) < 0) {
  335.         syslog(LOG_ERR, "wwvb clock: ioctl(%s, TCFLSH): %m", wwvbdev);
  336. #else
  337.     if (ioctl(fd232, TIOCFLUSH, (char *)&ldisc) < 0) {
  338.         syslog(LOG_ERR,
  339.             "wwvb clock: ioctl(%s, TIOCFLUSH): %m", wwvbdev);
  340. #endif
  341.         goto screwed;
  342.     }
  343.     if (!io_addclock(&wwvb->io)) {
  344.         goto screwed;
  345.     }
  346.  
  347.     /*
  348.      * All done.  Initialize a few random peer variables, then
  349.      * return success.
  350.      */
  351.     peer->precision = WWVBPRECISION;
  352.     peer->rootdelay = 0;
  353.     peer->rootdispersion = 0;
  354.     peer->stratum = stratumtouse[unit];
  355.     if (stratumtouse[unit] <= 1)
  356.         bcopy(WWVBREFID, (char *)&peer->refid, 4);
  357.     else
  358.         peer->refid = htonl(WWVBHSREFID);
  359.     unitinuse[unit] = 1;
  360.     return 1;
  361.  
  362.     /*
  363.      * Something broke; abandon ship
  364.      */
  365. screwed:
  366.     (void) close(fd232);
  367.     return 0;
  368. }
  369.  
  370. /*
  371.  * wwvb_shutdown - shut down a WWVB clock
  372.  */
  373. void
  374. wwvb_shutdown(unit)
  375.     int unit;
  376. {
  377.     register struct wwvbunit *wwvb;
  378.     extern void io_closeclock();
  379.  
  380.     if (unit >= MAXUNITS) {
  381.         syslog(LOG_ERR,
  382.           "wwvb clock: INTERNAL ERROR, unit number %d invalid (max 4)",
  383.             unit);
  384.         return;
  385.     }
  386.     if (!unitinuse[unit]) {
  387.         syslog(LOG_ERR,
  388.          "wwvb clock: INTERNAL ERROR, unit number %d not in use", unit);
  389.         return;
  390.     }
  391.  
  392.     /*
  393.      * Tell the I/O module to turn us off.  We're history.
  394.      */
  395.     wwvb = wwvbunits[unit];
  396.     io_closeclock(&wwvb->io);
  397.     unitinuse[unit] = 0;
  398. }
  399.  
  400.  
  401. /*
  402.  * wwvb_report_event - note the occurance of an event
  403.  */
  404. void
  405. wwvb_report_event(wwvb, code)
  406.     struct wwvbunit *wwvb;
  407.     int code;
  408. {
  409.     struct peer *peer;
  410.  
  411.     peer = wwvb->peer;
  412.     if (wwvb->status != (u_char)code) {
  413.         wwvb->status = (u_char)code;
  414.         if (code != CEVNT_NOMINAL)
  415.             wwvb->lastevent = (u_char)code;
  416.         /*
  417.          * Should report event to trap handler in here.
  418.          * Soon...
  419.          */
  420.     syslog(LOG_INFO, "clock %s event %x\n",
  421.         ntoa(&peer->srcadr), code);
  422.  
  423.     }
  424. }
  425.  
  426.  
  427. /*
  428.  * wwvb_receive - receive data from the serial interface on a Spectracom
  429.  * clock
  430.  */
  431. void
  432. wwvb_receive(rbufp)
  433.     struct recvbuf *rbufp;
  434. {
  435.     register int i;
  436.     register struct wwvbunit *wwvb;
  437.     register u_char *dpt;
  438.     register char *cp;
  439.     register u_char *dpend;
  440.     l_fp tstmp;
  441.     u_fp dispersion;
  442.     char wwvb_process();
  443.     extern void refclock_receive();
  444.     char *ulfptoa();
  445.  
  446.     /*
  447.      * Get the clock this applies to and a pointers to the data
  448.      */
  449.     wwvb = (struct wwvbunit *)rbufp->recv_srcclock;
  450.     dpt = (u_char *)&rbufp->recv_space;
  451.  
  452.     /*
  453.      * Note we get a buffer and timestamp for both a <cr> and <lf>.
  454.      * We use the difference between the <cr> and <lf> timestamps to
  455.      * determine the character time and correct the <cr> timestamp
  456.      * time to the middle of the <cr> start bit. The error is half a
  457.      * bit time +-jitter, or 52 +-13 us at 9600 bps. Note: the start
  458.      * bit is delayed 50-150 us relative to the pps pulse on a
  459.      * Netclock/2 in format 0.
  460.      */
  461.     if (rbufp->recv_length == 1) {
  462.         if (wwvb->tcswitch == 0) {
  463.             wwvb->tcswitch = 1;
  464.             wwvb->lastrec = rbufp->recv_time;
  465.         } else {
  466.             wwvb->tcswitch = 0;
  467.             tstmp = rbufp->recv_time;
  468.             L_SUB(&tstmp, &wwvb->lastrec);
  469.             L_SUB(&wwvb->lastrec, &tstmp);
  470.  
  471. #ifdef DEBUG
  472.             if (debug)
  473.                 printf("wwvb: timestamp lag %s\n",
  474.                 ulfptoa(&tstmp, 6));
  475. #endif
  476.         }
  477.         return;
  478.     }
  479.     tstmp = wwvb->lastrec;
  480.     wwvb->lastrec = rbufp->recv_time;
  481.     wwvb->tcswitch = 1;
  482.  
  483.     /*
  484.      * Edit timecode to remove control chars
  485.      */
  486.     dpend = dpt + rbufp->recv_length;
  487.     cp = wwvb->lastcode;
  488.     while (dpt < dpend)
  489.         if ((*cp = 0x7f & *dpt++) >= ' ') cp++; 
  490.     *cp = '\0';
  491.     wwvb->lencode = cp - wwvb->lastcode;
  492.     if (wwvb->lencode == 0) return;
  493.  
  494.     /*
  495.      * Note the receive timestamp is determined at the first <cr>;
  496.      * however, we don't get the timecode for that timestamp until
  497.      * the next <cr>. We assume that, if we happen to come up
  498.      * during a timestamp, the format and data checks will toss it
  499.      * out.
  500.      */
  501. #ifdef DEBUG
  502.     if (debug)
  503.             printf("wwvb: timecode %d %s\n",
  504.             wwvb->lencode, wwvb->lastcode);
  505. #endif
  506.  
  507.     /*
  508.      * We get down to business, check the timecode format and decode
  509.      * its contents. This code checks for and decodes both format 0
  510.      * and format 2 and need not be told which in advance.
  511.      */
  512.     cp = wwvb->lastcode;
  513.     wwvb->leap = 0;
  514.     wwvb->format = FMTWWVBU;
  515.     if (wwvb->lencode == LENWWVB0) {
  516.  
  517.         /*
  518.           * Check timecode format 0
  519.           */
  520.         if (cp[1] != ' ' ||        /* <sp> separator */
  521.             cp[2] != ' ' ||        /* <sp> separator */
  522.             !isdigit(cp[3]) ||    /* day of year */
  523.             !isdigit(cp[4]) ||
  524.             !isdigit(cp[5]) ||
  525.             cp[6] != ' ' ||        /* <sp> */
  526.             !isdigit(cp[7]) ||    /* hours */
  527.             !isdigit(cp[8]) ||
  528.             cp[9] != ':' ||        /* : separator */
  529.             !isdigit(cp[10]) ||    /* minutes */
  530.             !isdigit(cp[11]) ||
  531.             cp[12] != ':' ||    /* : separator */
  532.             !isdigit(cp[13]) ||    /* seconds */
  533.             !isdigit(cp[14]) ||
  534.             cp[15] != ' ' ||    /* <sp> separator */
  535.             cp[16] != ' ' ||    /* <sp> separator */
  536.             cp[17] != 'T' ||    /* T separator */
  537.             cp[18] != 'Z' ||    /* Z separator */
  538.             cp[19] != '=' ||    /* = separator */
  539.             !isdigit(cp[20]) ||    /* time zone */
  540.             !isdigit(cp[21])) {
  541.                 wwvb->badformat++;
  542.                 wwvb_report_event(wwvb, CEVNT_BADREPLY);
  543.                 return;
  544.             }
  545.         else wwvb->format = FMTWWVB0;
  546.  
  547.         /*
  548.          * Convert format 0 and check values 
  549.          */
  550.         wwvb->year = 0;        /* fake */
  551.         wwvb->day = cp[3] - '0';
  552.         wwvb->day = MULBY10(wwvb->day) + cp[4] - '0';
  553.         wwvb->day = MULBY10(wwvb->day) + cp[5] - '0';
  554.         wwvb->hour = MULBY10(cp[7] - '0') + cp[8] - '0';
  555.         wwvb->minute = MULBY10(cp[10] - '0') + cp[11] -  '0';
  556.         wwvb->second = MULBY10(cp[13] - '0') + cp[14] - '0';
  557.         wwvb->msec = 0;
  558.         if (cp[0] != ' ')
  559.             wwvb->leap = LEAP_NOTINSYNC;
  560.         else
  561.             wwvb->lasttime = current_time;
  562.         if (wwvb->day < 1 || wwvb->day > 366) {
  563.             wwvb->baddata++;
  564.             wwvb_report_event(wwvb, CEVNT_BADDATE);
  565.             return;
  566.         }
  567.         if (wwvb->hour > 23 || wwvb->minute > 59
  568.             || wwvb->second > 59) {
  569.             wwvb->baddata++;
  570.             wwvb_report_event(wwvb, CEVNT_BADTIME);
  571.             return;
  572.         }
  573.     } else if (wwvb->lencode == LENWWVB2) {
  574.  
  575.         /*
  576.           * Check timecode format 2
  577.           */
  578.         if (!isdigit(cp[2]) ||        /* year of century */
  579.             !isdigit(cp[3]) ||
  580.             cp[4] != ' ' ||        /* <sp> separator */
  581.             !isdigit(cp[5]) ||    /* day of year */
  582.             !isdigit(cp[6]) ||
  583.             !isdigit(cp[7]) ||
  584.             cp[8] != ' ' ||        /* <sp> separator */
  585.             !isdigit(cp[9]) ||    /* hour */
  586.             !isdigit(cp[10]) ||
  587.             cp[11] != ':' ||    /* : separator */
  588.             !isdigit(cp[12]) ||    /* minute */
  589.             !isdigit(cp[13]) ||
  590.             cp[14] != ':' ||    /* : separator */
  591.             !isdigit(cp[15]) ||    /* second */
  592.             !isdigit(cp[16]) ||
  593.             cp[17] != '.' ||    /* . separator */
  594.             !isdigit(cp[18]) ||    /* millisecond */
  595.             !isdigit(cp[19]) ||
  596.             !isdigit(cp[20]) ||
  597.             cp[21] != ' ') {    /* <sp> separator */
  598.                 wwvb->badformat++;
  599.                 wwvb_report_event(wwvb, CEVNT_BADREPLY);
  600.                 return;
  601.             }
  602.         else wwvb->format = FMTWWVB2;
  603.  
  604.         /*
  605.          * Convert format 2 and check values 
  606.          */
  607.         wwvb->year = MULBY10(cp[2] - '0') + cp[3] - '0';
  608.         wwvb->day = cp[5] - '0';
  609.         wwvb->day = MULBY10(wwvb->day) + cp[6] - '0';
  610.         wwvb->day = MULBY10(wwvb->day) + cp[7] - '0';
  611.         wwvb->hour = MULBY10(cp[9] - '0') + cp[10] - '0';
  612.         wwvb->minute = MULBY10(cp[12] - '0') + cp[13] -  '0';
  613.         wwvb->second = MULBY10(cp[15] - '0') + cp[16] - '0';
  614.         wwvb->msec = cp[18] - '0';
  615.         wwvb->msec = MULBY10(wwvb->msec) + cp[19] - '0';
  616.         wwvb->msec = MULBY10(wwvb->msec) + cp[20] - '0';
  617.         wwvb->quality = cp[1];
  618.         if (cp[0] != ' ')
  619.             wwvb->leap = LEAP_NOTINSYNC;
  620.         else if (cp[1] == ' ')
  621.             wwvb->lasttime = current_time;
  622.         if (cp[22] == 'L')
  623.             wwvb->leap = LEAP_ADDSECOND;
  624.         if (wwvb->day < 1 || wwvb->day > 366) {
  625.             wwvb->baddata++;
  626.             wwvb_report_event(wwvb, CEVNT_BADDATE);
  627.             return;
  628.         }
  629.         if (wwvb->hour > 23 || wwvb->minute > 59
  630.             || wwvb->second > 59) {
  631.             wwvb->baddata++;
  632.             wwvb_report_event(wwvb, CEVNT_BADTIME);
  633.             return;
  634.         }
  635.     } else {
  636.         wwvb->badformat++;
  637.         wwvb_report_event(wwvb, CEVNT_BADREPLY);
  638.         return;
  639.     }
  640.  
  641.     /*
  642.      * Now, compute the reference time value. Use the heavy
  643.      * machinery for the seconds and the millisecond field for the
  644.      * fraction when present.
  645.          *
  646.      * this code does not yet know how to do the years
  647.      */
  648.     if (!clocktime(wwvb->day, wwvb->hour, wwvb->minute,
  649.         wwvb->second, GMT, tstmp.l_ui,
  650.         &wwvb->yearstart, &wwvb->lastref.l_ui)) {
  651.         wwvb->baddata++;
  652.         wwvb_report_event(wwvb, CEVNT_BADTIME);
  653.         return;
  654.     }
  655.     MSUTOTSF(wwvb->msec, wwvb->lastref.l_uf);
  656.     i = ((int)(wwvb->coderecv)) % NCODES;
  657.     wwvb->offset[i] = wwvb->lastref;
  658.     L_SUB(&wwvb->offset[i], &tstmp);
  659.     if (wwvb->coderecv == 0)
  660.         for (i = 1; i < NCODES; i++)
  661.             wwvb->offset[i] = wwvb->offset[0];
  662.  
  663.     wwvb->coderecv++;
  664.     /*
  665.      * Process the median filter, add the fudge factor and pass the
  666.      * offset and dispersion along. We use lastrec as both the
  667.      * reference time and receive time in order to avoid being cute,
  668.      * like setting the reference time later than the receive time,
  669.      * which may cause a paranoid protocol module to chuck out the
  670.      * data.
  671.       */
  672.     if (!wwvb_process(wwvb, &tstmp, &dispersion)) {
  673.         wwvb->baddata++;
  674.         wwvb_report_event(wwvb, CEVNT_BADTIME);
  675.         return;
  676.     }
  677.     L_ADD(&tstmp, &(fudgefactor[wwvb->unit]));
  678.     refclock_receive(wwvb->peer, &tstmp, GMT, dispersion,
  679.         &wwvb->lastrec, &wwvb->lastrec, wwvb->leap);
  680. }
  681.  
  682. /*
  683.  * wwvb_process - process a pile of samples from the clock
  684.  */
  685. char
  686. wwvb_process(wwvb, offset, dispersion)
  687.     struct wwvbunit *wwvb;
  688.     l_fp *offset;
  689.     u_fp *dispersion;
  690. {
  691.     register int i, j;
  692.     register u_long tmp_ui, tmp_uf;
  693.     int not_median1, not_median2, median;
  694.     u_fp disp_tmp, disp_tmp2;
  695.  
  696.     /*
  697.      * This code implements a three-stage median filter. First, we
  698.          * check if the samples are within 125 ms of each other. If not,
  699.      * dump the sample set. We take the median of the three offsets
  700.      * and use that as the sample offset. We take the maximum
  701.      * difference and use that as the sample dispersion. There
  702.      * probably is not much to be gained by a longer filter, since
  703.      * the clock filter in ntp_proto should do its thing.
  704.      */
  705.     disp_tmp2 = 0;
  706.     for (i = 0; i < NCODES-1; i++) {
  707.         for (j = i+1; j < NCODES; j++) {
  708.             tmp_ui = wwvb->offset[i].l_ui;
  709.             tmp_uf = wwvb->offset[i].l_uf;
  710.             M_SUB(tmp_ui, tmp_uf, wwvb->offset[j].l_ui,
  711.                 wwvb->offset[j].l_uf);
  712.             if (M_ISNEG(tmp_ui, tmp_uf)) {
  713.                 M_NEG(tmp_ui, tmp_uf);
  714.             }
  715.             if (tmp_ui != 0 || tmp_uf > CODEDIFF) {
  716.                 return 0;
  717.             }
  718.             disp_tmp = MFPTOFP(0, tmp_uf);
  719.             if (disp_tmp > disp_tmp2) {
  720.                 disp_tmp2 = disp_tmp;
  721.                 not_median1 = i;
  722.                 not_median2 = j;
  723.             }
  724.         }
  725.     }
  726.  
  727.     /*
  728.      * It seems as if all are within 125 ms of each other.
  729.      * Now to determine the median of the three. Whlie the
  730.      * 125 ms check was going on, we also subtly catch the
  731.      * dispersion and set-up for a very easy median calculation.
  732.      * The largest difference between any two samples constitutes
  733.      * the dispersion. The sample not involve in the dispersion is
  734.      * the median sample. EASY!
  735.      */
  736.     if (wwvb->lasttime == 0 || disp_tmp2 > WWVBMAXDISPERSE)
  737.         disp_tmp2 = WWVBMAXDISPERSE;
  738.     if (not_median1 == 0) {
  739.         if (not_median2 == 1)
  740.             median = 2;
  741.         else
  742.             median = 1;
  743.         } else {
  744.         median = 0;
  745.         }
  746.     *offset = wwvb->offset[median];
  747.     *dispersion = disp_tmp2;
  748.     return 1;
  749. }
  750.  
  751. /*
  752.  * wwvb_poll - called by the transmit procedure
  753.  */
  754. void
  755. wwvb_poll(unit, peer)
  756.     int unit;
  757.     char *peer;
  758. {
  759.     struct wwvbunit *wwvb;
  760.  
  761.     /*
  762.      * Time to request a time code.  The Spectracom clock responds
  763.      * to a "T" sent to it by returning a time code as stated in the
  764.      * comments in the header.  Note there is no checking on state,
  765.      * since this may not be the only customer reading the clock.
  766.      * Only one customer need poll the clock; all others just listen
  767.      * in.
  768.      */
  769.     if (unit >= MAXUNITS) {
  770.         syslog(LOG_ERR,
  771.             "wwvb clock poll: INTERNAL: unit %d invalid", unit);
  772.         return;
  773.     }
  774.     if (!unitinuse[unit]) {
  775.         syslog(LOG_ERR,
  776.             "wwvb clock poll: INTERNAL: unit %d unused", unit);
  777.         return;
  778.     }
  779.     wwvb = wwvbunits[unit];
  780.     if ((current_time - wwvb->lasttime) > 150) {
  781.         wwvb->noreply++;
  782.         wwvb_report_event(wwvbunits[unit], CEVNT_TIMEOUT);
  783.     }
  784.     if (write(wwvb->io.fd, "T", 1) != 1) {
  785.         syslog(LOG_ERR,
  786.             "wwvb clock: write fails to unit %d: %m", wwvb->unit);
  787.         wwvb_report_event(wwvb, CEVNT_FAULT);
  788.     } else {
  789.         wwvb->polls++;
  790.     }
  791. }
  792.  
  793. /*
  794.  * wwvb_control - set fudge factors, return statistics
  795.  */
  796. void
  797. wwvb_control(unit, in, out)
  798.     u_int unit;
  799.     struct refclockstat *in;
  800.     struct refclockstat *out;
  801. {
  802.     register struct wwvbunit *wwvb;
  803.  
  804.     if (unit >= MAXUNITS) {
  805.         syslog(LOG_ERR,
  806.             "wwvb clock: unit %d invalid (max %d)", unit, MAXUNITS-1);
  807.         return;
  808.     }
  809.  
  810.     if (in != 0) {
  811.         if (in->haveflags & CLK_HAVETIME1)
  812.             fudgefactor[unit] = in->fudgetime1;
  813.         if (in->haveflags & CLK_HAVEVAL1) {
  814.             stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf);
  815.             if (unitinuse[unit]) {
  816.                 struct peer *peer;
  817.  
  818.                 /*
  819.                  * Should actually reselect clock, but
  820.                  * will wait for the next timecode
  821.                  */
  822.                 wwvb = wwvbunits[unit];
  823.                 peer = wwvb->peer;
  824.                 peer->stratum = stratumtouse[unit];
  825.                 if (stratumtouse[unit] <= 1)
  826.                     bcopy(WWVBREFID, (char *)&peer->refid,
  827.                         4);
  828.                 else
  829.                     peer->refid = htonl(WWVBHSREFID);
  830.             }
  831.         }
  832.         if (in->haveflags & CLK_HAVEFLAG1) {
  833.             sloppyclockflag[unit] = in->flags & CLK_FLAG1;
  834.         }
  835.     }
  836.  
  837.     if (out != 0) {
  838.         out->type = REFCLK_WWVB_SPECTRACOM;
  839.         out->haveflags
  840.             = CLK_HAVETIME1|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG1;
  841.         out->clockdesc = WWVBDESCRIPTION;
  842.         out->fudgetime1 = fudgefactor[unit];
  843.         out->fudgetime2.l_ui = 0;
  844.         out->fudgetime2.l_uf = 0;
  845.         out->fudgeval1 = (long)stratumtouse[unit];
  846.         out->fudgeval2 = 0;
  847.         out->flags = sloppyclockflag[unit];
  848.         if (unitinuse[unit]) {
  849.             wwvb = wwvbunits[unit];
  850.             out->lencode = wwvb->lencode;
  851.             out->lastcode = wwvb->lastcode;
  852.             out->timereset = current_time - wwvb->timestarted;
  853.             out->polls = wwvb->polls;
  854.             out->noresponse = wwvb->noreply;
  855.             out->badformat = wwvb->badformat;
  856.             out->baddata = wwvb->baddata;
  857.             out->lastevent = wwvb->lastevent;
  858.             out->currentstatus = wwvb->status;
  859.         } else {
  860.             out->lencode = 0;
  861.             out->lastcode = "";
  862.             out->polls = out->noresponse = 0;
  863.             out->badformat = out->baddata = 0;
  864.             out->timereset = 0;
  865.             out->currentstatus = out->lastevent = CEVNT_NOMINAL;
  866.         }
  867.     }
  868. }
  869.  
  870. /*
  871.  * wwvb_buginfo - return clock dependent debugging info
  872.  */
  873. void
  874. wwvb_buginfo(unit, bug)
  875.     int unit;
  876.     register struct refclockbug *bug;
  877. {
  878.     register struct wwvbunit *wwvb;
  879.     register int i;
  880.     register int n;
  881.  
  882.     if (unit >= MAXUNITS) {
  883.         syslog(LOG_ERR, "wwvb clock: unit %d invalid (max %d)",
  884.             unit, MAXUNITS-1);
  885.         return;
  886.     }
  887.  
  888.     if (!unitinuse[unit])
  889.         return;
  890.     wwvb = wwvbunits[unit];
  891.  
  892.     bug->nvalues = 11;
  893.     bug->ntimes = 5;
  894.     if (wwvb->lasttime != 0)
  895.         bug->values[0] = current_time - wwvb->lasttime;
  896.     else
  897.         bug->values[0] = 0;
  898.     bug->values[1] = (u_long)wwvb->reason;
  899.     bug->values[2] = (u_long)wwvb->year;
  900.     bug->values[3] = (u_long)wwvb->day;
  901.     bug->values[4] = (u_long)wwvb->hour;
  902.     bug->values[5] = (u_long)wwvb->minute;
  903.     bug->values[6] = (u_long)wwvb->second;
  904.     bug->values[7] = (u_long)wwvb->msec;
  905.     bug->values[8] = wwvb->noreply;
  906.     bug->values[9] = wwvb->yearstart;
  907.     bug->values[10] = wwvb->quality;
  908.     bug->stimes = 0x1c;
  909.     bug->times[0] = wwvb->lastref;
  910.     bug->times[1] = wwvb->lastrec;
  911.     bug->times[2] = wwvb->offset[0];
  912.     bug->times[3] = wwvb->offset[1];
  913.     bug->times[4] = wwvb->offset[2];
  914. }
  915. #endif
  916.