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