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