/contrib/ntp/ntpd/refclock_mx4200.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 1652 lines · 867 code · 168 blank · 617 comment · 133 complexity · 5c31728c7c3c5f68db581ef581850003 MD5 · raw file

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