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_goes. < prev    next >
Text File  |  1992-08-17  |  24KB  |  940 lines

  1. /*
  2.  * refclock_goes - clock driver for the Kinimetric Truetime GOES receiver
  3.  *    Version 2.0
  4.  */
  5. #include <stdio.h>
  6. #include <ctype.h>
  7. #include <sys/types.h>
  8. #include <sys/socket.h>
  9. #include <netinet/in.h>
  10. #include <sys/time.h>
  11. #include <sys/file.h>
  12. #include <sys/ioctl.h>
  13. #include <sgtty.h>
  14.  
  15. #include "ntp_syslog.h"
  16. #include "ntp_fp.h"
  17. #include "ntp.h"
  18. #include "ntp_refclock.h"
  19. #include "ntp_unixtime.h"
  20.  
  21. #if defined(REFCLOCK) && defined(GOES)
  22. /*
  23.  * Support for Kinemetrics Truetime 468-DC GOES Receiver
  24.  *
  25.  * Most of this code is copied from refclock_wwvb.c with thanks.
  26.  *
  27.  * the time code looks like follows;  Send the clock a R or C and once per
  28.  * second a timestamp will appear that looks like this:
  29.  * ADDD:HH:MM:SSQCL
  30.  * A - control A
  31.  * Q Quality indication: indicates possible error of
  32.  *     ?     +/- 500 milliseconds            #     +/- 50 milliseconds
  33.  *     *     +/- 5 milliseconds              .     +/- 1 millisecond
  34.  *   space   less than 1 millisecond
  35.  * C - Carriage return
  36.  * L - Line feed
  37.  * The cariage return start bit begins on 0 seconds and extends to 1 bit time.
  38.  *
  39.  * Unless you live on 125 degrees west longitude, you can't set your clock
  40.  * propagation delay settings correctly and still use automatic mode.
  41.  * The manual says to use a compromise when setting the switches.  This
  42.  * results in significant errors.  The solution; use fudge time1 and time2
  43.  * to incorporate corrections.  If your clock is set for 50 and it should
  44.  * be 58 for using the west and 46 for using the east, use the line
  45.  * fudge 127.127.5.0 time1 +0.008 time2 -0.004
  46.  * This corrects the 4 milliseconds advance and 5 milliseconds retard needed.
  47.  * The software will ask the clock which satellite it sees.
  48.  *
  49.  * Flag1 set to 1 will silence the clock side of xntpd, just reading the
  50.  * clock without trying to write to it.  This is usefull if several
  51.  * xntpds listen to the same clock.  This has not been tested yet...
  52.  */
  53.  
  54. /*
  55.  * Definitions
  56.  */
  57. #define    MAXUNITS    4    /* max number of GOES units */
  58. #define    GOES232    "/dev/goes%d"
  59. #define    SPEED232    B9600    /* 9600 baud */
  60.  
  61. /*
  62.  * Radio interface parameters
  63.  */
  64. #define    GOESMAXDISPERSE    (FP_SECOND>>1) /* max error for synchronized clock (0.5 s as an u_fp) */
  65. #define    GOESSKEWFACTOR    17    /* skew factor (for about 32 ppm) */
  66. #define    GOESPRECISION    (-9)    /* precision assumed (about 1 ms) */
  67. #define    GOESREFID    "GOES"    /* reference id */
  68. #define    GOESDESCRIPTION    "Kinemetrics GOES Receiver" /* who we are */
  69. #define    GOESHSREFID    0x7f7f050a /* 127.127.5.10 refid hi strata */
  70. #define GMT        0    /* hour offset from Greenwich */
  71. #define    NCODES        3    /* stages of median filter */
  72. #define    LENGOES0    13    /* format 0 timecode length */
  73. #define    LENGOES2    21    /* format 2 timecode length */
  74. #define FMTGOESU    0    /* unknown format timecode id */
  75. #define FMTGOES0    1    /* format 0 timecode id */
  76. #define FMTGOES2    2    /* format 2 timecode id */
  77. #define    DEFFUDGETIME    0    /* default fudge time (ms) */
  78. #define BMAX        50    /* timecode buffer length */
  79.  
  80. /*
  81.  * Tag which satellite we see
  82.  */
  83. #define GOES_SAT_NONE   0
  84. #define GOES_SAT_WEST   1
  85. #define GOES_SAT_EAST   2
  86. #define GOES_SAT_STAND  3
  87.  
  88. /*
  89.  * Hack to avoid excercising the multiplier.  I have no pride.
  90.  */
  91. #define    MULBY10(x)    (((x)<<3) + ((x)<<1))
  92.  
  93. #define    CODEDIFF    0x20000000    /* 0.125 seconds as an l_fp fraction */
  94.  
  95. /*
  96.  * Time conversion tables imported from the library
  97.  */
  98. extern u_long msutotsflo[];
  99. extern u_long msutotsfhi[];
  100. extern u_long ustotslo[];
  101. extern u_long ustotsmid[];
  102. extern u_long ustotshi[];
  103.  
  104. /*
  105.  * Imported from the timer module
  106.  */
  107. extern u_long current_time;
  108. extern struct event timerqueue[];
  109. /*
  110.  * Debug flag
  111.  */
  112. extern int debug;
  113.  
  114. /*
  115.  * GOES unit control structure.
  116.  */
  117. struct goesunit {
  118.     struct peer *peer;        /* associated peer structure */
  119.     struct refclockio io;        /* given to the I/O handler */
  120.     l_fp lastrec;            /* last receive time */
  121.     l_fp lastref;            /* last timecode time */
  122.     l_fp offset[NCODES];        /* recent sample offsets */
  123.     char lastcode[BMAX];        /* last timecode received */
  124.     u_short satellite;        /* which satellite we saw */
  125.     u_short polled;            /* Hand in a time sample? */
  126.     u_char format;            /* timecode format */
  127.     u_char lencode;            /* length of last timecode */
  128.     u_long lasttime;        /* last time clock heard from */
  129.     u_char unit;            /* unit number for this guy */
  130.     u_char status;            /* clock status */
  131.     u_char lastevent;        /* last clock event */
  132.     u_char reason;            /* reason for last abort */
  133.     u_char year;            /* year of eternity */
  134.     u_short day;            /* day of year */
  135.     u_char hour;            /* hour of day */
  136.     u_char minute;            /* minute of hour */
  137.     u_char second;            /* seconds of minute */
  138.     u_char leap;            /* leap indicators */
  139.     u_short msec;            /* millisecond of second */
  140.     u_char quality;            /* quality char from format 2 */
  141.     u_long yearstart;        /* start of current year */
  142.     /*
  143.      * Status tallies
  144.       */
  145.     u_long polls;            /* polls sent */
  146.     u_long noreply;            /* no replies to polls */
  147.     u_long coderecv;        /* timecodes received */
  148.     u_long badformat;        /* bad format */
  149.     u_long baddata;            /* bad data */
  150.     u_long timestarted;        /* time we started this */
  151. };
  152.  
  153. /*
  154.  * Data space for the unit structures.  Note that we allocate these on
  155.  * the fly, but never give them back.
  156.  */
  157. static struct goesunit *goesunits[MAXUNITS];
  158. static u_char unitinuse[MAXUNITS];
  159.  
  160. /*
  161.  * Keep the fudge factors separately so they can be set even
  162.  * when no clock is configured.
  163.  */
  164. static l_fp fudgefactor1[MAXUNITS];
  165. static l_fp fudgefactor2[MAXUNITS];
  166. static u_char stratumtouse[MAXUNITS];
  167. static u_char readonlyclockflag[MAXUNITS];
  168.  
  169. /*
  170.  * goes_init - initialize internal goes driver data
  171.  */
  172. void
  173. goes_init()
  174. {
  175.     register int i;
  176.     /*
  177.      * Just zero the data arrays
  178.      */
  179.     bzero((char *)goesunits, sizeof goesunits);
  180.     bzero((char *)unitinuse, sizeof unitinuse);
  181.  
  182.     /*
  183.      * Initialize fudge factors to default.
  184.      */
  185.     for (i = 0; i < MAXUNITS; i++) {
  186.         fudgefactor1[i].l_ui = 0;
  187.         fudgefactor1[i].l_uf = DEFFUDGETIME;
  188.         fudgefactor2[i].l_ui = 0;
  189.         fudgefactor2[i].l_uf = DEFFUDGETIME;
  190.         stratumtouse[i] = 0;
  191.         readonlyclockflag[i] = 0;
  192.     }
  193. }
  194.  
  195.  
  196. /*
  197.  * goes_start - open the GOES devices and initialize data for processing
  198.  */
  199. int
  200. goes_start(unit, peer)
  201.     u_int unit;
  202.     struct peer *peer;
  203. {
  204.     register struct goesunit *goes;
  205.     register int i;
  206.     int fd232;
  207.     char goesdev[20];
  208.     unsigned int ldisc;
  209.     struct sgttyb ttyb;
  210.     void goes_receive();
  211.     extern int io_addclock();
  212.     extern void io_closeclock();
  213.     extern char *emalloc();
  214.  
  215.     /*
  216.      * Check configuration info.
  217.      */
  218.     if (unit >= MAXUNITS) {
  219.         syslog(LOG_ERR,"goes clock: unit number %d invalid (max %d)",
  220.             unit,MAXUNITS-1);
  221.         return 0;
  222.     }
  223.     if (unitinuse[unit]) {
  224.         syslog(LOG_ERR,
  225.             "goes clock: unit number %d in use", unit);
  226.         return 0;
  227.     }
  228.  
  229.     /*
  230.      * Open serial port.
  231.      */
  232.     (void) sprintf(goesdev, GOES232, unit);
  233.     fd232 = open(goesdev, O_RDWR, 0777);
  234.     if (fd232 == -1) {
  235.         syslog(LOG_ERR,
  236.             "goes clock: open of %s failed: %m", goesdev);
  237.         return 0;
  238.     }
  239.  
  240.     /*
  241.      * Set for exclusive use, cooked mode and baud rate.
  242.      */
  243.     if (ioctl(fd232, TIOCEXCL, (char *)0) < 0) {
  244.         syslog(LOG_ERR,
  245.             "goes clock: ioctl(%s, TIOCEXCL): %m", goesdev);
  246.         goto screwed;
  247.     }
  248.     ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
  249.     /*
  250.      * If we have the clock discipline, set the port to raw.  Otherwise
  251.      */
  252. #ifdef CLKDISC
  253.     ttyb.sg_erase = ttyb.sg_kill = '\r';
  254.     ttyb.sg_flags = EVENP|ODDP|RAW|CRMOD;
  255. #else
  256.     ttyb.sg_erase = ttyb.sg_kill = '\0377';
  257.     ttyb.sg_flags = EVENP|ODDP|CRMOD|NOHANG;
  258. #endif
  259.     if (ioctl(fd232, TIOCSETP, (char *)&ttyb) < 0) {
  260.         syslog(LOG_ERR,
  261.             "goes clock: ioctl(%s, TIOCSETP): %m", goesdev);
  262.         goto screwed;
  263.     }
  264.  
  265.     /*
  266.      * Looks like this might succeed.  Find memory for the
  267.      * structure. Look to see if there are any unused ones, if not
  268.      * we malloc() one.
  269.      */
  270.     if (goesunits[unit] != 0) {
  271.         goes = goesunits[unit];    /* The one we want is okay */
  272.     } else {
  273.         for (i = 0; i < MAXUNITS; i++) {
  274.             if (!unitinuse[i] && goesunits[i] != 0)
  275.                 break;
  276.         }
  277.         if (i < MAXUNITS) {
  278.             /*
  279.              * Reclaim this one
  280.              */
  281.             goes = goesunits[i];
  282.             goesunits[i] = 0;
  283.         } else {
  284.             goes = (struct goesunit *)
  285.                 emalloc(sizeof(struct goesunit));
  286.         }
  287.     }
  288.     bzero((char *)goes, sizeof(struct goesunit));
  289.     goesunits[unit] = goes;
  290.  
  291.     /*
  292.      * Set up the structures
  293.      */
  294.     goes->peer = peer;
  295.     goes->unit = (u_char)unit;
  296.     goes->timestarted = current_time;
  297.     goes->satellite = GOES_SAT_NONE;
  298.  
  299.     goes->io.clock_recv = goes_receive;
  300.     goes->io.srcclock = (caddr_t)goes;
  301.     goes->io.datalen = 0;
  302.     goes->io.fd = fd232;
  303.  
  304. #ifdef STREAM
  305.     /*
  306.      * Flush buffers and link in I/O list.
  307.      */
  308.     ldisc = 0;
  309.     if (ioctl(fd232, TIOCFLUSH, (char *)&ldisc) < 0) {
  310.         syslog(LOG_ERR,
  311.             "goes clock: ioctl(%s, TIOCFLUSH): %m", goesdev);
  312.         goto screwed;
  313.     }
  314. #else
  315.     /*
  316.      * Okay.  Set the line discipline to the clock line discipline, then
  317.      * give it to the I/O code to start receiving stuff.  Note the
  318.      * change of line discipline will clear the read buffers, which
  319.      * makes the change clean if done quickly.
  320.      */
  321. #ifdef CLKDISC
  322.     ldisc = CLKLDISC;
  323.     if (ioctl(fd232, TIOCSETD, (char *)&ldisc) < 0) {
  324.         syslog(LOG_ERR, "goes clock: ioctl(%s, TIOCSETD): %m",goesdev);
  325.         goto screwed;
  326.     }
  327. #else
  328.     ldisc = 0;    /* Just flush buffers */
  329.     if (ioctl(fd232, TIOCFLUSH, (char *)&ldisc) < 0) {
  330.         syslog(LOG_ERR, "goes clock: ioctl(%s, TIOCFLUSH): %m",goesdev);
  331.         goto screwed;
  332.     }
  333. #endif /*CLKDISC*/
  334. #endif /*STREAM*/
  335.  
  336.     if (!io_addclock(&goes->io)) {
  337.         goto screwed;
  338.     }
  339.  
  340.     /*
  341.      * All done.  Initialize a few random peer variables, then
  342.      * return success.
  343.      */
  344.     peer->precision = GOESPRECISION;
  345.     peer->rootdelay = 0;
  346.     peer->rootdispersion = 0;
  347.     peer->stratum = stratumtouse[unit];
  348.     if (stratumtouse[unit] <= 1)
  349.         bcopy(GOESREFID, (char *)&peer->refid, 4);
  350.     else
  351.         peer->refid = htonl(GOESHSREFID);
  352.     unitinuse[unit] = 1;
  353.     return 1;
  354.  
  355.     /*
  356.      * Something broke; abandon ship
  357.      */
  358. screwed:
  359.     (void) close(fd232);
  360.     return 0;
  361. }
  362.  
  363. /*
  364.  * goes_shutdown - shut down a GOES clock
  365.  */
  366. void
  367. goes_shutdown(unit)
  368.     int unit;
  369. {
  370.     register struct goesunit *goes;
  371.     extern void io_closeclock();
  372.  
  373.     if (unit >= MAXUNITS) {
  374.         syslog(LOG_ERR,
  375.           "goes clock: INTERNAL ERROR, unit number %d invalid (max 4)",
  376.             unit);
  377.         return;
  378.     }
  379.     if (!unitinuse[unit]) {
  380.         syslog(LOG_ERR,
  381.          "goes clock: INTERNAL ERROR, unit number %d not in use", unit);
  382.         return;
  383.     }
  384.  
  385.     /*
  386.      * Tell the I/O module to turn us off.  We're history.
  387.      */
  388.     goes = goesunits[unit];
  389.     io_closeclock(&goes->io);
  390.     unitinuse[unit] = 0;
  391. }
  392.  
  393.  
  394. /*
  395.  * goes_report_event - note the occurance of an event
  396.  */
  397. void
  398. goes_report_event(goes, code)
  399.     struct goesunit *goes;
  400.     int code;
  401. {
  402.     struct peer *peer;
  403.  
  404.     peer = goes->peer;
  405.     if (goes->status != (u_char)code) {
  406.         goes->status = (u_char)code;
  407.         if (code != CEVNT_NOMINAL)
  408.             goes->lastevent = (u_char)code;
  409.         /*
  410.          * Should report event to trap handler in here.
  411.          * Soon...
  412.          */
  413.     syslog(LOG_INFO, "clock %s event %x\n",
  414.         ntoa(&peer->srcadr), code);
  415.  
  416.     }
  417. }
  418.  
  419.  
  420. /*
  421.  * goes_receive - receive data from the serial interface on a Kinimetrics
  422.  * clock
  423.  */
  424. void
  425. goes_receive(rbufp)
  426.     struct recvbuf *rbufp;
  427. {
  428.     register int i;
  429.     register struct goesunit *goes;
  430.     register u_char *dpt;
  431.     register char *cp;
  432.     register u_char *dpend;
  433.     l_fp tstmp;
  434.     u_fp dispersion;
  435.     char goes_process();
  436.     extern void refclock_receive();
  437.     void goes_send();
  438.     char *ulfptoa();
  439.  
  440.     /*
  441.      * Get the clock this applies to and a pointers to the data
  442.      */
  443.     goes = (struct goesunit *)rbufp->recv_srcclock;
  444.     dpt = (u_char *)&rbufp->recv_space;
  445.  
  446.     /*
  447.      * Edit timecode to remove control chars
  448.      */
  449.     dpend = dpt + rbufp->recv_length;
  450.     cp = goes->lastcode;
  451.     while (dpt < dpend) {
  452.         if ((*cp = 0x7f & *dpt++) >= ' ') cp++; 
  453. #ifdef CLKDISC
  454.         else if (*cp == '\r') {
  455.             if (dpend - dpt < 8) {
  456.                 /* short timestamp */
  457.                 return;
  458.             }
  459.             if (!buftvtots(dpt,&goes->lastrec)) {
  460.                 /* screwy timestamp */
  461.                 return;
  462.             }
  463.             dpt += 8;
  464.         }
  465. #endif
  466.     }
  467.     *cp = '\0';
  468.     goes->lencode = cp - goes->lastcode;
  469.     if (goes->lencode == 0) return;
  470.     tstmp = goes->lastrec;
  471. #ifndef CLKDISC
  472.     goes->lastrec = rbufp->rec_vtime;
  473. #endif
  474.  
  475. #ifdef DEBUG
  476.     if (debug)
  477.             printf("goes: timecode %d %s\n",
  478.             goes->lencode, goes->lastcode);
  479. #endif
  480.  
  481.     /*
  482.      * We get down to business, check the timecode format and decode
  483.      * its contents. This code checks for and decodes both format 0
  484.      * and format 2 and need not be told which in advance.
  485.      */
  486.     cp = goes->lastcode;
  487.     goes->leap = 0;
  488.     goes->format = FMTGOESU;
  489.     if (goes->lencode == LENGOES0) {
  490.  
  491.         /*
  492.           * Check timecode format 0
  493.           */
  494.         if (!isdigit(cp[0]) ||    /* day of year */
  495.             !isdigit(cp[1]) ||
  496.             !isdigit(cp[2]) ||
  497.             cp[3] != ':' ||        /* <sp> */
  498.             !isdigit(cp[4]) ||    /* hours */
  499.             !isdigit(cp[5]) ||
  500.             cp[6] != ':' ||        /* : separator */
  501.             !isdigit(cp[7]) ||    /* minutes */
  502.             !isdigit(cp[8]) ||
  503.             cp[9] != ':' ||        /* : separator */
  504.             !isdigit(cp[10]) ||    /* seconds */
  505.             !isdigit(cp[11])) {
  506.                 goes->badformat++;
  507.                 goes_report_event(goes, CEVNT_BADREPLY);
  508.                 return;
  509.             }
  510.         else goes->format = FMTGOES0;
  511.  
  512.         /*
  513.          * Convert format 0 and check values 
  514.          */
  515.         goes->year = 0;        /* fake */
  516.         goes->day = cp[0] - '0';
  517.         goes->day = MULBY10(goes->day) + cp[1] - '0';
  518.         goes->day = MULBY10(goes->day) + cp[2] - '0';
  519.         goes->hour = MULBY10(cp[4] - '0') + cp[5] - '0';
  520.         goes->minute = MULBY10(cp[7] - '0') + cp[8] -  '0';
  521.         goes->second = MULBY10(cp[10] - '0') + cp[11] - '0';
  522.         goes->msec = 0;
  523.         if (cp[12] != ' ')
  524.             goes->leap = LEAP_NOTINSYNC;
  525.         else
  526.             goes->lasttime = current_time;
  527.         if (goes->day < 1 || goes->day > 366) {
  528.             goes->baddata++;
  529.             goes_report_event(goes, CEVNT_BADDATE);
  530.             return;
  531.         }
  532.         if (goes->hour > 23 || goes->minute > 59
  533.             || goes->second > 59) {
  534.             goes->baddata++;
  535.             goes_report_event(goes, CEVNT_BADTIME);
  536.             return;
  537.         }
  538.  
  539.     } else if (goes->lencode == LENGOES2) {
  540.  
  541.         /*
  542.          * Extended precision satelite location info
  543.          */
  544.         if (!isdigit(cp[0]) ||        /* longitude */
  545.             !isdigit(cp[1]) ||
  546.             !isdigit(cp[2]) ||
  547.             cp[3] != '.' ||
  548.             !isdigit(cp[4]) ||
  549.             !isdigit(cp[5]) ||
  550.             !isdigit(cp[6]) ||
  551.             !isdigit(cp[7]) ||
  552.             (cp[8] != '+' && cp[8] != '-') ||
  553.             !isdigit(cp[9]) ||    /*latitude */
  554.             cp[10] != '.' ||
  555.             !isdigit(cp[11]) ||
  556.             !isdigit(cp[12]) ||
  557.             !isdigit(cp[13]) ||
  558.             !isdigit(cp[14]) ||
  559.             (cp[15] != '+' && cp[15] != '-') ||
  560.             !isdigit(cp[16]) ||    /* height */
  561.             !isdigit(cp[17]) ||
  562.             !isdigit(cp[18]) ||
  563.             cp[19] != '.' ||
  564.             !isdigit(cp[20])) {
  565.                 goes->badformat++;
  566.                 goes_report_event(goes, CEVNT_BADREPLY);
  567.                 return;
  568.             }
  569.         else goes->format = FMTGOES2;
  570.  
  571.         /*
  572.          * Figure out which satellite this is.
  573.          * This allows +-5 degrees from nominal.
  574.          */
  575.         if (cp[0] == '1' && (cp[1] == '3' || cp[1] == '2'))
  576.             goes->satellite = GOES_SAT_WEST;
  577.         else if (cp[0] == '1' && cp[1] == '0')
  578.             goes->satellite = GOES_SAT_STAND;
  579.         else if (cp[0] == '0' && cp[1] == '7')
  580.             goes->satellite = GOES_SAT_EAST;
  581.         else
  582.             goes->satellite = GOES_SAT_NONE;
  583.  
  584. #ifdef DEBUG
  585.         if (debug)
  586.             printf("goes_receive: select satellite %d\n",
  587.                 goes->satellite);
  588. #endif
  589.  
  590.         /*
  591.          * Switch back to on-second time codes.
  592.          */
  593.         goes_send(goes,"C");
  594.  
  595.         /*
  596.          * Since this is not a time code, just return...
  597.          */
  598.         return;
  599.     }
  600.  
  601.     /*
  602.      * The clock will blurt a timecode every second but we only
  603.      * want one when polled.  If we havn't been polled, bail out.
  604.      */
  605.     if (!goes->polled)
  606.         return;
  607.  
  608.     /*
  609.      * Now, compute the reference time value. Use the heavy
  610.      * machinery for the seconds and the millisecond field for the
  611.      * fraction when present.
  612.          *
  613.      * this code does not yet know how to do the years
  614.      */
  615.     if (!clocktime(goes->day, goes->hour, goes->minute,
  616.         goes->second, GMT, tstmp.l_ui,
  617.         &goes->yearstart, &goes->lastref.l_ui)) {
  618.         goes->baddata++;
  619.         goes_report_event(goes, CEVNT_BADTIME);
  620.         return;
  621.     }
  622.     MSUTOTSF(goes->msec, goes->lastref.l_uf);
  623.  
  624.     /*
  625.      * Slop the read value by fudgefactor1 or fudgefactor2 depending
  626.      * on which satellite we are viewing last time we checked.
  627.      */
  628.  
  629. #ifdef DEBUG
  630.     if (debug)
  631.         printf("GOES_RECEIVE: Slopping for satellite %d\n",
  632.             goes->satellite);
  633. #endif
  634.     if (goes->satellite == GOES_SAT_WEST)
  635.         L_ADD(&goes->lastref, &fudgefactor1[goes->unit]);
  636.     else if (goes->satellite == GOES_SAT_EAST)
  637.         L_ADD(&goes->lastref, &fudgefactor2[goes->unit]);
  638. /*    else if (goes->satellite == GOES_SAT_STAND)
  639.         L_ADD(&goes->lastref, &((fudgefactor1[goes->unit] +
  640.             fudgefactor2[goes->unit]) / 2)); */
  641.  
  642.     i = ((int)(goes->coderecv)) % NCODES;
  643.     goes->offset[i] = goes->lastref;
  644.     L_SUB(&goes->offset[i], &tstmp);
  645.     if (goes->coderecv == 0)
  646.         for (i = 1; i < NCODES; i++)
  647.             goes->offset[i] = goes->offset[0];
  648.  
  649.     goes->coderecv++;
  650.  
  651.     /*
  652.      * Check the satellite position
  653.      */
  654.     goes_send(goes,"E");
  655.  
  656.     /*
  657.      * Process the median filter, add the fudge factor and pass the
  658.      * offset and dispersion along. We use lastrec as both the
  659.      * reference time and receive time in order to avoid being cute,
  660.      * like setting the reference time later than the receive time,
  661.      * which may cause a paranoid protocol module to chuck out the
  662.      * data.
  663.       */
  664.     if (!goes_process(goes, &tstmp, &dispersion)) {
  665.         goes->baddata++;
  666.         goes_report_event(goes, CEVNT_BADTIME);
  667.         return;
  668.     }
  669.     refclock_receive(goes->peer, &tstmp, GMT, dispersion,
  670.         &goes->lastrec, &goes->lastrec, goes->leap);
  671.  
  672.     /*
  673.      * We have succedded in answering the poll.  Turn off the flag
  674.      */
  675.     goes->polled = 0;
  676. }
  677.  
  678.  
  679. /*
  680.  * goes_send - time to send the clock a signal to cough up a time sample
  681.  */
  682. void
  683. goes_send(goes,cmd)
  684.     struct goesunit *goes;
  685.     char *cmd;
  686. {
  687.     if (!readonlyclockflag[goes->unit]) {
  688.     /*
  689.      * Send a command to the clock.  C for on-second timecodes.
  690.      * E for extended resolution satelite postion information.
  691.      */
  692.         if (write(goes->io.fd, cmd, 1) != 1) {
  693.             syslog(LOG_ERR, "goes clock: write fails to unit %d: %m",
  694.                 goes->unit);
  695.             goes_report_event(goes, CEVNT_FAULT);
  696.         } else {
  697.             goes->polls++;
  698.         }
  699.     }
  700. }
  701.  
  702. /*
  703.  * goes_process - process a pile of samples from the clock
  704.  */
  705. char
  706. goes_process(goes, offset, dispersion)
  707.     struct goesunit *goes;
  708.     l_fp *offset;
  709.     u_fp *dispersion;
  710. {
  711.     register int i, j;
  712.     register u_long tmp_ui, tmp_uf;
  713.     int not_median1, not_median2, median;
  714.     u_fp disp_tmp, disp_tmp2;
  715.  
  716.     /*
  717.      * This code implements a three-stage median filter. First, we
  718.          * check if the samples are within 125 ms of each other. If not,
  719.      * dump the sample set. We take the median of the three offsets
  720.      * and use that as the sample offset. We take the maximum
  721.      * difference and use that as the sample dispersion. There
  722.      * probably is not much to be gained by a longer filter, since
  723.      * the clock filter in ntp_proto should do its thing.
  724.      */
  725.     disp_tmp2 = 0;
  726.     for (i = 0; i < NCODES-1; i++) {
  727.         for (j = i+1; j < NCODES; j++) {
  728.             tmp_ui = goes->offset[i].l_ui;
  729.             tmp_uf = goes->offset[i].l_uf;
  730.             M_SUB(tmp_ui, tmp_uf, goes->offset[j].l_ui,
  731.                 goes->offset[j].l_uf);
  732.             if (M_ISNEG(tmp_ui, tmp_uf)) {
  733.                 M_NEG(tmp_ui, tmp_uf);
  734.             }
  735.             if (tmp_ui != 0 || tmp_uf > CODEDIFF) {
  736.                 return 0;
  737.             }
  738.             disp_tmp = MFPTOFP(0, tmp_uf);
  739.             if (disp_tmp > disp_tmp2) {
  740.                 disp_tmp2 = disp_tmp;
  741.                 not_median1 = i;
  742.                 not_median2 = j;
  743.             }
  744.         }
  745.     }
  746.  
  747.     /*
  748.      * It seems as if all are within 125 ms of each other.
  749.      * Now to determine the median of the three. Whlie the
  750.      * 125 ms check was going on, we also subtly catch the
  751.      * dispersion and set-up for a very easy median calculation.
  752.      * The largest difference between any two samples constitutes
  753.      * the dispersion. The sample not involve in the dispersion is
  754.      * the median sample. EASY!
  755.      */
  756.     if (goes->lasttime == 0 || disp_tmp2 > GOESMAXDISPERSE)
  757.         disp_tmp2 = GOESMAXDISPERSE;
  758.     if (not_median1 == 0) {
  759.         if (not_median2 == 1)
  760.             median = 2;
  761.         else
  762.             median = 1;
  763.         } else {
  764.         median = 0;
  765.         }
  766.     *offset = goes->offset[median];
  767.     *dispersion = disp_tmp2;
  768.     return 1;
  769. }
  770.  
  771. /*
  772.  * goes_poll - called by the transmit procedure
  773.  */
  774. void
  775. goes_poll(unit, peer)
  776.     int unit;
  777.     char *peer;
  778. {
  779.     struct goesunit *goes;
  780.     void goes_send();
  781.  
  782.     /*
  783.      * You don't need to poll this clock.  It puts out timecodes
  784.      * once per second.  If asked for a timestamp, take note.
  785.      * The next time a timecode comes in, it will be fed back.
  786.      */
  787.     if (unit >= MAXUNITS) {
  788.         syslog(LOG_ERR,
  789.             "goes clock poll: INTERNAL: unit %d invalid", unit);
  790.         return;
  791.     }
  792.     if (!unitinuse[unit]) {
  793.         syslog(LOG_ERR,
  794.             "goes clock poll: INTERNAL: unit %d unused", unit);
  795.         return;
  796.     }
  797.     goes = goesunits[unit];
  798.     if ((current_time - goes->lasttime) > 150) {
  799.         goes->noreply++;
  800.         goes_report_event(goesunits[unit], CEVNT_TIMEOUT);
  801.     }
  802.  
  803.     /*
  804.      * polled every 64 seconds.  Ask GOES_RECEIVE to hand in a timestamp.
  805.      */
  806.     goes->polled = 1;
  807.     goes->polls++;
  808.  
  809.     goes_send(goes,"C");
  810. }
  811.  
  812. /*
  813.  * goes_control - set fudge factors, return statistics
  814.  */
  815. void
  816. goes_control(unit, in, out)
  817.     u_int unit;
  818.     struct refclockstat *in;
  819.     struct refclockstat *out;
  820. {
  821.     register struct goesunit *goes;
  822.  
  823.     if (unit >= MAXUNITS) {
  824.         syslog(LOG_ERR,
  825.             "goes clock: unit %d invalid (max %d)", unit, MAXUNITS-1);
  826.         return;
  827.     }
  828.  
  829.     if (in != 0) {
  830.         if (in->haveflags & CLK_HAVETIME1)
  831.             fudgefactor1[unit] = in->fudgetime1;
  832.         if (in->haveflags & CLK_HAVETIME2)
  833.             fudgefactor2[unit] = in->fudgetime2;
  834.         if (in->haveflags & CLK_HAVEVAL1) {
  835.             stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf);
  836.             if (unitinuse[unit]) {
  837.                 struct peer *peer;
  838.  
  839.                 /*
  840.                  * Should actually reselect clock, but
  841.                  * will wait for the next timecode
  842.                  */
  843.                 goes = goesunits[unit];
  844.                 peer = goes->peer;
  845.                 peer->stratum = stratumtouse[unit];
  846.                 if (stratumtouse[unit] <= 1)
  847.                     bcopy(GOESREFID, (char *)&peer->refid,
  848.                         4);
  849.                 else
  850.                     peer->refid = htonl(GOESHSREFID);
  851.             }
  852.         }
  853.         if (in->haveflags & CLK_HAVEFLAG1) {
  854.             readonlyclockflag[unit] = in->flags & CLK_FLAG1;
  855.         }
  856.     }
  857.  
  858.     if (out != 0) {
  859.         out->type = REFCLK_GOES_TRUETIME;
  860.         out->haveflags
  861.             = CLK_HAVETIME1|CLK_HAVETIME2|
  862.             CLK_HAVEVAL1|CLK_HAVEVAL2|
  863.             CLK_HAVEFLAG1|CLK_HAVEFLAG2;
  864.         out->clockdesc = GOESDESCRIPTION;
  865.         out->fudgetime1 = fudgefactor1[unit];
  866.         out->fudgetime2 = fudgefactor2[unit];
  867.         out->fudgeval1 = (long)stratumtouse[unit];
  868.         out->fudgeval2 = 0;
  869.         out->flags = readonlyclockflag[unit] |
  870.             (goes->satellite << 1);
  871.         if (unitinuse[unit]) {
  872.             goes = goesunits[unit];
  873.             out->lencode = goes->lencode;
  874.             out->lastcode = goes->lastcode;
  875.             out->timereset = current_time - goes->timestarted;
  876.             out->polls = goes->polls;
  877.             out->noresponse = goes->noreply;
  878.             out->badformat = goes->badformat;
  879.             out->baddata = goes->baddata;
  880.             out->lastevent = goes->lastevent;
  881.             out->currentstatus = goes->status;
  882.         } else {
  883.             out->lencode = 0;
  884.             out->lastcode = "";
  885.             out->polls = out->noresponse = 0;
  886.             out->badformat = out->baddata = 0;
  887.             out->timereset = 0;
  888.             out->currentstatus = out->lastevent = CEVNT_NOMINAL;
  889.         }
  890.     }
  891. }
  892.  
  893. /*
  894.  * goes_buginfo - return clock dependent debugging info
  895.  */
  896. void
  897. goes_buginfo(unit, bug)
  898.     int unit;
  899.     register struct refclockbug *bug;
  900. {
  901.     register struct goesunit *goes;
  902.     register int i;
  903.     register int n;
  904.  
  905.     if (unit >= MAXUNITS) {
  906.         syslog(LOG_ERR, "goes clock: unit %d invalid (max %d)",
  907.             unit, MAXUNITS-1);
  908.         return;
  909.     }
  910.  
  911.     if (!unitinuse[unit])
  912.         return;
  913.     goes = goesunits[unit];
  914.  
  915.     bug->nvalues = 11;
  916.     bug->ntimes = 5;
  917.     if (goes->lasttime != 0)
  918.         bug->values[0] = current_time - goes->lasttime;
  919.     else
  920.         bug->values[0] = 0;
  921.     bug->values[1] = (u_long)goes->reason;
  922.     bug->values[2] = (u_long)goes->year;
  923.     bug->values[3] = (u_long)goes->day;
  924.     bug->values[4] = (u_long)goes->hour;
  925.     bug->values[5] = (u_long)goes->minute;
  926.     bug->values[6] = (u_long)goes->second;
  927.     bug->values[7] = (u_long)goes->msec;
  928.     bug->values[8] = goes->noreply;
  929.     bug->values[9] = goes->yearstart;
  930.     bug->values[10] = goes->quality;
  931.     bug->stimes = 0x1c;
  932.     bug->times[0] = goes->lastref;
  933.     bug->times[1] = goes->lastrec;
  934.     bug->times[2] = goes->offset[0];
  935.     bug->times[3] = goes->offset[1];
  936.     bug->times[4] = goes->offset[2];
  937. }
  938. #endif
  939.  
  940.