Annotation of embedaddon/ntp/ntpd/refclock_mx4200.c, revision 1.1

1.1     ! misho       1: /*
        !             2:  * This software was developed by the Computer Systems Engineering group
        !             3:  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66.
        !             4:  *
        !             5:  * Copyright (c) 1992 The Regents of the University of California.
        !             6:  * All rights reserved.
        !             7:  *
        !             8:  * Redistribution and use in source and binary forms, with or without
        !             9:  * modification, are permitted provided that the following conditions
        !            10:  * are met:
        !            11:  * 1. Redistributions of source code must retain the above copyright
        !            12:  *    notice, this list of conditions and the following disclaimer.
        !            13:  * 2. Redistributions in binary form must reproduce the above copyright
        !            14:  *    notice, this list of conditions and the following disclaimer in the
        !            15:  *    documentation and/or other materials provided with the distribution.
        !            16:  * 3. All advertising materials mentioning features or use of this software
        !            17:  *    must display the following acknowledgement:
        !            18:  *     This product includes software developed by the University of
        !            19:  *     California, Lawrence Berkeley Laboratory.
        !            20:  * 4. The name of the University may not be used to endorse or promote
        !            21:  *    products derived from this software without specific prior
        !            22:  *    written permission.
        !            23:  *
        !            24:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
        !            25:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        !            26:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        !            27:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
        !            28:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        !            29:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
        !            30:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            31:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
        !            32:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
        !            33:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
        !            34:  * SUCH DAMAGE.
        !            35:  */
        !            36: 
        !            37: /*
        !            38:  * Modified: Marc Brett <marc.brett@westgeo.com>   Sept, 1999.
        !            39:  *
        !            40:  * 1. Added support for alternate PPS schemes, with code mostly
        !            41:  *    copied from the Oncore driver (Thanks, Poul-Henning Kamp).
        !            42:  *    This code runs on SunOS 4.1.3 with ppsclock-1.6a1 and Solaris 7.
        !            43:  */
        !            44: 
        !            45: 
        !            46: #ifdef HAVE_CONFIG_H
        !            47: # include <config.h>
        !            48: #endif
        !            49: 
        !            50: #if defined(REFCLOCK) && defined(CLOCK_MX4200) && defined(HAVE_PPSAPI)
        !            51: 
        !            52: #include "ntpd.h"
        !            53: #include "ntp_io.h"
        !            54: #include "ntp_refclock.h"
        !            55: #include "ntp_unixtime.h"
        !            56: #include "ntp_stdlib.h"
        !            57: 
        !            58: #include <stdio.h>
        !            59: #include <ctype.h>
        !            60: 
        !            61: #include "mx4200.h"
        !            62: 
        !            63: #ifdef HAVE_SYS_TERMIOS_H
        !            64: # include <sys/termios.h>
        !            65: #endif
        !            66: #ifdef HAVE_SYS_PPSCLOCK_H
        !            67: # include <sys/ppsclock.h>
        !            68: #endif
        !            69: 
        !            70: #include "ntp_sprintf.h"
        !            71: 
        !            72: #ifndef HAVE_STRUCT_PPSCLOCKEV
        !            73: struct ppsclockev {
        !            74: # ifdef HAVE_STRUCT_TIMESPEC
        !            75:        struct timespec tv;
        !            76: # else
        !            77:        struct timeval tv;
        !            78: # endif
        !            79:        u_int serial;
        !            80: };
        !            81: #endif /* ! HAVE_STRUCT_PPSCLOCKEV */
        !            82: 
        !            83: #ifdef HAVE_PPSAPI
        !            84: # include "ppsapi_timepps.h"
        !            85: #endif /* HAVE_PPSAPI */
        !            86: 
        !            87: /*
        !            88:  * This driver supports the Magnavox Model MX 4200 GPS Receiver
        !            89:  * adapted to precision timing applications.  It requires the
        !            90:  * ppsclock line discipline or streams module described in the
        !            91:  * Line Disciplines and Streams Drivers page. It also requires a
        !            92:  * gadget box and 1-PPS level converter, such as described in the
        !            93:  * Pulse-per-second (PPS) Signal Interfacing page.
        !            94:  *
        !            95:  * It's likely that other compatible Magnavox receivers such as the
        !            96:  * MX 4200D, MX 9212, MX 9012R, MX 9112 will be supported by this code.
        !            97:  */
        !            98: 
        !            99: /*
        !           100:  * Check this every time you edit the code!
        !           101:  */
        !           102: #define YEAR_LAST_MODIFIED 2000
        !           103: 
        !           104: /*
        !           105:  * GPS Definitions
        !           106:  */
        !           107: #define        DEVICE          "/dev/gps%d"    /* device name and unit */
        !           108: #define        SPEED232        B4800           /* baud */
        !           109: 
        !           110: /*
        !           111:  * Radio interface parameters
        !           112:  */
        !           113: #define        PRECISION       (-18)   /* precision assumed (about 4 us) */
        !           114: #define        REFID   "GPS\0"         /* reference id */
        !           115: #define        DESCRIPTION     "Magnavox MX4200 GPS Receiver" /* who we are */
        !           116: #define        DEFFUDGETIME    0       /* default fudge time (ms) */
        !           117: 
        !           118: #define        SLEEPTIME       32      /* seconds to wait for reconfig to complete */
        !           119: 
        !           120: /*
        !           121:  * Position Averaging.
        !           122:  */
        !           123: #define INTERVAL       1       /* Interval between position measurements (s) */
        !           124: #define AVGING_TIME    24      /* Number of hours to average */
        !           125: #define NOT_INITIALIZED        -9999.  /* initial pivot longitude */
        !           126: 
        !           127: /*
        !           128:  * MX4200 unit control structure.
        !           129:  */
        !           130: struct mx4200unit {
        !           131:        u_int  pollcnt;                 /* poll message counter */
        !           132:        u_int  polled;                  /* Hand in a time sample? */
        !           133:        u_int  lastserial;              /* last pps serial number */
        !           134:        struct ppsclockev ppsev;        /* PPS control structure */
        !           135:        double avg_lat;                 /* average latitude */
        !           136:        double avg_lon;                 /* average longitude */
        !           137:        double avg_alt;                 /* average height */
        !           138:        double central_meridian;        /* central meridian */
        !           139:        double N_fixes;                 /* Number of position measurements */
        !           140:        int    last_leap;               /* leap second warning */
        !           141:        u_int  moving;                  /* mobile platform? */
        !           142:        u_long sloppyclockflag;         /* fudge flags */
        !           143:        u_int  known;                   /* position known yet? */
        !           144:        u_long clamp_time;              /* when to stop postion averaging */
        !           145:        u_long log_time;                /* when to print receiver status */
        !           146:        pps_handle_t    pps_h;
        !           147:        pps_params_t    pps_p;
        !           148:        pps_info_t      pps_i;
        !           149: };
        !           150: 
        !           151: static char pmvxg[] = "PMVXG";
        !           152: 
        !           153: /* XXX should be somewhere else */
        !           154: #ifdef __GNUC__
        !           155: #if __GNUC__ < 2  || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)
        !           156: #ifndef __attribute__
        !           157: #define __attribute__(args)
        !           158: #endif /* __attribute__ */
        !           159: #endif /* __GNUC__ < 2  || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) */
        !           160: #else
        !           161: #ifndef __attribute__
        !           162: #define __attribute__(args)
        !           163: #endif /* __attribute__ */
        !           164: #endif /* __GNUC__ */
        !           165: /* XXX end */
        !           166: 
        !           167: /*
        !           168:  * Function prototypes
        !           169:  */
        !           170: static int     mx4200_start    (int, struct peer *);
        !           171: static void    mx4200_shutdown (int, struct peer *);
        !           172: static void    mx4200_receive  (struct recvbuf *);
        !           173: static void    mx4200_poll     (int, struct peer *);
        !           174: 
        !           175: static char *  mx4200_parse_t  (struct peer *);
        !           176: static char *  mx4200_parse_p  (struct peer *);
        !           177: static char *  mx4200_parse_s  (struct peer *);
        !           178: #ifdef QSORT_USES_VOID_P
        !           179: int    mx4200_cmpl_fp  (const void *, const void *);
        !           180: #else
        !           181: int    mx4200_cmpl_fp  (const l_fp *, const l_fp *);
        !           182: #endif /* not QSORT_USES_VOID_P */
        !           183: static int     mx4200_config   (struct peer *);
        !           184: static void    mx4200_ref      (struct peer *);
        !           185: static void    mx4200_send     (struct peer *, char *, ...)
        !           186:     __attribute__ ((format (printf, 2, 3)));
        !           187: static u_char  mx4200_cksum    (char *, int);
        !           188: static int     mx4200_jday     (int, int, int);
        !           189: static void    mx4200_debug    (struct peer *, char *, ...)
        !           190:     __attribute__ ((format (printf, 2, 3)));
        !           191: static int     mx4200_pps      (struct peer *);
        !           192: 
        !           193: /*
        !           194:  * Transfer vector
        !           195:  */
        !           196: struct refclock refclock_mx4200 = {
        !           197:        mx4200_start,           /* start up driver */
        !           198:        mx4200_shutdown,        /* shut down driver */
        !           199:        mx4200_poll,            /* transmit poll message */
        !           200:        noentry,                /* not used (old mx4200_control) */
        !           201:        noentry,                /* initialize driver (not used) */
        !           202:        noentry,                /* not used (old mx4200_buginfo) */
        !           203:        NOFLAGS                 /* not used */
        !           204: };
        !           205: 
        !           206: 
        !           207: 
        !           208: /*
        !           209:  * mx4200_start - open the devices and initialize data for processing
        !           210:  */
        !           211: static int
        !           212: mx4200_start(
        !           213:        int unit,
        !           214:        struct peer *peer
        !           215:        )
        !           216: {
        !           217:        register struct mx4200unit *up;
        !           218:        struct refclockproc *pp;
        !           219:        int fd;
        !           220:        char gpsdev[20];
        !           221: 
        !           222:        /*
        !           223:         * Open serial port
        !           224:         */
        !           225:        snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit);
        !           226:        if (!(fd = refclock_open(gpsdev, SPEED232, LDISC_PPS))) {
        !           227:            return (0);
        !           228:        }
        !           229: 
        !           230:        /*
        !           231:         * Allocate unit structure
        !           232:         */
        !           233:        up = emalloc(sizeof(*up));
        !           234:        memset(up, 0, sizeof(*up));
        !           235:        pp = peer->procptr;
        !           236:        pp->io.clock_recv = mx4200_receive;
        !           237:        pp->io.srcclock = (caddr_t)peer;
        !           238:        pp->io.datalen = 0;
        !           239:        pp->io.fd = fd;
        !           240:        if (!io_addclock(&pp->io)) {
        !           241:                close(fd);
        !           242:                pp->io.fd = -1;
        !           243:                free(up);
        !           244:                return (0);
        !           245:        }
        !           246:        pp->unitptr = (caddr_t)up;
        !           247: 
        !           248:        /*
        !           249:         * Initialize miscellaneous variables
        !           250:         */
        !           251:        peer->precision = PRECISION;
        !           252:        pp->clockdesc = DESCRIPTION;
        !           253:        memcpy((char *)&pp->refid, REFID, 4);
        !           254: 
        !           255:        /* Ensure the receiver is properly configured */
        !           256:        return mx4200_config(peer);
        !           257: }
        !           258: 
        !           259: 
        !           260: /*
        !           261:  * mx4200_shutdown - shut down the clock
        !           262:  */
        !           263: static void
        !           264: mx4200_shutdown(
        !           265:        int unit,
        !           266:        struct peer *peer
        !           267:        )
        !           268: {
        !           269:        register struct mx4200unit *up;
        !           270:        struct refclockproc *pp;
        !           271: 
        !           272:        pp = peer->procptr;
        !           273:        up = (struct mx4200unit *)pp->unitptr;
        !           274:        if (-1 != pp->io.fd)
        !           275:                io_closeclock(&pp->io);
        !           276:        if (NULL != up)
        !           277:                free(up);
        !           278: }
        !           279: 
        !           280: 
        !           281: /*
        !           282:  * mx4200_config - Configure the receiver
        !           283:  */
        !           284: static int
        !           285: mx4200_config(
        !           286:        struct peer *peer
        !           287:        )
        !           288: {
        !           289:        char tr_mode;
        !           290:        int add_mode;
        !           291:        register struct mx4200unit *up;
        !           292:        struct refclockproc *pp;
        !           293:        int mode;
        !           294: 
        !           295:        pp = peer->procptr;
        !           296:        up = (struct mx4200unit *)pp->unitptr;
        !           297: 
        !           298:        /*
        !           299:         * Initialize the unit variables
        !           300:         *
        !           301:         * STRANGE BEHAVIOUR WARNING: The fudge flags are not available
        !           302:         * at the time mx4200_start is called.  These are set later,
        !           303:         * and so the code must be prepared to handle changing flags.
        !           304:         */
        !           305:        up->sloppyclockflag = pp->sloppyclockflag;
        !           306:        if (pp->sloppyclockflag & CLK_FLAG2) {
        !           307:                up->moving   = 1;       /* Receiver on mobile platform */
        !           308:                msyslog(LOG_DEBUG, "mx4200_config: mobile platform");
        !           309:        } else {
        !           310:                up->moving   = 0;       /* Static Installation */
        !           311:        }
        !           312:        up->pollcnt             = 2;
        !           313:        up->polled              = 0;
        !           314:        up->known               = 0;
        !           315:        up->avg_lat             = 0.0;
        !           316:        up->avg_lon             = 0.0;
        !           317:        up->avg_alt             = 0.0;
        !           318:        up->central_meridian    = NOT_INITIALIZED;
        !           319:        up->N_fixes             = 0.0;
        !           320:        up->last_leap           = 0;    /* LEAP_NOWARNING */
        !           321:        up->clamp_time          = current_time + (AVGING_TIME * 60 * 60);
        !           322:        up->log_time            = current_time + SLEEPTIME;
        !           323: 
        !           324:        if (time_pps_create(pp->io.fd, &up->pps_h) < 0) {
        !           325:                perror("time_pps_create");
        !           326:                msyslog(LOG_ERR,
        !           327:                        "mx4200_config: time_pps_create failed: %m");
        !           328:                return (0);
        !           329:        }
        !           330:        if (time_pps_getcap(up->pps_h, &mode) < 0) {
        !           331:                msyslog(LOG_ERR,
        !           332:                        "mx4200_config: time_pps_getcap failed: %m");
        !           333:                return (0);
        !           334:        }
        !           335: 
        !           336:        if (time_pps_getparams(up->pps_h, &up->pps_p) < 0) {
        !           337:                msyslog(LOG_ERR,
        !           338:                        "mx4200_config: time_pps_getparams failed: %m");
        !           339:                return (0);
        !           340:        }
        !           341: 
        !           342:        /* nb. only turn things on, if someone else has turned something
        !           343:         *      on before we get here, leave it alone!
        !           344:         */
        !           345: 
        !           346:        up->pps_p.mode = PPS_CAPTUREASSERT | PPS_TSFMT_TSPEC;
        !           347:        up->pps_p.mode &= mode;         /* only set what is legal */
        !           348: 
        !           349:        if (time_pps_setparams(up->pps_h, &up->pps_p) < 0) {
        !           350:                perror("time_pps_setparams");
        !           351:                msyslog(LOG_ERR,
        !           352:                        "mx4200_config: time_pps_setparams failed: %m");
        !           353:                exit(1);
        !           354:        }
        !           355: 
        !           356:        if (time_pps_kcbind(up->pps_h, PPS_KC_HARDPPS, PPS_CAPTUREASSERT,
        !           357:                        PPS_TSFMT_TSPEC) < 0) {
        !           358:                perror("time_pps_kcbind");
        !           359:                msyslog(LOG_ERR,
        !           360:                        "mx4200_config: time_pps_kcbind failed: %m");
        !           361:                exit(1);
        !           362:        }
        !           363: 
        !           364: 
        !           365:        /*
        !           366:         * "007" Control Port Configuration
        !           367:         * Zero the output list (do it twice to flush possible junk)
        !           368:         */
        !           369:        mx4200_send(peer, "%s,%03d,,%d,,,,,,", pmvxg,
        !           370:            PMVXG_S_PORTCONF,
        !           371:            /* control port output block Label */
        !           372:            1);         /* clear current output control list (1=yes) */
        !           373:        /* add/delete sentences from list */
        !           374:        /* must be null */
        !           375:        /* sentence output rate (sec) */
        !           376:        /* precision for position output */
        !           377:        /* nmea version for cga & gll output */
        !           378:        /* pass-through control */
        !           379:        mx4200_send(peer, "%s,%03d,,%d,,,,,,", pmvxg,
        !           380:            PMVXG_S_PORTCONF, 1);
        !           381: 
        !           382:        /*
        !           383:         * Request software configuration so we can syslog the firmware version
        !           384:         */
        !           385:        mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_SOFTCONF);
        !           386: 
        !           387:        /*
        !           388:         * "001" Initialization/Mode Control, Part A
        !           389:         * Where ARE we?
        !           390:         */
        !           391:        mx4200_send(peer, "%s,%03d,,,,,,,,,,", pmvxg,
        !           392:            PMVXG_S_INITMODEA);
        !           393:        /* day of month */
        !           394:        /* month of year */
        !           395:        /* year */
        !           396:        /* gmt */
        !           397:        /* latitude   DDMM.MMMM */
        !           398:        /* north/south */
        !           399:        /* longitude DDDMM.MMMM */
        !           400:        /* east/west */
        !           401:        /* height */
        !           402:        /* Altitude Reference 1=MSL */
        !           403: 
        !           404:        /*
        !           405:         * "001" Initialization/Mode Control, Part B
        !           406:         * Start off in 2d/3d coast mode, holding altitude to last known
        !           407:         * value if only 3 satellites available.
        !           408:         */
        !           409:        mx4200_send(peer, "%s,%03d,%d,,%.1f,%.1f,%d,%d,%d,%c,%d",
        !           410:            pmvxg, PMVXG_S_INITMODEB,
        !           411:            3,          /* 2d/3d coast */
        !           412:            /* reserved */
        !           413:            0.1,        /* hor accel fact as per Steve (m/s**2) */
        !           414:            0.1,        /* ver accel fact as per Steve (m/s**2) */
        !           415:            10,         /* vdop */
        !           416:            10,         /* hdop limit as per Steve */
        !           417:            5,          /* elevation limit as per Steve (deg) */
        !           418:            'U',        /* time output mode (UTC) */
        !           419:            0);         /* local time offset from gmt (HHHMM) */
        !           420: 
        !           421:        /*
        !           422:         * "023" Time Recovery Configuration
        !           423:         * Get UTC time from a stationary receiver.
        !           424:         * (Set field 1 'D' == dynamic if we are on a moving platform).
        !           425:         * (Set field 1 'S' == static  if we are not moving).
        !           426:         * (Set field 1 'K' == known position if we can initialize lat/lon/alt).
        !           427:         */
        !           428: 
        !           429:        if (pp->sloppyclockflag & CLK_FLAG2)
        !           430:                up->moving   = 1;       /* Receiver on mobile platform */
        !           431:        else
        !           432:                up->moving   = 0;       /* Static Installation */
        !           433: 
        !           434:        up->pollcnt  = 2;
        !           435:        if (up->moving) {
        !           436:                /* dynamic: solve for pos, alt, time, while moving */
        !           437:                tr_mode = 'D';
        !           438:        } else {
        !           439:                /* static: solve for pos, alt, time, while stationary */
        !           440:                tr_mode = 'S';
        !           441:        }
        !           442:        mx4200_send(peer, "%s,%03d,%c,%c,%c,%d,%d,%d,", pmvxg,
        !           443:            PMVXG_S_TRECOVCONF,
        !           444:            tr_mode,    /* time recovery mode (see above ) */
        !           445:            'U',        /* synchronize to UTC */
        !           446:            'A',        /* always output a time pulse */
        !           447:            500,        /* max time error in ns */
        !           448:            0,          /* user bias in ns */
        !           449:            1);         /* output "830" sentences to control port */
        !           450:                        /* Multi-satellite mode */
        !           451: 
        !           452:        /*
        !           453:         * Output position information (to calculate fixed installation
        !           454:         * location) only if we are not moving
        !           455:         */
        !           456:        if (up->moving) {
        !           457:                add_mode = 2;   /* delete from list */
        !           458:        } else {
        !           459:                add_mode = 1;   /* add to list */
        !           460:        }
        !           461: 
        !           462: 
        !           463:        /*
        !           464:         * "007" Control Port Configuration
        !           465:         * Output "021" position, height, velocity reports
        !           466:         */
        !           467:        mx4200_send(peer, "%s,%03d,%03d,%d,%d,,%d,,,", pmvxg,
        !           468:            PMVXG_S_PORTCONF,
        !           469:            PMVXG_D_PHV, /* control port output block Label */
        !           470:            0,          /* clear current output control list (0=no) */
        !           471:            add_mode,   /* add/delete sentences from list (1=add, 2=del) */
        !           472:                        /* must be null */
        !           473:            INTERVAL);  /* sentence output rate (sec) */
        !           474:                        /* precision for position output */
        !           475:                        /* nmea version for cga & gll output */
        !           476:                        /* pass-through control */
        !           477: 
        !           478:        return (1);
        !           479: }
        !           480: 
        !           481: /*
        !           482:  * mx4200_ref - Reconfigure unit as a reference station at a known position.
        !           483:  */
        !           484: static void
        !           485: mx4200_ref(
        !           486:        struct peer *peer
        !           487:        )
        !           488: {
        !           489:        register struct mx4200unit *up;
        !           490:        struct refclockproc *pp;
        !           491:        double minute, lat, lon, alt;
        !           492:        char lats[16], lons[16];
        !           493:        char nsc, ewc;
        !           494: 
        !           495:        pp = peer->procptr;
        !           496:        up = (struct mx4200unit *)pp->unitptr;
        !           497: 
        !           498:        /* Should never happen! */
        !           499:        if (up->moving) return;
        !           500: 
        !           501:        /*
        !           502:         * Set up to output status information in the near future
        !           503:         */
        !           504:        up->log_time    = current_time + SLEEPTIME;
        !           505: 
        !           506:        /*
        !           507:         * "007" Control Port Configuration
        !           508:         * Stop outputting "021" position, height, velocity reports
        !           509:         */
        !           510:        mx4200_send(peer, "%s,%03d,%03d,%d,%d,,,,,", pmvxg,
        !           511:            PMVXG_S_PORTCONF,
        !           512:            PMVXG_D_PHV, /* control port output block Label */
        !           513:            0,          /* clear current output control list (0=no) */
        !           514:            2);         /* add/delete sentences from list (2=delete) */
        !           515:                        /* must be null */
        !           516:                        /* sentence output rate (sec) */
        !           517:                        /* precision for position output */
        !           518:                        /* nmea version for cga & gll output */
        !           519:                        /* pass-through control */
        !           520: 
        !           521:        /*
        !           522:         * "001" Initialization/Mode Control, Part B
        !           523:         * Put receiver in fully-constrained 2d nav mode
        !           524:         */
        !           525:        mx4200_send(peer, "%s,%03d,%d,,%.1f,%.1f,%d,%d,%d,%c,%d",
        !           526:            pmvxg, PMVXG_S_INITMODEB,
        !           527:            2,          /* 2d nav */
        !           528:            /* reserved */
        !           529:            0.1,        /* hor accel fact as per Steve (m/s**2) */
        !           530:            0.1,        /* ver accel fact as per Steve (m/s**2) */
        !           531:            10,         /* vdop */
        !           532:            10,         /* hdop limit as per Steve */
        !           533:            5,          /* elevation limit as per Steve (deg) */
        !           534:            'U',        /* time output mode (UTC) */
        !           535:            0);         /* local time offset from gmt (HHHMM) */
        !           536: 
        !           537:        /*
        !           538:         * "023" Time Recovery Configuration
        !           539:         * Get UTC time from a stationary receiver.  Solve for time only.
        !           540:         * This should improve the time resolution dramatically.
        !           541:         */
        !           542:        mx4200_send(peer, "%s,%03d,%c,%c,%c,%d,%d,%d,", pmvxg,
        !           543:            PMVXG_S_TRECOVCONF,
        !           544:            'K',        /* known position: solve for time only */
        !           545:            'U',        /* synchronize to UTC */
        !           546:            'A',        /* always output a time pulse */
        !           547:            500,        /* max time error in ns */
        !           548:            0,          /* user bias in ns */
        !           549:            1);         /* output "830" sentences to control port */
        !           550:        /* Multi-satellite mode */
        !           551: 
        !           552:        /*
        !           553:         * "000" Initialization/Mode Control - Part A
        !           554:         * Fix to our averaged position.
        !           555:         */
        !           556:        if (up->central_meridian != NOT_INITIALIZED) {
        !           557:                up->avg_lon += up->central_meridian;
        !           558:                if (up->avg_lon < -180.0) up->avg_lon += 360.0;
        !           559:                if (up->avg_lon >  180.0) up->avg_lon -= 360.0;
        !           560:        }
        !           561: 
        !           562:        if (up->avg_lat >= 0.0) {
        !           563:                lat = up->avg_lat;
        !           564:                nsc = 'N';
        !           565:        } else {
        !           566:                lat = up->avg_lat * (-1.0);
        !           567:                nsc = 'S';
        !           568:        }
        !           569:        if (up->avg_lon >= 0.0) {
        !           570:                lon = up->avg_lon;
        !           571:                ewc = 'E';
        !           572:        } else {
        !           573:                lon = up->avg_lon * (-1.0);
        !           574:                ewc = 'W';
        !           575:        }
        !           576:        alt = up->avg_alt;
        !           577:        minute = (lat - (double)(int)lat) * 60.0;
        !           578:        snprintf(lats, sizeof(lats), "%02d%02.4f", (int)lat, minute);
        !           579:        minute = (lon - (double)(int)lon) * 60.0;
        !           580:        snprintf(lons, sizeof(lons), "%03d%02.4f", (int)lon, minute);
        !           581: 
        !           582:        mx4200_send(peer, "%s,%03d,,,,,%s,%c,%s,%c,%.2f,%d", pmvxg,
        !           583:            PMVXG_S_INITMODEA,
        !           584:            /* day of month */
        !           585:            /* month of year */
        !           586:            /* year */
        !           587:            /* gmt */
        !           588:            lats,       /* latitude   DDMM.MMMM */
        !           589:            nsc,        /* north/south */
        !           590:            lons,       /* longitude DDDMM.MMMM */
        !           591:            ewc,        /* east/west */
        !           592:            alt,        /* Altitude */
        !           593:            1);         /* Altitude Reference (0=WGS84 ellipsoid, 1=MSL geoid)*/
        !           594: 
        !           595:        msyslog(LOG_DEBUG,
        !           596:            "mx4200: reconfig to fixed location: %s %c, %s %c, %.2f m",
        !           597:                lats, nsc, lons, ewc, alt );
        !           598: 
        !           599: }
        !           600: 
        !           601: /*
        !           602:  * mx4200_poll - mx4200 watchdog routine
        !           603:  */
        !           604: static void
        !           605: mx4200_poll(
        !           606:        int unit,
        !           607:        struct peer *peer
        !           608:        )
        !           609: {
        !           610:        register struct mx4200unit *up;
        !           611:        struct refclockproc *pp;
        !           612: 
        !           613:        pp = peer->procptr;
        !           614:        up = (struct mx4200unit *)pp->unitptr;
        !           615: 
        !           616:        /*
        !           617:         * You don't need to poll this clock.  It puts out timecodes
        !           618:         * once per second.  If asked for a timestamp, take note.
        !           619:         * The next time a timecode comes in, it will be fed back.
        !           620:         */
        !           621: 
        !           622:        /*
        !           623:         * If we haven't had a response in a while, reset the receiver.
        !           624:         */
        !           625:        if (up->pollcnt > 0) {
        !           626:                up->pollcnt--;
        !           627:        } else {
        !           628:                refclock_report(peer, CEVNT_TIMEOUT);
        !           629: 
        !           630:                /*
        !           631:                 * Request a "000" status message which should trigger a
        !           632:                 * reconfig
        !           633:                 */
        !           634:                mx4200_send(peer, "%s,%03d",
        !           635:                    "CDGPQ",            /* query from CDU to GPS */
        !           636:                    PMVXG_D_STATUS);    /* label of desired sentence */
        !           637:        }
        !           638: 
        !           639:        /*
        !           640:         * polled every 64 seconds. Ask mx4200_receive to hand in
        !           641:         * a timestamp.
        !           642:         */
        !           643:        up->polled = 1;
        !           644:        pp->polls++;
        !           645: 
        !           646:        /*
        !           647:         * Output receiver status information.
        !           648:         */
        !           649:        if ((up->log_time > 0) && (current_time > up->log_time)) {
        !           650:                up->log_time = 0;
        !           651:                /*
        !           652:                 * Output the following messages once, for debugging.
        !           653:                 *    "004" Mode Data
        !           654:                 *    "523" Time Recovery Parameters
        !           655:                 */
        !           656:                mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_MODEDATA);
        !           657:                mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_TRECOVUSEAGE);
        !           658:        }
        !           659: }
        !           660: 
        !           661: static char char2hex[] = "0123456789ABCDEF";
        !           662: 
        !           663: /*
        !           664:  * mx4200_receive - receive gps data
        !           665:  */
        !           666: static void
        !           667: mx4200_receive(
        !           668:        struct recvbuf *rbufp
        !           669:        )
        !           670: {
        !           671:        register struct mx4200unit *up;
        !           672:        struct refclockproc *pp;
        !           673:        struct peer *peer;
        !           674:        char *cp;
        !           675:        int sentence_type;
        !           676:        u_char ck;
        !           677: 
        !           678:        /*
        !           679:         * Initialize pointers and read the timecode and timestamp.
        !           680:         */
        !           681:        peer = (struct peer *)rbufp->recv_srcclock;
        !           682:        pp = peer->procptr;
        !           683:        up = (struct mx4200unit *)pp->unitptr;
        !           684: 
        !           685:        /*
        !           686:         * If operating mode has been changed, then reinitialize the receiver
        !           687:         * before doing anything else.
        !           688:         */
        !           689:        if ((pp->sloppyclockflag & CLK_FLAG2) !=
        !           690:            (up->sloppyclockflag & CLK_FLAG2)) {
        !           691:                up->sloppyclockflag = pp->sloppyclockflag;
        !           692:                mx4200_debug(peer,
        !           693:                    "mx4200_receive: mode switch: reset receiver\n");
        !           694:                mx4200_config(peer);
        !           695:                return;
        !           696:        }
        !           697:        up->sloppyclockflag = pp->sloppyclockflag;
        !           698: 
        !           699:        /*
        !           700:         * Read clock output.  Automatically handles STREAMS, CLKLDISC.
        !           701:         */
        !           702:        pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec);
        !           703: 
        !           704:        /*
        !           705:         * There is a case where <cr><lf> generates 2 timestamps.
        !           706:         */
        !           707:        if (pp->lencode == 0)
        !           708:                return;
        !           709: 
        !           710:        up->pollcnt = 2;
        !           711:        pp->a_lastcode[pp->lencode] = '\0';
        !           712:        record_clock_stats(&peer->srcadr, pp->a_lastcode);
        !           713:        mx4200_debug(peer, "mx4200_receive: %d %s\n",
        !           714:                     pp->lencode, pp->a_lastcode);
        !           715: 
        !           716:        /*
        !           717:         * The structure of the control port sentences is based on the
        !           718:         * NMEA-0183 Standard for interfacing Marine Electronics
        !           719:         * Navigation Devices (Version 1.5)
        !           720:         *
        !           721:         *      $PMVXG,XXX, ....................*CK<cr><lf>
        !           722:         *
        !           723:         *              $       Sentence Start Identifier (reserved char)
        !           724:         *                         (Start-of-Sentence Identifier)
        !           725:         *              P       Special ID (Proprietary)
        !           726:         *              MVX     Originator ID (Magnavox)
        !           727:         *              G       Interface ID (GPS)
        !           728:         *              ,       Field Delimiters (reserved char)
        !           729:         *              XXX     Sentence Type
        !           730:         *              ......  Data
        !           731:         *              *       Checksum Field Delimiter (reserved char)
        !           732:         *              CK      Checksum
        !           733:         *              <cr><lf> Carriage-Return/Line Feed (reserved chars)
        !           734:         *                         (End-of-Sentence Identifier)
        !           735:         *
        !           736:         * Reject if any important landmarks are missing.
        !           737:         */
        !           738:        cp = pp->a_lastcode + pp->lencode - 3;
        !           739:        if (cp < pp->a_lastcode || *pp->a_lastcode != '$' || cp[0] != '*' ) {
        !           740:                mx4200_debug(peer, "mx4200_receive: bad format\n");
        !           741:                refclock_report(peer, CEVNT_BADREPLY);
        !           742:                return;
        !           743:        }
        !           744: 
        !           745:        /*
        !           746:         * Check and discard the checksum
        !           747:         */
        !           748:        ck = mx4200_cksum(&pp->a_lastcode[1], pp->lencode - 4);
        !           749:        if (char2hex[ck >> 4] != cp[1] || char2hex[ck & 0xf] != cp[2]) {
        !           750:                mx4200_debug(peer, "mx4200_receive: bad checksum\n");
        !           751:                refclock_report(peer, CEVNT_BADREPLY);
        !           752:                return;
        !           753:        }
        !           754:        *cp = '\0';
        !           755: 
        !           756:        /*
        !           757:         * Get the sentence type.
        !           758:         */
        !           759:        sentence_type = 0;
        !           760:        if ((cp = strchr(pp->a_lastcode, ',')) == NULL) {
        !           761:                mx4200_debug(peer, "mx4200_receive: no sentence\n");
        !           762:                refclock_report(peer, CEVNT_BADREPLY);
        !           763:                return;
        !           764:        }
        !           765:        cp++;
        !           766:        sentence_type = strtol(cp, &cp, 10);
        !           767: 
        !           768:        /*
        !           769:         * Process the sentence according to its type.
        !           770:         */
        !           771:        switch (sentence_type) {
        !           772: 
        !           773:        /*
        !           774:         * "000" Status message
        !           775:         */
        !           776:        case PMVXG_D_STATUS:
        !           777:                /*
        !           778:                 * XXX
        !           779:                 * Since we configure the receiver to not give us status
        !           780:                 * messages and since the receiver outputs status messages by
        !           781:                 * default after being reset to factory defaults when sent the
        !           782:                 * "$PMVXG,018,C\r\n" message, any status message we get
        !           783:                 * indicates the reciever needs to be initialized; thus, it is
        !           784:                 * not necessary to decode the status message.
        !           785:                 */
        !           786:                if ((cp = mx4200_parse_s(peer)) != NULL) {
        !           787:                        mx4200_debug(peer,
        !           788:                                     "mx4200_receive: status: %s\n", cp);
        !           789:                }
        !           790:                mx4200_debug(peer, "mx4200_receive: reset receiver\n");
        !           791:                mx4200_config(peer);
        !           792:                break;
        !           793: 
        !           794:        /*
        !           795:         * "021" Position, Height, Velocity message,
        !           796:         *  if we are still averaging our position
        !           797:         */
        !           798:        case PMVXG_D_PHV:
        !           799:                if (!up->known) {
        !           800:                        /*
        !           801:                         * Parse the message, calculating our averaged position.
        !           802:                         */
        !           803:                        if ((cp = mx4200_parse_p(peer)) != NULL) {
        !           804:                                mx4200_debug(peer, "mx4200_receive: pos: %s\n", cp);
        !           805:                                return;
        !           806:                        }
        !           807:                        mx4200_debug(peer,
        !           808:                            "mx4200_receive: position avg %f %.9f %.9f %.4f\n",
        !           809:                            up->N_fixes, up->avg_lat, up->avg_lon, up->avg_alt);
        !           810:                        /*
        !           811:                         * Reinitialize as a reference station
        !           812:                         * if position is well known.
        !           813:                         */
        !           814:                        if (current_time > up->clamp_time) {
        !           815:                                up->known++;
        !           816:                                mx4200_debug(peer, "mx4200_receive: reconfiguring!\n");
        !           817:                                mx4200_ref(peer);
        !           818:                        }
        !           819:                }
        !           820:                break;
        !           821: 
        !           822:        /*
        !           823:         * Print to the syslog:
        !           824:         * "004" Mode Data
        !           825:         * "030" Software Configuration
        !           826:         * "523" Time Recovery Parameters Currently in Use
        !           827:         */
        !           828:        case PMVXG_D_MODEDATA:
        !           829:        case PMVXG_D_SOFTCONF:
        !           830:        case PMVXG_D_TRECOVUSEAGE:
        !           831: 
        !           832:                if ((cp = mx4200_parse_s(peer)) != NULL) {
        !           833:                        mx4200_debug(peer,
        !           834:                                     "mx4200_receive: multi-record: %s\n", cp);
        !           835:                }
        !           836:                break;
        !           837: 
        !           838:        /*
        !           839:         * "830" Time Recovery Results message
        !           840:         */
        !           841:        case PMVXG_D_TRECOVOUT:
        !           842: 
        !           843:                /*
        !           844:                 * Capture the last PPS signal.
        !           845:                 * Precision timestamp is returned in pp->lastrec
        !           846:                 */
        !           847:                if (mx4200_pps(peer) != NULL) {
        !           848:                        mx4200_debug(peer, "mx4200_receive: pps failure\n");
        !           849:                        refclock_report(peer, CEVNT_FAULT);
        !           850:                        return;
        !           851:                }
        !           852: 
        !           853: 
        !           854:                /*
        !           855:                 * Parse the time recovery message, and keep the info
        !           856:                 * to print the pretty billboards.
        !           857:                 */
        !           858:                if ((cp = mx4200_parse_t(peer)) != NULL) {
        !           859:                        mx4200_debug(peer, "mx4200_receive: time: %s\n", cp);
        !           860:                        refclock_report(peer, CEVNT_BADREPLY);
        !           861:                        return;
        !           862:                }
        !           863: 
        !           864:                /*
        !           865:                 * Add the new sample to a median filter.
        !           866:                 */
        !           867:                if (!refclock_process(pp)) {
        !           868:                        mx4200_debug(peer,"mx4200_receive: offset: %.6f\n",
        !           869:                            pp->offset);
        !           870:                        refclock_report(peer, CEVNT_BADTIME);
        !           871:                        return;
        !           872:                }
        !           873: 
        !           874:                /*
        !           875:                 * The clock will blurt a timecode every second but we only
        !           876:                 * want one when polled.  If we havn't been polled, bail out.
        !           877:                 */
        !           878:                if (!up->polled)
        !           879:                        return;
        !           880: 
        !           881:                /*
        !           882:                 * Return offset and dispersion to control module.  We use
        !           883:                 * lastrec as both the reference time and receive time in
        !           884:                 * order to avoid being cute, like setting the reference time
        !           885:                 * later than the receive time, which may cause a paranoid
        !           886:                 * protocol module to chuck out the data.
        !           887:                 */
        !           888:                mx4200_debug(peer, "mx4200_receive: process time: ");
        !           889:                mx4200_debug(peer, "%4d-%03d %02d:%02d:%02d at %s, %.6f\n",
        !           890:                    pp->year, pp->day, pp->hour, pp->minute, pp->second,
        !           891:                    prettydate(&pp->lastrec), pp->offset);
        !           892:                pp->lastref = pp->lastrec;
        !           893:                refclock_receive(peer);
        !           894: 
        !           895:                /*
        !           896:                 * We have succeeded in answering the poll.
        !           897:                 * Turn off the flag and return
        !           898:                 */
        !           899:                up->polled = 0;
        !           900:                break;
        !           901: 
        !           902:        /*
        !           903:         * Ignore all other sentence types
        !           904:         */
        !           905:        default:
        !           906:                break;
        !           907: 
        !           908:        } /* switch (sentence_type) */
        !           909: 
        !           910:        return;
        !           911: }
        !           912: 
        !           913: 
        !           914: /*
        !           915:  * Parse a mx4200 time recovery message. Returns a string if error.
        !           916:  *
        !           917:  * A typical message looks like this.  Checksum has already been stripped.
        !           918:  *
        !           919:  *    $PMVXG,830,T,YYYY,MM,DD,HH:MM:SS,U,S,FFFFFF,PPPPP,BBBBBB,LL
        !           920:  *
        !           921:  *     Field   Field Contents
        !           922:  *     -----   --------------
        !           923:  *             Block Label: $PMVXG
        !           924:  *             Sentence Type: 830=Time Recovery Results
        !           925:  *                     This sentence is output approximately 1 second
        !           926:  *                     preceding the 1PPS output.  It indicates the
        !           927:  *                     exact time of the next pulse, whether or not the
        !           928:  *                     time mark will be valid (based on operator-specified
        !           929:  *                     error tolerance), the time to which the pulse is
        !           930:  *                     synchronized, the receiver operating mode,
        !           931:  *                     and the time error of the *last* 1PPS output.
        !           932:  *     1  char Time Mark Valid: T=Valid, F=Not Valid
        !           933:  *     2  int  Year: 1993-
        !           934:  *     3  int  Month of Year: 1-12
        !           935:  *     4  int  Day of Month: 1-31
        !           936:  *     5  int  Time of Day: HH:MM:SS
        !           937:  *     6  char Time Synchronization: U=UTC, G=GPS
        !           938:  *     7  char Time Recovery Mode: D=Dynamic, S=Static,
        !           939:  *                     K=Known Position, N=No Time Recovery
        !           940:  *     8  int  Oscillator Offset: The filter's estimate of the oscillator
        !           941:  *                     frequency error, in parts per billion (ppb).
        !           942:  *     9  int  Time Mark Error: The computed error of the *last* pulse
        !           943:  *                     output, in nanoseconds.
        !           944:  *     10 int  User Time Bias: Operator specified bias, in nanoseconds
        !           945:  *     11 int  Leap Second Flag: Indicates that a leap second will
        !           946:  *                     occur.  This value is usually zero, except during
        !           947:  *                     the week prior to the leap second occurrence, when
        !           948:  *                     this value will be set to +1 or -1.  A value of
        !           949:  *                     +1 indicates that GPS time will be 1 second
        !           950:  *                     further ahead of UTC time.
        !           951:  *
        !           952:  */
        !           953: static char *
        !           954: mx4200_parse_t(
        !           955:        struct peer *peer
        !           956:        )
        !           957: {
        !           958:        struct refclockproc *pp;
        !           959:        struct mx4200unit *up;
        !           960:        char   time_mark_valid, time_sync, op_mode;
        !           961:        int    sentence_type, valid;
        !           962:        int    year, day_of_year, month, day_of_month;
        !           963:        int    hour, minute, second, leapsec_warn;
        !           964:        int    oscillator_offset, time_mark_error, time_bias;
        !           965: 
        !           966:        pp = peer->procptr;
        !           967:        up = (struct mx4200unit *)pp->unitptr;
        !           968: 
        !           969:        leapsec_warn = 0;  /* Not all receivers output leap second warnings (!) */
        !           970:        sscanf(pp->a_lastcode,
        !           971:                "$PMVXG,%d,%c,%d,%d,%d,%d:%d:%d,%c,%c,%d,%d,%d,%d",
        !           972:                &sentence_type, &time_mark_valid, &year, &month, &day_of_month,
        !           973:                &hour, &minute, &second, &time_sync, &op_mode,
        !           974:                &oscillator_offset, &time_mark_error, &time_bias, &leapsec_warn);
        !           975: 
        !           976:        if (sentence_type != PMVXG_D_TRECOVOUT)
        !           977:                return ("wrong rec-type");
        !           978: 
        !           979:        switch (time_mark_valid) {
        !           980:                case 'T':
        !           981:                        valid = 1;
        !           982:                        break;
        !           983:                case 'F':
        !           984:                        valid = 0;
        !           985:                        break;
        !           986:                default:
        !           987:                        return ("bad pulse-valid");
        !           988:        }
        !           989: 
        !           990:        switch (time_sync) {
        !           991:                case 'G':
        !           992:                        return ("synchronized to GPS; should be UTC");
        !           993:                case 'U':
        !           994:                        break; /* UTC -> ok */
        !           995:                default:
        !           996:                        return ("not synchronized to UTC");
        !           997:        }
        !           998: 
        !           999:        /*
        !          1000:         * Check for insane time (allow for possible leap seconds)
        !          1001:         */
        !          1002:        if (second > 60 || minute > 59 || hour > 23 ||
        !          1003:            second <  0 || minute <  0 || hour <  0) {
        !          1004:                mx4200_debug(peer,
        !          1005:                    "mx4200_parse_t: bad time %02d:%02d:%02d",
        !          1006:                    hour, minute, second);
        !          1007:                if (leapsec_warn != 0)
        !          1008:                        mx4200_debug(peer, " (leap %+d\n)", leapsec_warn);
        !          1009:                mx4200_debug(peer, "\n");
        !          1010:                refclock_report(peer, CEVNT_BADTIME);
        !          1011:                return ("bad time");
        !          1012:        }
        !          1013:        if ( second == 60 ) {
        !          1014:                msyslog(LOG_DEBUG,
        !          1015:                    "mx4200: leap second! %02d:%02d:%02d",
        !          1016:                    hour, minute, second);
        !          1017:        }
        !          1018: 
        !          1019:        /*
        !          1020:         * Check for insane date
        !          1021:         * (Certainly can't be any year before this code was last altered!)
        !          1022:         */
        !          1023:        if (day_of_month > 31 || month > 12 ||
        !          1024:            day_of_month <  1 || month <  1 || year < YEAR_LAST_MODIFIED) {
        !          1025:                mx4200_debug(peer,
        !          1026:                    "mx4200_parse_t: bad date (%4d-%02d-%02d)\n",
        !          1027:                    year, month, day_of_month);
        !          1028:                refclock_report(peer, CEVNT_BADDATE);
        !          1029:                return ("bad date");
        !          1030:        }
        !          1031: 
        !          1032:        /*
        !          1033:         * Silly Hack for MX4200:
        !          1034:         * ASCII message is for *next* 1PPS signal, but we have the
        !          1035:         * timestamp for the *last* 1PPS signal.  So we have to subtract
        !          1036:         * a second.  Discard if we are on a month boundary to avoid
        !          1037:         * possible leap seconds and leap days.
        !          1038:         */
        !          1039:        second--;
        !          1040:        if (second < 0) {
        !          1041:                second = 59;
        !          1042:                minute--;
        !          1043:                if (minute < 0) {
        !          1044:                        minute = 59;
        !          1045:                        hour--;
        !          1046:                        if (hour < 0) {
        !          1047:                                hour = 23;
        !          1048:                                day_of_month--;
        !          1049:                                if (day_of_month < 1) {
        !          1050:                                        return ("sorry, month boundary");
        !          1051:                                }
        !          1052:                        }
        !          1053:                }
        !          1054:        }
        !          1055: 
        !          1056:        /*
        !          1057:         * Calculate Julian date
        !          1058:         */
        !          1059:        if (!(day_of_year = mx4200_jday(year, month, day_of_month))) {
        !          1060:                mx4200_debug(peer,
        !          1061:                    "mx4200_parse_t: bad julian date %d (%4d-%02d-%02d)\n",
        !          1062:                    day_of_year, year, month, day_of_month);
        !          1063:                refclock_report(peer, CEVNT_BADDATE);
        !          1064:                return("invalid julian date");
        !          1065:        }
        !          1066: 
        !          1067:        /*
        !          1068:         * Setup leap second indicator
        !          1069:         */
        !          1070:        switch (leapsec_warn) {
        !          1071:                case 0:
        !          1072:                        pp->leap = LEAP_NOWARNING;
        !          1073:                        break;
        !          1074:                case 1:
        !          1075:                        pp->leap = LEAP_ADDSECOND;
        !          1076:                        break;
        !          1077:                case -1:
        !          1078:                        pp->leap = LEAP_DELSECOND;
        !          1079:                        break;
        !          1080:                default:
        !          1081:                        pp->leap = LEAP_NOTINSYNC;
        !          1082:        }
        !          1083: 
        !          1084:        /*
        !          1085:         * Any change to the leap second warning status?
        !          1086:         */
        !          1087:        if (leapsec_warn != up->last_leap ) {
        !          1088:                msyslog(LOG_DEBUG,
        !          1089:                    "mx4200: leap second warning: %d to %d (%d)",
        !          1090:                    up->last_leap, leapsec_warn, pp->leap);
        !          1091:        }
        !          1092:        up->last_leap = leapsec_warn;
        !          1093: 
        !          1094:        /*
        !          1095:         * Copy time data for billboard monitoring.
        !          1096:         */
        !          1097: 
        !          1098:        pp->year   = year;
        !          1099:        pp->day    = day_of_year;
        !          1100:        pp->hour   = hour;
        !          1101:        pp->minute = minute;
        !          1102:        pp->second = second;
        !          1103: 
        !          1104:        /*
        !          1105:         * Toss if sentence is marked invalid
        !          1106:         */
        !          1107:        if (!valid || pp->leap == LEAP_NOTINSYNC) {
        !          1108:                mx4200_debug(peer, "mx4200_parse_t: time mark not valid\n");
        !          1109:                refclock_report(peer, CEVNT_BADTIME);
        !          1110:                return ("pulse invalid");
        !          1111:        }
        !          1112: 
        !          1113:        return (NULL);
        !          1114: }
        !          1115: 
        !          1116: /*
        !          1117:  * Calculate the checksum
        !          1118:  */
        !          1119: static u_char
        !          1120: mx4200_cksum(
        !          1121:        register char *cp,
        !          1122:        register int n
        !          1123:        )
        !          1124: {
        !          1125:        register u_char ck;
        !          1126: 
        !          1127:        for (ck = 0; n-- > 0; cp++)
        !          1128:                ck ^= *cp;
        !          1129:        return (ck);
        !          1130: }
        !          1131: 
        !          1132: /*
        !          1133:  * Tables to compute the day of year.  Viva la leap.
        !          1134:  */
        !          1135: static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        !          1136: static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
        !          1137: 
        !          1138: /*
        !          1139:  * Calculate the the Julian Day
        !          1140:  */
        !          1141: static int
        !          1142: mx4200_jday(
        !          1143:        int year,
        !          1144:        int month,
        !          1145:        int day_of_month
        !          1146:        )
        !          1147: {
        !          1148:        register int day, i;
        !          1149:        int leap_year;
        !          1150: 
        !          1151:        /*
        !          1152:         * Is this a leap year ?
        !          1153:         */
        !          1154:        if (year % 4) {
        !          1155:                leap_year = 0; /* FALSE */
        !          1156:        } else {
        !          1157:                if (year % 100) {
        !          1158:                        leap_year = 1; /* TRUE */
        !          1159:                } else {
        !          1160:                        if (year % 400) {
        !          1161:                                leap_year = 0; /* FALSE */
        !          1162:                        } else {
        !          1163:                                leap_year = 1; /* TRUE */
        !          1164:                        }
        !          1165:                }
        !          1166:        }
        !          1167: 
        !          1168:        /*
        !          1169:         * Calculate the Julian Date
        !          1170:         */
        !          1171:        day = day_of_month;
        !          1172: 
        !          1173:        if (leap_year) {
        !          1174:                /* a leap year */
        !          1175:                if (day > day2tab[month - 1]) {
        !          1176:                        return (0);
        !          1177:                }
        !          1178:                for (i = 0; i < month - 1; i++)
        !          1179:                    day += day2tab[i];
        !          1180:        } else {
        !          1181:                /* not a leap year */
        !          1182:                if (day > day1tab[month - 1]) {
        !          1183:                        return (0);
        !          1184:                }
        !          1185:                for (i = 0; i < month - 1; i++)
        !          1186:                    day += day1tab[i];
        !          1187:        }
        !          1188:        return (day);
        !          1189: }
        !          1190: 
        !          1191: /*
        !          1192:  * Parse a mx4200 position/height/velocity sentence.
        !          1193:  *
        !          1194:  * A typical message looks like this.  Checksum has already been stripped.
        !          1195:  *
        !          1196:  * $PMVXG,021,SSSSSS.SS,DDMM.MMMM,N,DDDMM.MMMM,E,HHHHH.H,GGGG.G,EEEE.E,WWWW.W,MM
        !          1197:  *
        !          1198:  *     Field   Field Contents
        !          1199:  *     -----   --------------
        !          1200:  *             Block Label: $PMVXG
        !          1201:  *             Sentence Type: 021=Position, Height Velocity Data
        !          1202:  *                     This sentence gives the receiver position, height,
        !          1203:  *                     navigation mode, and velocity north/east.
        !          1204:  *                     *This sentence is intended for post-analysis
        !          1205:  *                     applications.*
        !          1206:  *     1 float UTC measurement time (seconds into week)
        !          1207:  *     2 float WGS-84 Lattitude (degrees, minutes)
        !          1208:  *     3  char N=North, S=South
        !          1209:  *     4 float WGS-84 Longitude (degrees, minutes)
        !          1210:  *     5  char E=East, W=West
        !          1211:  *     6 float Altitude (meters above mean sea level)
        !          1212:  *     7 float Geoidal height (meters)
        !          1213:  *     8 float East velocity (m/sec)
        !          1214:  *     9 float West Velocity (m/sec)
        !          1215:  *     10  int Navigation Mode
        !          1216:  *                 Mode if navigating:
        !          1217:  *                     1 = Position from remote device
        !          1218:  *                     2 = 2-D position
        !          1219:  *                     3 = 3-D position
        !          1220:  *                     4 = 2-D differential position
        !          1221:  *                     5 = 3-D differential position
        !          1222:  *                     6 = Static
        !          1223:  *                     8 = Position known -- reference station
        !          1224:  *                     9 = Position known -- Navigator
        !          1225:  *                 Mode if not navigating:
        !          1226:  *                     51 = Too few satellites
        !          1227:  *                     52 = DOPs too large
        !          1228:  *                     53 = Position STD too large
        !          1229:  *                     54 = Velocity STD too large
        !          1230:  *                     55 = Too many iterations for velocity
        !          1231:  *                     56 = Too many iterations for position
        !          1232:  *                     57 = 3 sat startup failed
        !          1233:  *                     58 = Command abort
        !          1234:  */
        !          1235: static char *
        !          1236: mx4200_parse_p(
        !          1237:        struct peer *peer
        !          1238:        )
        !          1239: {
        !          1240:        struct refclockproc *pp;
        !          1241:        struct mx4200unit *up;
        !          1242:        int sentence_type, mode;
        !          1243:        double mtime, lat, lon, alt, geoid, vele, veln;
        !          1244:        char   north_south, east_west;
        !          1245: 
        !          1246:        pp = peer->procptr;
        !          1247:        up = (struct mx4200unit *)pp->unitptr;
        !          1248: 
        !          1249:        /* Should never happen! */
        !          1250:        if (up->moving) return ("mobile platform - no pos!");
        !          1251: 
        !          1252:        sscanf ( pp->a_lastcode,
        !          1253:                "$PMVXG,%d,%lf,%lf,%c,%lf,%c,%lf,%lf,%lf,%lf,%d",
        !          1254:                &sentence_type, &mtime, &lat, &north_south, &lon, &east_west,
        !          1255:                &alt, &geoid, &vele, &veln, &mode);
        !          1256: 
        !          1257:        /* Sentence type */
        !          1258:        if (sentence_type != PMVXG_D_PHV)
        !          1259:                return ("wrong rec-type");
        !          1260: 
        !          1261:        /*
        !          1262:         * return if not navigating
        !          1263:         */
        !          1264:        if (mode > 10)
        !          1265:                return ("not navigating");
        !          1266:        if (mode != 3 && mode != 5)
        !          1267:                return ("not navigating in 3D");
        !          1268: 
        !          1269:        /* Latitude (always +ve) and convert DDMM.MMMM to decimal */
        !          1270:        if (lat <  0.0) return ("negative latitude");
        !          1271:        if (lat > 9000.0) lat = 9000.0;
        !          1272:        lat *= 0.01;
        !          1273:        lat = ((int)lat) + (((lat - (int)lat)) * 1.6666666666666666);
        !          1274: 
        !          1275:        /* North/South */
        !          1276:        switch (north_south) {
        !          1277:                case 'N':
        !          1278:                        break;
        !          1279:                case 'S':
        !          1280:                        lat *= -1.0;
        !          1281:                        break;
        !          1282:                default:
        !          1283:                        return ("invalid north/south indicator");
        !          1284:        }
        !          1285: 
        !          1286:        /* Longitude (always +ve) and convert DDDMM.MMMM to decimal */
        !          1287:        if (lon <   0.0) return ("negative longitude");
        !          1288:        if (lon > 180.0) lon = 180.0;
        !          1289:        lon *= 0.01;
        !          1290:        lon = ((int)lon) + (((lon - (int)lon)) * 1.6666666666666666);
        !          1291: 
        !          1292:        /* East/West */
        !          1293:        switch (east_west) {
        !          1294:                case 'E':
        !          1295:                        break;
        !          1296:                case 'W':
        !          1297:                        lon *= -1.0;
        !          1298:                        break;
        !          1299:                default:
        !          1300:                        return ("invalid east/west indicator");
        !          1301:        }
        !          1302: 
        !          1303:        /*
        !          1304:         * Normalize longitude to near 0 degrees.
        !          1305:         * Assume all data are clustered around first reading.
        !          1306:         */
        !          1307:        if (up->central_meridian == NOT_INITIALIZED) {
        !          1308:                up->central_meridian = lon;
        !          1309:                mx4200_debug(peer,
        !          1310:                    "mx4200_receive: central meridian =  %.9f \n",
        !          1311:                    up->central_meridian);
        !          1312:        }
        !          1313:        lon -= up->central_meridian;
        !          1314:        if (lon < -180.0) lon += 360.0;
        !          1315:        if (lon >  180.0) lon -= 360.0;
        !          1316: 
        !          1317:        /*
        !          1318:         * Calculate running averages
        !          1319:         */
        !          1320: 
        !          1321:        up->avg_lon = (up->N_fixes * up->avg_lon) + lon;
        !          1322:        up->avg_lat = (up->N_fixes * up->avg_lat) + lat;
        !          1323:        up->avg_alt = (up->N_fixes * up->avg_alt) + alt;
        !          1324: 
        !          1325:        up->N_fixes += 1.0;
        !          1326: 
        !          1327:        up->avg_lon /= up->N_fixes;
        !          1328:        up->avg_lat /= up->N_fixes;
        !          1329:        up->avg_alt /= up->N_fixes;
        !          1330: 
        !          1331:        mx4200_debug(peer,
        !          1332:            "mx4200_receive: position rdg %.0f: %.9f %.9f %.4f (CM=%.9f)\n",
        !          1333:            up->N_fixes, lat, lon, alt, up->central_meridian);
        !          1334: 
        !          1335:        return (NULL);
        !          1336: }
        !          1337: 
        !          1338: /*
        !          1339:  * Parse a mx4200 Status sentence
        !          1340:  * Parse a mx4200 Mode Data sentence
        !          1341:  * Parse a mx4200 Software Configuration sentence
        !          1342:  * Parse a mx4200 Time Recovery Parameters Currently in Use sentence
        !          1343:  * (used only for logging raw strings)
        !          1344:  *
        !          1345:  * A typical message looks like this.  Checksum has already been stripped.
        !          1346:  *
        !          1347:  * $PMVXG,000,XXX,XX,X,HHMM,X
        !          1348:  *
        !          1349:  *     Field   Field Contents
        !          1350:  *     -----   --------------
        !          1351:  *             Block Label: $PMVXG
        !          1352:  *             Sentence Type: 000=Status.
        !          1353:  *                     Returns status of the receiver to the controller.
        !          1354:  *     1       Current Receiver Status:
        !          1355:  *             ACQ = Satellite re-acquisition
        !          1356:  *             ALT = Constellation selection
        !          1357:  *             COR = Providing corrections (for reference stations only)
        !          1358:  *             IAC = Initial acquisition
        !          1359:  *             IDL = Idle, no satellites
        !          1360:  *             NAV = Navigation
        !          1361:  *             STS = Search the Sky (no almanac available)
        !          1362:  *             TRK = Tracking
        !          1363:  *     2       Number of satellites that should be visible
        !          1364:  *     3       Number of satellites being tracked
        !          1365:  *     4       Time since last navigation status if not currently navigating
        !          1366:  *             (hours, minutes)
        !          1367:  *     5       Initialization status:
        !          1368:  *             0 = Waiting for initialization parameters
        !          1369:  *             1 = Initialization completed
        !          1370:  *
        !          1371:  * A typical message looks like this.  Checksum has already been stripped.
        !          1372:  *
        !          1373:  * $PMVXG,004,C,R,D,H.HH,V.VV,TT,HHHH,VVVV,T
        !          1374:  *
        !          1375:  *     Field   Field Contents
        !          1376:  *     -----   --------------
        !          1377:  *             Block Label: $PMVXG
        !          1378:  *             Sentence Type: 004=Software Configuration.
        !          1379:  *                     Defines the navigation mode and criteria for
        !          1380:  *                     acceptable navigation for the receiver.
        !          1381:  *     1       Constrain Altitude Mode:
        !          1382:  *             0 = Auto.  Constrain altitude (2-D solution) and use
        !          1383:  *                 manual altitude input when 3 sats avalable.  Do
        !          1384:  *                 not constrain altitude (3-D solution) when 4 sats
        !          1385:  *                 available.
        !          1386:  *             1 = Always constrain altitude (2-D solution).
        !          1387:  *             2 = Never constrain altitude (3-D solution).
        !          1388:  *             3 = Coast.  Constrain altitude (2-D solution) and use
        !          1389:  *                 last GPS altitude calculation when 3 sats avalable.
        !          1390:  *                 Do not constrain altitude (3-D solution) when 4 sats
        !          1391:  *                 available.
        !          1392:  *     2       Altitude Reference: (always 0 for MX4200)
        !          1393:  *             0 = Ellipsoid
        !          1394:  *             1 = Geoid (MSL)
        !          1395:  *     3       Differential Navigation Control:
        !          1396:  *             0 = Disabled
        !          1397:  *             1 = Enabled
        !          1398:  *     4       Horizontal Acceleration Constant (m/sec**2)
        !          1399:  *     5       Vertical Acceleration Constant (m/sec**2) (0 for MX4200)
        !          1400:  *     6       Tracking Elevation Limit (degrees)
        !          1401:  *     7       HDOP Limit
        !          1402:  *     8       VDOP Limit
        !          1403:  *     9       Time Output Mode:
        !          1404:  *             U = UTC
        !          1405:  *             L = Local time
        !          1406:  *     10      Local Time Offset (minutes) (absent on MX4200)
        !          1407:  *
        !          1408:  * A typical message looks like this.  Checksum has already been stripped.
        !          1409:  *
        !          1410:  * $PMVXG,030,NNNN,FFF
        !          1411:  *
        !          1412:  *     Field   Field Contents
        !          1413:  *     -----   --------------
        !          1414:  *             Block Label: $PMVXG
        !          1415:  *             Sentence Type: 030=Software Configuration.
        !          1416:  *                     This sentence contains the navigation processor
        !          1417:  *                     and baseband firmware version numbers.
        !          1418:  *     1       Nav Processor Version Number
        !          1419:  *     2       Baseband Firmware Version Number
        !          1420:  *
        !          1421:  * A typical message looks like this.  Checksum has already been stripped.
        !          1422:  *
        !          1423:  * $PMVXG,523,M,S,M,EEEE,BBBBBB,C,R
        !          1424:  *
        !          1425:  *     Field   Field Contents
        !          1426:  *     -----   --------------
        !          1427:  *             Block Label: $PMVXG
        !          1428:  *             Sentence Type: 523=Time Recovery Parameters Currently in Use.
        !          1429:  *                     This sentence contains the configuration of the
        !          1430:  *                     time recovery feature of the receiver.
        !          1431:  *     1       Time Recovery Mode:
        !          1432:  *             D = Dynamic; solve for position and time while moving
        !          1433:  *             S = Static; solve for position and time while stationary
        !          1434:  *             K = Known position input, solve for time only
        !          1435:  *             N = No time recovery
        !          1436:  *     2       Time Synchronization:
        !          1437:  *             U = UTC time
        !          1438:  *             G = GPS time
        !          1439:  *     3       Time Mark Mode:
        !          1440:  *             A = Always output a time pulse
        !          1441:  *             V = Only output time pulse if time is valid (as determined
        !          1442:  *                 by Maximum Time Error)
        !          1443:  *     4       Maximum Time Error - the maximum error (in nanoseconds) for
        !          1444:  *             which a time mark will be considered valid.
        !          1445:  *     5       User Time Bias - external bias in nanoseconds
        !          1446:  *     6       Time Message Control:
        !          1447:  *             0 = Do not output the time recovery message
        !          1448:  *             1 = Output the time recovery message (record 830) to
        !          1449:  *                 Control port
        !          1450:  *             2 = Output the time recovery message (record 830) to
        !          1451:  *                 Equipment port
        !          1452:  *     7       Reserved
        !          1453:  *     8       Position Known PRN (absent on MX 4200)
        !          1454:  *
        !          1455:  */
        !          1456: static char *
        !          1457: mx4200_parse_s(
        !          1458:        struct peer *peer
        !          1459:        )
        !          1460: {
        !          1461:        struct refclockproc *pp;
        !          1462:        struct mx4200unit *up;
        !          1463:        int sentence_type;
        !          1464: 
        !          1465:        pp = peer->procptr;
        !          1466:        up = (struct mx4200unit *)pp->unitptr;
        !          1467: 
        !          1468:         sscanf ( pp->a_lastcode, "$PMVXG,%d", &sentence_type);
        !          1469: 
        !          1470:        /* Sentence type */
        !          1471:        switch (sentence_type) {
        !          1472: 
        !          1473:                case PMVXG_D_STATUS:
        !          1474:                        msyslog(LOG_DEBUG,
        !          1475:                          "mx4200: status: %s", pp->a_lastcode);
        !          1476:                        break;
        !          1477:                case PMVXG_D_MODEDATA:
        !          1478:                        msyslog(LOG_DEBUG,
        !          1479:                          "mx4200: mode data: %s", pp->a_lastcode);
        !          1480:                        break;
        !          1481:                case PMVXG_D_SOFTCONF:
        !          1482:                        msyslog(LOG_DEBUG,
        !          1483:                          "mx4200: firmware configuration: %s", pp->a_lastcode);
        !          1484:                        break;
        !          1485:                case PMVXG_D_TRECOVUSEAGE:
        !          1486:                        msyslog(LOG_DEBUG,
        !          1487:                          "mx4200: time recovery parms: %s", pp->a_lastcode);
        !          1488:                        break;
        !          1489:                default:
        !          1490:                        return ("wrong rec-type");
        !          1491:        }
        !          1492: 
        !          1493:        return (NULL);
        !          1494: }
        !          1495: 
        !          1496: /*
        !          1497:  * Process a PPS signal, placing a timestamp in pp->lastrec.
        !          1498:  */
        !          1499: static int
        !          1500: mx4200_pps(
        !          1501:        struct peer *peer
        !          1502:        )
        !          1503: {
        !          1504:        int temp_serial;
        !          1505:        struct refclockproc *pp;
        !          1506:        struct mx4200unit *up;
        !          1507: 
        !          1508:        struct timespec timeout;
        !          1509: 
        !          1510:        pp = peer->procptr;
        !          1511:        up = (struct mx4200unit *)pp->unitptr;
        !          1512: 
        !          1513:        /*
        !          1514:         * Grab the timestamp of the PPS signal.
        !          1515:         */
        !          1516:        temp_serial = up->pps_i.assert_sequence;
        !          1517:        timeout.tv_sec  = 0;
        !          1518:        timeout.tv_nsec = 0;
        !          1519:        if (time_pps_fetch(up->pps_h, PPS_TSFMT_TSPEC, &(up->pps_i),
        !          1520:                        &timeout) < 0) {
        !          1521:                mx4200_debug(peer,
        !          1522:                  "mx4200_pps: time_pps_fetch: serial=%lu, %s\n",
        !          1523:                     (unsigned long)up->pps_i.assert_sequence, strerror(errno));
        !          1524:                refclock_report(peer, CEVNT_FAULT);
        !          1525:                return(1);
        !          1526:        }
        !          1527:        if (temp_serial == up->pps_i.assert_sequence) {
        !          1528:                mx4200_debug(peer,
        !          1529:                   "mx4200_pps: assert_sequence serial not incrementing: %lu\n",
        !          1530:                        (unsigned long)up->pps_i.assert_sequence);
        !          1531:                refclock_report(peer, CEVNT_FAULT);
        !          1532:                return(1);
        !          1533:        }
        !          1534:        /*
        !          1535:         * Check pps serial number against last one
        !          1536:         */
        !          1537:        if (up->lastserial + 1 != up->pps_i.assert_sequence &&
        !          1538:            up->lastserial != 0) {
        !          1539:                if (up->pps_i.assert_sequence == up->lastserial) {
        !          1540:                        mx4200_debug(peer, "mx4200_pps: no new pps event\n");
        !          1541:                } else {
        !          1542:                        mx4200_debug(peer, "mx4200_pps: missed %lu pps events\n",
        !          1543:                            up->pps_i.assert_sequence - up->lastserial - 1UL);
        !          1544:                }
        !          1545:                refclock_report(peer, CEVNT_FAULT);
        !          1546:        }
        !          1547:        up->lastserial = up->pps_i.assert_sequence;
        !          1548: 
        !          1549:        /*
        !          1550:         * Return the timestamp in pp->lastrec
        !          1551:         */
        !          1552: 
        !          1553:        pp->lastrec.l_ui = up->pps_i.assert_timestamp.tv_sec +
        !          1554:                           (u_int32) JAN_1970;
        !          1555:        pp->lastrec.l_uf = ((double)(up->pps_i.assert_timestamp.tv_nsec) *
        !          1556:                           4.2949672960) + 0.5;
        !          1557: 
        !          1558:        return(0);
        !          1559: }
        !          1560: 
        !          1561: /*
        !          1562:  * mx4200_debug - print debug messages
        !          1563:  */
        !          1564: #if defined(__STDC__)
        !          1565: static void
        !          1566: mx4200_debug(struct peer *peer, char *fmt, ...)
        !          1567: #else
        !          1568: static void
        !          1569: mx4200_debug(peer, fmt, va_alist)
        !          1570:      struct peer *peer;
        !          1571:      char *fmt;
        !          1572: #endif /* __STDC__ */
        !          1573: {
        !          1574: #ifdef DEBUG
        !          1575:        va_list ap;
        !          1576:        struct refclockproc *pp;
        !          1577:        struct mx4200unit *up;
        !          1578: 
        !          1579:        if (debug) {
        !          1580: 
        !          1581: #if defined(__STDC__)
        !          1582:                va_start(ap, fmt);
        !          1583: #else
        !          1584:                va_start(ap);
        !          1585: #endif /* __STDC__ */
        !          1586: 
        !          1587:                pp = peer->procptr;
        !          1588:                up = (struct mx4200unit *)pp->unitptr;
        !          1589: 
        !          1590: 
        !          1591:                /*
        !          1592:                 * Print debug message to stdout
        !          1593:                 * In the future, we may want to get get more creative...
        !          1594:                 */
        !          1595:                vprintf(fmt, ap);
        !          1596: 
        !          1597:                va_end(ap);
        !          1598:        }
        !          1599: #endif
        !          1600: }
        !          1601: 
        !          1602: /*
        !          1603:  * Send a character string to the receiver.  Checksum is appended here.
        !          1604:  */
        !          1605: #if defined(__STDC__)
        !          1606: static void
        !          1607: mx4200_send(struct peer *peer, char *fmt, ...)
        !          1608: #else
        !          1609: static void
        !          1610: mx4200_send(peer, fmt, va_alist)
        !          1611:      struct peer *peer;
        !          1612:      char *fmt;
        !          1613:      va_dcl
        !          1614: #endif /* __STDC__ */
        !          1615: {
        !          1616:        struct refclockproc *pp;
        !          1617:        struct mx4200unit *up;
        !          1618: 
        !          1619:        register char *cp;
        !          1620:        register int n, m;
        !          1621:        va_list ap;
        !          1622:        char buf[1024];
        !          1623:        u_char ck;
        !          1624: 
        !          1625: #if defined(__STDC__)
        !          1626:        va_start(ap, fmt);
        !          1627: #else
        !          1628:        va_start(ap);
        !          1629: #endif /* __STDC__ */
        !          1630: 
        !          1631:        pp = peer->procptr;
        !          1632:        up = (struct mx4200unit *)pp->unitptr;
        !          1633: 
        !          1634:        cp = buf;
        !          1635:        *cp++ = '$';
        !          1636:        n = VSNPRINTF((cp, sizeof(buf) - 1, fmt, ap));
        !          1637:        ck = mx4200_cksum(cp, n);
        !          1638:        cp += n;
        !          1639:        ++n;
        !          1640:        n += SNPRINTF((cp, sizeof(buf) - n - 5, "*%02X\r\n", ck));
        !          1641: 
        !          1642:        m = write(pp->io.fd, buf, (unsigned)n);
        !          1643:        if (m < 0)
        !          1644:                msyslog(LOG_ERR, "mx4200_send: write: %m (%s)", buf);
        !          1645:        mx4200_debug(peer, "mx4200_send: %d %s\n", m, buf);
        !          1646:        va_end(ap);
        !          1647: }
        !          1648: 
        !          1649: #else
        !          1650: int refclock_mx4200_bs;
        !          1651: #endif /* REFCLOCK */

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