Annotation of embedaddon/ntp/ntpd/refclock_nmea.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:  * refclock_nmea.c - clock driver for an NMEA GPS CLOCK
                      3:  *             Michael Petry Jun 20, 1994
                      4:  *              based on refclock_heathn.c
                      5:  *
                      6:  * Updated to add support for Accord GPS Clock
                      7:  *             Venu Gopal Dec 05, 2007
                      8:  *             neo.venu@gmail.com, venugopal_d@pgad.gov.in
                      9:  *
                     10:  * Updated to process 'time1' fudge factor
                     11:  *             Venu Gopal May 05, 2008
                     12:  *
                     13:  * Converted to common PPSAPI code, separate PPS fudge time1
                     14:  * from serial timecode fudge time2.
                     15:  *             Dave Hart July 1, 2009
                     16:  *             hart@ntp.org, davehart@davehart.com
                     17:  */
                     18: 
                     19: #ifdef HAVE_CONFIG_H
                     20: #include <config.h>
                     21: #endif
                     22: 
                     23: #if defined(REFCLOCK) && defined(CLOCK_NMEA)
                     24: 
                     25: #include <sys/stat.h>
                     26: #include <stdio.h>
                     27: #include <ctype.h>
                     28: 
                     29: #include "ntpd.h"
                     30: #include "ntp_io.h"
                     31: #include "ntp_unixtime.h"
                     32: #include "ntp_refclock.h"
                     33: #include "ntp_stdlib.h"
                     34: #include "ntp_calendar.h"
                     35: 
                     36: #ifdef HAVE_PPSAPI
                     37: # include "ppsapi_timepps.h"
                     38: # include "refclock_atom.h"
                     39: #endif /* HAVE_PPSAPI */
                     40: 
                     41: #ifdef SYS_WINNT
                     42: #undef write   /* ports/winnt/include/config.h: #define write _write */
                     43: extern int async_write(int, const void *, unsigned int);
                     44: #define write(fd, data, octets)        async_write(fd, data, octets)
                     45: #endif
                     46: 
                     47: #ifndef TIMESPECTOTS
                     48: #define TIMESPECTOTS(ptspec, pts)                                      \
                     49:        do {                                                            \
                     50:                DTOLFP((ptspec)->tv_nsec * 1.0e-9, pts);                \
                     51:                (pts)->l_ui += (u_int32)((ptspec)->tv_sec) + JAN_1970;  \
                     52:        } while (0)
                     53: #endif
                     54: 
                     55: 
                     56: /*
                     57:  * This driver supports NMEA-compatible GPS receivers
                     58:  *
                     59:  * Prototype was refclock_trak.c, Thanks a lot.
                     60:  *
                     61:  * The receiver used spits out the NMEA sentences for boat navigation.
                     62:  * And you thought it was an information superhighway. Try a raging river
                     63:  * filled with rapids and whirlpools that rip away your data and warp time.
                     64:  *
                     65:  * If HAVE_PPSAPI is defined code to use the PPSAPI will be compiled in.
                     66:  * On startup if initialization of the PPSAPI fails, it will fall back
                     67:  * to the "normal" timestamps.
                     68:  *
                     69:  * The PPSAPI part of the driver understands fudge flag2 and flag3. If
                     70:  * flag2 is set, it will use the clear edge of the pulse. If flag3 is
                     71:  * set, kernel hardpps is enabled.
                     72:  *
                     73:  * GPS sentences other than RMC (the default) may be enabled by setting
                     74:  * the relevent bits of 'mode' in the server configuration line
                     75:  * server 127.127.20.x mode X
                     76:  * 
                     77:  * bit 0 - enables RMC (1)
                     78:  * bit 1 - enables GGA (2)
                     79:  * bit 2 - enables GLL (4)
                     80:  * bit 3 - enables ZDA (8) - Standard Time & Date
                     81:  * bit 3 - enables ZDG (8) - Accord GPS Clock's custom sentence with GPS time 
                     82:  *                          very close to standard ZDA
                     83:  * 
                     84:  * Multiple sentences may be selected except when ZDG/ZDA is selected.
                     85:  *
                     86:  * bit 4/5/6 - selects the baudrate for serial port :
                     87:  *             0 for 4800 (default) 
                     88:  *             1 for 9600 
                     89:  *             2 for 19200 
                     90:  *             3 for 38400 
                     91:  *             4 for 57600 
                     92:  *             5 for 115200 
                     93:  */
                     94: #define NMEA_MESSAGE_MASK_OLD   0x07
                     95: #define NMEA_MESSAGE_MASK_SINGLE 0x08
                     96: #define NMEA_MESSAGE_MASK       (NMEA_MESSAGE_MASK_OLD | NMEA_MESSAGE_MASK_SINGLE)
                     97: 
                     98: #define NMEA_BAUDRATE_MASK      0x70
                     99: #define NMEA_BAUDRATE_SHIFT     4
                    100: 
                    101: /*
                    102:  * Definitions
                    103:  */
                    104: #define        DEVICE          "/dev/gps%d"    /* GPS serial device */
                    105: #define        PPSDEV          "/dev/gpspps%d" /* PPSAPI device override */
                    106: #define        SPEED232        B4800   /* uart speed (4800 bps) */
                    107: #define        PRECISION       (-9)    /* precision assumed (about 2 ms) */
                    108: #define        PPS_PRECISION   (-20)   /* precision assumed (about 1 us) */
                    109: #define        REFID           "GPS\0" /* reference id */
                    110: #define        DESCRIPTION     "NMEA GPS Clock" /* who we are */
                    111: #ifndef O_NOCTTY
                    112: #define M_NOCTTY       0
                    113: #else
                    114: #define M_NOCTTY       O_NOCTTY
                    115: #endif
                    116: #ifndef O_NONBLOCK
                    117: #define M_NONBLOCK     0
                    118: #else
                    119: #define M_NONBLOCK     O_NONBLOCK
                    120: #endif
                    121: #define PPSOPENMODE    (O_RDWR | M_NOCTTY | M_NONBLOCK)
                    122: 
                    123: /* NMEA sentence array indexes for those we use */
                    124: #define NMEA_GPRMC     0       /* recommended min. nav. */
                    125: #define NMEA_GPGGA     1       /* fix and quality */
                    126: #define NMEA_GPGLL     2       /* geo. lat/long */
                    127: #define NMEA_GPZDA     3       /* date/time */
                    128: /*
                    129:  * $GPZDG is a proprietary sentence that violates the spec, by not
                    130:  * using $P and an assigned company identifier to prefix the sentence
                    131:  * identifier. When used with this driver, the system needs to be
                    132:  * isolated from other NTP networks, as it operates in GPS time, not
                    133:  * UTC as is much more common. GPS time is >15 seconds different from
                    134:  * UTC due to not respecting leap seconds since 1970 or so.  Other
                    135:  * than the different timebase, $GPZDG is similar to $GPZDA.
                    136:  */
                    137: #define NMEA_GPZDG     4
                    138: #define NMEA_ARRAY_SIZE (NMEA_GPZDG + 1)
                    139: 
                    140: /*
                    141:  * Sentence selection mode bits
                    142:  */
                    143: #define USE_ALL                        0       /* any/all */
                    144: #define USE_GPRMC              1
                    145: #define USE_GPGGA              2
                    146: #define USE_GPGLL              4
                    147: #define USE_GPZDA_ZDG          8       /* affects both */
                    148: 
                    149: /* mapping from sentence index to controlling mode bit */
                    150: u_char sentence_mode[NMEA_ARRAY_SIZE] =
                    151: {
                    152:        USE_GPRMC,
                    153:        USE_GPGGA,
                    154:        USE_GPGLL,
                    155:        USE_GPZDA_ZDG,
                    156:        USE_GPZDA_ZDG
                    157: };
                    158: 
                    159: /*
                    160:  * Unit control structure
                    161:  */
                    162: struct nmeaunit {
                    163: #ifdef HAVE_PPSAPI
                    164:        struct refclock_atom atom; /* PPSAPI structure */
                    165:        int     ppsapi_tried;   /* attempt PPSAPI once */
                    166:        int     ppsapi_lit;     /* time_pps_create() worked */
                    167:        int     ppsapi_fd;      /* fd used with PPSAPI */
                    168:        int     ppsapi_gate;    /* allow edge detection processing */
                    169:        int     tcount;         /* timecode sample counter */
                    170:        int     pcount;         /* PPS sample counter */
                    171: #endif /* HAVE_PPSAPI */
                    172:        l_fp    tstamp;         /* timestamp of last poll */
                    173:        int     gps_time;       /* 0 UTC, 1 GPS time */
                    174:                /* per sentence checksum seen flag */
                    175:        struct calendar used;   /* hh:mm:ss of used sentence */
                    176:        u_char  cksum_seen[NMEA_ARRAY_SIZE];
                    177: };
                    178: 
                    179: /*
                    180:  * Function prototypes
                    181:  */
                    182: static int     nmea_start      (int, struct peer *);
                    183: static void    nmea_shutdown   (int, struct peer *);
                    184: static void    nmea_receive    (struct recvbuf *);
                    185: static void    nmea_poll       (int, struct peer *);
                    186: #ifdef HAVE_PPSAPI
                    187: static void    nmea_control    (int, struct refclockstat *,
                    188:                                 struct refclockstat *, struct peer *);
                    189: static void    nmea_timer      (int, struct peer *);
                    190: #define                NMEA_CONTROL    nmea_control
                    191: #define                NMEA_TIMER      nmea_timer
                    192: #else
                    193: #define                NMEA_CONTROL    noentry
                    194: #define                NMEA_TIMER      noentry
                    195: #endif /* HAVE_PPSAPI */
                    196: static void    gps_send        (int, const char *, struct peer *);
                    197: static char *  field_parse     (char *, int);
                    198: static int     nmea_checksum_ok(const char *);
                    199: static void nmea_day_unfold(struct calendar*);
                    200: static void nmea_century_unfold(struct calendar*);
                    201: 
                    202: /*
                    203:  * Transfer vector
                    204:  */
                    205: struct refclock refclock_nmea = {
                    206:        nmea_start,             /* start up driver */
                    207:        nmea_shutdown,          /* shut down driver */
                    208:        nmea_poll,              /* transmit poll message */
                    209:        NMEA_CONTROL,           /* fudge control */
                    210:        noentry,                /* initialize driver */
                    211:        noentry,                /* buginfo */
                    212:        NMEA_TIMER              /* called once per second */
                    213: };
                    214: 
                    215: /*
                    216:  * nmea_start - open the GPS devices and initialize data for processing
                    217:  */
                    218: static int
                    219: nmea_start(
                    220:        int unit,
                    221:        struct peer *peer
                    222:        )
                    223: {
                    224:        register struct nmeaunit *up;
                    225:        struct refclockproc *pp;
                    226:        int fd;
                    227:        char device[20];
                    228:        int baudrate;
                    229:        char *baudtext;
                    230: 
                    231:        pp = peer->procptr;
                    232: 
                    233:        /*
                    234:         * Open serial port. Use CLK line discipline, if available.
                    235:         */
                    236:        snprintf(device, sizeof(device), DEVICE, unit);
                    237:        
                    238:        /*
                    239:         * Opening the serial port with appropriate baudrate
                    240:         * based on the value of bit 4/5/6
                    241:         */
                    242:        switch ((peer->ttl & NMEA_BAUDRATE_MASK) >> NMEA_BAUDRATE_SHIFT) {
                    243:        case 0:
                    244:        case 6:
                    245:        case 7:
                    246:        default:
                    247:                baudrate = SPEED232;
                    248:                baudtext = "4800";
                    249:                break;
                    250:        case 1:
                    251:                baudrate = B9600;
                    252:                baudtext = "9600";
                    253:                break;
                    254:        case 2:
                    255:                baudrate = B19200;
                    256:                baudtext = "19200";
                    257:                break;
                    258:        case 3:
                    259:                baudrate = B38400;
                    260:                baudtext = "38400";
                    261:                break;
                    262: #ifdef B57600
                    263:        case 4:
                    264:                baudrate = B57600;
                    265:                baudtext = "57600";
                    266:                break;
                    267: #endif
                    268: #ifdef B115200
                    269:        case 5:
                    270:                baudrate = B115200;
                    271:                baudtext = "115200";
                    272:                break;
                    273: #endif
                    274:        }
                    275: 
                    276:        fd = refclock_open(device, baudrate, LDISC_CLK);
                    277:        
                    278:        if (fd <= 0) {
                    279: #ifdef HAVE_READLINK
                    280:                /* nmead support added by Jon Miner (cp_n18@yahoo.com)
                    281:                 *
                    282:                 * See http://home.hiwaay.net/~taylorc/gps/nmea-server/
                    283:                 * for information about nmead
                    284:                 *
                    285:                 * To use this, you need to create a link from /dev/gpsX
                    286:                 * to the server:port where nmead is running.  Something
                    287:                 * like this:
                    288:                 *
                    289:                 * ln -s server:port /dev/gps1
                    290:                 */
                    291:                char buffer[80];
                    292:                char *nmea_host, *nmea_tail;
                    293:                int   nmea_port;
                    294:                int   len;
                    295:                struct hostent *he;
                    296:                struct protoent *p;
                    297:                struct sockaddr_in so_addr;
                    298: 
                    299:                if ((len = readlink(device,buffer,sizeof(buffer))) == -1)
                    300:                        return(0);
                    301:                buffer[len] = 0;
                    302: 
                    303:                if ((nmea_host = strtok(buffer,":")) == NULL)
                    304:                        return(0);
                    305:                if ((nmea_tail = strtok(NULL,":")) == NULL)
                    306:                        return(0);
                    307: 
                    308:                nmea_port = atoi(nmea_tail);
                    309: 
                    310:                if ((he = gethostbyname(nmea_host)) == NULL)
                    311:                        return(0);
                    312:                if ((p = getprotobyname("tcp")) == NULL)
                    313:                        return(0);
                    314:                memset(&so_addr, 0, sizeof(so_addr));
                    315:                so_addr.sin_family = AF_INET;
                    316:                so_addr.sin_port = htons(nmea_port);
                    317:                so_addr.sin_addr = *((struct in_addr *) he->h_addr);
                    318: 
                    319:                if ((fd = socket(PF_INET,SOCK_STREAM,p->p_proto)) == -1)
                    320:                        return(0);
                    321:                if (connect(fd,(struct sockaddr *)&so_addr, sizeof(so_addr)) == -1) {
                    322:                        close(fd);
                    323:                        return (0);
                    324:                }
                    325: #else
                    326:                pp->io.fd = -1;
                    327:                return (0);
                    328: #endif
                    329:        }
                    330: 
                    331:        msyslog(LOG_NOTICE, "%s serial %s open at %s bps",
                    332:                refnumtoa(&peer->srcadr), device, baudtext);
                    333: 
                    334:        /*
                    335:         * Allocate and initialize unit structure
                    336:         */
                    337:        up = emalloc(sizeof(*up));
                    338:        memset(up, 0, sizeof(*up));
                    339:        pp->io.clock_recv = nmea_receive;
                    340:        pp->io.srcclock = (caddr_t)peer;
                    341:        pp->io.datalen = 0;
                    342:        pp->io.fd = fd;
                    343:        if (!io_addclock(&pp->io)) {
                    344:                pp->io.fd = -1;
                    345:                close(fd);
                    346:                free(up);
                    347:                return (0);
                    348:        }
                    349:        pp->unitptr = (caddr_t)up;
                    350: 
                    351:        /*
                    352:         * Initialize miscellaneous variables
                    353:         */
                    354:        peer->precision = PRECISION;
                    355:        pp->clockdesc = DESCRIPTION;
                    356:        memcpy(&pp->refid, REFID, 4);
                    357: 
                    358:        gps_send(fd,"$PMOTG,RMC,0000*1D\r\n", peer);
                    359: 
                    360:        return (1);
                    361: }
                    362: 
                    363: 
                    364: /*
                    365:  * nmea_shutdown - shut down a GPS clock
                    366:  * 
                    367:  * NOTE this routine is called after nmea_start() returns failure,
                    368:  * as well as during a normal shutdown due to ntpq :config unpeer.
                    369:  */
                    370: static void
                    371: nmea_shutdown(
                    372:        int unit,
                    373:        struct peer *peer
                    374:        )
                    375: {
                    376:        register struct nmeaunit *up;
                    377:        struct refclockproc *pp;
                    378: 
                    379:        UNUSED_ARG(unit);
                    380: 
                    381:        pp = peer->procptr;
                    382:        up = (struct nmeaunit *)pp->unitptr;
                    383:        if (up != NULL) {
                    384: #ifdef HAVE_PPSAPI
                    385:                if (up->ppsapi_lit) {
                    386:                        time_pps_destroy(up->atom.handle);
                    387:                        if (up->ppsapi_fd != pp->io.fd)
                    388:                                close(up->ppsapi_fd);
                    389:                }
                    390: #endif
                    391:                free(up);
                    392:        }
                    393:        if (-1 != pp->io.fd)
                    394:                io_closeclock(&pp->io);
                    395: }
                    396: 
                    397: /*
                    398:  * nmea_control - configure fudge params
                    399:  */
                    400: #ifdef HAVE_PPSAPI
                    401: static void
                    402: nmea_control(
                    403:        int unit,
                    404:        struct refclockstat *in_st,
                    405:        struct refclockstat *out_st,
                    406:        struct peer *peer
                    407:        )
                    408: {
                    409:        char device[32];
                    410:        register struct nmeaunit *up;
                    411:        struct refclockproc *pp;
                    412:        int pps_fd;
                    413:        
                    414:        UNUSED_ARG(in_st);
                    415:        UNUSED_ARG(out_st);
                    416: 
                    417:        pp = peer->procptr;
                    418:        up = (struct nmeaunit *)pp->unitptr;
                    419: 
                    420:        if (!(CLK_FLAG1 & pp->sloppyclockflag)) {
                    421:                if (!up->ppsapi_tried)
                    422:                        return;
                    423:                up->ppsapi_tried = 0;
                    424:                if (!up->ppsapi_lit)
                    425:                        return;
                    426:                peer->flags &= ~FLAG_PPS;
                    427:                peer->precision = PRECISION;
                    428:                time_pps_destroy(up->atom.handle);
                    429:                if (up->ppsapi_fd != pp->io.fd)
                    430:                        close(up->ppsapi_fd);
                    431:                up->atom.handle = 0;
                    432:                up->ppsapi_lit = 0;
                    433:                up->ppsapi_fd = -1;
                    434:                return;
                    435:        }
                    436: 
                    437:        if (up->ppsapi_tried)
                    438:                return;
                    439:        /*
                    440:         * Light up the PPSAPI interface.
                    441:         */
                    442:        up->ppsapi_tried = 1;
                    443: 
                    444:        /*
                    445:         * if /dev/gpspps$UNIT can be opened that will be used for
                    446:         * PPSAPI.  Otherwise, the GPS serial device /dev/gps$UNIT
                    447:         * already opened is used for PPSAPI as well.
                    448:         */
                    449:        snprintf(device, sizeof(device), PPSDEV, unit);
                    450: 
                    451:        pps_fd = open(device, PPSOPENMODE, S_IRUSR | S_IWUSR);
                    452: 
                    453:        if (-1 == pps_fd)
                    454:                pps_fd = pp->io.fd;
                    455:        
                    456:        if (refclock_ppsapi(pps_fd, &up->atom)) {
                    457:                up->ppsapi_lit = 1;
                    458:                up->ppsapi_fd = pps_fd;
                    459:                /* prepare to use the PPS API for our own purposes now. */
                    460:                refclock_params(pp->sloppyclockflag, &up->atom);
                    461:                return;
                    462:        }
                    463: 
                    464:        NLOG(NLOG_CLOCKINFO)
                    465:                msyslog(LOG_WARNING, "%s flag1 1 but PPSAPI fails",
                    466:                        refnumtoa(&peer->srcadr));
                    467: }
                    468: #endif /* HAVE_PPSAPI */
                    469: 
                    470: 
                    471: /*
                    472:  * nmea_timer - called once per second, fetches PPS
                    473:  *             timestamp and stuffs in median filter.
                    474:  */
                    475: #ifdef HAVE_PPSAPI
                    476: static void
                    477: nmea_timer(
                    478:        int             unit,
                    479:        struct peer *   peer
                    480:        )
                    481: {
                    482:        struct nmeaunit *up;
                    483:        struct refclockproc *pp;
                    484: 
                    485:        UNUSED_ARG(unit);
                    486: 
                    487:        pp = peer->procptr;
                    488:        up = (struct nmeaunit *)pp->unitptr;
                    489: 
                    490:        if (up->ppsapi_lit && up->ppsapi_gate &&
                    491:            refclock_pps(peer, &up->atom, pp->sloppyclockflag) > 0) {
                    492:                up->pcount++,
                    493:                peer->flags |= FLAG_PPS;
                    494:                peer->precision = PPS_PRECISION;
                    495:        }
                    496: }
                    497: #endif /* HAVE_PPSAPI */
                    498: 
                    499: #ifdef HAVE_PPSAPI
                    500: /*
                    501:  * This function is used to correlate a receive time stamp and a
                    502:  * reference time with a PPS edge time stamp. It applies the necessary
                    503:  * fudges (fudge1 for PPS, fudge2 for receive time) and then tries to
                    504:  * move the receive time stamp to the corresponding edge. This can
                    505:  * warp into future, if a transmission delay of more than 500ms is not
                    506:  * compensated with a corresponding fudge time2 value, because then
                    507:  * the next PPS edge is nearer than the last. (Similiar to what the
                    508:  * PPS ATOM driver does, but we deal with full time stamps here, not
                    509:  * just phase shift information.) Likewise, a negative fudge time2
                    510:  * value must be used if the reference time stamp correlates with the
                    511:  * *following* PPS pulse.
                    512:  *
                    513:  * Note that the receive time fudge value only needs to move the receive
                    514:  * stamp near a PPS edge but that close proximity is not required;
                    515:  * +/-100ms precision should be enough. But since the fudge value will
                    516:  * probably also be used to compensate the transmission delay when no PPS
                    517:  * edge can be related to the time stamp, it's best to get it as close
                    518:  * as possible.
                    519:  *
                    520:  * It should also be noted that the typical use case is matching to
                    521:  * the preceeding edge, as most units relate their sentences to the
                    522:  * current second.
                    523:  *
                    524:  * The function returns PPS_RELATE_NONE (0) if no PPS edge correlation
                    525:  * can be fixed; PPS_RELATE_EDGE (1) when a PPS edge could be fixed, but
                    526:  * the distance to the reference time stamp is too big (exceeds +/-400ms)
                    527:  * and the ATOM driver PLL cannot be used to fix the phase; and
                    528:  * PPS_RELATE_PHASE (2) when the ATOM driver PLL code can be used.
                    529:  *
                    530:  * On output, the receive time stamp is replaced with the
                    531:  * corresponding PPS edge time if a fix could be made; the PPS fudge
                    532:  * is updated to reflect the proper fudge time to apply. (This implies
                    533:  * that 'refclock_process_f()' must be used!)
                    534:  */
                    535: #define PPS_RELATE_NONE         0      /* no pps correlation possible    */
                    536: #define PPS_RELATE_EDGE         1      /* recv time fixed, no phase lock */
                    537: #define PPS_RELATE_PHASE 2     /* recv time fixed, phase lock ok */
                    538: 
                    539: static int
                    540: refclock_ppsrelate(
                    541:        const struct refclockproc  *pp      ,   /* for sanity     */
                    542:        const struct refclock_atom *ap      ,   /* for PPS io     */
                    543:        const l_fp                 *reftime ,
                    544:        l_fp                       *rd_stamp,   /* i/o read stamp */
                    545:        double                      pp_fudge,   /* pps fudge      */
                    546:        double                     *rd_fudge)   /* i/o read fudge */
                    547: {
                    548:        pps_info_t      pps_info;
                    549:        struct timespec timeout;
                    550:        l_fp            pp_stamp, pp_delta;
                    551:        double          delta, idelta;
                    552: 
                    553:        if (pp->leap == LEAP_NOTINSYNC)
                    554:                return PPS_RELATE_NONE; /* clock is insane, no chance */
                    555:        
                    556:        memset(&timeout, 0, sizeof(timeout));
                    557:        memset(&pps_info, 0, sizeof(pps_info_t));
                    558: 
                    559:        if (time_pps_fetch(ap->handle, PPS_TSFMT_TSPEC,
                    560:                           &pps_info, &timeout) < 0)
                    561:                return PPS_RELATE_NONE;
                    562: 
                    563:        /* get last active PPS edge before receive */
                    564:        if (ap->pps_params.mode & PPS_CAPTUREASSERT)
                    565:                timeout = pps_info.assert_timestamp;
                    566:        else if (ap->pps_params.mode & PPS_CAPTURECLEAR)
                    567:                timeout = pps_info.clear_timestamp;
                    568:        else
                    569:                return PPS_RELATE_NONE;
                    570: 
                    571:        /* get delta between receive time and PPS time */
                    572:        TIMESPECTOTS(&timeout, &pp_stamp);
                    573:        pp_delta = *rd_stamp;
                    574:        L_SUB(&pp_delta, &pp_stamp);
                    575:        LFPTOD(&pp_delta, delta);
                    576:        delta += pp_fudge - *rd_fudge;
                    577:        if (fabs(delta) > 1.5)
                    578:                return PPS_RELATE_NONE; /* PPS timeout control */
                    579:        
                    580:        /* eventually warp edges, check phase */
                    581:        idelta    = floor(delta + 0.5);
                    582:        pp_fudge -= idelta;
                    583:        delta    -= idelta;
                    584:        if (fabs(delta) > 0.45)
                    585:                return PPS_RELATE_NONE; /* dead band control */
                    586: 
                    587:        /* we actually have a PPS edge to relate with! */
                    588:        *rd_stamp = pp_stamp;
                    589:        *rd_fudge = pp_fudge;
                    590: 
                    591:        /* if whole system out-of-sync, do not try to PLL */
                    592:        if (sys_leap == LEAP_NOTINSYNC)
                    593:                return PPS_RELATE_EDGE; /* cannot PLL with atom code */
                    594: 
                    595:        /* check against reftime if ATOM PLL can be used */
                    596:        pp_delta = *reftime;
                    597:        L_SUB(&pp_delta, &pp_stamp);
                    598:        LFPTOD(&pp_delta, delta);
                    599:        delta += pp_fudge;
                    600:        if (fabs(delta) > 0.45)
                    601:                return PPS_RELATE_EDGE; /* cannot PLL with atom code */
                    602: 
                    603:        /* all checks passed, gets an AAA rating here! */
                    604:        return PPS_RELATE_PHASE; /* can PLL with atom code */
                    605: }
                    606: #endif /* HAVE_PPSAPI */
                    607: 
                    608: /*
                    609:  * nmea_receive - receive data from the serial interface
                    610:  */
                    611: static void
                    612: nmea_receive(
                    613:        struct recvbuf *rbufp
                    614:        )
                    615: {
                    616:        register struct nmeaunit *up;
                    617:        struct refclockproc *pp;
                    618:        struct peer *peer;
                    619:        char *cp, *dp, *msg;
                    620:        u_char sentence;
                    621:        /* Use these variables to hold data until we decide its worth
                    622:         * keeping */
                    623:        char    rd_lastcode[BMAX];
                    624:        l_fp    rd_timestamp, reftime;
                    625:        int     rd_lencode;
                    626:        double  rd_fudge;
                    627:        struct calendar date;
                    628: 
                    629:        /*
                    630:         * Initialize pointers and read the timecode and timestamp
                    631:         */
                    632:        peer = rbufp->recv_peer;
                    633:        pp = peer->procptr;
                    634:        up = (struct nmeaunit *)pp->unitptr;
                    635: 
                    636:        rd_lencode = refclock_gtlin(
                    637:                        rbufp, 
                    638:                        rd_lastcode, 
                    639:                        sizeof(rd_lastcode), 
                    640:                        &rd_timestamp);
                    641: 
                    642:        /*
                    643:         * There is a case that a <CR><LF> gives back a "blank" line.
                    644:         * We can't have a well-formed sentence with less than 8 chars.
                    645:         */
                    646:        if (0 == rd_lencode)
                    647:                return;
                    648: 
                    649:        if (rd_lencode < 8) {
                    650:                refclock_report(peer, CEVNT_BADREPLY);
                    651:                return;
                    652:        }
                    653: 
                    654:        DPRINTF(1, ("nmea: gpsread %d %s\n", rd_lencode, rd_lastcode));
                    655: 
                    656:        /*
                    657:         * We check the timecode format and decode its contents. The
                    658:         * we only care about a few of them.  The most important being
                    659:         * the $GPRMC format
                    660:         * $GPRMC,hhmmss,a,fddmm.xx,n,dddmmm.xx,w,zz.z,yyy.,ddmmyy,dd,v*CC
                    661:         * mode (0,1,2,3) selects sentence ANY/ALL, RMC, GGA, GLL, ZDA
                    662:         * $GPGLL,3513.8385,S,14900.7851,E,232420.594,A*21
                    663:         * $GPGGA,232420.59,3513.8385,S,14900.7851,E,1,05,3.4,00519,M,,,,*3F
                    664:         * $GPRMC,232418.19,A,3513.8386,S,14900.7853,E,00.0,000.0,121199,12.,E*77
                    665:         *
                    666:         * Defining GPZDA to support Standard Time & Date
                    667:         * sentence. The sentence has the following format 
                    668:         *  
                    669:         *  $--ZDA,HHMMSS.SS,DD,MM,YYYY,TH,TM,*CS<CR><LF>
                    670:         *
                    671:         *  Apart from the familiar fields, 
                    672:         *  'TH'    Time zone Hours
                    673:         *  'TM'    Time zone Minutes
                    674:         *
                    675:         * Defining GPZDG to support Accord GPS Clock's custom NMEA 
                    676:         * sentence. The sentence has the following format 
                    677:         *  
                    678:         *  $GPZDG,HHMMSS.S,DD,MM,YYYY,AA.BB,V*CS<CR><LF>
                    679:         *
                    680:         *  It contains the GPS timestamp valid for next PPS pulse.
                    681:         *  Apart from the familiar fields, 
                    682:         *  'AA.BB' denotes the signal strength( should be < 05.00 ) 
                    683:         *  'V'     denotes the GPS sync status : 
                    684:         *         '0' indicates INVALID time, 
                    685:         *         '1' indicates accuracy of +/-20 ms
                    686:         *         '2' indicates accuracy of +/-100 ns
                    687:         */
                    688: 
                    689:        cp = rd_lastcode;
                    690:        if (cp[0] == '$') {
                    691:                /* Allow for GLGGA and GPGGA etc. */
                    692:                msg = cp + 3;
                    693: 
                    694:                if (strncmp(msg, "RMC", 3) == 0)
                    695:                        sentence = NMEA_GPRMC;
                    696:                else if (strncmp(msg, "GGA", 3) == 0)
                    697:                        sentence = NMEA_GPGGA;
                    698:                else if (strncmp(msg, "GLL", 3) == 0)
                    699:                        sentence = NMEA_GPGLL;
                    700:                else if (strncmp(msg, "ZDG", 3) == 0)
                    701:                        sentence = NMEA_GPZDG;
                    702:                else if (strncmp(msg, "ZDA", 3) == 0)
                    703:                        sentence = NMEA_GPZDA;
                    704:                else
                    705:                        return;
                    706:        } else
                    707:                return;
                    708: 
                    709:        /* See if I want to process this message type */
                    710:        if ((peer->ttl & NMEA_MESSAGE_MASK) &&
                    711:           !(peer->ttl & sentence_mode[sentence]))
                    712:                return;
                    713: 
                    714:        /* 
                    715:         * $GPZDG provides GPS time not UTC, and the two mix poorly.
                    716:         * Once have processed a $GPZDG, do not process any further
                    717:         * UTC sentences (all but $GPZDG currently).
                    718:         */
                    719:        if (up->gps_time && NMEA_GPZDG != sentence)
                    720:                return;
                    721: 
                    722:        /*
                    723:         * Apparently, older NMEA specifications (which are expensive)
                    724:         * did not require the checksum for all sentences.  $GPMRC is
                    725:         * the only one so far identified which has always been required
                    726:         * to include a checksum.
                    727:         *
                    728:         * Today, most NMEA GPS receivers checksum every sentence.  To
                    729:         * preserve its error-detection capabilities with modern GPSes
                    730:         * while allowing operation without checksums on all but $GPMRC,
                    731:         * we keep track of whether we've ever seen a checksum on a
                    732:         * given sentence, and if so, reject future checksum failures.
                    733:         */
                    734:        if (nmea_checksum_ok(rd_lastcode)) {
                    735:                up->cksum_seen[sentence] = TRUE;
                    736:        } else if (NMEA_GPRMC == sentence || up->cksum_seen[sentence]) {
                    737:                refclock_report(peer, CEVNT_BADREPLY);
                    738:                return;
                    739:        }
                    740: 
                    741:        cp = rd_lastcode;
                    742: 
                    743:        /* Grab field depending on clock string type */
                    744:        memset(&date, 0, sizeof(date));
                    745:        switch (sentence) {
                    746: 
                    747:        case NMEA_GPRMC:
                    748:                /*
                    749:                 * Test for synchronization.  Check for quality byte.
                    750:                 */
                    751:                dp = field_parse(cp, 2);
                    752:                if (dp[0] != 'A')
                    753:                        pp->leap = LEAP_NOTINSYNC;
                    754:                else
                    755:                        pp->leap = LEAP_NOWARNING;
                    756: 
                    757:                /* Now point at the time field */
                    758:                dp = field_parse(cp, 1);
                    759:                break;
                    760: 
                    761:        case NMEA_GPGGA:
                    762:                /*
                    763:                 * Test for synchronization.  Check for quality byte.
                    764:                 */
                    765:                dp = field_parse(cp, 6);
                    766:                if (dp[0] == '0')
                    767:                        pp->leap = LEAP_NOTINSYNC;
                    768:                else
                    769:                        pp->leap = LEAP_NOWARNING;
                    770: 
                    771:                /* Now point at the time field */
                    772:                dp = field_parse(cp, 1);
                    773:                break;
                    774: 
                    775:        case NMEA_GPGLL:
                    776:                /*
                    777:                 * Test for synchronization.  Check for quality byte.
                    778:                 */
                    779:                dp = field_parse(cp, 6);
                    780:                if (dp[0] != 'A')
                    781:                        pp->leap = LEAP_NOTINSYNC;
                    782:                else
                    783:                        pp->leap = LEAP_NOWARNING;
                    784: 
                    785:                /* Now point at the time field */
                    786:                dp = field_parse(cp, 5);
                    787:                break;
                    788:        
                    789:        case NMEA_GPZDG:
                    790:                /* For $GPZDG check for validity of GPS time. */
                    791:                dp = field_parse(cp, 6);
                    792:                if (dp[0] == '0') 
                    793:                        pp->leap = LEAP_NOTINSYNC;
                    794:                else 
                    795:                        pp->leap = LEAP_NOWARNING;
                    796:                /* fall through to NMEA_GPZDA */
                    797: 
                    798:        case NMEA_GPZDA:
                    799:                if (NMEA_GPZDA == sentence)
                    800:                        pp->leap = LEAP_NOWARNING;
                    801: 
                    802:                /* Now point at the time field */
                    803:                dp = field_parse(cp, 1);
                    804:                break;
                    805: 
                    806:        default:
                    807:                return;
                    808:        }
                    809: 
                    810:        /*
                    811:         * Check time code format of NMEA
                    812:         */
                    813:        if (!isdigit((int)dp[0]) ||
                    814:            !isdigit((int)dp[1]) ||
                    815:            !isdigit((int)dp[2]) ||
                    816:            !isdigit((int)dp[3]) ||
                    817:            !isdigit((int)dp[4]) ||
                    818:            !isdigit((int)dp[5])) {
                    819: 
                    820:                DPRINTF(1, ("NMEA time code %c%c%c%c%c%c non-numeric",
                    821:                            dp[0], dp[1], dp[2], dp[3], dp[4], dp[5]));
                    822:                refclock_report(peer, CEVNT_BADTIME);
                    823:                return;
                    824:        }
                    825: 
                    826:        /*
                    827:         * Convert time and check values.
                    828:         */
                    829:        date.hour = ((dp[0] - '0') * 10) + dp[1] - '0';
                    830:        date.minute = ((dp[2] - '0') * 10) + dp[3] -  '0';
                    831:        date.second = ((dp[4] - '0') * 10) + dp[5] - '0';
                    832:        /* 
                    833:         * Default to 0 milliseconds, if decimal convert milliseconds in
                    834:         * one, two or three digits
                    835:         */
                    836:        pp->nsec = 0; 
                    837:        if (dp[6] == '.') {
                    838:                if (isdigit((int)dp[7])) {
                    839:                        pp->nsec = (dp[7] - '0') * 100000000;
                    840:                        if (isdigit((int)dp[8])) {
                    841:                                pp->nsec += (dp[8] - '0') * 10000000;
                    842:                                if (isdigit((int)dp[9])) {
                    843:                                        pp->nsec += (dp[9] - '0') * 1000000;
                    844:                                }
                    845:                        }
                    846:                }
                    847:        }
                    848: 
                    849:        if (date.hour > 23 || date.minute > 59 || 
                    850:            date.second > 59 || pp->nsec > 1000000000) {
                    851: 
                    852:                DPRINTF(1, ("NMEA hour/min/sec/nsec range %02d:%02d:%02d.%09ld\n",
                    853:                            pp->hour, pp->minute, pp->second, pp->nsec));
                    854:                refclock_report(peer, CEVNT_BADTIME);
                    855:                return;
                    856:        }
                    857: 
                    858:        /*
                    859:         * Used only the first recognized sentence each second.
                    860:         */
                    861:        if (date.hour   == up->used.hour   &&
                    862:            date.minute == up->used.minute &&
                    863:            date.second == up->used.second)
                    864:                return;
                    865: 
                    866:        pp->lencode = (u_short)rd_lencode;
                    867:        memcpy(pp->a_lastcode, rd_lastcode, pp->lencode + 1);
                    868:        up->tstamp = rd_timestamp;
                    869:        pp->lastrec = up->tstamp;
                    870:        DPRINTF(1, ("nmea: timecode %d %s\n", pp->lencode, pp->a_lastcode));
                    871: 
                    872:        /*
                    873:         * Convert date and check values.
                    874:         */
                    875:        if (NMEA_GPRMC == sentence) {
                    876: 
                    877:                dp = field_parse(cp,9);
                    878:                date.monthday = 10 * (dp[0] - '0') + (dp[1] - '0');
                    879:                date.month    = 10 * (dp[2] - '0') + (dp[3] - '0');
                    880:                date.year     = 10 * (dp[4] - '0') + (dp[5] - '0');
                    881:                nmea_century_unfold(&date);
                    882: 
                    883:        } else if (NMEA_GPZDA == sentence || NMEA_GPZDG == sentence) {
                    884: 
                    885:                dp = field_parse(cp, 2);
                    886:                date.monthday = 10 * (dp[0] - '0') + (dp[1] - '0');
                    887:                dp = field_parse(cp, 3);
                    888:                date.month = 10 * (dp[0] - '0') + (dp[1] - '0');
                    889:                dp = field_parse(cp, 4);
                    890:                date.year = 1000 * (dp[0] - '0') + 100 * (dp[1] - '0')
                    891:                          + 10 * (dp[2] - '0') + (dp[3] - '0');
                    892: 
                    893:        } else
                    894:                nmea_day_unfold(&date);
                    895: 
                    896:        if (date.month < 1 || date.month > 12 ||
                    897:            date.monthday < 1 || date.monthday > 31) {
                    898:                refclock_report(peer, CEVNT_BADDATE);
                    899:                return;
                    900:        }
                    901: 
                    902:        up->used.hour = date.hour;
                    903:        up->used.minute = date.minute;
                    904:        up->used.second = date.second;
                    905: 
                    906:        /*
                    907:         * If "fudge 127.127.20.__ flag4 1" is configured in ntp.conf,
                    908:         * remove the location and checksum from the NMEA sentence
                    909:         * recorded as the last timecode and visible to remote users
                    910:         * with:
                    911:         *
                    912:         * ntpq -c clockvar <server>
                    913:         *
                    914:         * Note that this also removes the location from the clockstats
                    915:         * log (if it is enabled).  Some NTP operators monitor their
                    916:         * NMEA GPS using the change in location in clockstats over
                    917:         * time as as a proxy for the quality of GPS reception and
                    918:         * thereby time reported.
                    919:         */
                    920:        if (CLK_FLAG4 & pp->sloppyclockflag) {
                    921:                /*
                    922:                 * Start by pointing cp and dp at the fields with 
                    923:                 * longitude and latitude in the last timecode.
                    924:                 */
                    925:                switch (sentence) {
                    926: 
                    927:                case NMEA_GPGLL:
                    928:                        cp = field_parse(pp->a_lastcode, 1);
                    929:                        dp = field_parse(cp, 2);
                    930:                        break;
                    931: 
                    932:                case NMEA_GPGGA:
                    933:                        cp = field_parse(pp->a_lastcode, 2);
                    934:                        dp = field_parse(cp, 2);
                    935:                        break;
                    936: 
                    937:                case NMEA_GPRMC:
                    938:                        cp = field_parse(pp->a_lastcode, 3);
                    939:                        dp = field_parse(cp, 2);
                    940:                        break;
                    941: 
                    942:                case NMEA_GPZDA:
                    943:                case NMEA_GPZDG:
                    944:                default:
                    945:                        cp = dp = NULL;
                    946:                }
                    947: 
                    948:                /* Blank the entire latitude & longitude. */
                    949:                while (cp) {
                    950:                        while (',' != *cp) {
                    951:                                if ('.' != *cp)
                    952:                                        *cp = '_';
                    953:                                cp++;
                    954:                        }
                    955: 
                    956:                        /* Longitude at cp then latitude at dp */
                    957:                        if (cp < dp)
                    958:                                cp = dp;
                    959:                        else
                    960:                                cp = NULL;
                    961:                }
                    962: 
                    963:                /* Blank the checksum, the last two characters */
                    964:                if (dp) {
                    965:                        cp = pp->a_lastcode + pp->lencode - 2;
                    966:                        if (0 == cp[2])
                    967:                                cp[0] = cp[1] = '_';
                    968:                }
                    969: 
                    970:        }
                    971: 
                    972:        /*
                    973:         * Get the reference time stamp from the calendar buffer.
                    974:         * Process the new sample in the median filter and determine
                    975:         * the timecode timestamp, but only if the PPS is not in
                    976:         * control.
                    977:         */
                    978:        rd_fudge = pp->fudgetime2;
                    979:        date.yearday = 0; /* make sure it's not used */
                    980:        DTOLFP(pp->nsec * 1.0e-9, &reftime);
                    981:        reftime.l_ui += caltontp(&date);
                    982: 
                    983:        /* $GPZDG postprocessing first... */
                    984:        if (NMEA_GPZDG == sentence) {
                    985:                /*
                    986:                 * Note if we're only using GPS timescale from now on.
                    987:                 */
                    988:                if (!up->gps_time) {
                    989:                        up->gps_time = 1;
                    990:                        NLOG(NLOG_CLOCKINFO)
                    991:                        msyslog(LOG_INFO, "%s using only $GPZDG",
                    992:                                refnumtoa(&peer->srcadr));
                    993:                }
                    994:                /*
                    995:                 * $GPZDG indicates the second after the *next* PPS
                    996:                 * pulse. So we remove 1 second from the reference
                    997:                 * time now.
                    998:                 */
                    999:                reftime.l_ui--;
                   1000:        }
                   1001: 
                   1002: #ifdef HAVE_PPSAPI
                   1003:        up->tcount++;
                   1004:        /*
                   1005:         * If we have PPS running, we try to associate the sentence with
                   1006:         * the last active edge of the PPS signal.
                   1007:         */
                   1008:        if (up->ppsapi_lit)
                   1009:                switch (refclock_ppsrelate(pp, &up->atom, &reftime,
                   1010:                                          &rd_timestamp, pp->fudgetime1,
                   1011:                                          &rd_fudge))
                   1012:                {
                   1013:                case PPS_RELATE_EDGE:
                   1014:                        up->ppsapi_gate = 0;
                   1015:                        break;
                   1016:                case PPS_RELATE_PHASE:
                   1017:                        up->ppsapi_gate = 1;
                   1018:                        break;
                   1019:                default:
                   1020:                        break;
                   1021:                }
                   1022:        else 
                   1023:                up->ppsapi_gate = 0;
                   1024: 
                   1025:        if (up->ppsapi_gate && (peer->flags & FLAG_PPS))
                   1026:                return;
                   1027: #endif /* HAVE_PPSAPI */
                   1028: 
                   1029:        refclock_process_offset(pp, reftime, rd_timestamp, rd_fudge);
                   1030: }
                   1031: 
                   1032: 
                   1033: /*
                   1034:  * nmea_poll - called by the transmit procedure
                   1035:  *
                   1036:  * We go to great pains to avoid changing state here, since there may be
                   1037:  * more than one eavesdropper receiving the same timecode.
                   1038:  */
                   1039: static void
                   1040: nmea_poll(
                   1041:        int unit,
                   1042:        struct peer *peer
                   1043:        )
                   1044: {
                   1045:        register struct nmeaunit *up;
                   1046:        struct refclockproc *pp;
                   1047: 
                   1048:        pp = peer->procptr;
                   1049:        up = (struct nmeaunit *)pp->unitptr;
                   1050: 
                   1051:        /*
                   1052:         * Process median filter samples. If none received, declare a
                   1053:         * timeout and keep going.
                   1054:         */
                   1055: #ifdef HAVE_PPSAPI
                   1056:        if (up->pcount == 0) {
                   1057:                peer->flags &= ~FLAG_PPS;
                   1058:                peer->precision = PRECISION;
                   1059:        }
                   1060:        if (up->tcount == 0) {
                   1061:                pp->coderecv = pp->codeproc;
                   1062:                refclock_report(peer, CEVNT_TIMEOUT);
                   1063:                return;
                   1064:        }
                   1065:        up->pcount = up->tcount = 0;
                   1066: #else /* HAVE_PPSAPI */
                   1067:        if (pp->coderecv == pp->codeproc) {
                   1068:                refclock_report(peer, CEVNT_TIMEOUT);
                   1069:                return;
                   1070:        }
                   1071: #endif /* HAVE_PPSAPI */
                   1072: 
                   1073:        pp->polls++;
                   1074:        pp->lastref = pp->lastrec;
                   1075:        refclock_receive(peer);
                   1076:        record_clock_stats(&peer->srcadr, pp->a_lastcode);
                   1077: 
                   1078:        /*
                   1079:         * usually nmea_receive can get a timestamp every second, 
                   1080:         * but at least one Motorola unit needs prompting each
                   1081:         * time.
                   1082:         */
                   1083: 
                   1084:        gps_send(pp->io.fd,"$PMOTG,RMC,0000*1D\r\n", peer);
                   1085: }
                   1086: 
                   1087: 
                   1088: /*
                   1089:  *
                   1090:  *     gps_send(fd,cmd, peer)  Sends a command to the GPS receiver.
                   1091:  *      as     gps_send(fd,"rqts,u\r", peer);
                   1092:  *
                   1093:  *     We don't currently send any data, but would like to send
                   1094:  *     RTCM SC104 messages for differential positioning. It should
                   1095:  *     also give us better time. Without a PPS output, we're
                   1096:  *     Just fooling ourselves because of the serial code paths
                   1097:  *
                   1098:  */
                   1099: static void
                   1100: gps_send(
                   1101:        int fd,
                   1102:        const char *cmd,
                   1103:        struct peer *peer
                   1104:        )
                   1105: {
                   1106:        if (write(fd, cmd, strlen(cmd)) == -1) {
                   1107:                refclock_report(peer, CEVNT_FAULT);
                   1108:        }
                   1109: }
                   1110: 
                   1111: 
                   1112: static char *
                   1113: field_parse(
                   1114:        char *cp,
                   1115:        int fn
                   1116:        )
                   1117: {
                   1118:        char *tp;
                   1119:        int i = fn;
                   1120: 
                   1121:        for (tp = cp; i && *tp; tp++)
                   1122:                if (*tp == ',')
                   1123:                        i--;
                   1124: 
                   1125:        return tp;
                   1126: }
                   1127: 
                   1128: 
                   1129: /*
                   1130:  * nmea_checksum_ok verifies 8-bit XOR checksum is correct then returns 1
                   1131:  *
                   1132:  * format is $XXXXX,1,2,3,4*ML
                   1133:  *
                   1134:  * 8-bit XOR of characters between $ and * noninclusive is transmitted
                   1135:  * in last two chars M and L holding most and least significant nibbles
                   1136:  * in hex representation such as:
                   1137:  *
                   1138:  *   $GPGLL,5057.970,N,00146.110,E,142451,A*27
                   1139:  *   $GPVTG,089.0,T,,,15.2,N,,*7F
                   1140:  */
                   1141: int
                   1142: nmea_checksum_ok(
                   1143:        const char *sentence
                   1144:        )
                   1145: {
                   1146:        u_char my_cs;
                   1147:        u_long input_cs;
                   1148:        const char *p;
                   1149: 
                   1150:        my_cs = 0;
                   1151:        p = sentence;
                   1152: 
                   1153:        if ('$' != *p++)
                   1154:                return 0;
                   1155: 
                   1156:        for ( ; *p && '*' != *p; p++) {
                   1157: 
                   1158:                my_cs ^= *p;
                   1159:        }
                   1160: 
                   1161:        if ('*' != *p++)
                   1162:                return 0;
                   1163: 
                   1164:        if (0 == p[0] || 0 == p[1] || 0 != p[2])
                   1165:                return 0;
                   1166: 
                   1167:        if (0 == hextoint(p, &input_cs))
                   1168:                return 0;
                   1169: 
                   1170:        if (my_cs != input_cs)
                   1171:                return 0;
                   1172: 
                   1173:        return 1;
                   1174: }
                   1175: 
                   1176: /*
                   1177:  * -------------------------------------------------------------------
                   1178:  * funny calendar-oriented stuff -- a bit hard to grok.
                   1179:  * -------------------------------------------------------------------
                   1180:  */
                   1181: /*
                   1182:  * Do a periodic unfolding of a truncated value around a given pivot
                   1183:  * value.
                   1184:  * The result r will hold to pivot <= r < pivot+period (period>0) or
                   1185:  * pivot+period < r <= pivot (period < 0) and value % period == r % period,
                   1186:  * using floor division convention.
                   1187:  */
                   1188: static time_t
                   1189: nmea_periodic_unfold(
                   1190:        time_t pivot,
                   1191:        time_t value,
                   1192:        time_t period)
                   1193: {
                   1194:        /*
                   1195:         * This will only work as long as 'value - pivot%period' does
                   1196:         * not create a signed overflow condition.
                   1197:         */
                   1198:        value = (value - (pivot % period)) % period;
                   1199:        if (value && (value ^ period) < 0)
                   1200:                value += period;
                   1201:        return pivot + value;
                   1202: }
                   1203: 
                   1204: /*
                   1205:  * Unfold a time-of-day (seconds since midnight) around the current
                   1206:  * system time in a manner that guarantees an absolute difference of
                   1207:  * less than 12hrs.
                   1208:  *
                   1209:  * This function is used for NMEA sentences that contain no date
                   1210:  * information. This requires the system clock to be in +/-12hrs
                   1211:  * around the true time, or the clock will synchronize the system 1day
                   1212:  * off if not augmented with a time sources that also provide the
                   1213:  * necessary date information.
                   1214:  *
                   1215:  * The function updates the refclockproc structure is also uses as
                   1216:  * input to fetch the time from.
                   1217:  */
                   1218: static void
                   1219: nmea_day_unfold(
                   1220:        struct calendar *jd)
                   1221: {
                   1222:        time_t value, pivot;
                   1223:        struct tm *tdate;
                   1224: 
                   1225:        value = ((time_t)jd->hour * MINSPERHR
                   1226:                 + (time_t)jd->minute) * SECSPERMIN
                   1227:                  + (time_t)jd->second;
                   1228:        pivot = time(NULL) - SECSPERDAY/2;
                   1229: 
                   1230:        value = nmea_periodic_unfold(pivot, value, SECSPERDAY);
                   1231:        tdate = gmtime(&value);
                   1232:        if (tdate) {
                   1233:                jd->year     = tdate->tm_year + 1900;
                   1234:                jd->yearday  = tdate->tm_yday + 1;
                   1235:                jd->month    = tdate->tm_mon + 1;
                   1236:                jd->monthday = tdate->tm_mday;
                   1237:                jd->hour     = tdate->tm_hour;
                   1238:                jd->minute   = tdate->tm_min;
                   1239:                jd->second   = tdate->tm_sec;
                   1240:        } else {
                   1241:                jd->year     = 0;
                   1242:                jd->yearday  = 0;
                   1243:                jd->month    = 0;
                   1244:                jd->monthday = 0;
                   1245:        }
                   1246: }
                   1247: 
                   1248: /*
                   1249:  * Unfold a 2-digit year into full year spec around the current year
                   1250:  * of the system time. This requires the system clock to be in -79/+19
                   1251:  * years around the true time, or the result will be off by
                   1252:  * 100years. The assymetric behaviour was chosen to enable inital sync
                   1253:  * for systems that do not have a battery-backup-clock and start with
                   1254:  * a date that is typically years in the past.
                   1255:  *
                   1256:  * The function updates the calendar structure that is also used as
                   1257:  * input to fetch the year from.
                   1258:  */
                   1259: static void
                   1260: nmea_century_unfold(
                   1261:        struct calendar *jd)
                   1262: {
                   1263:        time_t     pivot_time;
                   1264:        struct tm *pivot_date;
                   1265:        time_t     pivot_year;
                   1266: 
                   1267:        /* get warp limit and century start of pivot from system time */
                   1268:        pivot_time = time(NULL);
                   1269:        pivot_date = gmtime(&pivot_time);
                   1270:        pivot_year = pivot_date->tm_year + 1900 - 20;
                   1271:        jd->year = nmea_periodic_unfold(pivot_year, jd->year, 100);
                   1272: }
                   1273: 
                   1274: #else
                   1275: int refclock_nmea_bs;
                   1276: #endif /* REFCLOCK && CLOCK_NMEA */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>