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 | #ifdef HAVE_CONFIG_H |
---|
38 | # include <config.h> |
---|
39 | #endif |
---|
40 | |
---|
41 | #if defined(REFCLOCK) && defined(MX4200) && defined(PPS) |
---|
42 | |
---|
43 | #include <stdio.h> |
---|
44 | #include <ctype.h> |
---|
45 | #include <sys/time.h> |
---|
46 | #include <errno.h> |
---|
47 | |
---|
48 | #include "ntpd.h" |
---|
49 | #include "ntp_io.h" |
---|
50 | #include "ntp_refclock.h" |
---|
51 | #include "ntp_unixtime.h" |
---|
52 | #include "ntp_stdlib.h" |
---|
53 | |
---|
54 | #include "mx4200.h" |
---|
55 | |
---|
56 | #if __STDC__ |
---|
57 | #include <stdarg.h> |
---|
58 | #else |
---|
59 | #include <varargs.h> |
---|
60 | #endif /* __STDC__ */ |
---|
61 | |
---|
62 | #ifdef PPS |
---|
63 | #include <sys/ppsclock.h> |
---|
64 | #endif /* PPS */ |
---|
65 | |
---|
66 | /* |
---|
67 | * This driver supports the Magnavox Model MX 4200 GPS Receiver |
---|
68 | * adapted to precision timing applications. It requires the |
---|
69 | * ppsclock line discipline or streams module described in the |
---|
70 | * Line Disciplines and Streams Drivers page. It also requires a |
---|
71 | * gadget box and 1-PPS level converter, such as described in the |
---|
72 | * Pulse-per-second (PPS) Signal Interfacing page. |
---|
73 | * |
---|
74 | * It's likely that other compatible Magnavox receivers such as the |
---|
75 | * MX 4200D, MX 9212, MX 9012R, MX 9112 will be supported by this code. |
---|
76 | */ |
---|
77 | |
---|
78 | /* |
---|
79 | * GPS Definitions |
---|
80 | */ |
---|
81 | #define DEVICE "/dev/gps%d" /* device name and unit */ |
---|
82 | #define SPEED232 B4800 /* baud */ |
---|
83 | |
---|
84 | /* |
---|
85 | * The number of raw samples which we acquire to derive a single estimate. |
---|
86 | * NSAMPLES ideally should not exceed the default poll interval 64. |
---|
87 | * NKEEP must be a power of 2 to simplify the averaging process. |
---|
88 | */ |
---|
89 | #define NSAMPLES 64 |
---|
90 | #define NKEEP 8 |
---|
91 | #define REFCLOCKMAXDISPERSE (FP_SECOND/4) /* max sample dispersion */ |
---|
92 | |
---|
93 | /* |
---|
94 | * Radio interface parameters |
---|
95 | */ |
---|
96 | #define PRECISION (-18) /* precision assumed (about 4 us) */ |
---|
97 | #define REFID "GPS\0" /* reference id */ |
---|
98 | #define DESCRIPTION "Magnavox MX4200 GPS Receiver" /* who we are */ |
---|
99 | #define DEFFUDGETIME 0 /* default fudge time (ms) */ |
---|
100 | |
---|
101 | #define SLEEPTIME 32 /* seconds to wait for reconfig to complete */ |
---|
102 | |
---|
103 | /* |
---|
104 | * Position Averaging. |
---|
105 | * Reference: Dr. Thomas A. Clark's Totally Accurate Clock (TAC) files at |
---|
106 | * ftp://aleph.gsfc.nasa.gov/GPS/totally.accurate.clock/ |
---|
107 | */ |
---|
108 | #define INTERVAL 1 /* Interval between position measurements (s) */ |
---|
109 | #define AVGING_TIME 24 /* Number of hours to average */ |
---|
110 | #define USUAL_EDOP 0.75 /* used for normalizing EDOP */ |
---|
111 | #define USUAL_NDOP 0.75 /* used for normalizing NDOP */ |
---|
112 | #define USUAL_VDOP 1.70 /* used for normalizing VDOP */ |
---|
113 | |
---|
114 | /* |
---|
115 | * Imported from the ntp_timer module |
---|
116 | */ |
---|
117 | extern u_long current_time; /* current time (s) */ |
---|
118 | |
---|
119 | /* |
---|
120 | * Imported from ntpd module |
---|
121 | */ |
---|
122 | extern int debug; /* global debug flag */ |
---|
123 | |
---|
124 | #ifdef PPS |
---|
125 | /* |
---|
126 | * Imported from loop_filter module |
---|
127 | */ |
---|
128 | extern int fdpps; /* ppsclock file descriptor */ |
---|
129 | #endif /* PPS */ |
---|
130 | |
---|
131 | /* |
---|
132 | * Imported from perror(3) |
---|
133 | */ |
---|
134 | extern int sys_nerr; |
---|
135 | extern char *sys_errlist[]; |
---|
136 | extern int errno; |
---|
137 | |
---|
138 | /* |
---|
139 | * MX4200 unit control structure. |
---|
140 | */ |
---|
141 | struct mx4200unit { |
---|
142 | u_int pollcnt; /* poll message counter */ |
---|
143 | u_int polled; /* Hand in a time sample? */ |
---|
144 | #ifdef PPS |
---|
145 | u_int lastserial; /* last pps serial number */ |
---|
146 | struct ppsclockev ppsev; /* PPS control structure */ |
---|
147 | #endif /* PPS */ |
---|
148 | double avg_lat; /* average latitude */ |
---|
149 | double avg_lon; /* average longitude */ |
---|
150 | double avg_alt; /* average height */ |
---|
151 | double filt_lat; /* latitude filter length */ |
---|
152 | double filt_lon; /* longitude filter length */ |
---|
153 | double filt_alt; /* height filter length */ |
---|
154 | double edop; /* EDOP (east DOP) */ |
---|
155 | double ndop; /* NDOP (north DOP) */ |
---|
156 | double vdop; /* VDOP (vertical DOP) */ |
---|
157 | int last_leap; /* leap second warning */ |
---|
158 | u_int moving; /* mobile platform? */ |
---|
159 | u_long sloppyclockflag; /* fudge flags */ |
---|
160 | u_int known; /* position known yet? */ |
---|
161 | u_long clamp_time; /* when to stop postion averaging */ |
---|
162 | u_long log_time; /* when to print receiver status */ |
---|
163 | int coderecv; /* total received samples */ |
---|
164 | int nkeep; /* number of samples to preserve */ |
---|
165 | int rshift; /* number of rshifts for division */ |
---|
166 | l_fp filter[NSAMPLES]; /* offset filter */ |
---|
167 | l_fp lastref; /* last reference timestamp */ |
---|
168 | }; |
---|
169 | |
---|
170 | static char pmvxg[] = "PMVXG"; |
---|
171 | |
---|
172 | /* |
---|
173 | * Function prototypes |
---|
174 | */ |
---|
175 | static int mx4200_start P((int, struct peer *)); |
---|
176 | static void mx4200_shutdown P((int, struct peer *)); |
---|
177 | static void mx4200_receive P((struct recvbuf *)); |
---|
178 | static void mx4200_poll P((int, struct peer *)); |
---|
179 | |
---|
180 | static char * mx4200_parse_t P((struct peer *)); |
---|
181 | static char * mx4200_parse_p P((struct peer *)); |
---|
182 | static char * mx4200_parse_d P((struct peer *)); |
---|
183 | static char * mx4200_parse_s P((struct peer *)); |
---|
184 | static char * mx4200_offset P((struct peer *)); |
---|
185 | static char * mx4200_process P((struct peer *)); |
---|
186 | #ifdef QSORT_USES_VOID_P |
---|
187 | int mx4200_cmpl_fp P((const void *, const void *)); |
---|
188 | #else |
---|
189 | int mx4200_cmpl_fp P((const l_fp *, const l_fp *)); |
---|
190 | #endif /* not QSORT_USES_VOID_P */ |
---|
191 | static void mx4200_config P((struct peer *)); |
---|
192 | static void mx4200_ref P((struct peer *)); |
---|
193 | static void mx4200_send P((struct peer *, char *, ...)); |
---|
194 | static u_char mx4200_cksum P((char *, u_int)); |
---|
195 | static int mx4200_jday P((int, int, int)); |
---|
196 | static void mx4200_debug P((struct peer *, char *, ...)); |
---|
197 | static int mx4200_pps P((struct peer *)); |
---|
198 | |
---|
199 | /* |
---|
200 | * Transfer vector |
---|
201 | */ |
---|
202 | struct refclock refclock_mx4200 = { |
---|
203 | mx4200_start, /* start up driver */ |
---|
204 | mx4200_shutdown, /* shut down driver */ |
---|
205 | mx4200_poll, /* transmit poll message */ |
---|
206 | noentry, /* not used (old mx4200_control) */ |
---|
207 | noentry, /* initialize driver (not used) */ |
---|
208 | noentry, /* not used (old mx4200_buginfo) */ |
---|
209 | NOFLAGS /* not used */ |
---|
210 | }; |
---|
211 | |
---|
212 | |
---|
213 | |
---|
214 | /* |
---|
215 | * mx4200_start - open the devices and initialize data for processing |
---|
216 | */ |
---|
217 | static int |
---|
218 | mx4200_start(unit, peer) |
---|
219 | int unit; |
---|
220 | struct peer *peer; |
---|
221 | { |
---|
222 | register struct mx4200unit *up; |
---|
223 | struct refclockproc *pp; |
---|
224 | int fd; |
---|
225 | char gpsdev[20]; |
---|
226 | |
---|
227 | /* |
---|
228 | * Open serial port |
---|
229 | */ |
---|
230 | (void)sprintf(gpsdev, DEVICE, unit); |
---|
231 | if (!(fd = refclock_open(gpsdev, SPEED232, |
---|
232 | #ifdef PPS |
---|
233 | LDISC_PPS |
---|
234 | #else /* not PPS */ |
---|
235 | 0 |
---|
236 | #endif /* not PPS */ |
---|
237 | ))) |
---|
238 | return (0); |
---|
239 | |
---|
240 | /* |
---|
241 | * Allocate unit structure |
---|
242 | */ |
---|
243 | if (!(up = (struct mx4200unit *) emalloc(sizeof(struct mx4200unit)))) { |
---|
244 | (void) close(fd); |
---|
245 | return (0); |
---|
246 | } |
---|
247 | memset((char *)up, 0, sizeof(struct mx4200unit)); |
---|
248 | pp = peer->procptr; |
---|
249 | pp->io.clock_recv = mx4200_receive; |
---|
250 | pp->io.srcclock = (caddr_t)peer; |
---|
251 | pp->io.datalen = 0; |
---|
252 | pp->io.fd = fd; |
---|
253 | if (!io_addclock(&pp->io)) { |
---|
254 | (void) close(fd); |
---|
255 | free(up); |
---|
256 | return (0); |
---|
257 | } |
---|
258 | pp->unitptr = (caddr_t)up; |
---|
259 | |
---|
260 | /* |
---|
261 | * Initialize miscellaneous variables |
---|
262 | */ |
---|
263 | peer->precision = PRECISION; |
---|
264 | pp->clockdesc = DESCRIPTION; |
---|
265 | memcpy((char *)&pp->refid, REFID, 4); |
---|
266 | |
---|
267 | |
---|
268 | /* Ensure the receiver is properly configured */ |
---|
269 | mx4200_config(peer); |
---|
270 | return (1); |
---|
271 | } |
---|
272 | |
---|
273 | |
---|
274 | /* |
---|
275 | * mx4200_shutdown - shut down the clock |
---|
276 | */ |
---|
277 | static void |
---|
278 | mx4200_shutdown(unit, peer) |
---|
279 | int unit; |
---|
280 | struct peer *peer; |
---|
281 | { |
---|
282 | register struct mx4200unit *up; |
---|
283 | struct refclockproc *pp; |
---|
284 | |
---|
285 | pp = peer->procptr; |
---|
286 | up = (struct mx4200unit *)pp->unitptr; |
---|
287 | io_closeclock(&pp->io); |
---|
288 | free(up); |
---|
289 | } |
---|
290 | |
---|
291 | |
---|
292 | /* |
---|
293 | * mx4200_config - Configure the receiver |
---|
294 | */ |
---|
295 | static void |
---|
296 | mx4200_config(peer) |
---|
297 | struct peer *peer; |
---|
298 | { |
---|
299 | char tr_mode; |
---|
300 | char add_mode; |
---|
301 | int i; |
---|
302 | register struct mx4200unit *up; |
---|
303 | struct refclockproc *pp; |
---|
304 | |
---|
305 | pp = peer->procptr; |
---|
306 | up = (struct mx4200unit *)pp->unitptr; |
---|
307 | |
---|
308 | /* |
---|
309 | * Initialize the unit variables |
---|
310 | * |
---|
311 | * STRANGE BEHAVIOUR WARNING: The fudge flags are not available |
---|
312 | * at the time mx4200_start is called. These are set later, |
---|
313 | * and so the code must be prepared to handle changing flags. |
---|
314 | */ |
---|
315 | up->sloppyclockflag = pp->sloppyclockflag; |
---|
316 | if (pp->sloppyclockflag & CLK_FLAG2) { |
---|
317 | up->moving = 1; /* Receiver on mobile platform */ |
---|
318 | msyslog(LOG_DEBUG, "mx4200_config: mobile platform"); |
---|
319 | } else { |
---|
320 | up->moving = 0; /* Static Installation */ |
---|
321 | } |
---|
322 | up->pollcnt = 2; |
---|
323 | up->polled = 0; |
---|
324 | up->known = 0; |
---|
325 | up->avg_lat = 0.0; |
---|
326 | up->avg_lon = 0.0; |
---|
327 | up->avg_alt = 0.0; |
---|
328 | up->filt_lat = 0.0; |
---|
329 | up->filt_lon = 0.0; |
---|
330 | up->filt_alt = 0.0; |
---|
331 | up->edop = USUAL_EDOP; |
---|
332 | up->ndop = USUAL_NDOP; |
---|
333 | up->vdop = USUAL_VDOP; |
---|
334 | up->last_leap = 0; /* LEAP_NOWARNING */ |
---|
335 | up->clamp_time = current_time + (AVGING_TIME * 60 * 60); |
---|
336 | up->log_time = current_time + SLEEPTIME; |
---|
337 | up->coderecv = 0; |
---|
338 | up->nkeep = NKEEP; |
---|
339 | if (up->nkeep > NSAMPLES) up->nkeep = NSAMPLES; |
---|
340 | if (up->nkeep >= 1) up->rshift = 0; |
---|
341 | if (up->nkeep >= 2) up->rshift = 1; |
---|
342 | if (up->nkeep >= 4) up->rshift = 2; |
---|
343 | if (up->nkeep >= 8) up->rshift = 3; |
---|
344 | if (up->nkeep >= 16) up->rshift = 4; |
---|
345 | if (up->nkeep >= 32) up->rshift = 5; |
---|
346 | if (up->nkeep >= 64) up->rshift = 6; |
---|
347 | up->nkeep =1; |
---|
348 | i = up->rshift; |
---|
349 | while (i > 0) { |
---|
350 | up->nkeep *= 2; |
---|
351 | i--; |
---|
352 | } |
---|
353 | |
---|
354 | /* |
---|
355 | * "007" Control Port Configuration |
---|
356 | * Zero the output list (do it twice to flush possible junk) |
---|
357 | */ |
---|
358 | mx4200_send(peer, "%s,%03d,,%d,,,,,,", pmvxg, |
---|
359 | PMVXG_S_PORTCONF, |
---|
360 | /* control port output block Label */ |
---|
361 | 1); /* clear current output control list (1=yes) */ |
---|
362 | /* add/delete sentences from list */ |
---|
363 | /* must be null */ |
---|
364 | /* sentence output rate (sec) */ |
---|
365 | /* precision for position output */ |
---|
366 | /* nmea version for cga & gll output */ |
---|
367 | /* pass-through control */ |
---|
368 | mx4200_send(peer, "%s,%03d,,%d,,,,,,", pmvxg, |
---|
369 | PMVXG_S_PORTCONF, 1); |
---|
370 | |
---|
371 | /* |
---|
372 | * Request software configuration so we can syslog the firmware version |
---|
373 | */ |
---|
374 | mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_SOFTCONF); |
---|
375 | |
---|
376 | /* |
---|
377 | * "001" Initialization/Mode Control, Part A |
---|
378 | * Where ARE we? |
---|
379 | */ |
---|
380 | mx4200_send(peer, "%s,%03d,,,,,,,,,,", pmvxg, |
---|
381 | PMVXG_S_INITMODEA); |
---|
382 | /* day of month */ |
---|
383 | /* month of year */ |
---|
384 | /* year */ |
---|
385 | /* gmt */ |
---|
386 | /* latitude DDMM.MMMM */ |
---|
387 | /* north/south */ |
---|
388 | /* longitude DDDMM.MMMM */ |
---|
389 | /* east/west */ |
---|
390 | /* height */ |
---|
391 | /* Altitude Reference 1=MSL */ |
---|
392 | |
---|
393 | /* |
---|
394 | * "001" Initialization/Mode Control, Part B |
---|
395 | * Start off in 2d/3d coast mode, holding altitude to last known |
---|
396 | * value if only 3 satellites available. |
---|
397 | */ |
---|
398 | mx4200_send(peer, "%s,%03d,%d,,%.1f,%.1f,%d,%d,%d,%c,%d", |
---|
399 | pmvxg, PMVXG_S_INITMODEB, |
---|
400 | 3, /* 2d/3d coast */ |
---|
401 | /* reserved */ |
---|
402 | 0.1, /* hor accel fact as per Steve (m/s**2) */ |
---|
403 | 0.1, /* ver accel fact as per Steve (m/s**2) */ |
---|
404 | 10, /* vdop */ |
---|
405 | 10, /* hdop limit as per Steve */ |
---|
406 | 5, /* elevation limit as per Steve (deg) */ |
---|
407 | 'U', /* time output mode (UTC) */ |
---|
408 | 0); /* local time offset from gmt (HHHMM) */ |
---|
409 | |
---|
410 | /* |
---|
411 | * "023" Time Recovery Configuration |
---|
412 | * Get UTC time from a stationary receiver. |
---|
413 | * (Set field 1 'D' == dynamic if we are on a moving platform). |
---|
414 | * (Set field 1 'S' == static if we are not moving). |
---|
415 | * (Set field 1 'K' == known position if we can initialize lat/lon/alt). |
---|
416 | */ |
---|
417 | |
---|
418 | if (pp->sloppyclockflag & CLK_FLAG2) |
---|
419 | up->moving = 1; /* Receiver on mobile platform */ |
---|
420 | else |
---|
421 | up->moving = 0; /* Static Installation */ |
---|
422 | |
---|
423 | up->pollcnt = 2; |
---|
424 | if (up->moving) { |
---|
425 | /* dynamic: solve for pos, alt, time, while moving */ |
---|
426 | tr_mode = 'D'; |
---|
427 | add_mode = 0; |
---|
428 | } else { |
---|
429 | /* static: solve for pos, alt, time, while stationary */ |
---|
430 | tr_mode = 'S'; |
---|
431 | add_mode = 1; |
---|
432 | } |
---|
433 | mx4200_send(peer, "%s,%03d,%c,%c,%c,%d,%d,%d,", pmvxg, |
---|
434 | PMVXG_S_TRECOVCONF, |
---|
435 | tr_mode, /* time recovery mode (see above ) */ |
---|
436 | 'U', /* synchronize to UTC */ |
---|
437 | 'A', /* always output a time pulse */ |
---|
438 | 500, /* max time error in ns */ |
---|
439 | 0, /* user bias in ns */ |
---|
440 | 1); /* output "830" sentences to control port */ |
---|
441 | /* Multi-satellite mode */ |
---|
442 | |
---|
443 | /* |
---|
444 | * Output position information (to calculate fixed installation |
---|
445 | * location) only if we are not moving |
---|
446 | */ |
---|
447 | if (up->moving) { |
---|
448 | add_mode = 0; /* delete from list */ |
---|
449 | } else { |
---|
450 | add_mode = 1; /* add to list */ |
---|
451 | } |
---|
452 | |
---|
453 | /* |
---|
454 | * "007" Control Port Configuration |
---|
455 | * Output "022" DOPs |
---|
456 | */ |
---|
457 | mx4200_send(peer, "%s,%03d,%03d,%d,%d,,%d,,,", pmvxg, |
---|
458 | PMVXG_S_PORTCONF, |
---|
459 | PMVXG_D_DOPS, /* control port output block Label */ |
---|
460 | 0, /* clear current output control list (0=no) */ |
---|
461 | add_mode, /* add/delete sentences from list (1=add) */ |
---|
462 | /* must be null */ |
---|
463 | INTERVAL); /* sentence output rate (sec) */ |
---|
464 | /* precision for position output */ |
---|
465 | /* nmea version for cga & gll output */ |
---|
466 | /* pass-through control */ |
---|
467 | |
---|
468 | |
---|
469 | /* |
---|
470 | * "007" Control Port Configuration |
---|
471 | * Output "021" position, height, velocity reports |
---|
472 | */ |
---|
473 | mx4200_send(peer, "%s,%03d,%03d,%d,%d,,%d,,,", pmvxg, |
---|
474 | PMVXG_S_PORTCONF, |
---|
475 | PMVXG_D_PHV, /* control port output block Label */ |
---|
476 | 0, /* clear current output control list (0=no) */ |
---|
477 | add_mode, /* add/delete sentences from list (1=add) */ |
---|
478 | /* must be null */ |
---|
479 | INTERVAL); /* sentence output rate (sec) */ |
---|
480 | /* precision for position output */ |
---|
481 | /* nmea version for cga & gll output */ |
---|
482 | /* pass-through control */ |
---|
483 | } |
---|
484 | |
---|
485 | /* |
---|
486 | * mx4200_ref - Reconfigure unit as a reference station at a known position. |
---|
487 | */ |
---|
488 | static void |
---|
489 | mx4200_ref(peer) |
---|
490 | struct peer *peer; |
---|
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 | * "001" Initialization/Mode Control, Part B |
---|
511 | * Put receiver in fully-constrained 2d nav mode |
---|
512 | */ |
---|
513 | mx4200_send(peer, "%s,%03d,%d,,%.1f,%.1f,%d,%d,%d,%c,%d", |
---|
514 | pmvxg, PMVXG_S_INITMODEB, |
---|
515 | 2, /* 2d nav */ |
---|
516 | /* reserved */ |
---|
517 | 0.1, /* hor accel fact as per Steve (m/s**2) */ |
---|
518 | 0.1, /* ver accel fact as per Steve (m/s**2) */ |
---|
519 | 10, /* vdop */ |
---|
520 | 10, /* hdop limit as per Steve */ |
---|
521 | 5, /* elevation limit as per Steve (deg) */ |
---|
522 | 'U', /* time output mode (UTC) */ |
---|
523 | 0); /* local time offset from gmt (HHHMM) */ |
---|
524 | |
---|
525 | /* |
---|
526 | * "023" Time Recovery Configuration |
---|
527 | * Get UTC time from a stationary receiver. Solve for time only. |
---|
528 | * This should improve the time resolution dramatically. |
---|
529 | */ |
---|
530 | mx4200_send(peer, "%s,%03d,%c,%c,%c,%d,%d,%d,", pmvxg, |
---|
531 | PMVXG_S_TRECOVCONF, |
---|
532 | 'K', /* known position: solve for time only */ |
---|
533 | 'U', /* synchronize to UTC */ |
---|
534 | 'A', /* always output a time pulse */ |
---|
535 | 500, /* max time error in ns */ |
---|
536 | 0, /* user bias in ns */ |
---|
537 | 1); /* output "830" sentences to control port */ |
---|
538 | /* Multi-satellite mode */ |
---|
539 | |
---|
540 | /* |
---|
541 | * "000" Initialization/Mode Control - Part A |
---|
542 | * Fix to our averaged position. |
---|
543 | */ |
---|
544 | if (up->avg_lat >= 0.0) { |
---|
545 | lat = up->avg_lat; |
---|
546 | nsc = 'N'; |
---|
547 | } else { |
---|
548 | lat = up->avg_lat * (-1.0); |
---|
549 | nsc = 'S'; |
---|
550 | } |
---|
551 | if (up->avg_lon >= 0.0) { |
---|
552 | lon = up->avg_lon; |
---|
553 | ewc = 'E'; |
---|
554 | } else { |
---|
555 | lon = up->avg_lon * (-1.0); |
---|
556 | ewc = 'W'; |
---|
557 | } |
---|
558 | alt = up->avg_alt; |
---|
559 | minute = (lat - (double)(int)lat) * 600.0 / 10.0; |
---|
560 | sprintf(lats,"%02d%02.4f", (int)lat, minute); |
---|
561 | minute = (lon - (double)(int)lon) * 600.0 / 10.0; |
---|
562 | sprintf(lons,"%03d%02.4f", (int)lon, minute); |
---|
563 | |
---|
564 | mx4200_send(peer, "%s,%03d,,,,,%s,%c,%s,%c,%.2f,%d", pmvxg, |
---|
565 | PMVXG_S_INITMODEA, |
---|
566 | /* day of month */ |
---|
567 | /* month of year */ |
---|
568 | /* year */ |
---|
569 | /* gmt */ |
---|
570 | lats, /* latitude DDMM.MMMM */ |
---|
571 | nsc, /* north/south */ |
---|
572 | lons, /* longitude DDDMM.MMMM */ |
---|
573 | ewc, /* east/west */ |
---|
574 | alt, /* height */ |
---|
575 | 1); /* Altitude Reference 1=MSL */ |
---|
576 | |
---|
577 | |
---|
578 | /* |
---|
579 | * "007" Control Port Configuration |
---|
580 | * Stop outputting "022" DOPs |
---|
581 | */ |
---|
582 | mx4200_send(peer, "%s,%03d,%03d,%d,%d,,,,,", pmvxg, |
---|
583 | PMVXG_S_PORTCONF, |
---|
584 | PMVXG_D_DOPS, /* control port output block Label */ |
---|
585 | 0, /* clear current output control list (0=no) */ |
---|
586 | 0); /* add/delete sentences from list (0=delete) */ |
---|
587 | /* must be null */ |
---|
588 | /* sentence output rate (sec) */ |
---|
589 | /* precision for position output */ |
---|
590 | /* nmea version for cga & gll output */ |
---|
591 | /* pass-through control */ |
---|
592 | |
---|
593 | /* |
---|
594 | * "007" Control Port Configuration |
---|
595 | * Stop outputting "021" position, height, velocity reports |
---|
596 | */ |
---|
597 | mx4200_send(peer, "%s,%03d,%03d,%d,%d,,%d,,,", pmvxg, |
---|
598 | PMVXG_S_PORTCONF, |
---|
599 | PMVXG_D_PHV, /* control port output block Label */ |
---|
600 | 0, /* clear current output control list (0=no) */ |
---|
601 | 0); /* add/delete sentences from list (0=delete) */ |
---|
602 | /* must be null */ |
---|
603 | /* sentence output rate (sec) */ |
---|
604 | /* precision for position output */ |
---|
605 | /* nmea version for cga & gll output */ |
---|
606 | /* pass-through control */ |
---|
607 | |
---|
608 | msyslog(LOG_DEBUG, |
---|
609 | "mx4200_ref: reconfig to fixed location: %s %c, %s %c, %.2f m MSL", |
---|
610 | lats, nsc, lons, ewc, alt ); |
---|
611 | |
---|
612 | } |
---|
613 | |
---|
614 | /* |
---|
615 | * mx4200_poll - mx4200 watchdog routine |
---|
616 | */ |
---|
617 | static void |
---|
618 | mx4200_poll(unit, peer) |
---|
619 | int unit; |
---|
620 | struct peer *peer; |
---|
621 | { |
---|
622 | register struct mx4200unit *up; |
---|
623 | struct refclockproc *pp; |
---|
624 | |
---|
625 | pp = peer->procptr; |
---|
626 | up = (struct mx4200unit *)pp->unitptr; |
---|
627 | |
---|
628 | /* |
---|
629 | * You don't need to poll this clock. It puts out timecodes |
---|
630 | * once per second. If asked for a timestamp, take note. |
---|
631 | * The next time a timecode comes in, it will be fed back. |
---|
632 | */ |
---|
633 | |
---|
634 | /* |
---|
635 | * If we haven't had a response in a while, reset the receiver. |
---|
636 | */ |
---|
637 | if (up->pollcnt > 0) { |
---|
638 | up->pollcnt--; |
---|
639 | } else { |
---|
640 | refclock_report(peer, CEVNT_TIMEOUT); |
---|
641 | |
---|
642 | /* |
---|
643 | * Request a "000" status message which should trigger a |
---|
644 | * reconfig |
---|
645 | */ |
---|
646 | mx4200_send(peer, "%s,%03d", |
---|
647 | "CDGPQ", /* query from CDU to GPS */ |
---|
648 | PMVXG_D_STATUS); /* label of desired sentence */ |
---|
649 | } |
---|
650 | |
---|
651 | /* |
---|
652 | * polled every 64 seconds. Ask mx4200_receive to hand in |
---|
653 | * a timestamp. |
---|
654 | */ |
---|
655 | up->polled = 1; |
---|
656 | pp->polls++; |
---|
657 | |
---|
658 | /* |
---|
659 | * Output receiver status information. |
---|
660 | */ |
---|
661 | if ((up->log_time > 0) && (current_time > up->log_time)) { |
---|
662 | up->log_time = 0; |
---|
663 | /* |
---|
664 | * Output the following messages once, for debugging. |
---|
665 | * "004" Mode Data |
---|
666 | * "523" Time Recovery Parameters |
---|
667 | */ |
---|
668 | mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_MODEDATA); |
---|
669 | mx4200_send(peer, "%s,%03d", "CDGPQ", PMVXG_D_TRECOVUSEAGE); |
---|
670 | } |
---|
671 | } |
---|
672 | |
---|
673 | static char char2hex[] = "0123456789ABCDEF"; |
---|
674 | |
---|
675 | /* |
---|
676 | * mx4200_receive - receive gps data |
---|
677 | */ |
---|
678 | static void |
---|
679 | mx4200_receive(rbufp) |
---|
680 | struct recvbuf *rbufp; |
---|
681 | { |
---|
682 | register struct mx4200unit *up; |
---|
683 | struct refclockproc *pp; |
---|
684 | struct peer *peer; |
---|
685 | char *cp; |
---|
686 | int sentence_type; |
---|
687 | u_char ck; |
---|
688 | |
---|
689 | /* |
---|
690 | * Initialize pointers and read the timecode and timestamp. |
---|
691 | */ |
---|
692 | peer = (struct peer *)rbufp->recv_srcclock; |
---|
693 | pp = peer->procptr; |
---|
694 | up = (struct mx4200unit *)pp->unitptr; |
---|
695 | |
---|
696 | /* |
---|
697 | * If operating mode has been changed, then reinitialize the receiver |
---|
698 | * before doing anything else. |
---|
699 | */ |
---|
700 | if ((pp->sloppyclockflag & CLK_FLAG2) != |
---|
701 | (up->sloppyclockflag & CLK_FLAG2)) { |
---|
702 | up->sloppyclockflag = pp->sloppyclockflag; |
---|
703 | mx4200_debug(peer, "mx4200_receive: mode switch: reset receiver\n", cp); |
---|
704 | mx4200_config(peer); |
---|
705 | return; |
---|
706 | } |
---|
707 | up->sloppyclockflag = pp->sloppyclockflag; |
---|
708 | |
---|
709 | /* |
---|
710 | * Read clock output. Automatically handles STREAMS, CLKLDISC. |
---|
711 | */ |
---|
712 | pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec); |
---|
713 | |
---|
714 | /* |
---|
715 | * There is a case where <cr><lf> generates 2 timestamps. |
---|
716 | */ |
---|
717 | if (pp->lencode == 0) |
---|
718 | return; |
---|
719 | |
---|
720 | up->pollcnt = 2; |
---|
721 | pp->a_lastcode[pp->lencode] = '\0'; |
---|
722 | record_clock_stats(&peer->srcadr, pp->a_lastcode); |
---|
723 | mx4200_debug(peer, "mx4200_receive: %d %s\n", |
---|
724 | pp->lencode, pp->a_lastcode); |
---|
725 | |
---|
726 | /* |
---|
727 | * The structure of the control port sentences is based on the |
---|
728 | * NMEA-0183 Standard for interfacing Marine Electronics |
---|
729 | * Navigation Devices (Version 1.5) |
---|
730 | * |
---|
731 | * $PMVXG,XXX, ....................*CK<cr><lf> |
---|
732 | * |
---|
733 | * $ Sentence Start Identifier (reserved char) |
---|
734 | * (Start-of-Sentence Identifier) |
---|
735 | * P Special ID (Proprietary) |
---|
736 | * MVX Originator ID (Magnavox) |
---|
737 | * G Interface ID (GPS) |
---|
738 | * , Field Delimiters (reserved char) |
---|
739 | * XXX Sentence Type |
---|
740 | * ...... Data |
---|
741 | * * Checksum Field Delimiter (reserved char) |
---|
742 | * CK Checksum |
---|
743 | * <cr><lf> Carriage-Return/Line Feed (reserved chars) |
---|
744 | * (End-of-Sentence Identifier) |
---|
745 | * |
---|
746 | * Reject if any important landmarks are missing. |
---|
747 | */ |
---|
748 | cp = pp->a_lastcode + pp->lencode - 3; |
---|
749 | if (cp < pp->a_lastcode || *pp->a_lastcode != '$' || cp[0] != '*' ) { |
---|
750 | mx4200_debug(peer, "mx4200_receive: bad format\n"); |
---|
751 | refclock_report(peer, CEVNT_BADREPLY); |
---|
752 | return; |
---|
753 | } |
---|
754 | |
---|
755 | /* |
---|
756 | * Check and discard the checksum |
---|
757 | */ |
---|
758 | ck = mx4200_cksum(&pp->a_lastcode[1], pp->lencode - 4); |
---|
759 | if (char2hex[ck >> 4] != cp[1] || char2hex[ck & 0xf] != cp[2]) { |
---|
760 | mx4200_debug(peer, "mx4200_receive: bad checksum\n"); |
---|
761 | refclock_report(peer, CEVNT_BADREPLY); |
---|
762 | return; |
---|
763 | } |
---|
764 | *cp = '\0'; |
---|
765 | |
---|
766 | /* |
---|
767 | * Get the sentence type. |
---|
768 | */ |
---|
769 | sentence_type = 0; |
---|
770 | if ((cp = strchr(pp->a_lastcode, ',')) == NULL) { |
---|
771 | mx4200_debug(peer, "mx4200_receive: no sentence\n", cp); |
---|
772 | refclock_report(peer, CEVNT_BADREPLY); |
---|
773 | return; |
---|
774 | } |
---|
775 | cp++; |
---|
776 | sentence_type = strtol(cp, &cp, 10); |
---|
777 | |
---|
778 | /* |
---|
779 | * "000" Status message |
---|
780 | */ |
---|
781 | |
---|
782 | if (sentence_type == PMVXG_D_STATUS) { |
---|
783 | /* |
---|
784 | * XXX |
---|
785 | * Since we configure the receiver to not give us status |
---|
786 | * messages and since the receiver outputs status messages by |
---|
787 | * default after being reset to factory defaults when sent the |
---|
788 | * "$PMVXG,018,C\r\n" message, any status message we get |
---|
789 | * indicates the reciever needs to be initialized; thus, it is |
---|
790 | * not necessary to decode the status message. |
---|
791 | */ |
---|
792 | if ((cp = mx4200_parse_s(peer)) != NULL) { |
---|
793 | mx4200_debug(peer, |
---|
794 | "mx4200_receive: status: %s\n", cp); |
---|
795 | } |
---|
796 | mx4200_debug(peer, "mx4200_receive: reset receiver\n", cp); |
---|
797 | mx4200_config(peer); |
---|
798 | return; |
---|
799 | } |
---|
800 | |
---|
801 | /* |
---|
802 | * "021" Position, Height, Velocity message, |
---|
803 | * if we are still averaging our position |
---|
804 | */ |
---|
805 | if (sentence_type == PMVXG_D_PHV && !up->known) { |
---|
806 | /* |
---|
807 | * Parse the message, calculating our averaged position. |
---|
808 | */ |
---|
809 | if ((cp = mx4200_parse_p(peer)) != NULL) { |
---|
810 | mx4200_debug(peer, "mx4200_receive: pos: %s\n", cp); |
---|
811 | return; |
---|
812 | } |
---|
813 | mx4200_debug(peer, |
---|
814 | "mx4200_receive: position avg %.9f %.9f %.4f\n", |
---|
815 | up->avg_lat, up->avg_lon, up->avg_alt); |
---|
816 | mx4200_debug(peer, |
---|
817 | "mx4200_receive: position len %.4f %.4f %.4f\n", |
---|
818 | up->filt_lat, up->filt_lon, up->filt_alt); |
---|
819 | mx4200_debug(peer, |
---|
820 | "mx4200_receive: position dop %.2f %.2f %.2f\n", |
---|
821 | up->ndop, up->edop, up->vdop); |
---|
822 | /* |
---|
823 | * Reinitialize as a reference station |
---|
824 | * if position is well known. |
---|
825 | */ |
---|
826 | if (current_time > up->clamp_time) { |
---|
827 | up->known++; |
---|
828 | mx4200_debug(peer, "mx4200_receive: reconfiguring!\n"); |
---|
829 | mx4200_ref(peer); |
---|
830 | } |
---|
831 | return; |
---|
832 | } |
---|
833 | |
---|
834 | /* |
---|
835 | * "022" DOPs, if we are still averaging our position |
---|
836 | */ |
---|
837 | if (sentence_type == PMVXG_D_DOPS && !up->known) { |
---|
838 | if ((cp = mx4200_parse_d(peer)) != NULL) { |
---|
839 | mx4200_debug(peer, "mx4200_receive: dop: %s\n", cp); |
---|
840 | return; |
---|
841 | } |
---|
842 | return; |
---|
843 | } |
---|
844 | |
---|
845 | /* |
---|
846 | * Print to the syslog: |
---|
847 | * "004" Mode Data |
---|
848 | * "030" Software Configuration |
---|
849 | * "523" Time Recovery Parameters Currently in Use |
---|
850 | */ |
---|
851 | if (sentence_type == PMVXG_D_MODEDATA || |
---|
852 | sentence_type == PMVXG_D_SOFTCONF || |
---|
853 | sentence_type == PMVXG_D_TRECOVUSEAGE ) { |
---|
854 | if ((cp = mx4200_parse_s(peer)) != NULL) { |
---|
855 | mx4200_debug(peer, |
---|
856 | "mx4200_receive: multi-record: %s\n", cp); |
---|
857 | return; |
---|
858 | } |
---|
859 | return; |
---|
860 | } |
---|
861 | |
---|
862 | /* |
---|
863 | * "830" Time Recovery Results message |
---|
864 | */ |
---|
865 | if (sentence_type == PMVXG_D_TRECOVOUT) { |
---|
866 | |
---|
867 | /* |
---|
868 | * Capture the last PPS signal. |
---|
869 | * Precision timestamp is returned in pp->lastrec |
---|
870 | */ |
---|
871 | if (mx4200_pps(peer) != NULL) { |
---|
872 | mx4200_debug(peer, "mx4200_receive: pps failure\n"); |
---|
873 | refclock_report(peer, CEVNT_FAULT); |
---|
874 | return; |
---|
875 | } |
---|
876 | |
---|
877 | /* |
---|
878 | * Parse the time recovery message, and keep the info |
---|
879 | * to print the pretty billboards. |
---|
880 | */ |
---|
881 | if ((cp = mx4200_parse_t(peer)) != NULL) { |
---|
882 | mx4200_debug(peer, "mx4200_receive: time: %s\n", cp); |
---|
883 | refclock_report(peer, CEVNT_BADREPLY); |
---|
884 | return; |
---|
885 | } |
---|
886 | |
---|
887 | /* |
---|
888 | * Add the new sample to a median filter. |
---|
889 | */ |
---|
890 | if ((cp =mx4200_offset(peer)) != NULL) { |
---|
891 | mx4200_debug(peer,"mx4200_receive: offset: %s\n", cp); |
---|
892 | refclock_report(peer, CEVNT_BADTIME); |
---|
893 | return; |
---|
894 | } |
---|
895 | |
---|
896 | /* |
---|
897 | * The clock will blurt a timecode every second but we only |
---|
898 | * want one when polled. If we havn't been polled, bail out. |
---|
899 | */ |
---|
900 | if (!up->polled) |
---|
901 | return; |
---|
902 | |
---|
903 | /* |
---|
904 | * It's a live one! Remember this time. |
---|
905 | */ |
---|
906 | pp->lasttime = current_time; |
---|
907 | |
---|
908 | /* |
---|
909 | * Determine the reference clock offset and dispersion. |
---|
910 | * NKEEP of NSAMPLE offsets are passed through a median filter. |
---|
911 | * Save the (filtered) offset and dispersion in |
---|
912 | * pp->offset and pp->dispersion. |
---|
913 | */ |
---|
914 | if ((cp =mx4200_process(peer)) != NULL) { |
---|
915 | mx4200_debug(peer,"mx4200_receive: process: %s\n", cp); |
---|
916 | refclock_report(peer, CEVNT_BADTIME); |
---|
917 | return; |
---|
918 | } |
---|
919 | |
---|
920 | /* |
---|
921 | * Return offset and dispersion to control module. We use |
---|
922 | * lastrec as both the reference time and receive time in |
---|
923 | * order to avoid being cute, like setting the reference time |
---|
924 | * later than the receive time, which may cause a paranoid |
---|
925 | * protocol module to chuck out the data. |
---|
926 | */ |
---|
927 | mx4200_debug(peer, "mx4200_receive: process time: "); |
---|
928 | mx4200_debug(peer, "%4d-%03d %02d:%02d:%02d at %s, %s\n", |
---|
929 | pp->year, pp->day, pp->hour, pp->minute, pp->second, |
---|
930 | prettydate(&pp->lastrec), lfptoa(&pp->offset, 6)); |
---|
931 | |
---|
932 | refclock_receive(peer, &pp->offset, 0, pp->dispersion, |
---|
933 | &pp->lastrec, &pp->lastrec, pp->leap); |
---|
934 | |
---|
935 | /* |
---|
936 | * We have succeeded in answering the poll. |
---|
937 | * Turn off the flag and return |
---|
938 | */ |
---|
939 | up->polled = 0; |
---|
940 | return; |
---|
941 | } |
---|
942 | |
---|
943 | /* |
---|
944 | * Ignore all other sentence types |
---|
945 | */ |
---|
946 | return; |
---|
947 | } |
---|
948 | |
---|
949 | /* |
---|
950 | * mx4200_offset - Calculate the offset, and add to the rolling filter. |
---|
951 | */ |
---|
952 | static char * |
---|
953 | mx4200_offset(peer) |
---|
954 | struct peer *peer; |
---|
955 | { |
---|
956 | register struct mx4200unit *up; |
---|
957 | struct refclockproc *pp; |
---|
958 | register int i; |
---|
959 | l_fp offset; |
---|
960 | |
---|
961 | pp = peer->procptr; |
---|
962 | up = (struct mx4200unit *)pp->unitptr; |
---|
963 | |
---|
964 | /* |
---|
965 | * Calculate the offset |
---|
966 | */ |
---|
967 | if (!clocktime(pp->day, pp->hour, pp->minute, pp->second, GMT, |
---|
968 | pp->lastrec.l_ui, &pp->yearstart, &offset.l_ui)) { |
---|
969 | return ("mx4200_process: clocktime failed"); |
---|
970 | } |
---|
971 | if (pp->usec) { |
---|
972 | TVUTOTSF(pp->usec, offset.l_uf); |
---|
973 | } else { |
---|
974 | MSUTOTSF(pp->msec, offset.l_uf); |
---|
975 | } |
---|
976 | L_ADD(&offset, &pp->fudgetime1); |
---|
977 | up->lastref = offset; /* save last reference time */ |
---|
978 | L_SUB(&offset, &pp->lastrec); /* form true offset */ |
---|
979 | |
---|
980 | /* |
---|
981 | * A rolling filter. Initialize first time around. |
---|
982 | */ |
---|
983 | i = ((up->coderecv)) % NSAMPLES; |
---|
984 | |
---|
985 | up->filter[i] = offset; |
---|
986 | if (up->coderecv == 0) |
---|
987 | for (i = 1; (u_int) i < NSAMPLES; i++) |
---|
988 | up->filter[i] = up->filter[0]; |
---|
989 | up->coderecv++; |
---|
990 | |
---|
991 | return (NULL); |
---|
992 | } |
---|
993 | |
---|
994 | /* |
---|
995 | * mx4200_process - process the sample from the clock, |
---|
996 | * passing it through a median filter and optionally averaging |
---|
997 | * the samples. Returns offset and dispersion in "up" structure. |
---|
998 | */ |
---|
999 | static char * |
---|
1000 | mx4200_process(peer) |
---|
1001 | struct peer *peer; |
---|
1002 | { |
---|
1003 | register struct mx4200unit *up; |
---|
1004 | struct refclockproc *pp; |
---|
1005 | register int i, n; |
---|
1006 | int j, k; |
---|
1007 | l_fp offset, median, lftmp; |
---|
1008 | u_fp disp; |
---|
1009 | l_fp off[NSAMPLES]; |
---|
1010 | |
---|
1011 | pp = peer->procptr; |
---|
1012 | up = (struct mx4200unit *)pp->unitptr; |
---|
1013 | |
---|
1014 | /* |
---|
1015 | * Copy the raw offsets and sort into ascending order |
---|
1016 | */ |
---|
1017 | for (i = 0; i < NSAMPLES; i++) |
---|
1018 | off[i] = up->filter[i]; |
---|
1019 | qsort((char *)off, NSAMPLES, sizeof(l_fp), mx4200_cmpl_fp); |
---|
1020 | |
---|
1021 | /* |
---|
1022 | * Reject the furthest from the median of NSAMPLES samples until |
---|
1023 | * NKEEP samples remain. |
---|
1024 | */ |
---|
1025 | i = 0; |
---|
1026 | n = NSAMPLES; |
---|
1027 | while ((n - i) > up->nkeep) { |
---|
1028 | lftmp = off[n - 1]; |
---|
1029 | median = off[(n + i) / 2]; |
---|
1030 | L_SUB(&lftmp, &median); |
---|
1031 | L_SUB(&median, &off[i]); |
---|
1032 | if (L_ISHIS(&median, &lftmp)) { |
---|
1033 | /* reject low end */ |
---|
1034 | i++; |
---|
1035 | } else { |
---|
1036 | /* reject high end */ |
---|
1037 | n--; |
---|
1038 | } |
---|
1039 | } |
---|
1040 | |
---|
1041 | /* |
---|
1042 | * Copy key values to the billboard to measure performance. |
---|
1043 | */ |
---|
1044 | pp->lastref = up->lastref; |
---|
1045 | pp->coderecv = up->coderecv; |
---|
1046 | pp->nstages = up->nkeep + 2; |
---|
1047 | pp->filter[0] = off[0]; /* smallest offset */ |
---|
1048 | pp->filter[1] = off[NSAMPLES-1]; /* largest offset */ |
---|
1049 | for (j=2, k=i; k < n; j++, k++) |
---|
1050 | pp->filter[j] = off[k]; /* offsets actually examined */ |
---|
1051 | |
---|
1052 | /* |
---|
1053 | * Compute the dispersion based on the difference between the |
---|
1054 | * extremes of the remaining offsets. Add to this the time since |
---|
1055 | * the last clock update, which represents the dispersion |
---|
1056 | * increase with time. We know that NTP_MAXSKEW is 16. If the |
---|
1057 | * sum is greater than the allowed sample dispersion, bail out. |
---|
1058 | * If the loop is unlocked, return the most recent offset; |
---|
1059 | * otherwise, return the median offset. |
---|
1060 | */ |
---|
1061 | lftmp = off[n - 1]; |
---|
1062 | L_SUB(&lftmp, &off[i]); |
---|
1063 | disp = LFPTOFP(&lftmp); |
---|
1064 | if (disp > REFCLOCKMAXDISPERSE) { |
---|
1065 | return("Maximum dispersion exceeded"); |
---|
1066 | } |
---|
1067 | |
---|
1068 | /* |
---|
1069 | * Now compute the offset estimate. If fudge flag 1 |
---|
1070 | * is set, average the remainder, otherwise pick the |
---|
1071 | * median. |
---|
1072 | */ |
---|
1073 | if (pp->sloppyclockflag & CLK_FLAG1) { |
---|
1074 | L_CLR(&lftmp); |
---|
1075 | while (i < n) { |
---|
1076 | L_ADD(&lftmp, &off[i]); |
---|
1077 | i++; |
---|
1078 | } |
---|
1079 | i = up->rshift; |
---|
1080 | while (i > 0) { |
---|
1081 | L_RSHIFT(&lftmp); |
---|
1082 | i--; |
---|
1083 | } |
---|
1084 | offset = lftmp; |
---|
1085 | } else { |
---|
1086 | i = (n + i) / 2; |
---|
1087 | offset = off[i]; |
---|
1088 | } |
---|
1089 | |
---|
1090 | /* |
---|
1091 | * The payload: filtered offset and dispersion. |
---|
1092 | */ |
---|
1093 | |
---|
1094 | pp->offset = offset; |
---|
1095 | pp->dispersion = disp; |
---|
1096 | |
---|
1097 | return(NULL); |
---|
1098 | |
---|
1099 | } |
---|
1100 | |
---|
1101 | /* Compare two l_fp's, used with qsort() */ |
---|
1102 | int |
---|
1103 | #ifdef QSORT_USES_VOID_P |
---|
1104 | mx4200_cmpl_fp(p1, p2) |
---|
1105 | const void *p1, *p2; |
---|
1106 | { |
---|
1107 | register const l_fp *fp1 = (const l_fp *)p1; |
---|
1108 | register const l_fp *fp2 = (const l_fp *)p2; |
---|
1109 | #else |
---|
1110 | mx4200_cmpl_fp(fp1, fp2) |
---|
1111 | register const l_fp *fp1; |
---|
1112 | register const l_fp *fp2; |
---|
1113 | { |
---|
1114 | #endif /* not QSORT_USES_VOID_P */ |
---|
1115 | |
---|
1116 | if (!L_ISGEQ(fp1, fp2)) |
---|
1117 | return (-1); |
---|
1118 | if (L_ISEQU(fp1, fp2)) |
---|
1119 | return (0); |
---|
1120 | return (1); |
---|
1121 | } |
---|
1122 | |
---|
1123 | |
---|
1124 | /* |
---|
1125 | * Parse a mx4200 time recovery message. Returns a string if error. |
---|
1126 | * |
---|
1127 | * A typical message looks like this. Checksum has already been stripped. |
---|
1128 | * |
---|
1129 | * $PMVXG,830,T,YYYY,MM,DD,HH:MM:SS,U,S,FFFFFF,PPPPP,BBBBBB,LL |
---|
1130 | * |
---|
1131 | * Field Field Contents |
---|
1132 | * ----- -------------- |
---|
1133 | * Block Label: $PMVXG |
---|
1134 | * Sentence Type: 830=Time Recovery Results |
---|
1135 | * This sentence is output approximately 1 second |
---|
1136 | * preceding the 1PPS output. It indicates the |
---|
1137 | * exact time of the next pulse, whether or not the |
---|
1138 | * time mark will be valid (based on operator-specified |
---|
1139 | * error tolerance), the time to which the pulse is |
---|
1140 | * synchronized, the receiver operating mode, |
---|
1141 | * and the time error of the *last* 1PPS output. |
---|
1142 | * 1 Time Mark Valid: T=Valid, F=Not Valid |
---|
1143 | * 2 Year: 1993- |
---|
1144 | * 3 Month of Year: 1-12 |
---|
1145 | * 4 Day of Month: 1-31 |
---|
1146 | * 5 Time of Day: HH:MM:SS |
---|
1147 | * 6 Time Synchronization: U=UTC, G=GPS |
---|
1148 | * 7 Time Recovery Mode: D=Dynamic, S=Static, |
---|
1149 | * K=Known Position, N=No Time Recovery |
---|
1150 | * 8 Oscillator Offset: The filter's estimate of the oscillator |
---|
1151 | * frequency error, in parts per billion (ppb). |
---|
1152 | * 9 Time Mark Error: The computed error of the *last* pulse |
---|
1153 | * output, in nanoseconds. |
---|
1154 | * 10 User Time Bias: Operator specified bias, in nanoseconds |
---|
1155 | * 11 Leap Second Flag: Indicates that a leap second will |
---|
1156 | * occur. This value is usually zero, except during |
---|
1157 | * the week prior to the leap second occurence, when |
---|
1158 | * this value will be set to +1 or -1. A value of |
---|
1159 | * +1 indicates that GPS time will be 1 second |
---|
1160 | * further ahead of UTC time. |
---|
1161 | * |
---|
1162 | */ |
---|
1163 | static char * |
---|
1164 | mx4200_parse_t(peer) |
---|
1165 | struct peer *peer; |
---|
1166 | { |
---|
1167 | struct refclockproc *pp; |
---|
1168 | struct mx4200unit *up; |
---|
1169 | int sentence_type, valid; |
---|
1170 | int year, yearday, month, monthday, hour, minute, second, leapsec; |
---|
1171 | char *cp; |
---|
1172 | |
---|
1173 | pp = peer->procptr; |
---|
1174 | up = (struct mx4200unit *)pp->unitptr; |
---|
1175 | |
---|
1176 | cp = pp->a_lastcode; |
---|
1177 | |
---|
1178 | if ((cp = strchr(cp, ',')) == NULL) |
---|
1179 | return ("no rec-type"); |
---|
1180 | cp++; |
---|
1181 | |
---|
1182 | /* Sentence type */ |
---|
1183 | sentence_type = strtol(cp, &cp, 10); |
---|
1184 | if (sentence_type != PMVXG_D_TRECOVOUT) |
---|
1185 | return ("wrong rec-type"); |
---|
1186 | |
---|
1187 | /* Pulse valid indicator */ |
---|
1188 | if (*cp++ != ',') |
---|
1189 | return ("no pulse-valid"); |
---|
1190 | if (*cp == 'T') |
---|
1191 | valid = 1; |
---|
1192 | else if (*cp == 'F') |
---|
1193 | valid = 0; |
---|
1194 | else |
---|
1195 | return ("bad pulse-valid"); |
---|
1196 | cp++; |
---|
1197 | |
---|
1198 | /* Year */ |
---|
1199 | if (*cp++ != ',') |
---|
1200 | return ("no year"); |
---|
1201 | year = strtol(cp, &cp, 10); |
---|
1202 | |
---|
1203 | /* Month of year */ |
---|
1204 | if (*cp++ != ',') |
---|
1205 | return ("no month"); |
---|
1206 | month = strtol(cp, &cp, 10); |
---|
1207 | |
---|
1208 | /* Day of month */ |
---|
1209 | if (*cp++ != ',') |
---|
1210 | return ("no month day"); |
---|
1211 | monthday = strtol(cp, &cp, 10); |
---|
1212 | |
---|
1213 | /* Hour */ |
---|
1214 | if (*cp++ != ',') |
---|
1215 | return ("no hour"); |
---|
1216 | hour = strtol(cp, &cp, 10); |
---|
1217 | |
---|
1218 | /* Minute */ |
---|
1219 | if (*cp++ != ':') |
---|
1220 | return ("no minute"); |
---|
1221 | minute = strtol(cp, &cp, 10); |
---|
1222 | |
---|
1223 | /* Second */ |
---|
1224 | if (*cp++ != ':') |
---|
1225 | return ("no second"); |
---|
1226 | second = strtol(cp, &cp, 10); |
---|
1227 | |
---|
1228 | /* Time indicator */ |
---|
1229 | if (*cp++ != ',') |
---|
1230 | return ("no time indicator"); |
---|
1231 | if (*cp == 'G') |
---|
1232 | return ("synchronized to GPS; should be UTC"); |
---|
1233 | else if (*cp != 'U') |
---|
1234 | return ("not synchronized to UTC"); |
---|
1235 | cp++; |
---|
1236 | |
---|
1237 | /* Time recovery mode */ |
---|
1238 | if (*cp++ != ',' || *cp++ == '\0') |
---|
1239 | return ("no time mode"); |
---|
1240 | |
---|
1241 | /* Oscillator offset */ |
---|
1242 | if ((cp = strchr(cp, ',')) == NULL) |
---|
1243 | return ("no osc off"); |
---|
1244 | cp++; |
---|
1245 | |
---|
1246 | /* Time mark error */ |
---|
1247 | /* (by request, always less than 0.5 usec (500 nsec), so ignore) */ |
---|
1248 | if ((cp = strchr(cp, ',')) == NULL) |
---|
1249 | return ("no time mark err"); |
---|
1250 | cp++; |
---|
1251 | |
---|
1252 | /* User time bias */ |
---|
1253 | /* (by request, always zero, so ignore */ |
---|
1254 | if ((cp = strchr(cp, ',')) == NULL) |
---|
1255 | return ("no user bias"); |
---|
1256 | cp++; |
---|
1257 | |
---|
1258 | /* Leap second flag */ |
---|
1259 | if ((cp = strchr(cp, ',')) == NULL) |
---|
1260 | return ("no leap"); |
---|
1261 | cp++; |
---|
1262 | leapsec = strtol(cp, &cp, 10); |
---|
1263 | |
---|
1264 | |
---|
1265 | /* |
---|
1266 | * Check for insane time (allow for possible leap seconds) |
---|
1267 | */ |
---|
1268 | if (second > 60 || minute > 59 || hour > 23 || |
---|
1269 | second < 0 || minute < 0 || hour < 0) { |
---|
1270 | mx4200_debug(peer, |
---|
1271 | "mx4200_parse_t: bad time %02d:%02d:%02d", |
---|
1272 | hour, minute, second); |
---|
1273 | if (leapsec != 0) |
---|
1274 | mx4200_debug(peer, " (leap %+d\n)", leapsec); |
---|
1275 | mx4200_debug(peer, "\n"); |
---|
1276 | refclock_report(peer, CEVNT_BADTIME); |
---|
1277 | return ("bad time"); |
---|
1278 | } |
---|
1279 | if ( second == 60 ) { |
---|
1280 | msyslog(LOG_DEBUG, "mx4200_parse_t: leap second! %02d:%02d:%02d", |
---|
1281 | hour, minute, second); |
---|
1282 | } |
---|
1283 | |
---|
1284 | /* |
---|
1285 | * Check for insane date |
---|
1286 | * (Certainly can't be a year before this code was last altered!) |
---|
1287 | */ |
---|
1288 | if (monthday > 31 || month > 12 || |
---|
1289 | monthday < 1 || month < 1 || year < 1997) { |
---|
1290 | mx4200_debug(peer, |
---|
1291 | "mx4200_parse_t: bad date (%4d-%02d-%02d)\n", |
---|
1292 | year, month, monthday); |
---|
1293 | refclock_report(peer, CEVNT_BADDATE); |
---|
1294 | return ("bad date"); |
---|
1295 | } |
---|
1296 | |
---|
1297 | /* |
---|
1298 | * Silly Hack for MX4200: |
---|
1299 | * ASCII message is for *next* 1PPS signal, but we have the |
---|
1300 | * timestamp for the *last* 1PPS signal. So we have to subtract |
---|
1301 | * a second. Discard if we are on a month boundary to avoid |
---|
1302 | * possible leap seconds and leap days. |
---|
1303 | */ |
---|
1304 | second--; |
---|
1305 | if (second < 0) { |
---|
1306 | second = 59; |
---|
1307 | minute--; |
---|
1308 | if (minute < 0) { |
---|
1309 | minute = 59; |
---|
1310 | hour--; |
---|
1311 | if (hour < 0) { |
---|
1312 | hour = 23; |
---|
1313 | monthday--; |
---|
1314 | if (monthday < 1) { |
---|
1315 | return ("sorry, month boundary"); |
---|
1316 | } |
---|
1317 | } |
---|
1318 | } |
---|
1319 | } |
---|
1320 | |
---|
1321 | /* |
---|
1322 | * Calculate Julian date |
---|
1323 | */ |
---|
1324 | if (!(yearday = mx4200_jday(year, month, monthday))) { |
---|
1325 | mx4200_debug(peer, |
---|
1326 | "mx4200_parse_t: bad julian date %d (%4d-%02d-%02d)\n", |
---|
1327 | yearday, year, month, monthday); |
---|
1328 | refclock_report(peer, CEVNT_BADDATE); |
---|
1329 | return("invalid julian date"); |
---|
1330 | } |
---|
1331 | |
---|
1332 | /* |
---|
1333 | * Setup leap second indicator |
---|
1334 | */ |
---|
1335 | if (leapsec == 0) |
---|
1336 | pp->leap = LEAP_NOWARNING; |
---|
1337 | else if (leapsec == 1) |
---|
1338 | pp->leap = LEAP_ADDSECOND; |
---|
1339 | else if (leapsec == -1) |
---|
1340 | pp->leap = LEAP_DELSECOND; |
---|
1341 | else |
---|
1342 | pp->leap = LEAP_NOTINSYNC; /* shouldn't happen */ |
---|
1343 | |
---|
1344 | /* |
---|
1345 | * Any change to the leap second warning status? |
---|
1346 | */ |
---|
1347 | if (leapsec != up->last_leap ) { |
---|
1348 | msyslog(LOG_DEBUG, |
---|
1349 | "mx4200_parse_t: leap second warning: %d to %d (%d)", |
---|
1350 | up->last_leap, leapsec, pp->leap); |
---|
1351 | } |
---|
1352 | up->last_leap = leapsec; |
---|
1353 | |
---|
1354 | /* |
---|
1355 | * Copy time data for billboard monitoring. |
---|
1356 | */ |
---|
1357 | |
---|
1358 | pp->year = year; |
---|
1359 | pp->day = yearday; |
---|
1360 | pp->hour = hour; |
---|
1361 | pp->minute = minute; |
---|
1362 | pp->second = second; |
---|
1363 | pp->msec = 0; |
---|
1364 | pp->usec = 0; |
---|
1365 | |
---|
1366 | /* |
---|
1367 | * Toss if sentence is marked invalid |
---|
1368 | */ |
---|
1369 | if (!valid || pp->leap == LEAP_NOTINSYNC) { |
---|
1370 | mx4200_debug(peer, "mx4200_parse_t: time mark not valid\n"); |
---|
1371 | refclock_report(peer, CEVNT_BADTIME); |
---|
1372 | return ("pulse invalid"); |
---|
1373 | } |
---|
1374 | |
---|
1375 | return (NULL); |
---|
1376 | } |
---|
1377 | |
---|
1378 | /* |
---|
1379 | * Calculate the checksum |
---|
1380 | */ |
---|
1381 | static u_char |
---|
1382 | mx4200_cksum(cp, n) |
---|
1383 | register char *cp; |
---|
1384 | register u_int n; |
---|
1385 | { |
---|
1386 | register u_char ck; |
---|
1387 | |
---|
1388 | for (ck = 0; n-- > 0; cp++) |
---|
1389 | ck ^= *cp; |
---|
1390 | return (ck); |
---|
1391 | } |
---|
1392 | |
---|
1393 | /* |
---|
1394 | * Tables to compute the day of year. Viva la leap. |
---|
1395 | */ |
---|
1396 | static day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; |
---|
1397 | static day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; |
---|
1398 | |
---|
1399 | /* |
---|
1400 | * Calculate the the Julian Day |
---|
1401 | */ |
---|
1402 | static int |
---|
1403 | mx4200_jday(year, month, monthday) |
---|
1404 | int year; |
---|
1405 | int month; |
---|
1406 | int monthday; |
---|
1407 | { |
---|
1408 | register int day, i; |
---|
1409 | int leap_year; |
---|
1410 | |
---|
1411 | |
---|
1412 | /* |
---|
1413 | * Is this a leap year ? |
---|
1414 | */ |
---|
1415 | if (year % 4) { |
---|
1416 | leap_year = 0; /* FALSE */ |
---|
1417 | } else { |
---|
1418 | if (year % 100) { |
---|
1419 | leap_year = 1; /* TRUE */ |
---|
1420 | } else { |
---|
1421 | if (year % 400) { |
---|
1422 | leap_year = 0; /* FALSE */ |
---|
1423 | } else { |
---|
1424 | leap_year = 1; /* TRUE */ |
---|
1425 | } |
---|
1426 | } |
---|
1427 | } |
---|
1428 | |
---|
1429 | /* |
---|
1430 | * Calculate the Julian Date |
---|
1431 | */ |
---|
1432 | day = monthday; |
---|
1433 | |
---|
1434 | if (leap_year) { |
---|
1435 | /* a leap year */ |
---|
1436 | if (day > day2tab[month - 1]) { |
---|
1437 | return (0); |
---|
1438 | } |
---|
1439 | for (i = 0; i < month - 1; i++) |
---|
1440 | day += day2tab[i]; |
---|
1441 | } else { |
---|
1442 | /* not a leap year */ |
---|
1443 | if (day > day1tab[month - 1]) { |
---|
1444 | return (0); |
---|
1445 | } |
---|
1446 | for (i = 0; i < month - 1; i++) |
---|
1447 | day += day1tab[i]; |
---|
1448 | } |
---|
1449 | return (day); |
---|
1450 | } |
---|
1451 | |
---|
1452 | /* |
---|
1453 | * Parse a mx4200 position/height/velocity sentence. |
---|
1454 | * |
---|
1455 | * A typical message looks like this. Checksum has already been stripped. |
---|
1456 | * |
---|
1457 | * $PMVXG,021,SSSSSS.SS,DDMM.MMMM,N,DDDMM.MMMM,E,HHHHH.H,GGGG.G,EEEE.E,WWWW.W,MM |
---|
1458 | * |
---|
1459 | * Field Field Contents |
---|
1460 | * ----- -------------- |
---|
1461 | * Block Label: $PMVXG |
---|
1462 | * Sentence Type: 021=Position, Height Velocity Data |
---|
1463 | * This sentence gives the receiver position, height, |
---|
1464 | * navigation mode, and velocity north/east. |
---|
1465 | * *This sentence is intended for post-analysis |
---|
1466 | * applications.* |
---|
1467 | * 1 UTC measurement time (seconds into week) |
---|
1468 | * 2 WGS-84 Lattitude (degrees, minutes) |
---|
1469 | * 3 N=North, S=South |
---|
1470 | * 4 WGS-84 Longitude (degrees, minutes) |
---|
1471 | * 5 E=East, W=West |
---|
1472 | * 6 Altitude (meters above mean sea level) |
---|
1473 | * 7 Geoidal height (meters) |
---|
1474 | * 8 East velocity (m/sec) |
---|
1475 | * 9 West Velocity (m/sec) |
---|
1476 | * 10 Navigation Mode |
---|
1477 | * Mode if navigating: |
---|
1478 | * 1 = Position from remote device |
---|
1479 | * 2 = 2-D position |
---|
1480 | * 3 = 3-D position |
---|
1481 | * 4 = 2-D differential position |
---|
1482 | * 5 = 3-D differential position |
---|
1483 | * 6 = Static |
---|
1484 | * 8 = Position known -- reference station |
---|
1485 | * 9 = Position known -- Navigator |
---|
1486 | * Mode if not navigating: |
---|
1487 | * 51 = Too few satellites |
---|
1488 | * 52 = DOPs too large |
---|
1489 | * 53 = Position STD too large |
---|
1490 | * 54 = Velocity STD too large |
---|
1491 | * 55 = Too many iterations for velocity |
---|
1492 | * 56 = Too many iterations for position |
---|
1493 | * 57 = 3 sat startup failed |
---|
1494 | * 58 = Command abort |
---|
1495 | */ |
---|
1496 | static char * |
---|
1497 | mx4200_parse_p(peer) |
---|
1498 | struct peer *peer; |
---|
1499 | { |
---|
1500 | struct refclockproc *pp; |
---|
1501 | struct mx4200unit *up; |
---|
1502 | int sentence_type, mode; |
---|
1503 | double dtemp, dtemp2, mtime, lat, lon, alt, geoid, vele, veln, weight; |
---|
1504 | char *cp; |
---|
1505 | |
---|
1506 | pp = peer->procptr; |
---|
1507 | up = (struct mx4200unit *)pp->unitptr; |
---|
1508 | |
---|
1509 | /* Should never happen! */ |
---|
1510 | if (up->moving) return ("mobile platform - no pos!"); |
---|
1511 | |
---|
1512 | cp = pp->a_lastcode; |
---|
1513 | |
---|
1514 | if ((cp = strchr(cp, ',')) == NULL) |
---|
1515 | return ("no rec-type"); |
---|
1516 | cp++; |
---|
1517 | |
---|
1518 | /* Sentence type */ |
---|
1519 | sentence_type = strtol(cp, &cp, 10); |
---|
1520 | if (sentence_type != PMVXG_D_PHV) |
---|
1521 | return ("wrong rec-type"); |
---|
1522 | |
---|
1523 | /* Measurement Time */ |
---|
1524 | if (*cp++ != ',') |
---|
1525 | return ("no measurement time"); |
---|
1526 | mtime = strtod(cp,&cp); |
---|
1527 | |
---|
1528 | /* Latitude (always +ve) */ |
---|
1529 | if (*cp++ != ',') |
---|
1530 | return ("no latitude"); |
---|
1531 | dtemp = atof(cp); |
---|
1532 | dtemp2 = strtod(cp,&cp); |
---|
1533 | if (dtemp < 0.0) |
---|
1534 | return ("negative latitude"); |
---|
1535 | lat = (double) ( (int)dtemp / 100); |
---|
1536 | lat += (dtemp - (lat*100.0)) * 10.0 / 600.0; |
---|
1537 | |
---|
1538 | /* North/South */ |
---|
1539 | if (*cp++ != ',') |
---|
1540 | return ("no north/south indicator"); |
---|
1541 | if (*cp == 'N') |
---|
1542 | lat = lat; |
---|
1543 | else if (*cp == 'S') |
---|
1544 | lat *= -1.0; |
---|
1545 | else |
---|
1546 | return ("invalid north/south indicator"); |
---|
1547 | cp++; |
---|
1548 | |
---|
1549 | /* Longitude (always +ve) */ |
---|
1550 | if (*cp++ != ',') |
---|
1551 | return ("no longitude"); |
---|
1552 | dtemp = atof(cp); |
---|
1553 | dtemp2 = strtod(cp,&cp); |
---|
1554 | if (dtemp < 0.0) |
---|
1555 | return ("negative latitude"); |
---|
1556 | lon = (double) ( (int)dtemp / 100); |
---|
1557 | lon += (dtemp - (lon*100.0)) * 10.0 / 600.0; |
---|
1558 | |
---|
1559 | /* East/West */ |
---|
1560 | if (*cp++ != ',') |
---|
1561 | return ("no east/west indicator"); |
---|
1562 | if (*cp == 'E') |
---|
1563 | lon = lon; |
---|
1564 | else if (*cp == 'W') |
---|
1565 | lon *= -1.0; |
---|
1566 | else |
---|
1567 | return ("invalid east/west indicator"); |
---|
1568 | cp++; |
---|
1569 | |
---|
1570 | /* Altitude */ |
---|
1571 | if (*cp++ != ',') |
---|
1572 | return ("no altitude"); |
---|
1573 | alt = atof(cp); |
---|
1574 | dtemp2 = strtod(cp,&cp); |
---|
1575 | |
---|
1576 | /* geoid height */ |
---|
1577 | if (*cp++ != ',') |
---|
1578 | return ("no geoid height"); |
---|
1579 | geoid = strtod(cp,&cp); |
---|
1580 | |
---|
1581 | /* East velocity */ |
---|
1582 | if (*cp++ != ',') |
---|
1583 | return ("no east velocity"); |
---|
1584 | vele = strtod(cp,&cp); |
---|
1585 | |
---|
1586 | /* north velocity */ |
---|
1587 | if (*cp++ != ',') |
---|
1588 | return ("no north velocity"); |
---|
1589 | veln = strtod(cp,&cp); |
---|
1590 | |
---|
1591 | /* nav mode */ |
---|
1592 | if (*cp++ != ',') |
---|
1593 | return ("no nav mode"); |
---|
1594 | mode = strtol(cp, &cp, 10); |
---|
1595 | |
---|
1596 | |
---|
1597 | /* |
---|
1598 | * return if not navigating |
---|
1599 | */ |
---|
1600 | if (mode > 10) |
---|
1601 | return ("not navigating"); |
---|
1602 | |
---|
1603 | if (mode != 3 && mode != 5) |
---|
1604 | return ("not navigating in 3D"); |
---|
1605 | |
---|
1606 | /* |
---|
1607 | * Calculate running weighted averages |
---|
1608 | */ |
---|
1609 | |
---|
1610 | weight = (USUAL_EDOP/up->edop); |
---|
1611 | weight = weight * weight; |
---|
1612 | up->avg_lon = up->filt_lon * up->avg_lon + weight * lon; |
---|
1613 | up->filt_lon += weight; |
---|
1614 | up->avg_lon = up->avg_lon / up->filt_lon; |
---|
1615 | |
---|
1616 | weight = (USUAL_NDOP/up->ndop); |
---|
1617 | weight = weight * weight; |
---|
1618 | up->avg_lat = up->filt_lat * up->avg_lat + weight * lat; |
---|
1619 | up->filt_lat += weight; |
---|
1620 | up->avg_lat = up->avg_lat / up->filt_lat; |
---|
1621 | |
---|
1622 | weight = (USUAL_VDOP/up->vdop); |
---|
1623 | weight = weight * weight; |
---|
1624 | up->avg_alt = up->filt_alt * up->avg_alt + weight * alt; |
---|
1625 | up->filt_alt += weight; |
---|
1626 | up->avg_alt = up->avg_alt / up->filt_alt; |
---|
1627 | |
---|
1628 | return (NULL); |
---|
1629 | } |
---|
1630 | |
---|
1631 | /* |
---|
1632 | * Parse a mx4200 DOP sentence. |
---|
1633 | * |
---|
1634 | * A typical message looks like this. Checksum has already been stripped. |
---|
1635 | * |
---|
1636 | * $PMVXG,022,SSSSSS.SSEE.E,NN.N,VV.V,XX,XX,XX,XX,XX,XX |
---|
1637 | * |
---|
1638 | * Field Field Contents |
---|
1639 | * ----- -------------- |
---|
1640 | * Block Label: $PMVXG |
---|
1641 | * Sentence Type: 022=DOPs. The DOP values in this sentence |
---|
1642 | * correspond to the satellites listed. The PRNs in |
---|
1643 | * the message are listed in receiver channel number order |
---|
1644 | * 1 UTC measurement time (seconds into week) |
---|
1645 | * 2 EDOP (east DOP) |
---|
1646 | * 3 NDOP (north DOP) |
---|
1647 | * 4 VDOP (vertical DOP) |
---|
1648 | * 5 PRN on channel 1 |
---|
1649 | * 6 PRN on channel 2 |
---|
1650 | * 7 PRN on channel 3 |
---|
1651 | * 8 PRN on channel 4 |
---|
1652 | * 9 PRN on channel 5 |
---|
1653 | * 10 PRN on channel 6 |
---|
1654 | */ |
---|
1655 | static char * |
---|
1656 | mx4200_parse_d(peer) |
---|
1657 | struct peer *peer; |
---|
1658 | { |
---|
1659 | struct refclockproc *pp; |
---|
1660 | struct mx4200unit *up; |
---|
1661 | int sentence_type; |
---|
1662 | double mtime, dtemp2, edop, ndop, vdop; |
---|
1663 | char *cp; |
---|
1664 | |
---|
1665 | pp = peer->procptr; |
---|
1666 | up = (struct mx4200unit *)pp->unitptr; |
---|
1667 | |
---|
1668 | /* Should never happen! */ |
---|
1669 | if (up->moving) return ("mobile platform - no dop!"); |
---|
1670 | |
---|
1671 | cp = pp->a_lastcode; |
---|
1672 | |
---|
1673 | if ((cp = strchr(cp, ',')) == NULL) |
---|
1674 | return ("no rec-type"); |
---|
1675 | cp++; |
---|
1676 | |
---|
1677 | /* Sentence type */ |
---|
1678 | sentence_type = strtol(cp, &cp, 10); |
---|
1679 | if (sentence_type != PMVXG_D_DOPS) |
---|
1680 | return ("wrong rec-type"); |
---|
1681 | |
---|
1682 | /* Measurement Time */ |
---|
1683 | if (*cp++ != ',') |
---|
1684 | return ("no measurement time"); |
---|
1685 | mtime = strtod(cp,&cp); |
---|
1686 | |
---|
1687 | /* EDOP */ |
---|
1688 | if (*cp++ != ',') |
---|
1689 | return ("no edop"); |
---|
1690 | edop = atof(cp); |
---|
1691 | dtemp2 = strtod(cp,&cp); |
---|
1692 | |
---|
1693 | /* NDOP */ |
---|
1694 | if (*cp++ != ',') |
---|
1695 | return ("no ndop"); |
---|
1696 | ndop = atof(cp); |
---|
1697 | dtemp2 = strtod(cp,&cp); |
---|
1698 | |
---|
1699 | /* VDOP */ |
---|
1700 | if (*cp++ != ',') |
---|
1701 | return ("no vdop"); |
---|
1702 | vdop = atof(cp); |
---|
1703 | dtemp2 = strtod(cp,&cp); |
---|
1704 | |
---|
1705 | /* Ignore the PRNs... */ |
---|
1706 | |
---|
1707 | |
---|
1708 | /* Update values */ |
---|
1709 | if (ndop <= 0.0 || ndop<= 0.0 || vdop <= 0.0) |
---|
1710 | return ("nonpositive dop"); |
---|
1711 | up->edop = edop; |
---|
1712 | up->ndop = ndop; |
---|
1713 | up->vdop = vdop; |
---|
1714 | |
---|
1715 | return (NULL); |
---|
1716 | } |
---|
1717 | |
---|
1718 | /* |
---|
1719 | * Parse a mx4200 Status sentence |
---|
1720 | * Parse a mx4200 Mode Data sentence |
---|
1721 | * Parse a mx4200 Software Configuration sentence |
---|
1722 | * Parse a mx4200 Time Recovery Parameters Currently in Use sentence |
---|
1723 | * (used only for logging raw strings) |
---|
1724 | * |
---|
1725 | * A typical message looks like this. Checksum has already been stripped. |
---|
1726 | * |
---|
1727 | * $PMVXG,000,XXX,XX,X,HHMM,X |
---|
1728 | * |
---|
1729 | * Field Field Contents |
---|
1730 | * ----- -------------- |
---|
1731 | * Block Label: $PMVXG |
---|
1732 | * Sentence Type: 000=Status. |
---|
1733 | * Returns status of the receiver to the controller. |
---|
1734 | * 1 Current Receiver Status: |
---|
1735 | * ACQ = Satellite re-acquisition |
---|
1736 | * ALT = Constellation selection |
---|
1737 | * COR = Providing corrections (for reference stations only) |
---|
1738 | * IAC = Initial acquisition |
---|
1739 | * IDL = Idle, no satellites |
---|
1740 | * NAV = Navigation |
---|
1741 | * STS = Search the Sky (no almanac available) |
---|
1742 | * TRK = Tracking |
---|
1743 | * 2 Number of satellites that should be visible |
---|
1744 | * 3 Number of satellites being tracked |
---|
1745 | * 4 Time since last navigation status if not currently navigating |
---|
1746 | * (hours, minutes) |
---|
1747 | * 5 Initialization status: |
---|
1748 | * 0 = Waiting for initialization parameters |
---|
1749 | * 1 = Initialization completed |
---|
1750 | * |
---|
1751 | * A typical message looks like this. Checksum has already been stripped. |
---|
1752 | * |
---|
1753 | * $PMVXG,004,C,R,D,H.HH,V.VV,TT,HHHH,VVVV,T |
---|
1754 | * |
---|
1755 | * Field Field Contents |
---|
1756 | * ----- -------------- |
---|
1757 | * Block Label: $PMVXG |
---|
1758 | * Sentence Type: 004=Software Configuration. |
---|
1759 | * Defines the navigation mode and criteria for |
---|
1760 | * acceptable navigation for the receiver. |
---|
1761 | * 1 Constrain Altitude Mode: |
---|
1762 | * 0 = Auto. Constrain altitude (2-D solution) and use |
---|
1763 | * manual altitude input when 3 sats avalable. Do |
---|
1764 | * not constrain altitude (3-D solution) when 4 sats |
---|
1765 | * available. |
---|
1766 | * 1 = Always constrain altitude (2-D solution). |
---|
1767 | * 2 = Never constrain altitude (3-D solution). |
---|
1768 | * 3 = Coast. Constrain altitude (2-D solution) and use |
---|
1769 | * last GPS altitude calculation when 3 sats avalable. |
---|
1770 | * Do not constrain altitude (3-D solution) when 4 sats |
---|
1771 | * available. |
---|
1772 | * 2 Altitude Reference: (always 0 for MX4200) |
---|
1773 | * 0 = Ellipsoid |
---|
1774 | * 1 = Geoid (MSL) |
---|
1775 | * 3 Differential Navigation Control: |
---|
1776 | * 0 = Disabled |
---|
1777 | * 1 = Enabled |
---|
1778 | * 4 Horizontal Acceleration Constant (m/sec**2) |
---|
1779 | * 5 Vertical Acceleration Constant (m/sec**2) (0 for MX4200) |
---|
1780 | * 6 Tracking Elevation Limit (degrees) |
---|
1781 | * 7 HDOP Limit |
---|
1782 | * 8 VDOP Limit |
---|
1783 | * 9 Time Output Mode: |
---|
1784 | * U = UTC |
---|
1785 | * L = Local time |
---|
1786 | * 10 Local Time Offset (minutes) (absent on MX4200) |
---|
1787 | * |
---|
1788 | * A typical message looks like this. Checksum has already been stripped. |
---|
1789 | * |
---|
1790 | * $PMVXG,030,NNNN,FFF |
---|
1791 | * |
---|
1792 | * Field Field Contents |
---|
1793 | * ----- -------------- |
---|
1794 | * Block Label: $PMVXG |
---|
1795 | * Sentence Type: 030=Software Configuration. |
---|
1796 | * This sentence contains the navigation processor |
---|
1797 | * and baseband firmware version numbers. |
---|
1798 | * 1 Nav Processor Version Number |
---|
1799 | * 2 Baseband Firmware Version Number |
---|
1800 | * |
---|
1801 | * A typical message looks like this. Checksum has already been stripped. |
---|
1802 | * |
---|
1803 | * $PMVXG,523,M,S,M,EEEE,BBBBBB,C,R |
---|
1804 | * |
---|
1805 | * Field Field Contents |
---|
1806 | * ----- -------------- |
---|
1807 | * Block Label: $PMVXG |
---|
1808 | * Sentence Type: 523=Time Recovery Parameters Currently in Use. |
---|
1809 | * This sentence contains the configuration of the |
---|
1810 | * time recovery feature of the receiver. |
---|
1811 | * 1 Time Recovery Mode: |
---|
1812 | * D = Dynamic; solve for position and time while moving |
---|
1813 | * S = Static; solve for position and time while stationary |
---|
1814 | * K = Known position input, solve for time only |
---|
1815 | * N = No time recovery |
---|
1816 | * 2 Time Synchronization: |
---|
1817 | * U = UTC time |
---|
1818 | * G = GPS time |
---|
1819 | * 3 Time Mark Mode: |
---|
1820 | * A = Always output a time pulse |
---|
1821 | * V = Only output time pulse if time is valid (as determined |
---|
1822 | * by Maximum Time Error) |
---|
1823 | * 4 Maximum Time Error - the maximum error (in nanoseconds) for |
---|
1824 | * which a time mark will be considered valid. |
---|
1825 | * 5 User Time Bias - external bias in nanoseconds |
---|
1826 | * 6 Time Message Control: |
---|
1827 | * 0 = Do not output the time recovery message |
---|
1828 | * 1 = Output the time recovery message (record 830) to |
---|
1829 | * Control port |
---|
1830 | * 2 = Output the time recovery message (record 830) to |
---|
1831 | * Equipment port |
---|
1832 | * 7 Reserved |
---|
1833 | * 8 Position Known PRN (absent on MX 4200) |
---|
1834 | * |
---|
1835 | */ |
---|
1836 | static char * |
---|
1837 | mx4200_parse_s(peer) |
---|
1838 | struct peer *peer; |
---|
1839 | { |
---|
1840 | struct refclockproc *pp; |
---|
1841 | struct mx4200unit *up; |
---|
1842 | int sentence_type; |
---|
1843 | char *cp; |
---|
1844 | |
---|
1845 | pp = peer->procptr; |
---|
1846 | up = (struct mx4200unit *)pp->unitptr; |
---|
1847 | |
---|
1848 | cp = pp->a_lastcode; |
---|
1849 | |
---|
1850 | if ((cp = strchr(cp, ',')) == NULL) |
---|
1851 | return ("no rec-type"); |
---|
1852 | cp++; |
---|
1853 | |
---|
1854 | /* Sentence type */ |
---|
1855 | sentence_type = strtol(cp, &cp, 10); |
---|
1856 | if (sentence_type != PMVXG_D_STATUS && |
---|
1857 | sentence_type != PMVXG_D_MODEDATA && |
---|
1858 | sentence_type != PMVXG_D_SOFTCONF && |
---|
1859 | sentence_type != PMVXG_D_TRECOVUSEAGE ) |
---|
1860 | return ("wrong rec-type"); |
---|
1861 | cp++; |
---|
1862 | |
---|
1863 | /* Log the Status */ |
---|
1864 | if (sentence_type == PMVXG_D_STATUS) { |
---|
1865 | msyslog(LOG_DEBUG, |
---|
1866 | "mx4200_parse_s: status: %s", cp); |
---|
1867 | } |
---|
1868 | |
---|
1869 | /* Log the Mode Data */ |
---|
1870 | if (sentence_type == PMVXG_D_MODEDATA) { |
---|
1871 | msyslog(LOG_DEBUG, |
---|
1872 | "mx4200_parse_s: mode data: %s", cp); |
---|
1873 | } |
---|
1874 | |
---|
1875 | /* Log the Software Version */ |
---|
1876 | if (sentence_type == PMVXG_D_SOFTCONF) { |
---|
1877 | msyslog(LOG_DEBUG, |
---|
1878 | "mx4200_parse_s: firmware configuration: %s", cp); |
---|
1879 | } |
---|
1880 | |
---|
1881 | /* Log the Time Recovery Parameters */ |
---|
1882 | if (sentence_type == PMVXG_D_TRECOVUSEAGE) { |
---|
1883 | msyslog(LOG_DEBUG, |
---|
1884 | "mx4200_parse_s: time recovery parms: %s", cp); |
---|
1885 | } |
---|
1886 | |
---|
1887 | return (NULL); |
---|
1888 | } |
---|
1889 | |
---|
1890 | /* |
---|
1891 | * Process a PPS signal, returning a timestamp. |
---|
1892 | */ |
---|
1893 | static int |
---|
1894 | mx4200_pps(peer) |
---|
1895 | struct peer *peer; |
---|
1896 | { |
---|
1897 | #ifdef PPS |
---|
1898 | struct refclockproc *pp; |
---|
1899 | struct mx4200unit *up; |
---|
1900 | |
---|
1901 | int temp_serial; |
---|
1902 | |
---|
1903 | pp = peer->procptr; |
---|
1904 | up = (struct mx4200unit *)pp->unitptr; |
---|
1905 | |
---|
1906 | |
---|
1907 | /* |
---|
1908 | * Grab the timestamp of the PPS signal. |
---|
1909 | */ |
---|
1910 | temp_serial = up->ppsev.serial; |
---|
1911 | if (ioctl(fdpps, CIOGETEV, (caddr_t)&up->ppsev) < 0) { |
---|
1912 | /* XXX Actually, if this fails, we're pretty much screwed */ |
---|
1913 | mx4200_debug(peer, "mx4200_pps: CIOGETEV: "); |
---|
1914 | if (errno < sys_nerr) |
---|
1915 | mx4200_debug(peer, "%s", sys_errlist[errno]); |
---|
1916 | mx4200_debug(peer, "\n"); |
---|
1917 | refclock_report(peer, CEVNT_FAULT); |
---|
1918 | return(1); |
---|
1919 | } |
---|
1920 | if (temp_serial == up->ppsev.serial) { |
---|
1921 | mx4200_debug(peer, |
---|
1922 | "mx4200_pps: ppsev serial not incrementing: %d\n", |
---|
1923 | up->ppsev.serial); |
---|
1924 | refclock_report(peer, CEVNT_FAULT); |
---|
1925 | return(1); |
---|
1926 | } |
---|
1927 | |
---|
1928 | /* |
---|
1929 | * Check pps serial number against last one |
---|
1930 | */ |
---|
1931 | if (up->lastserial + 1 != up->ppsev.serial && up->lastserial != 0) { |
---|
1932 | if (up->ppsev.serial == up->lastserial) |
---|
1933 | mx4200_debug(peer, "mx4200_pps: no new pps event\n"); |
---|
1934 | else |
---|
1935 | mx4200_debug(peer, "mx4200_pps: missed %d pps events\n", |
---|
1936 | up->ppsev.serial - up->lastserial - 1); |
---|
1937 | refclock_report(peer, CEVNT_FAULT); |
---|
1938 | } |
---|
1939 | up->lastserial = up->ppsev.serial; |
---|
1940 | |
---|
1941 | /* |
---|
1942 | * Return the timestamp in pp->lastrec |
---|
1943 | */ |
---|
1944 | up->ppsev.tv.tv_sec += (u_int32) JAN_1970; |
---|
1945 | TVTOTS(&up->ppsev.tv,&pp->lastrec); |
---|
1946 | |
---|
1947 | #endif /* PPS */ |
---|
1948 | |
---|
1949 | return(0); |
---|
1950 | } |
---|
1951 | |
---|
1952 | /* |
---|
1953 | * mx4200_debug - print debug messages |
---|
1954 | */ |
---|
1955 | #if __STDC__ |
---|
1956 | static void |
---|
1957 | mx4200_debug(struct peer *peer, char *fmt, ...) |
---|
1958 | #else |
---|
1959 | static void |
---|
1960 | mx4200_debug(peer, fmt, va_alist) |
---|
1961 | struct peer *peer; |
---|
1962 | char *fmt; |
---|
1963 | #endif |
---|
1964 | { |
---|
1965 | va_list ap; |
---|
1966 | struct refclockproc *pp; |
---|
1967 | struct mx4200unit *up; |
---|
1968 | |
---|
1969 | if (debug) { |
---|
1970 | |
---|
1971 | #if __STDC__ |
---|
1972 | va_start(ap, fmt); |
---|
1973 | #else |
---|
1974 | va_start(ap); |
---|
1975 | #endif |
---|
1976 | |
---|
1977 | pp = peer->procptr; |
---|
1978 | up = (struct mx4200unit *)pp->unitptr; |
---|
1979 | |
---|
1980 | |
---|
1981 | /* |
---|
1982 | * Print debug message to stdout |
---|
1983 | * In the future, we may want to get get more creative... |
---|
1984 | */ |
---|
1985 | vprintf(fmt, ap); |
---|
1986 | |
---|
1987 | va_end(ap); |
---|
1988 | } |
---|
1989 | } |
---|
1990 | |
---|
1991 | /* |
---|
1992 | * Send a character string to the receiver. Checksum is appended here. |
---|
1993 | */ |
---|
1994 | static void |
---|
1995 | #if __STDC__ |
---|
1996 | mx4200_send(struct peer *peer, char *fmt, ...) |
---|
1997 | #else |
---|
1998 | mx4200_send(peer, fmt, va_alist) |
---|
1999 | struct peer *peer; |
---|
2000 | char *fmt; |
---|
2001 | va_dcl |
---|
2002 | #endif /* __STDC__ */ |
---|
2003 | { |
---|
2004 | struct refclockproc *pp; |
---|
2005 | struct mx4200unit *up; |
---|
2006 | |
---|
2007 | register char *cp; |
---|
2008 | register int n, m; |
---|
2009 | va_list ap; |
---|
2010 | char buf[1024]; |
---|
2011 | u_char ck; |
---|
2012 | |
---|
2013 | #if __STDC__ |
---|
2014 | va_start(ap, fmt); |
---|
2015 | #else |
---|
2016 | va_start(ap); |
---|
2017 | #endif /* __STDC__ */ |
---|
2018 | |
---|
2019 | pp = peer->procptr; |
---|
2020 | up = (struct mx4200unit *)pp->unitptr; |
---|
2021 | |
---|
2022 | cp = buf; |
---|
2023 | *cp++ = '$'; |
---|
2024 | #ifdef notdef |
---|
2025 | /* BSD is rational */ |
---|
2026 | n = vsnprintf(cp, sizeof(buf) - 1, fmt, ap); |
---|
2027 | #else |
---|
2028 | /* SunOS sucks */ |
---|
2029 | (void)vsprintf(cp, fmt, ap); |
---|
2030 | n = strlen(cp); |
---|
2031 | #endif /* notdef */ |
---|
2032 | ck = mx4200_cksum(cp, n); |
---|
2033 | cp += n; |
---|
2034 | ++n; |
---|
2035 | #ifdef notdef |
---|
2036 | /* BSD is rational */ |
---|
2037 | n += snprintf(cp, sizeof(buf) - n - 5, "*%02X\r\n", ck); |
---|
2038 | #else |
---|
2039 | /* SunOS sucks */ |
---|
2040 | sprintf(cp, "*%02X\r\n", ck); |
---|
2041 | n += strlen(cp); |
---|
2042 | #endif /* notdef */ |
---|
2043 | |
---|
2044 | m = write(pp->io.fd, buf, n); |
---|
2045 | if (m < 0) |
---|
2046 | msyslog(LOG_ERR, "mx4200_send: write: %m (%s)", buf); |
---|
2047 | mx4200_debug(peer, "mx4200_send: %d %s\n", m, buf); |
---|
2048 | va_end(ap); |
---|
2049 | } |
---|
2050 | |
---|
2051 | #else /* not (REFCLOCK && MX4200 && PPS) */ |
---|
2052 | int refclock_mx4200_bs; |
---|
2053 | #endif /* not (REFCLOCK && MX4200 && PPS) */ |
---|