Lines Matching +full:hart +full:- +full:compatible
2 * refclock_nmea.c - clock driver for an NMEA GPS CLOCK
15 * Dave Hart July 1, 2009
16 * hart@ntp.org, davehart@davehart.com
51 * This driver supports NMEA-compatible GPS receivers
71 * bit 0 - enables RMC (1)
72 * bit 1 - enables GGA (2)
73 * bit 2 - enables GLL (4)
74 * bit 3 - enables ZDA (8) - Standard Time & Date
75 * bit 3 - enables ZDG (8) - Accord GPS Clock's custom sentence with GPS time
80 * bit 4/5/6 - selects the baudrate for serial port :
117 * $--ZDA,HHMMSS.SS,DD,MM,YYYY,TH,TM,*CS<CR><LF>
133 * '1' indicates accuracy of +/-20 ms
134 * '2' indicates accuracy of +/-100 ns
138 * WN -- GPS week number (weeks since 1980-01-06, mod 1024)
139 * WS -- GPS seconds in week
140 * LS -- GPS leap seconds, accumulated ( UTC + LS == GPS )
141 * FIX -- Fix type: 0=nofix, 1=2D, 2=3D
154 #define PRECISION (-9) /* precision assumed (about 2 ms) */
155 #define PPS_PRECISION (-20) /* precision assumed (about 1 us) */
215 DATE_1_DDMMYY, /* use 1 field with 2-digit year */
216 DATE_3_DDMMYYYY /* use 3 fields with 4-digit year */
222 DTYP_Y2D, /* 2-digit year */
223 DTYP_W10B, /* 10-bit week in GPS epoch */
224 DTYP_Y4D, /* 4-digit (full) year */
233 #define CHECK_EMPTY -1 /* no data */
250 uint16_t rcvtout; /* one-shot for sample expiration */
320 * If we want the driver to output sentences, too: re-enable the send
321 * support functions by defining NMEA_WRITE_SUPPORT to non-zero...
328 * -------------------------------------------------------------------
330 * -------------------------------------------------------------------
344 * -------------------------------------------------------------------
345 * nmea_start - open the GPS devices and initialize data for processing
350 * -------------------------------------------------------------------
358 struct refclockproc * const pp = peer->procptr;
366 rate = (peer->ttl & NMEA_BAUDRATE_MASK) >> NMEA_BAUDRATE_SHIFT;
395 pp->unitptr = (caddr_t)up;
396 pp->io.fd = -1;
397 pp->io.clock_recv = nmea_receive;
398 pp->io.srcclock = peer;
399 pp->io.datalen = 0;
401 memset(&up->last_reftime, 0xFF, sizeof(up->last_reftime));
402 memset(&up->last_gpsdate, 0x00, sizeof(up->last_gpsdate));
404 up->cksum_type[NMEA_GPRMC] = CHECK_CSVALID;
406 up->ppsapi_fd = -1;
408 ZERO(up->tally);
411 peer->precision = PRECISION;
412 pp->clockdesc = DESCRIPTION;
413 memcpy(&pp->refid, REFID, 4);
419 refnumtoa(&peer->srcadr));
422 pp->io.fd = refclock_open(&peer->srcadr, device, baudrate, LDISC_CLK);
423 if (0 >= pp->io.fd) {
424 pp->io.fd = nmead_open(device);
425 if (-1 == pp->io.fd)
430 return io_addclock(&pp->io) != 0;
434 * -------------------------------------------------------------------
435 * nmea_shutdown - shut down a GPS clock
439 * -------------------------------------------------------------------
447 struct refclockproc * const pp = peer->procptr;
448 nmea_unit * const up = (nmea_unit *)pp->unitptr;
454 if (up->ppsapi_lit)
455 time_pps_destroy(up->atom.handle);
456 ppsdev_close(pp->io.fd, up->ppsapi_fd);
460 pp->unitptr = (caddr_t)NULL;
461 if (-1 != pp->io.fd)
462 io_closeclock(&pp->io);
463 pp->io.fd = -1;
467 * -------------------------------------------------------------------
468 * nmea_control - configure fudge params
469 * -------------------------------------------------------------------
480 struct refclockproc * const pp = peer->procptr;
481 nmea_unit * const up = (nmea_unit *)pp->unitptr;
501 if ((CLK_FLAG1 & pp->sloppyclockflag) && !up->ppsapi_tried) {
503 up->ppsapi_tried = TRUE;
508 refnumtoa(&peer->srcadr));
511 up->ppsapi_fd = ppsdev_reopen(
512 &peer->srcadr,
513 pp->io.fd, up->ppsapi_fd,
517 * - the clock is shut down
518 * - flag1 is set again after being cleared
520 if (refclock_ppsapi(up->ppsapi_fd, &up->atom)) {
522 up->ppsapi_lit = refclock_params(
523 pp->sloppyclockflag, &up->atom);
524 if (!up->ppsapi_lit) {
526 time_pps_destroy(up->atom.handle);
529 refnumtoa(&peer->srcadr));
534 refnumtoa(&peer->srcadr));
539 if ( !(CLK_FLAG1 & pp->sloppyclockflag) && up->ppsapi_tried) {
541 if (up->ppsapi_lit)
542 time_pps_destroy(up->atom.handle);
543 up->atom.handle = 0;
547 up->ppsapi_gate = FALSE;
548 up->ppsapi_lit = FALSE;
549 up->ppsapi_tried = FALSE;
551 peer->flags &= ~FLAG_PPS;
552 peer->precision = PRECISION;
558 * -------------------------------------------------------------------
559 * nmea_timer - called once per second
569 * -------------------------------------------------------------------
577 struct refclockproc * const pp = peer->procptr;
578 nmea_unit * const up = (nmea_unit *)pp->unitptr;
584 if (-1 != pp->io.fd) /* any mode bits to evaluate here? */
585 gps_send(pp->io.fd, "$PMOTG,RMC,0000*1D\r\n", peer);
590 if (up->rcvtout) {
591 --up->rcvtout;
592 } else if (pp->codeproc != pp->coderecv) {
596 up->lb_buf[0] = '\0';
597 up->lb_len = 0;
600 if (up->hold_gpsdate && (--up->hold_gpsdate < DATE_HLIM))
601 up->type_gpsdate = DTYP_NONE;
605 * -------------------------------------------------------------------
606 * nmea_procrec - receive data from the serial interface
620 * This function assumes a non-empty line in the unit line buffer.
621 * -------------------------------------------------------------------
630 struct refclockproc * const pp = peer->procptr;
631 nmea_unit * const up = (nmea_unit*)pp->unitptr;
641 l_fp tofs; /* offset to full-second reftime */
666 checkres = field_init(&rdata, up->lb_buf, up->lb_len);
671 refnumtoa(&peer->srcadr), up->lb_buf));
680 refnumtoa(&peer->srcadr), up->lb_len,
681 up->lb_buf));
684 up->tally.total++;
687 * --> below this point we have a valid NMEA sentence <--
713 if (peer->ttl & NMEA_DELAYMEAS_MASK) {
714 mprintf_clock_stats(&peer->srcadr, "delay %0.6f %.*s",
715 ldexp(rd_timestamp.l_uf, -32),
716 (int)(strchr(up->lb_buf, ',') - up->lb_buf),
717 up->lb_buf);
721 if ((peer->ttl & NMEA_MESSAGE_MASK) &&
722 !(peer->ttl & sentence_mode[sentence])) {
723 up->tally.filtered++;
736 * preserve its error-detection capabilities with modern GPSes
740 * checksum. ('up->cksum_type[NMEA_GPRMC]' is set in
744 if (up->cksum_type[sentence] <= (u_char)checkres) {
745 up->cksum_type[sentence] = (u_char)checkres;
748 refnumtoa(&peer->srcadr), up->lb_buf));
750 up->tally.malformed++;
760 if (!up->gps_time) {
763 refnumtoa(&peer->srcadr));
764 up->gps_time = 1;
767 if (up->gps_time) {
768 up->tally.filtered++;
774 refnumtoa(&peer->srcadr), up->lb_len, up->lb_buf));
780 rc_date = -1; /* assume we have to do day-time mapping */
787 pp->leap = parse_qual(&rdata, 2, 'A', 0);
788 if (up->type_gpsdate <= DTYP_Y2D) {
792 if (CLK_FLAG4 & pp->sloppyclockflag)
793 field_wipe(&rdata, 3, 4, 5, 6, -1);
799 pp->leap = parse_qual(&rdata, 6, '0', 1);
800 if (CLK_FLAG4 & pp->sloppyclockflag)
801 field_wipe(&rdata, 2, 4, -1);
807 pp->leap = parse_qual(&rdata, 6, 'A', 0);
808 if (CLK_FLAG4 & pp->sloppyclockflag)
809 field_wipe(&rdata, 1, 3, -1);
815 if (up->type_gpsdate <= DTYP_Y4D) {
824 pp->leap = parse_qual(&rdata, 4, '0', 1);
825 --tofs.l_ui; /* GPZDG gives *following* second */
826 if (up->type_gpsdate <= DTYP_Y4D) {
835 if (up->type_gpsdate <= DTYP_W10B) {
839 pp->leap = parse_qual(&rdata, 11, '0', 1);
840 if (CLK_FLAG4 & pp->sloppyclockflag)
841 field_wipe(&rdata, 6, 8, -1);
845 /* PUBX,04 is peculiar. The UTC time-of-week is the *internal*
849 if (up->type_gpsdate <= DTYP_WEXT) {
850 rc_date = parse_gpsw(&wgps, &rdata, 5, 4, -1);
861 if (peer->ttl & NMEA_IGNSTATUS_MASK) { /* assume always good? */
862 pp->leap = LEAP_NOWARNING;
866 if (pp->leap == LEAP_NOTINSYNC) { /* no good status? */
868 up->tally.rejected++;
870 /* Check sanity of time-of-day. */
873 up->tally.malformed++;
878 up->tally.malformed++;
881 checkres = -1;
884 if (checkres != -1) {
885 refclock_save_lcode(pp, up->lb_buf, up->lb_len);
894 if (up->ppsapi_lit && pp->leap != LEAP_NOTINSYNC)
896 &up->atom, &rd_timestamp,
897 pp->fudgetime2, pp->fudgetime1);
901 rd_timestamp, pp->fudgetime2);
904 warp = !(peer->ttl & NMEA_DATETRUST_MASK);
907 refnumtoa(&peer->srcadr), rc_dtyp));
910 up->last_gpsdate = gpsntp_from_gpscal_ex(
914 up->last_gpsdate = gpsntp_from_gpscal_ex(
918 up->last_gpsdate = gpsntp_from_calendar_ex(
922 up->type_gpsdate = rc_dtyp;
923 up->hold_gpsdate = DATE_HOLD;
926 if (up->hold_gpsdate) { /* time of day, based */
928 &date, tofs, &up->last_gpsdate, warp);
939 refnumtoa(&peer->srcadr),
944 refnumtoa(&peer->srcadr),
955 if (L_ISEQU(&up->last_reftime, &rd_reftime)) {
956 /* Do not touch pp->a_lastcode on purpose! */
957 up->tally.filtered++;
960 up->last_reftime = rd_reftime;
963 refnumtoa(&peer->srcadr), up->lb_buf));
966 up->tally.accepted++;
967 refclock_save_lcode(pp, up->lb_buf, up->lb_len);
968 pp->lastrec = rd_timestamp;
975 up->ppsapi_gate = TRUE;
976 peer->precision = PPS_PRECISION;
978 if ( ! (peer->ttl & NMEA_QUIETPPS_MASK))
979 peer->flags |= FLAG_PPS;
981 refnumtoa(&peer->srcadr)));
982 up->tally.pps_used++;
985 refnumtoa(&peer->srcadr)));
990 /* Whether the receive time stamp is PPS-augmented or not,
995 up->rcvtout = 2;
999 * -------------------------------------------------------------------
1000 * nmea_receive - receive data from the serial interface
1005 * not line oriented, and there's no one to do the line-splitting work
1020 * previous line-oriented input: One line is copied to the buffer and
1024 * -------------------------------------------------------------------
1032 struct peer * const peer = rbufp->recv_peer;
1033 struct refclockproc * const pp = peer->procptr;
1034 nmea_unit * const up = (nmea_unit*)pp->unitptr;
1040 if (up->lb_len >= sizeof(up->lb_buf))
1041 up->lb_len = 0;
1044 dp = up->lb_buf + up->lb_len;
1045 de = up->lb_buf + sizeof(up->lb_buf) - 1;
1047 sp = (const char *)rbufp->recv_buffer;
1048 se = sp + rbufp->recv_length;
1052 * complete non-empty line.
1056 if (dp == up->lb_buf) {
1060 dp = up->lb_buf;
1063 up->lb_len = (int)(dp - up->lb_buf);
1064 dp = up->lb_buf;
1065 nmea_procrec(peer, rbufp->recv_time);
1072 up->lb_len = (int)(dp - up->lb_buf);
1076 * -------------------------------------------------------------------
1077 * nmea_poll - called by the transmit procedure
1084 * -------------------------------------------------------------------
1092 struct refclockproc * const pp = peer->procptr;
1093 nmea_unit * const up = (nmea_unit *)pp->unitptr;
1104 if (!up->ppsapi_gate) {
1105 peer->flags &= ~FLAG_PPS;
1106 peer->precision = PRECISION;
1108 up->ppsapi_gate = FALSE;
1116 if (pp->coderecv == pp->codeproc) {
1117 peer->flags &= ~FLAG_PPS;
1118 if (pp->currentstatus < CEVNT_TIMEOUT)
1120 memset(&up->last_gpsdate, 0, sizeof(up->last_gpsdate));
1122 pp->polls++;
1123 pp->lastref = pp->lastrec;
1125 if (pp->currentstatus > CEVNT_NOMINAL)
1134 if (peer->ttl & NMEA_EXTLOG_MASK) {
1136 const char *nmea = pp->a_lastcode;
1139 &peer->srcadr, "%s %u %u %u %u %u %u",
1141 up->tally.total, up->tally.accepted,
1142 up->tally.rejected, up->tally.malformed,
1143 up->tally.filtered, up->tally.pps_used);
1145 record_clock_stats(&peer->srcadr, pp->a_lastcode);
1147 ZERO(up->tally);
1152 * -------------------------------------------------------------------
1166 * -------------------------------------------------------------------
1187 len = end - beg;
1193 refnumtoa(&peer->srcadr), cmd));
1201 DPRINTF(1, ("%s gps_send: '%.*s'\n", refnumtoa(&peer->srcadr),
1202 len - 2, cmd));
1211 * -------------------------------------------------------------------
1213 * -------------------------------------------------------------------
1219 * 8-bit XOR of characters between $ and * noninclusive is transmitted
1241 * -------------------------------------------------------------------
1264 data->base = cptr;
1265 data->cptr = cptr;
1266 data->cidx = 0;
1267 data->blen = dlen;
1272 * regex equiv: '^\$[A-Z][A-Z0-9]{4,}[^*]*(\*[0-9A-F]{2})?$'
1275 /* -*- start character: '^\$' */
1281 /* -*- advance context beyond start character */
1282 data->base++;
1283 data->cptr++;
1284 data->blen--;
1286 /* -*- field name: '[A-Z][A-Z0-9]{4,},' */
1293 if (*cptr != ',' || (cptr - data->base) < NMEA_PROTO_IDLEN)
1297 /* -*- data: '[^*]*' */
1301 /* -*- checksum field: (\*[0-9A-F]{2})?$ */
1304 if (*cptr != '*' || cptr != eptr - 3 ||
1305 (cptr - data->base) >= NMEA_PROTO_MAXLEN)
1310 cs_r = (cs_r << 4) + (tmp - '0');
1312 cs_r = (cs_r << 4) + (tmp - 'A' + 10);
1317 /* -*- make sure we are at end of string and csum matches */
1325 * -------------------------------------------------------------------
1331 * -------------------------------------------------------------------
1341 if (fn < data->cidx) {
1342 data->cidx = 0;
1343 data->cptr = data->base;
1345 while ((fn > data->cidx) && (tmp = *data->cptr) != '\0') {
1346 data->cidx += (tmp == ',');
1347 data->cptr++;
1349 return data->cptr;
1353 * -------------------------------------------------------------------
1364 * ntpq -c clockvar <server>
1370 * -------------------------------------------------------------------
1380 int fidx; /* field to nuke, or -1 for checksum */
1391 cp = data->base + data->blen;
1392 if (data->blen >= 3 && cp[-3] == '*')
1393 cp -= 2;
1398 } while (fcnt-- && fidx >= 0);
1403 * -------------------------------------------------------------------
1405 * -------------------------------------------------------------------
1417 while (--nfields && ep && *ep == ',')
1420 ? (int)((UCC*)ep - cp)
1425 /* /[,*\r\n]/ --> skip */
1433 /* /,/ --> skip */
1441 /* /[[:digit:]]{2}/ --> uint16_t */
1447 *into = (cp[0] - '0') * 10 + (cp[1] - '0');
1455 /* /[[:digit:]]+/ --> uint16_t */
1463 num = (num * 10) + (*cp - '0');
1464 while (isdigit(*++cp) && --ndig);
1471 /* /[[:digit:]]+/ --> uint32_t */
1479 num = (num * 10) + (*cp - '0');
1480 while (isdigit(*++cp) && --ndig);
1487 /* /(\.[[:digit:]]*)?/ --> l_fp{0, f}
1491 * away but silently ignored. (--> truncation to 1 nanosecond)
1508 ts.tv_nsec = fval * powtab[(size_t)(cp - sp)];
1518 /* /[[:digit:]]{6}/ --> time-of-day
1532 into->hour = (uint8_t)h;
1533 into->minute = (uint8_t)m;
1534 into->second = (uint8_t)s;
1544 /* /[[:digit:]]{6}/ --> civil date
1553 rc = _parse_num2d(cp, &cp, &d) && (d - 1 < 31)
1554 && _parse_num2d(cp, &cp, &m) && (m - 1 < 12)
1558 into->monthday = (uint8_t )d;
1559 into->month = (uint8_t )m;
1560 into->year = (uint16_t)y;
1570 /* /[[:digit:]]+,[[:digit:]]+,[[:digit:]]+/ --> civil date
1579 rc = _parse_u16(cp, &cp, &d, 2) && (d - 1 < 31)
1581 && _parse_u16(cp, &cp, &m, 2) && (m - 1 < 12)
1586 into->monthday = (uint8_t )d;
1587 into->month = (uint8_t )m;
1588 into->year = (uint16_t)y;
1599 * -------------------------------------------------------------------
1606 * -------------------------------------------------------------------
1625 * -------------------------------------------------------------------
1629 * -------------------------------------------------------------------
1647 * -------------------------------------------------------------------
1654 * -------------------------------------------------------------------
1679 * -------------------------------------------------------------------
1681 * the GPS week number, the GPS time-of-week and the leap seconds GPS
1685 * -------------------------------------------------------------------
1716 fofs.l_ui -= leap;
1746 * See http://home.hiwaay.net/~taylorc/gps/nmea-server/
1752 * ln -s server:port /dev/gps1
1755 * (perlinger-at-ntp-dot-org)
1764 int fd = -1; /* result file descriptor */
1775 fd = -1;
1803 for (ai = ai_list; ai && (fd == -1); ai = ai->ai_next) {
1804 sh = socket(ai->ai_family, ai->ai_socktype,
1805 ai->ai_protocol);
1808 rc = connect(sh, ai->ai_addr, ai->ai_addrlen);
1809 if (-1 != rc)
1815 if (fd != -1)
1818 fd = -1;