Annotation of embedaddon/ntp/ntpd/refclock_mx4200.c, revision 1.1.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>