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>