1c0b746e5SOllivier Robert /* 2c0b746e5SOllivier Robert * refclock_nmea.c - clock driver for an NMEA GPS CLOCK 3c0b746e5SOllivier Robert * Michael Petry Jun 20, 1994 4c0b746e5SOllivier Robert * based on refclock_heathn.c 52b15cb3dSCy Schubert * 62b15cb3dSCy Schubert * Updated to add support for Accord GPS Clock 72b15cb3dSCy Schubert * Venu Gopal Dec 05, 2007 82b15cb3dSCy Schubert * neo.venu@gmail.com, venugopal_d@pgad.gov.in 92b15cb3dSCy Schubert * 102b15cb3dSCy Schubert * Updated to process 'time1' fudge factor 112b15cb3dSCy Schubert * Venu Gopal May 05, 2008 122b15cb3dSCy Schubert * 132b15cb3dSCy Schubert * Converted to common PPSAPI code, separate PPS fudge time1 142b15cb3dSCy Schubert * from serial timecode fudge time2. 152b15cb3dSCy Schubert * Dave Hart July 1, 2009 162b15cb3dSCy Schubert * hart@ntp.org, davehart@davehart.com 17c0b746e5SOllivier Robert */ 182b15cb3dSCy Schubert 19c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H 20c0b746e5SOllivier Robert #include <config.h> 21c0b746e5SOllivier Robert #endif 22c0b746e5SOllivier Robert 232b15cb3dSCy Schubert #include "ntp_types.h" 242b15cb3dSCy Schubert 25c0b746e5SOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_NMEA) 26c0b746e5SOllivier Robert 272b15cb3dSCy Schubert #define NMEA_WRITE_SUPPORT 0 /* no write support at the moment */ 282b15cb3dSCy Schubert 292b15cb3dSCy Schubert #include <sys/stat.h> 30ea906c41SOllivier Robert #include <stdio.h> 31ea906c41SOllivier Robert #include <ctype.h> 322b15cb3dSCy Schubert #ifdef HAVE_SYS_SOCKET_H 332b15cb3dSCy Schubert #include <sys/socket.h> 342b15cb3dSCy Schubert #endif 35ea906c41SOllivier Robert 36c0b746e5SOllivier Robert #include "ntpd.h" 37c0b746e5SOllivier Robert #include "ntp_io.h" 38224ba2bdSOllivier Robert #include "ntp_unixtime.h" 39c0b746e5SOllivier Robert #include "ntp_refclock.h" 40c0b746e5SOllivier Robert #include "ntp_stdlib.h" 412d4e511cSCy Schubert #include "ntp_calgps.h" 422b15cb3dSCy Schubert #include "timespecops.h" 43c0b746e5SOllivier Robert 44224ba2bdSOllivier Robert #ifdef HAVE_PPSAPI 45ea906c41SOllivier Robert # include "ppsapi_timepps.h" 462b15cb3dSCy Schubert # include "refclock_atom.h" 47224ba2bdSOllivier Robert #endif /* HAVE_PPSAPI */ 48224ba2bdSOllivier Robert 49eb6d21b4SOllivier Robert 50c0b746e5SOllivier Robert /* 512b15cb3dSCy Schubert * This driver supports NMEA-compatible GPS receivers 52c0b746e5SOllivier Robert * 532b15cb3dSCy Schubert * Prototype was refclock_trak.c, Thanks a lot. 54c0b746e5SOllivier Robert * 55c0b746e5SOllivier Robert * The receiver used spits out the NMEA sentences for boat navigation. 56c0b746e5SOllivier Robert * And you thought it was an information superhighway. Try a raging river 57c0b746e5SOllivier Robert * filled with rapids and whirlpools that rip away your data and warp time. 58224ba2bdSOllivier Robert * 59224ba2bdSOllivier Robert * If HAVE_PPSAPI is defined code to use the PPSAPI will be compiled in. 60224ba2bdSOllivier Robert * On startup if initialization of the PPSAPI fails, it will fall back 61224ba2bdSOllivier Robert * to the "normal" timestamps. 62224ba2bdSOllivier Robert * 63224ba2bdSOllivier Robert * The PPSAPI part of the driver understands fudge flag2 and flag3. If 64224ba2bdSOllivier Robert * flag2 is set, it will use the clear edge of the pulse. If flag3 is 65224ba2bdSOllivier Robert * set, kernel hardpps is enabled. 66224ba2bdSOllivier Robert * 67224ba2bdSOllivier Robert * GPS sentences other than RMC (the default) may be enabled by setting 68224ba2bdSOllivier Robert * the relevent bits of 'mode' in the server configuration line 69224ba2bdSOllivier Robert * server 127.127.20.x mode X 70224ba2bdSOllivier Robert * 71224ba2bdSOllivier Robert * bit 0 - enables RMC (1) 72224ba2bdSOllivier Robert * bit 1 - enables GGA (2) 73224ba2bdSOllivier Robert * bit 2 - enables GLL (4) 742b15cb3dSCy Schubert * bit 3 - enables ZDA (8) - Standard Time & Date 752b15cb3dSCy Schubert * bit 3 - enables ZDG (8) - Accord GPS Clock's custom sentence with GPS time 762b15cb3dSCy Schubert * very close to standard ZDA 772b15cb3dSCy Schubert * 782b15cb3dSCy Schubert * Multiple sentences may be selected except when ZDG/ZDA is selected. 792b15cb3dSCy Schubert * 802b15cb3dSCy Schubert * bit 4/5/6 - selects the baudrate for serial port : 812b15cb3dSCy Schubert * 0 for 4800 (default) 822b15cb3dSCy Schubert * 1 for 9600 832b15cb3dSCy Schubert * 2 for 19200 842b15cb3dSCy Schubert * 3 for 38400 852b15cb3dSCy Schubert * 4 for 57600 862b15cb3dSCy Schubert * 5 for 115200 872b15cb3dSCy Schubert */ 882b15cb3dSCy Schubert #define NMEA_MESSAGE_MASK 0x0000FF0FU 892b15cb3dSCy Schubert #define NMEA_BAUDRATE_MASK 0x00000070U 902b15cb3dSCy Schubert #define NMEA_BAUDRATE_SHIFT 4 912b15cb3dSCy Schubert 922d4e511cSCy Schubert #define NMEA_DELAYMEAS_MASK 0x00000080U 932b15cb3dSCy Schubert #define NMEA_EXTLOG_MASK 0x00010000U 942d4e511cSCy Schubert #define NMEA_QUIETPPS_MASK 0x00020000U 952d4e511cSCy Schubert #define NMEA_DATETRUST_MASK 0x00040000U 96a466cc55SCy Schubert #define NMEA_IGNSTATUS_MASK 0x00080000U 972b15cb3dSCy Schubert 982d4e511cSCy Schubert #define NMEA_PROTO_IDLEN 4 /* tag name must be at least 4 chars */ 992b15cb3dSCy Schubert #define NMEA_PROTO_MINLEN 6 /* min chars in sentence, excluding CS */ 1002b15cb3dSCy Schubert #define NMEA_PROTO_MAXLEN 80 /* max chars in sentence, excluding CS */ 1012b15cb3dSCy Schubert #define NMEA_PROTO_FIELDS 32 /* not official; limit on fields per record */ 1022b15cb3dSCy Schubert 1032b15cb3dSCy Schubert /* 1042b15cb3dSCy Schubert * We check the timecode format and decode its contents. We only care 1052b15cb3dSCy Schubert * about a few of them, the most important being the $GPRMC format: 1062b15cb3dSCy Schubert * 1072b15cb3dSCy Schubert * $GPRMC,hhmmss,a,fddmm.xx,n,dddmmm.xx,w,zz.z,yyy.,ddmmyy,dd,v*CC 1082b15cb3dSCy Schubert * 1092b15cb3dSCy Schubert * mode (0,1,2,3) selects sentence ANY/ALL, RMC, GGA, GLL, ZDA 1102b15cb3dSCy Schubert * $GPGLL,3513.8385,S,14900.7851,E,232420.594,A*21 1112b15cb3dSCy Schubert * $GPGGA,232420.59,3513.8385,S,14900.7851,E,1,05,3.4,00519,M,,,,*3F 1122b15cb3dSCy Schubert * $GPRMC,232418.19,A,3513.8386,S,14900.7853,E,00.0,000.0,121199,12.,E*77 1132b15cb3dSCy Schubert * 1142b15cb3dSCy Schubert * Defining GPZDA to support Standard Time & Date 1152b15cb3dSCy Schubert * sentence. The sentence has the following format 1162b15cb3dSCy Schubert * 1172b15cb3dSCy Schubert * $--ZDA,HHMMSS.SS,DD,MM,YYYY,TH,TM,*CS<CR><LF> 1182b15cb3dSCy Schubert * 1192b15cb3dSCy Schubert * Apart from the familiar fields, 1202b15cb3dSCy Schubert * 'TH' Time zone Hours 1212b15cb3dSCy Schubert * 'TM' Time zone Minutes 1222b15cb3dSCy Schubert * 1232b15cb3dSCy Schubert * Defining GPZDG to support Accord GPS Clock's custom NMEA 1242b15cb3dSCy Schubert * sentence. The sentence has the following format 1252b15cb3dSCy Schubert * 1262b15cb3dSCy Schubert * $GPZDG,HHMMSS.S,DD,MM,YYYY,AA.BB,V*CS<CR><LF> 1272b15cb3dSCy Schubert * 1282b15cb3dSCy Schubert * It contains the GPS timestamp valid for next PPS pulse. 1292b15cb3dSCy Schubert * Apart from the familiar fields, 1302b15cb3dSCy Schubert * 'AA.BB' denotes the signal strength( should be < 05.00 ) 1312b15cb3dSCy Schubert * 'V' denotes the GPS sync status : 1322b15cb3dSCy Schubert * '0' indicates INVALID time, 1332b15cb3dSCy Schubert * '1' indicates accuracy of +/-20 ms 1342b15cb3dSCy Schubert * '2' indicates accuracy of +/-100 ns 1352b15cb3dSCy Schubert * 1362b15cb3dSCy Schubert * Defining PGRMF for Garmin GPS Fix Data 1372b15cb3dSCy Schubert * $PGRMF,WN,WS,DATE,TIME,LS,LAT,LAT_DIR,LON,LON_DIR,MODE,FIX,SPD,DIR,PDOP,TDOP 1382b15cb3dSCy Schubert * WN -- GPS week number (weeks since 1980-01-06, mod 1024) 1392b15cb3dSCy Schubert * WS -- GPS seconds in week 1402b15cb3dSCy Schubert * LS -- GPS leap seconds, accumulated ( UTC + LS == GPS ) 1412b15cb3dSCy Schubert * FIX -- Fix type: 0=nofix, 1=2D, 2=3D 1422b15cb3dSCy Schubert * DATE/TIME are standard date/time strings in UTC time scale 1432b15cb3dSCy Schubert * 1442b15cb3dSCy Schubert * The GPS time can be used to get the full century for the truncated 1452b15cb3dSCy Schubert * date spec. 146c0b746e5SOllivier Robert */ 147c0b746e5SOllivier Robert 148c0b746e5SOllivier Robert /* 149c0b746e5SOllivier Robert * Definitions 150c0b746e5SOllivier Robert */ 1512b15cb3dSCy Schubert #define DEVICE "/dev/gps%d" /* GPS serial device */ 1522b15cb3dSCy Schubert #define PPSDEV "/dev/gpspps%d" /* PPSAPI device override */ 153c0b746e5SOllivier Robert #define SPEED232 B4800 /* uart speed (4800 bps) */ 154c0b746e5SOllivier Robert #define PRECISION (-9) /* precision assumed (about 2 ms) */ 155224ba2bdSOllivier Robert #define PPS_PRECISION (-20) /* precision assumed (about 1 us) */ 1562d4e511cSCy Schubert #define DATE_HOLD 16 /* seconds to hold on provided GPS date */ 1572d4e511cSCy Schubert #define DATE_HLIM 4 /* when do we take ANY date format */ 158c0b746e5SOllivier Robert #define REFID "GPS\0" /* reference id */ 159c0b746e5SOllivier Robert #define DESCRIPTION "NMEA GPS Clock" /* who we are */ 1602b15cb3dSCy Schubert #ifndef O_NOCTTY 1612b15cb3dSCy Schubert #define M_NOCTTY 0 1622b15cb3dSCy Schubert #else 1632b15cb3dSCy Schubert #define M_NOCTTY O_NOCTTY 1642b15cb3dSCy Schubert #endif 1652b15cb3dSCy Schubert #ifndef O_NONBLOCK 1662b15cb3dSCy Schubert #define M_NONBLOCK 0 1672b15cb3dSCy Schubert #else 1682b15cb3dSCy Schubert #define M_NONBLOCK O_NONBLOCK 1692b15cb3dSCy Schubert #endif 1702b15cb3dSCy Schubert #define PPSOPENMODE (O_RDWR | M_NOCTTY | M_NONBLOCK) 171c0b746e5SOllivier Robert 1722b15cb3dSCy Schubert /* NMEA sentence array indexes for those we use */ 1732b15cb3dSCy Schubert #define NMEA_GPRMC 0 /* recommended min. nav. */ 1742b15cb3dSCy Schubert #define NMEA_GPGGA 1 /* fix and quality */ 1752b15cb3dSCy Schubert #define NMEA_GPGLL 2 /* geo. lat/long */ 1762b15cb3dSCy Schubert #define NMEA_GPZDA 3 /* date/time */ 1772b15cb3dSCy Schubert /* 1782b15cb3dSCy Schubert * $GPZDG is a proprietary sentence that violates the spec, by not 1792b15cb3dSCy Schubert * using $P and an assigned company identifier to prefix the sentence 1802b15cb3dSCy Schubert * identifier. When used with this driver, the system needs to be 1812b15cb3dSCy Schubert * isolated from other NTP networks, as it operates in GPS time, not 1822b15cb3dSCy Schubert * UTC as is much more common. GPS time is >15 seconds different from 1832b15cb3dSCy Schubert * UTC due to not respecting leap seconds since 1970 or so. Other 1842b15cb3dSCy Schubert * than the different timebase, $GPZDG is similar to $GPZDA. 1852b15cb3dSCy Schubert */ 1862b15cb3dSCy Schubert #define NMEA_GPZDG 4 1872b15cb3dSCy Schubert #define NMEA_PGRMF 5 1882d4e511cSCy Schubert #define NMEA_PUBX04 6 1892d4e511cSCy Schubert #define NMEA_ARRAY_SIZE (NMEA_PUBX04 + 1) 190c0b746e5SOllivier Robert 191c0b746e5SOllivier Robert /* 1922b15cb3dSCy Schubert * Sentence selection mode bits 193c0b746e5SOllivier Robert */ 1942b15cb3dSCy Schubert #define USE_GPRMC 0x00000001u 1952b15cb3dSCy Schubert #define USE_GPGGA 0x00000002u 1962b15cb3dSCy Schubert #define USE_GPGLL 0x00000004u 1972b15cb3dSCy Schubert #define USE_GPZDA 0x00000008u 1982b15cb3dSCy Schubert #define USE_PGRMF 0x00000100u 1992d4e511cSCy Schubert #define USE_PUBX04 0x00000200u 2002b15cb3dSCy Schubert 2012b15cb3dSCy Schubert /* mapping from sentence index to controlling mode bit */ 2022b15cb3dSCy Schubert static const u_int32 sentence_mode[NMEA_ARRAY_SIZE] = 2032b15cb3dSCy Schubert { 2042b15cb3dSCy Schubert USE_GPRMC, 2052b15cb3dSCy Schubert USE_GPGGA, 2062b15cb3dSCy Schubert USE_GPGLL, 2072b15cb3dSCy Schubert USE_GPZDA, 2082b15cb3dSCy Schubert USE_GPZDA, 2092d4e511cSCy Schubert USE_PGRMF, 2102d4e511cSCy Schubert USE_PUBX04 2112b15cb3dSCy Schubert }; 2122b15cb3dSCy Schubert 2132b15cb3dSCy Schubert /* date formats we support */ 2142b15cb3dSCy Schubert enum date_fmt { 2152b15cb3dSCy Schubert DATE_1_DDMMYY, /* use 1 field with 2-digit year */ 2162b15cb3dSCy Schubert DATE_3_DDMMYYYY /* use 3 fields with 4-digit year */ 2172b15cb3dSCy Schubert }; 2182b15cb3dSCy Schubert 2192d4e511cSCy Schubert /* date type */ 2202d4e511cSCy Schubert enum date_type { 2212d4e511cSCy Schubert DTYP_NONE, 2222d4e511cSCy Schubert DTYP_Y2D, /* 2-digit year */ 2232d4e511cSCy Schubert DTYP_W10B, /* 10-bit week in GPS epoch */ 2242d4e511cSCy Schubert DTYP_Y4D, /* 4-digit (full) year */ 2252d4e511cSCy Schubert DTYP_WEXT /* extended week in GPS epoch */ 2262d4e511cSCy Schubert }; 2272d4e511cSCy Schubert 2282b15cb3dSCy Schubert /* results for 'field_init()' 2292b15cb3dSCy Schubert * 2302b15cb3dSCy Schubert * Note: If a checksum is present, the checksum test must pass OK or the 2312b15cb3dSCy Schubert * sentence is tagged invalid. 2322b15cb3dSCy Schubert */ 2332b15cb3dSCy Schubert #define CHECK_EMPTY -1 /* no data */ 2342b15cb3dSCy Schubert #define CHECK_INVALID 0 /* not a valid NMEA sentence */ 2352b15cb3dSCy Schubert #define CHECK_VALID 1 /* valid but without checksum */ 2362b15cb3dSCy Schubert #define CHECK_CSVALID 2 /* valid with checksum OK */ 237c0b746e5SOllivier Robert 238c0b746e5SOllivier Robert /* 239c0b746e5SOllivier Robert * Unit control structure 240c0b746e5SOllivier Robert */ 2412d4e511cSCy Schubert struct refclock_atom; 2422d4e511cSCy Schubert typedef struct refclock_atom TAtomUnit; 2432b15cb3dSCy Schubert typedef struct { 244224ba2bdSOllivier Robert # ifdef HAVE_PPSAPI 2452d4e511cSCy Schubert TAtomUnit atom; /* PPSAPI structure */ 2462b15cb3dSCy Schubert int ppsapi_fd; /* fd used with PPSAPI */ 2472b15cb3dSCy Schubert u_char ppsapi_tried; /* attempt PPSAPI once */ 2482b15cb3dSCy Schubert u_char ppsapi_lit; /* time_pps_create() worked */ 249224ba2bdSOllivier Robert # endif /* HAVE_PPSAPI */ 2502d4e511cSCy Schubert uint16_t rcvtout; /* one-shot for sample expiration */ 2512d4e511cSCy Schubert u_char ppsapi_gate; /* system is on PPS */ 2522b15cb3dSCy Schubert u_char gps_time; /* use GPS time, not UTC */ 2532b15cb3dSCy Schubert l_fp last_reftime; /* last processed reference stamp */ 2542d4e511cSCy Schubert TNtpDatum last_gpsdate; /* last processed split date/time */ 2552d4e511cSCy Schubert u_short hold_gpsdate; /* validity ticker for above */ 2562d4e511cSCy Schubert u_short type_gpsdate; /* date info type for above */ 2572b15cb3dSCy Schubert /* tally stats, reset each poll cycle */ 2582b15cb3dSCy Schubert struct 2592b15cb3dSCy Schubert { 2602b15cb3dSCy Schubert u_int total; 2612b15cb3dSCy Schubert u_int accepted; 2622b15cb3dSCy Schubert u_int rejected; /* GPS said not enough signal */ 2632b15cb3dSCy Schubert u_int malformed; /* Bad checksum, invalid date or time */ 2642b15cb3dSCy Schubert u_int filtered; /* mode bits, not GPZDG, same second */ 2652b15cb3dSCy Schubert u_int pps_used; 2662b15cb3dSCy Schubert } 2672b15cb3dSCy Schubert tally; 2682b15cb3dSCy Schubert /* per sentence checksum seen flag */ 2692b15cb3dSCy Schubert u_char cksum_type[NMEA_ARRAY_SIZE]; 2702d4e511cSCy Schubert 2712d4e511cSCy Schubert /* line assembly buffer (NMEAD support) */ 2722d4e511cSCy Schubert u_short lb_len; 2732d4e511cSCy Schubert char lb_buf[BMAX]; /* assembly buffer */ 2742b15cb3dSCy Schubert } nmea_unit; 2752b15cb3dSCy Schubert 2762b15cb3dSCy Schubert /* 2772b15cb3dSCy Schubert * helper for faster field access 2782b15cb3dSCy Schubert */ 2792b15cb3dSCy Schubert typedef struct { 2802b15cb3dSCy Schubert char *base; /* buffer base */ 2812b15cb3dSCy Schubert char *cptr; /* current field ptr */ 2822b15cb3dSCy Schubert int blen; /* buffer length */ 2832b15cb3dSCy Schubert int cidx; /* current field index */ 2842b15cb3dSCy Schubert } nmea_data; 2852b15cb3dSCy Schubert 2862b15cb3dSCy Schubert /* 287c0b746e5SOllivier Robert * Function prototypes 288c0b746e5SOllivier Robert */ 2892b15cb3dSCy Schubert static int nmea_start (int, struct peer *); 2902b15cb3dSCy Schubert static void nmea_shutdown (int, struct peer *); 2912b15cb3dSCy Schubert static void nmea_receive (struct recvbuf *); 2922b15cb3dSCy Schubert static void nmea_poll (int, struct peer *); 293767173ceSCy Schubert static void nmea_procrec (struct peer * const, l_fp); 294224ba2bdSOllivier Robert #ifdef HAVE_PPSAPI 2952d4e511cSCy Schubert static double tabsdiffd (l_fp, l_fp); 2962b15cb3dSCy Schubert static void nmea_control (int, const struct refclockstat *, 2972b15cb3dSCy Schubert struct refclockstat *, struct peer *); 2982b15cb3dSCy Schubert #define NMEA_CONTROL nmea_control 2992b15cb3dSCy Schubert #else 3002b15cb3dSCy Schubert #define NMEA_CONTROL noentry 301224ba2bdSOllivier Robert #endif /* HAVE_PPSAPI */ 3022b15cb3dSCy Schubert static void nmea_timer (int, struct peer *); 3032b15cb3dSCy Schubert 3042b15cb3dSCy Schubert /* parsing helpers */ 3052b15cb3dSCy Schubert static int field_init (nmea_data * data, char * cp, int len); 3062b15cb3dSCy Schubert static char * field_parse (nmea_data * data, int fn); 3072b15cb3dSCy Schubert static void field_wipe (nmea_data * data, ...); 3082b15cb3dSCy Schubert static u_char parse_qual (nmea_data * data, int idx, 3092b15cb3dSCy Schubert char tag, int inv); 3102d4e511cSCy Schubert static int parse_time (TCivilDate * jd, l_fp * fofs, 3112b15cb3dSCy Schubert nmea_data *, int idx); 3122d4e511cSCy Schubert static int parse_date (TCivilDate * jd, nmea_data *, 3132b15cb3dSCy Schubert int idx, enum date_fmt fmt); 3142d4e511cSCy Schubert static int parse_gpsw (TGpsDatum *, nmea_data *, 3152b15cb3dSCy Schubert int weekidx, int timeidx, int leapidx); 3162b15cb3dSCy Schubert 3172b15cb3dSCy Schubert static int nmead_open (const char * device); 318c0b746e5SOllivier Robert 319c0b746e5SOllivier Robert /* 3202d4e511cSCy Schubert * If we want the driver to output sentences, too: re-enable the send 3212b15cb3dSCy Schubert * support functions by defining NMEA_WRITE_SUPPORT to non-zero... 3222b15cb3dSCy Schubert */ 3232b15cb3dSCy Schubert #if NMEA_WRITE_SUPPORT 3242b15cb3dSCy Schubert static void gps_send(int, const char *, struct peer *); 3252b15cb3dSCy Schubert #endif /* NMEA_WRITE_SUPPORT */ 3262b15cb3dSCy Schubert 3272b15cb3dSCy Schubert /* 3282b15cb3dSCy Schubert * ------------------------------------------------------------------- 329c0b746e5SOllivier Robert * Transfer vector 3302b15cb3dSCy Schubert * ------------------------------------------------------------------- 331c0b746e5SOllivier Robert */ 332c0b746e5SOllivier Robert struct refclock refclock_nmea = { 333c0b746e5SOllivier Robert nmea_start, /* start up driver */ 334c0b746e5SOllivier Robert nmea_shutdown, /* shut down driver */ 335c0b746e5SOllivier Robert nmea_poll, /* transmit poll message */ 3362b15cb3dSCy Schubert NMEA_CONTROL, /* fudge control */ 3372d4e511cSCy Schubert noentry, /* initialize driver */ 338c0b746e5SOllivier Robert noentry, /* buginfo */ 3392b15cb3dSCy Schubert nmea_timer /* called once per second */ 340c0b746e5SOllivier Robert }; 341c0b746e5SOllivier Robert 3422b15cb3dSCy Schubert 3432b15cb3dSCy Schubert /* 3442b15cb3dSCy Schubert * ------------------------------------------------------------------- 345c0b746e5SOllivier Robert * nmea_start - open the GPS devices and initialize data for processing 3462b15cb3dSCy Schubert * 3472b15cb3dSCy Schubert * return 0 on error, 1 on success. Even on error the peer structures 3482b15cb3dSCy Schubert * must be in a state that permits 'nmea_shutdown()' to clean up all 3492b15cb3dSCy Schubert * resources, because it will be called immediately to do so. 3502b15cb3dSCy Schubert * ------------------------------------------------------------------- 351c0b746e5SOllivier Robert */ 352c0b746e5SOllivier Robert static int 353c0b746e5SOllivier Robert nmea_start( 354c0b746e5SOllivier Robert int unit, 355c0b746e5SOllivier Robert struct peer * peer 356c0b746e5SOllivier Robert ) 357c0b746e5SOllivier Robert { 3582b15cb3dSCy Schubert struct refclockproc * const pp = peer->procptr; 3592b15cb3dSCy Schubert nmea_unit * const up = emalloc_zero(sizeof(*up)); 360c0b746e5SOllivier Robert char device[20]; 3612b15cb3dSCy Schubert size_t devlen; 3622b15cb3dSCy Schubert u_int32 rate; 3632b15cb3dSCy Schubert int baudrate; 364a151a66cSOllivier Robert 3652b15cb3dSCy Schubert /* Get baudrate choice from mode byte bits 4/5/6 */ 3662b15cb3dSCy Schubert rate = (peer->ttl & NMEA_BAUDRATE_MASK) >> NMEA_BAUDRATE_SHIFT; 367ea906c41SOllivier Robert 3682b15cb3dSCy Schubert switch (rate) { 369a466cc55SCy Schubert default: 3702b15cb3dSCy Schubert case 0: 3712b15cb3dSCy Schubert baudrate = SPEED232; 3722b15cb3dSCy Schubert break; 3732b15cb3dSCy Schubert case 1: 3742b15cb3dSCy Schubert baudrate = B9600; 3752b15cb3dSCy Schubert break; 3762b15cb3dSCy Schubert case 2: 3772b15cb3dSCy Schubert baudrate = B19200; 3782b15cb3dSCy Schubert break; 3792b15cb3dSCy Schubert case 3: 3802b15cb3dSCy Schubert baudrate = B38400; 3812b15cb3dSCy Schubert break; 3822b15cb3dSCy Schubert # ifdef B57600 3832b15cb3dSCy Schubert case 4: 3842b15cb3dSCy Schubert baudrate = B57600; 3852b15cb3dSCy Schubert break; 386ea906c41SOllivier Robert # endif 3872b15cb3dSCy Schubert # ifdef B115200 3882b15cb3dSCy Schubert case 5: 3892b15cb3dSCy Schubert baudrate = B115200; 3902b15cb3dSCy Schubert break; 3912b15cb3dSCy Schubert # endif 392ea906c41SOllivier Robert } 393c0b746e5SOllivier Robert 3942b15cb3dSCy Schubert /* Allocate and initialize unit structure */ 395c0b746e5SOllivier Robert pp->unitptr = (caddr_t)up; 3962b15cb3dSCy Schubert pp->io.fd = -1; 3972b15cb3dSCy Schubert pp->io.clock_recv = nmea_receive; 3982b15cb3dSCy Schubert pp->io.srcclock = peer; 3992b15cb3dSCy Schubert pp->io.datalen = 0; 4002b15cb3dSCy Schubert /* force change detection on first valid message */ 4012b15cb3dSCy Schubert memset(&up->last_reftime, 0xFF, sizeof(up->last_reftime)); 4022d4e511cSCy Schubert memset(&up->last_gpsdate, 0x00, sizeof(up->last_gpsdate)); 4032b15cb3dSCy Schubert /* force checksum on GPRMC, see below */ 4042b15cb3dSCy Schubert up->cksum_type[NMEA_GPRMC] = CHECK_CSVALID; 4052b15cb3dSCy Schubert # ifdef HAVE_PPSAPI 4062b15cb3dSCy Schubert up->ppsapi_fd = -1; 4072d4e511cSCy Schubert # endif /* HAVE_PPSAPI */ 4082b15cb3dSCy Schubert ZERO(up->tally); 409c0b746e5SOllivier Robert 4102b15cb3dSCy Schubert /* Initialize miscellaneous variables */ 411224ba2bdSOllivier Robert peer->precision = PRECISION; 412c0b746e5SOllivier Robert pp->clockdesc = DESCRIPTION; 4132b15cb3dSCy Schubert memcpy(&pp->refid, REFID, 4); 414c0b746e5SOllivier Robert 4152b15cb3dSCy Schubert /* Open serial port. Use CLK line discipline, if available. */ 4162b15cb3dSCy Schubert devlen = snprintf(device, sizeof(device), DEVICE, unit); 4172b15cb3dSCy Schubert if (devlen >= sizeof(device)) { 4182b15cb3dSCy Schubert msyslog(LOG_ERR, "%s clock device name too long", 4192b15cb3dSCy Schubert refnumtoa(&peer->srcadr)); 4202b15cb3dSCy Schubert return FALSE; /* buffer overflow */ 421c0b746e5SOllivier Robert } 422a466cc55SCy Schubert pp->io.fd = refclock_open(&peer->srcadr, device, baudrate, LDISC_CLK); 4232b15cb3dSCy Schubert if (0 >= pp->io.fd) { 4242b15cb3dSCy Schubert pp->io.fd = nmead_open(device); 4252b15cb3dSCy Schubert if (-1 == pp->io.fd) 4262b15cb3dSCy Schubert return FALSE; 4272b15cb3dSCy Schubert } 4282b15cb3dSCy Schubert 4292b15cb3dSCy Schubert /* succeed if this clock can be added */ 4302b15cb3dSCy Schubert return io_addclock(&pp->io) != 0; 431224ba2bdSOllivier Robert } 432c0b746e5SOllivier Robert 433c0b746e5SOllivier Robert /* 4342b15cb3dSCy Schubert * ------------------------------------------------------------------- 435c0b746e5SOllivier Robert * nmea_shutdown - shut down a GPS clock 4362b15cb3dSCy Schubert * 4372b15cb3dSCy Schubert * NOTE this routine is called after nmea_start() returns failure, 4382b15cb3dSCy Schubert * as well as during a normal shutdown due to ntpq :config unpeer. 4392b15cb3dSCy Schubert * ------------------------------------------------------------------- 440c0b746e5SOllivier Robert */ 441c0b746e5SOllivier Robert static void 442c0b746e5SOllivier Robert nmea_shutdown( 443c0b746e5SOllivier Robert int unit, 444c0b746e5SOllivier Robert struct peer * peer 445c0b746e5SOllivier Robert ) 446c0b746e5SOllivier Robert { 4472b15cb3dSCy Schubert struct refclockproc * const pp = peer->procptr; 4482b15cb3dSCy Schubert nmea_unit * const up = (nmea_unit *)pp->unitptr; 449c0b746e5SOllivier Robert 4502b15cb3dSCy Schubert UNUSED_ARG(unit); 4512b15cb3dSCy Schubert 4522b15cb3dSCy Schubert if (up != NULL) { 453224ba2bdSOllivier Robert # ifdef HAVE_PPSAPI 4542b15cb3dSCy Schubert if (up->ppsapi_lit) 4552b15cb3dSCy Schubert time_pps_destroy(up->atom.handle); 456a466cc55SCy Schubert ppsdev_close(pp->io.fd, up->ppsapi_fd); 4572b15cb3dSCy Schubert # endif 458c0b746e5SOllivier Robert free(up); 459c0b746e5SOllivier Robert } 4602b15cb3dSCy Schubert pp->unitptr = (caddr_t)NULL; 4612b15cb3dSCy Schubert if (-1 != pp->io.fd) 4622b15cb3dSCy Schubert io_closeclock(&pp->io); 4632b15cb3dSCy Schubert pp->io.fd = -1; 4642b15cb3dSCy Schubert } 4652b15cb3dSCy Schubert 4662b15cb3dSCy Schubert /* 4672b15cb3dSCy Schubert * ------------------------------------------------------------------- 4682b15cb3dSCy Schubert * nmea_control - configure fudge params 4692b15cb3dSCy Schubert * ------------------------------------------------------------------- 4702b15cb3dSCy Schubert */ 4712b15cb3dSCy Schubert #ifdef HAVE_PPSAPI 4722b15cb3dSCy Schubert static void 4732b15cb3dSCy Schubert nmea_control( 4742b15cb3dSCy Schubert int unit, 4752b15cb3dSCy Schubert const struct refclockstat * in_st, 4762b15cb3dSCy Schubert struct refclockstat * out_st, 4772b15cb3dSCy Schubert struct peer * peer 4782b15cb3dSCy Schubert ) 4792b15cb3dSCy Schubert { 4802b15cb3dSCy Schubert struct refclockproc * const pp = peer->procptr; 4812b15cb3dSCy Schubert nmea_unit * const up = (nmea_unit *)pp->unitptr; 4822b15cb3dSCy Schubert 4832b15cb3dSCy Schubert char device[32]; 4842b15cb3dSCy Schubert size_t devlen; 4852b15cb3dSCy Schubert 4862b15cb3dSCy Schubert UNUSED_ARG(in_st); 4872b15cb3dSCy Schubert UNUSED_ARG(out_st); 4882b15cb3dSCy Schubert 4892b15cb3dSCy Schubert /* 4902b15cb3dSCy Schubert * PPS control 4912b15cb3dSCy Schubert * 4922b15cb3dSCy Schubert * If /dev/gpspps$UNIT can be opened that will be used for 493a466cc55SCy Schubert * PPSAPI. On Linux, a PPS device mathing the TTY will be 494a466cc55SCy Schubert * searched for and possibly created on the fly. Otherwise, the 495a466cc55SCy Schubert * GPS serial device /dev/gps$UNIT already opened is used for 496a466cc55SCy Schubert * PPSAPI as well. (This might not work, in which case the PPS 497a466cc55SCy Schubert * API remains unavailable...) 4982b15cb3dSCy Schubert */ 4992b15cb3dSCy Schubert 5002b15cb3dSCy Schubert /* Light up the PPSAPI interface if not yet attempted. */ 5012b15cb3dSCy Schubert if ((CLK_FLAG1 & pp->sloppyclockflag) && !up->ppsapi_tried) { 502a466cc55SCy Schubert const char *ppsname = device; 5032b15cb3dSCy Schubert up->ppsapi_tried = TRUE; 504a466cc55SCy Schubert /* get FD for the pps device; might be the tty itself! */ 5052b15cb3dSCy Schubert devlen = snprintf(device, sizeof(device), PPSDEV, unit); 506a466cc55SCy Schubert if (devlen >= sizeof(device)) { 5072b15cb3dSCy Schubert msyslog(LOG_ERR, "%s PPS device name too long", 5082b15cb3dSCy Schubert refnumtoa(&peer->srcadr)); 509a466cc55SCy Schubert ppsname = NULL; 5102b15cb3dSCy Schubert } 511a466cc55SCy Schubert up->ppsapi_fd = ppsdev_reopen( 512a466cc55SCy Schubert &peer->srcadr, 513a466cc55SCy Schubert pp->io.fd, up->ppsapi_fd, 514a466cc55SCy Schubert ppsname, PPSOPENMODE, (S_IRUSR|S_IWUSR)); 515a466cc55SCy Schubert /* note 1: the pps fd might be the same as the tty fd 516a466cc55SCy Schubert * note 2: the current PPS fd remains valid until 517a466cc55SCy Schubert * - the clock is shut down 518a466cc55SCy Schubert * - flag1 is set again after being cleared 519a466cc55SCy Schubert */ 5202b15cb3dSCy Schubert if (refclock_ppsapi(up->ppsapi_fd, &up->atom)) { 5212b15cb3dSCy Schubert /* use the PPS API for our own purposes now. */ 5222b15cb3dSCy Schubert up->ppsapi_lit = refclock_params( 5232b15cb3dSCy Schubert pp->sloppyclockflag, &up->atom); 5242b15cb3dSCy Schubert if (!up->ppsapi_lit) { 5252b15cb3dSCy Schubert /* failed to configure, drop PPS unit */ 5262b15cb3dSCy Schubert time_pps_destroy(up->atom.handle); 5272b15cb3dSCy Schubert msyslog(LOG_WARNING, 5282b15cb3dSCy Schubert "%s set PPSAPI params fails", 5292b15cb3dSCy Schubert refnumtoa(&peer->srcadr)); 5302b15cb3dSCy Schubert } 5312b15cb3dSCy Schubert } else { 5322b15cb3dSCy Schubert msyslog(LOG_WARNING, 5332b15cb3dSCy Schubert "%s flag1 1 but PPSAPI fails", 5342b15cb3dSCy Schubert refnumtoa(&peer->srcadr)); 5352b15cb3dSCy Schubert } 5362b15cb3dSCy Schubert } 5372b15cb3dSCy Schubert 5382b15cb3dSCy Schubert /* shut down PPS API if activated */ 5392b15cb3dSCy Schubert if ( !(CLK_FLAG1 & pp->sloppyclockflag) && up->ppsapi_tried) { 5402b15cb3dSCy Schubert /* shutdown PPS API */ 5412b15cb3dSCy Schubert if (up->ppsapi_lit) 5422b15cb3dSCy Schubert time_pps_destroy(up->atom.handle); 5432b15cb3dSCy Schubert up->atom.handle = 0; 544a466cc55SCy Schubert /* do !!NOT!! close/drop PPS fd here! */ 5452b15cb3dSCy Schubert 5462b15cb3dSCy Schubert /* clear markers and peer items */ 5472b15cb3dSCy Schubert up->ppsapi_gate = FALSE; 5482b15cb3dSCy Schubert up->ppsapi_lit = FALSE; 5492b15cb3dSCy Schubert up->ppsapi_tried = FALSE; 5502b15cb3dSCy Schubert 5512b15cb3dSCy Schubert peer->flags &= ~FLAG_PPS; 5522b15cb3dSCy Schubert peer->precision = PRECISION; 5532b15cb3dSCy Schubert } 5542b15cb3dSCy Schubert } 5552b15cb3dSCy Schubert #endif /* HAVE_PPSAPI */ 5562b15cb3dSCy Schubert 5572b15cb3dSCy Schubert /* 5582b15cb3dSCy Schubert * ------------------------------------------------------------------- 5592b15cb3dSCy Schubert * nmea_timer - called once per second 5602b15cb3dSCy Schubert * 5612b15cb3dSCy Schubert * Usually 'nmea_receive()' can get a timestamp every second, but at 5622b15cb3dSCy Schubert * least one Motorola unit needs prompting each time. Doing so in 5632b15cb3dSCy Schubert * 'nmea_poll()' gives only one sample per poll cycle, which actually 5642b15cb3dSCy Schubert * defeats the purpose of the median filter. Polling once per second 5652b15cb3dSCy Schubert * seems a much better idea. 5662d4e511cSCy Schubert * 5672d4e511cSCy Schubert * Also takes care of sample expiration if the receiver fails to 5682d4e511cSCy Schubert * provide new input data. 5692b15cb3dSCy Schubert * ------------------------------------------------------------------- 5702b15cb3dSCy Schubert */ 5712b15cb3dSCy Schubert static void 5722b15cb3dSCy Schubert nmea_timer( 5732b15cb3dSCy Schubert int unit, 5742b15cb3dSCy Schubert struct peer * peer 5752b15cb3dSCy Schubert ) 5762b15cb3dSCy Schubert { 5772b15cb3dSCy Schubert struct refclockproc * const pp = peer->procptr; 5782d4e511cSCy Schubert nmea_unit * const up = (nmea_unit *)pp->unitptr; 5792b15cb3dSCy Schubert 5802b15cb3dSCy Schubert UNUSED_ARG(unit); 5812b15cb3dSCy Schubert 5822d4e511cSCy Schubert # if NMEA_WRITE_SUPPORT 5832d4e511cSCy Schubert 5842b15cb3dSCy Schubert if (-1 != pp->io.fd) /* any mode bits to evaluate here? */ 5852b15cb3dSCy Schubert gps_send(pp->io.fd, "$PMOTG,RMC,0000*1D\r\n", peer); 5862b15cb3dSCy Schubert 5872b15cb3dSCy Schubert # endif /* NMEA_WRITE_SUPPORT */ 5882d4e511cSCy Schubert 5892d4e511cSCy Schubert /* receive timeout occurred? */ 5902d4e511cSCy Schubert if (up->rcvtout) { 5912d4e511cSCy Schubert --up->rcvtout; 5922d4e511cSCy Schubert } else if (pp->codeproc != pp->coderecv) { 5932d4e511cSCy Schubert /* expire one (the oldest) sample, if any */ 5942d4e511cSCy Schubert refclock_samples_expire(pp, 1); 5952d4e511cSCy Schubert /* reset message assembly buffer */ 5962d4e511cSCy Schubert up->lb_buf[0] = '\0'; 5972d4e511cSCy Schubert up->lb_len = 0; 5982b15cb3dSCy Schubert } 599c0b746e5SOllivier Robert 6002d4e511cSCy Schubert if (up->hold_gpsdate && (--up->hold_gpsdate < DATE_HLIM)) 6012d4e511cSCy Schubert up->type_gpsdate = DTYP_NONE; 602224ba2bdSOllivier Robert } 603224ba2bdSOllivier Robert 604c0b746e5SOllivier Robert /* 6052b15cb3dSCy Schubert * ------------------------------------------------------------------- 6062d4e511cSCy Schubert * nmea_procrec - receive data from the serial interface 6072b15cb3dSCy Schubert * 6082b15cb3dSCy Schubert * This is the workhorse for NMEA data evaluation: 6092b15cb3dSCy Schubert * 6102b15cb3dSCy Schubert * + it checks all NMEA data, and rejects sentences that are not valid 6112b15cb3dSCy Schubert * NMEA sentences 6122b15cb3dSCy Schubert * + it checks whether a sentence is known and to be used 6132b15cb3dSCy Schubert * + it parses the time and date data from the NMEA data string and 6142d4e511cSCy Schubert * augments the missing bits. (century in date, whole date, ...) 6152b15cb3dSCy Schubert * + it rejects data that is not from the first accepted sentence in a 6162b15cb3dSCy Schubert * burst 6172b15cb3dSCy Schubert * + it eventually replaces the receive time with the PPS edge time. 6182b15cb3dSCy Schubert * + it feeds the data to the internal processing stages. 6192d4e511cSCy Schubert * 6202d4e511cSCy Schubert * This function assumes a non-empty line in the unit line buffer. 6212b15cb3dSCy Schubert * ------------------------------------------------------------------- 622c0b746e5SOllivier Robert */ 623c0b746e5SOllivier Robert static void 6242d4e511cSCy Schubert nmea_procrec( 6252d4e511cSCy Schubert struct peer * const peer, 6262d4e511cSCy Schubert l_fp rd_timestamp 627c0b746e5SOllivier Robert ) 628c0b746e5SOllivier Robert { 6292d4e511cSCy Schubert /* declare & init control structure pointers */ 6302b15cb3dSCy Schubert struct refclockproc * const pp = peer->procptr; 6312b15cb3dSCy Schubert nmea_unit * const up = (nmea_unit*)pp->unitptr; 6322b15cb3dSCy Schubert 633224ba2bdSOllivier Robert /* Use these variables to hold data until we decide its worth keeping */ 6342b15cb3dSCy Schubert nmea_data rdata; 6352d4e511cSCy Schubert l_fp rd_reftime; 636c0b746e5SOllivier Robert 6372b15cb3dSCy Schubert /* working stuff */ 6382d4e511cSCy Schubert TCivilDate date; /* to keep & convert the time stamp */ 6392d4e511cSCy Schubert TGpsDatum wgps; /* week time storage */ 6402d4e511cSCy Schubert TNtpDatum dntp; 6412d4e511cSCy Schubert l_fp tofs; /* offset to full-second reftime */ 6422b15cb3dSCy Schubert /* results of sentence/date/time parsing */ 6432b15cb3dSCy Schubert u_char sentence; /* sentence tag */ 6442b15cb3dSCy Schubert int checkres; 6452d4e511cSCy Schubert int warp; /* warp to GPS base date */ 6462b15cb3dSCy Schubert char * cp; 6472d4e511cSCy Schubert int rc_date, rc_time; 6482d4e511cSCy Schubert u_short rc_dtyp; 6492d4e511cSCy Schubert # ifdef HAVE_PPSAPI 6502d4e511cSCy Schubert int withpps = 0; 6512d4e511cSCy Schubert # endif /* HAVE_PPSAPI */ 6522b15cb3dSCy Schubert 6532b15cb3dSCy Schubert /* make sure data has defined pristine state */ 6542b15cb3dSCy Schubert ZERO(tofs); 6552b15cb3dSCy Schubert ZERO(date); 6562d4e511cSCy Schubert ZERO(wgps); 6572d4e511cSCy Schubert ZERO(dntp); 6589034852cSGleb Smirnoff 659c0b746e5SOllivier Robert /* 6602d4e511cSCy Schubert * Read the timecode and timestamp, then initialize field 6612b15cb3dSCy Schubert * processing. The <CR><LF> at the NMEA line end is translated 6622b15cb3dSCy Schubert * to <LF><LF> by the terminal input routines on most systems, 6632b15cb3dSCy Schubert * and this gives us one spurious empty read per record which we 6642b15cb3dSCy Schubert * better ignore silently. 665c0b746e5SOllivier Robert */ 6662d4e511cSCy Schubert checkres = field_init(&rdata, up->lb_buf, up->lb_len); 6672b15cb3dSCy Schubert switch (checkres) { 668c0b746e5SOllivier Robert 6692b15cb3dSCy Schubert case CHECK_INVALID: 6702b15cb3dSCy Schubert DPRINTF(1, ("%s invalid data: '%s'\n", 6712d4e511cSCy Schubert refnumtoa(&peer->srcadr), up->lb_buf)); 672c0b746e5SOllivier Robert refclock_report(peer, CEVNT_BADREPLY); 673c0b746e5SOllivier Robert return; 674c0b746e5SOllivier Robert 6752b15cb3dSCy Schubert case CHECK_EMPTY: 6762b15cb3dSCy Schubert return; 6772b15cb3dSCy Schubert 6782b15cb3dSCy Schubert default: 6792b15cb3dSCy Schubert DPRINTF(1, ("%s gpsread: %d '%s'\n", 6802d4e511cSCy Schubert refnumtoa(&peer->srcadr), up->lb_len, 6812d4e511cSCy Schubert up->lb_buf)); 6822b15cb3dSCy Schubert break; 6832b15cb3dSCy Schubert } 6842b15cb3dSCy Schubert up->tally.total++; 685224ba2bdSOllivier Robert 686c0b746e5SOllivier Robert /* 6872b15cb3dSCy Schubert * --> below this point we have a valid NMEA sentence <-- 6882b15cb3dSCy Schubert * 6892b15cb3dSCy Schubert * Check sentence name. Skip first 2 chars (talker ID) in most 6902b15cb3dSCy Schubert * cases, to allow for $GLGGA and $GPGGA etc. Since the name 6912b15cb3dSCy Schubert * field has at least 5 chars we can simply shift the field 6922b15cb3dSCy Schubert * start. 693c0b746e5SOllivier Robert */ 6942b15cb3dSCy Schubert cp = field_parse(&rdata, 0); 6952b15cb3dSCy Schubert if (strncmp(cp + 2, "RMC,", 4) == 0) 6962b15cb3dSCy Schubert sentence = NMEA_GPRMC; 6972b15cb3dSCy Schubert else if (strncmp(cp + 2, "GGA,", 4) == 0) 6982b15cb3dSCy Schubert sentence = NMEA_GPGGA; 6992b15cb3dSCy Schubert else if (strncmp(cp + 2, "GLL,", 4) == 0) 7002b15cb3dSCy Schubert sentence = NMEA_GPGLL; 7012b15cb3dSCy Schubert else if (strncmp(cp + 2, "ZDA,", 4) == 0) 7022b15cb3dSCy Schubert sentence = NMEA_GPZDA; 7032b15cb3dSCy Schubert else if (strncmp(cp + 2, "ZDG,", 4) == 0) 7042b15cb3dSCy Schubert sentence = NMEA_GPZDG; 7052b15cb3dSCy Schubert else if (strncmp(cp, "PGRMF,", 6) == 0) 7062b15cb3dSCy Schubert sentence = NMEA_PGRMF; 7072d4e511cSCy Schubert else if (strncmp(cp, "PUBX,04,", 8) == 0) 7082d4e511cSCy Schubert sentence = NMEA_PUBX04; 7092b15cb3dSCy Schubert else 7102b15cb3dSCy Schubert return; /* not something we know about */ 7112b15cb3dSCy Schubert 7122b15cb3dSCy Schubert /* Eventually output delay measurement now. */ 7132b15cb3dSCy Schubert if (peer->ttl & NMEA_DELAYMEAS_MASK) { 7142b15cb3dSCy Schubert mprintf_clock_stats(&peer->srcadr, "delay %0.6f %.*s", 7152b15cb3dSCy Schubert ldexp(rd_timestamp.l_uf, -32), 7162d4e511cSCy Schubert (int)(strchr(up->lb_buf, ',') - up->lb_buf), 7172d4e511cSCy Schubert up->lb_buf); 718c0b746e5SOllivier Robert } 719c0b746e5SOllivier Robert 7202b15cb3dSCy Schubert /* See if I want to process this message type */ 7212b15cb3dSCy Schubert if ((peer->ttl & NMEA_MESSAGE_MASK) && 7222b15cb3dSCy Schubert !(peer->ttl & sentence_mode[sentence])) { 7232b15cb3dSCy Schubert up->tally.filtered++; 724224ba2bdSOllivier Robert return; 725224ba2bdSOllivier Robert } 726224ba2bdSOllivier Robert 727c0b746e5SOllivier Robert /* 7282b15cb3dSCy Schubert * make sure it came in clean 7292b15cb3dSCy Schubert * 7302b15cb3dSCy Schubert * Apparently, older NMEA specifications (which are expensive) 7312b15cb3dSCy Schubert * did not require the checksum for all sentences. $GPMRC is 7322b15cb3dSCy Schubert * the only one so far identified which has always been required 7332b15cb3dSCy Schubert * to include a checksum. 7342b15cb3dSCy Schubert * 7352b15cb3dSCy Schubert * Today, most NMEA GPS receivers checksum every sentence. To 7362b15cb3dSCy Schubert * preserve its error-detection capabilities with modern GPSes 7372b15cb3dSCy Schubert * while allowing operation without checksums on all but $GPMRC, 7382b15cb3dSCy Schubert * we keep track of whether we've ever seen a valid checksum on 7392b15cb3dSCy Schubert * a given sentence, and if so, reject future instances without 7402b15cb3dSCy Schubert * checksum. ('up->cksum_type[NMEA_GPRMC]' is set in 7412b15cb3dSCy Schubert * 'nmea_start()' to enforce checksums for $GPRMC right from the 7422b15cb3dSCy Schubert * start.) 743c0b746e5SOllivier Robert */ 7442b15cb3dSCy Schubert if (up->cksum_type[sentence] <= (u_char)checkres) { 7452b15cb3dSCy Schubert up->cksum_type[sentence] = (u_char)checkres; 746c0b746e5SOllivier Robert } else { 7472b15cb3dSCy Schubert DPRINTF(1, ("%s checksum missing: '%s'\n", 7482d4e511cSCy Schubert refnumtoa(&peer->srcadr), up->lb_buf)); 7492b15cb3dSCy Schubert refclock_report(peer, CEVNT_BADREPLY); 7502b15cb3dSCy Schubert up->tally.malformed++; 751c0b746e5SOllivier Robert return; 752c0b746e5SOllivier Robert } 753c0b746e5SOllivier Robert 7542b15cb3dSCy Schubert /* 7552b15cb3dSCy Schubert * $GPZDG provides GPS time not UTC, and the two mix poorly. 7562b15cb3dSCy Schubert * Once have processed a $GPZDG, do not process any further UTC 7572b15cb3dSCy Schubert * sentences (all but $GPZDG currently). 7582b15cb3dSCy Schubert */ 7592d4e511cSCy Schubert if (sentence == NMEA_GPZDG) { 7602d4e511cSCy Schubert if (!up->gps_time) { 7612d4e511cSCy Schubert msyslog(LOG_INFO, 7622d4e511cSCy Schubert "%s using GPS time as if it were UTC", 7632d4e511cSCy Schubert refnumtoa(&peer->srcadr)); 7642d4e511cSCy Schubert up->gps_time = 1; 7652d4e511cSCy Schubert } 7662d4e511cSCy Schubert } else { 7672d4e511cSCy Schubert if (up->gps_time) { 7682b15cb3dSCy Schubert up->tally.filtered++; 7692b15cb3dSCy Schubert return; 7702b15cb3dSCy Schubert } 7712d4e511cSCy Schubert } 7722b15cb3dSCy Schubert 7732b15cb3dSCy Schubert DPRINTF(1, ("%s processing %d bytes, timecode '%s'\n", 7742d4e511cSCy Schubert refnumtoa(&peer->srcadr), up->lb_len, up->lb_buf)); 7752b15cb3dSCy Schubert 7762b15cb3dSCy Schubert /* 7772b15cb3dSCy Schubert * Grab fields depending on clock string type and possibly wipe 7782b15cb3dSCy Schubert * sensitive data from the last timecode. 7792b15cb3dSCy Schubert */ 7802d4e511cSCy Schubert rc_date = -1; /* assume we have to do day-time mapping */ 7812d4e511cSCy Schubert rc_dtyp = DTYP_NONE; 7822b15cb3dSCy Schubert switch (sentence) { 7832b15cb3dSCy Schubert 7842b15cb3dSCy Schubert case NMEA_GPRMC: 7852b15cb3dSCy Schubert /* Check quality byte, fetch data & time */ 7862d4e511cSCy Schubert rc_time = parse_time(&date, &tofs, &rdata, 1); 7872b15cb3dSCy Schubert pp->leap = parse_qual(&rdata, 2, 'A', 0); 7882d4e511cSCy Schubert if (up->type_gpsdate <= DTYP_Y2D) { 7892d4e511cSCy Schubert rc_date = parse_date(&date, &rdata, 9, DATE_1_DDMMYY); 7902d4e511cSCy Schubert rc_dtyp = DTYP_Y2D; 7912d4e511cSCy Schubert } 7922b15cb3dSCy Schubert if (CLK_FLAG4 & pp->sloppyclockflag) 7932b15cb3dSCy Schubert field_wipe(&rdata, 3, 4, 5, 6, -1); 7942b15cb3dSCy Schubert break; 7952b15cb3dSCy Schubert 7962b15cb3dSCy Schubert case NMEA_GPGGA: 7972b15cb3dSCy Schubert /* Check quality byte, fetch time only */ 7982d4e511cSCy Schubert rc_time = parse_time(&date, &tofs, &rdata, 1); 7992b15cb3dSCy Schubert pp->leap = parse_qual(&rdata, 6, '0', 1); 8002b15cb3dSCy Schubert if (CLK_FLAG4 & pp->sloppyclockflag) 8012b15cb3dSCy Schubert field_wipe(&rdata, 2, 4, -1); 8022b15cb3dSCy Schubert break; 8032b15cb3dSCy Schubert 8042b15cb3dSCy Schubert case NMEA_GPGLL: 8052b15cb3dSCy Schubert /* Check quality byte, fetch time only */ 8062d4e511cSCy Schubert rc_time = parse_time(&date, &tofs, &rdata, 5); 8072b15cb3dSCy Schubert pp->leap = parse_qual(&rdata, 6, 'A', 0); 8082b15cb3dSCy Schubert if (CLK_FLAG4 & pp->sloppyclockflag) 8092b15cb3dSCy Schubert field_wipe(&rdata, 1, 3, -1); 8102b15cb3dSCy Schubert break; 8112b15cb3dSCy Schubert 8122b15cb3dSCy Schubert case NMEA_GPZDA: 8132b15cb3dSCy Schubert /* No quality. Assume best, fetch time & full date */ 8142d4e511cSCy Schubert rc_time = parse_time(&date, &tofs, &rdata, 1); 8152d4e511cSCy Schubert if (up->type_gpsdate <= DTYP_Y4D) { 8162b15cb3dSCy Schubert rc_date = parse_date(&date, &rdata, 2, DATE_3_DDMMYYYY); 8172d4e511cSCy Schubert rc_dtyp = DTYP_Y4D; 8182d4e511cSCy Schubert } 8192b15cb3dSCy Schubert break; 8202b15cb3dSCy Schubert 8212b15cb3dSCy Schubert case NMEA_GPZDG: 8222b15cb3dSCy Schubert /* Check quality byte, fetch time & full date */ 8232d4e511cSCy Schubert rc_time = parse_time(&date, &tofs, &rdata, 1); 8242b15cb3dSCy Schubert pp->leap = parse_qual(&rdata, 4, '0', 1); 8252d4e511cSCy Schubert --tofs.l_ui; /* GPZDG gives *following* second */ 8262d4e511cSCy Schubert if (up->type_gpsdate <= DTYP_Y4D) { 8272d4e511cSCy Schubert rc_date = parse_date(&date, &rdata, 2, DATE_3_DDMMYYYY); 8282d4e511cSCy Schubert rc_dtyp = DTYP_Y4D; 8292d4e511cSCy Schubert } 8302b15cb3dSCy Schubert break; 8312b15cb3dSCy Schubert 8322b15cb3dSCy Schubert case NMEA_PGRMF: 8332d4e511cSCy Schubert /* get time, qualifier and GPS weektime. */ 8342d4e511cSCy Schubert rc_time = parse_time(&date, &tofs, &rdata, 4); 8352d4e511cSCy Schubert if (up->type_gpsdate <= DTYP_W10B) { 8362d4e511cSCy Schubert rc_date = parse_gpsw(&wgps, &rdata, 1, 2, 5); 8372d4e511cSCy Schubert rc_dtyp = DTYP_W10B; 8382d4e511cSCy Schubert } 8392b15cb3dSCy Schubert pp->leap = parse_qual(&rdata, 11, '0', 1); 8402b15cb3dSCy Schubert if (CLK_FLAG4 & pp->sloppyclockflag) 8412b15cb3dSCy Schubert field_wipe(&rdata, 6, 8, -1); 8422b15cb3dSCy Schubert break; 8432b15cb3dSCy Schubert 8442d4e511cSCy Schubert case NMEA_PUBX04: 8452d4e511cSCy Schubert /* PUBX,04 is peculiar. The UTC time-of-week is the *internal* 8462d4e511cSCy Schubert * time base, which is not exactly on par with the fix time. 8472d4e511cSCy Schubert */ 8482d4e511cSCy Schubert rc_time = parse_time(&date, &tofs, &rdata, 2); 8492d4e511cSCy Schubert if (up->type_gpsdate <= DTYP_WEXT) { 8502d4e511cSCy Schubert rc_date = parse_gpsw(&wgps, &rdata, 5, 4, -1); 8512d4e511cSCy Schubert rc_dtyp = DTYP_WEXT; 8522d4e511cSCy Schubert } 8532d4e511cSCy Schubert break; 8542d4e511cSCy Schubert 8552b15cb3dSCy Schubert default: 8562b15cb3dSCy Schubert INVARIANT(0); /* Coverity 97123 */ 8572b15cb3dSCy Schubert return; 8582b15cb3dSCy Schubert } 8592b15cb3dSCy Schubert 860a466cc55SCy Schubert /* ignore receiver status? [bug 3694] */ 861a466cc55SCy Schubert if (peer->ttl & NMEA_IGNSTATUS_MASK) { /* assume always good? */ 862a466cc55SCy Schubert pp->leap = LEAP_NOWARNING; 863a466cc55SCy Schubert } 864a466cc55SCy Schubert 8652d4e511cSCy Schubert /* check clock sanity; [bug 2143] */ 8662d4e511cSCy Schubert if (pp->leap == LEAP_NOTINSYNC) { /* no good status? */ 8672d4e511cSCy Schubert checkres = CEVNT_PROP; 8682d4e511cSCy Schubert up->tally.rejected++; 8692d4e511cSCy Schubert } 8702b15cb3dSCy Schubert /* Check sanity of time-of-day. */ 8712d4e511cSCy Schubert else if (rc_time == 0) { /* no time or conversion error? */ 8722b15cb3dSCy Schubert checkres = CEVNT_BADTIME; 8732b15cb3dSCy Schubert up->tally.malformed++; 8742b15cb3dSCy Schubert } 8752b15cb3dSCy Schubert /* Check sanity of date. */ 8762b15cb3dSCy Schubert else if (rc_date == 0) { /* no date or conversion error? */ 8772b15cb3dSCy Schubert checkres = CEVNT_BADDATE; 8782b15cb3dSCy Schubert up->tally.malformed++; 8792b15cb3dSCy Schubert } 8802d4e511cSCy Schubert else { 8812b15cb3dSCy Schubert checkres = -1; 8822d4e511cSCy Schubert } 8832b15cb3dSCy Schubert 8842b15cb3dSCy Schubert if (checkres != -1) { 8852d4e511cSCy Schubert refclock_save_lcode(pp, up->lb_buf, up->lb_len); 8862b15cb3dSCy Schubert refclock_report(peer, checkres); 8872b15cb3dSCy Schubert return; 8882b15cb3dSCy Schubert } 8892b15cb3dSCy Schubert 8902d4e511cSCy Schubert /* See if we can augment the receive time stamp. If not, apply 8912d4e511cSCy Schubert * fudge time 2 to the receive time stamp directly. 8922d4e511cSCy Schubert */ 8932d4e511cSCy Schubert # ifdef HAVE_PPSAPI 8942d4e511cSCy Schubert if (up->ppsapi_lit && pp->leap != LEAP_NOTINSYNC) 8952d4e511cSCy Schubert withpps = refclock_ppsaugment( 8962d4e511cSCy Schubert &up->atom, &rd_timestamp, 8972d4e511cSCy Schubert pp->fudgetime2, pp->fudgetime1); 8982d4e511cSCy Schubert else 8992d4e511cSCy Schubert # endif /* HAVE_PPSAPI */ 9002d4e511cSCy Schubert rd_timestamp = ntpfp_with_fudge( 9012d4e511cSCy Schubert rd_timestamp, pp->fudgetime2); 9022b15cb3dSCy Schubert 9032d4e511cSCy Schubert /* set the GPS base date, if possible */ 9042d4e511cSCy Schubert warp = !(peer->ttl & NMEA_DATETRUST_MASK); 9052d4e511cSCy Schubert if (rc_dtyp != DTYP_NONE) { 9062d4e511cSCy Schubert DPRINTF(1, ("%s saving date, type=%hu\n", 9072d4e511cSCy Schubert refnumtoa(&peer->srcadr), rc_dtyp)); 9082d4e511cSCy Schubert switch (rc_dtyp) { 9092d4e511cSCy Schubert case DTYP_W10B: 9102d4e511cSCy Schubert up->last_gpsdate = gpsntp_from_gpscal_ex( 9112d4e511cSCy Schubert &wgps, (warp = TRUE)); 9122d4e511cSCy Schubert break; 9132d4e511cSCy Schubert case DTYP_WEXT: 9142d4e511cSCy Schubert up->last_gpsdate = gpsntp_from_gpscal_ex( 9152d4e511cSCy Schubert &wgps, warp); 9162d4e511cSCy Schubert break; 9172d4e511cSCy Schubert default: 9182d4e511cSCy Schubert up->last_gpsdate = gpsntp_from_calendar_ex( 9192d4e511cSCy Schubert &date, tofs, warp); 9202d4e511cSCy Schubert break; 9212d4e511cSCy Schubert } 9222d4e511cSCy Schubert up->type_gpsdate = rc_dtyp; 9232d4e511cSCy Schubert up->hold_gpsdate = DATE_HOLD; 9242d4e511cSCy Schubert } 9252d4e511cSCy Schubert /* now convert and possibly extend/expand the time stamp. */ 9262d4e511cSCy Schubert if (up->hold_gpsdate) { /* time of day, based */ 9272d4e511cSCy Schubert dntp = gpsntp_from_daytime2_ex( 9282d4e511cSCy Schubert &date, tofs, &up->last_gpsdate, warp); 9292d4e511cSCy Schubert } else { /* time of day, floating */ 9302d4e511cSCy Schubert dntp = gpsntp_from_daytime1_ex( 9312d4e511cSCy Schubert &date, tofs, rd_timestamp, warp); 9322b15cb3dSCy Schubert } 9332b15cb3dSCy Schubert 9342d4e511cSCy Schubert if (debug) { 9352d4e511cSCy Schubert /* debug print time stamp */ 9362d4e511cSCy Schubert gpsntp_to_calendar(&date, &dntp); 9372d4e511cSCy Schubert # ifdef HAVE_PPSAPI 9382d4e511cSCy Schubert DPRINTF(1, ("%s effective timecode: %s (%s PPS)\n", 9392d4e511cSCy Schubert refnumtoa(&peer->srcadr), 9402d4e511cSCy Schubert ntpcal_iso8601std(NULL, 0, &date), 9412d4e511cSCy Schubert (withpps ? "with" : "without"))); 9422d4e511cSCy Schubert # else /* ?HAVE_PPSAPI */ 9432d4e511cSCy Schubert DPRINTF(1, ("%s effective timecode: %s\n", 9442d4e511cSCy Schubert refnumtoa(&peer->srcadr), 9452d4e511cSCy Schubert ntpcal_iso8601std(NULL, 0, &date))); 9462d4e511cSCy Schubert # endif /* !HAVE_PPSAPI */ 9472d4e511cSCy Schubert } 9482d4e511cSCy Schubert 9492d4e511cSCy Schubert /* Get the reference time stamp from the calendar buffer. 9502b15cb3dSCy Schubert * Process the new sample in the median filter and determine the 9512b15cb3dSCy Schubert * timecode timestamp, but only if the PPS is not in control. 9522b15cb3dSCy Schubert * Discard sentence if reference time did not change. 9532b15cb3dSCy Schubert */ 9542d4e511cSCy Schubert rd_reftime = ntpfp_from_ntpdatum(&dntp); 9552b15cb3dSCy Schubert if (L_ISEQU(&up->last_reftime, &rd_reftime)) { 9562b15cb3dSCy Schubert /* Do not touch pp->a_lastcode on purpose! */ 9572b15cb3dSCy Schubert up->tally.filtered++; 9582b15cb3dSCy Schubert return; 9592b15cb3dSCy Schubert } 9602b15cb3dSCy Schubert up->last_reftime = rd_reftime; 9612b15cb3dSCy Schubert 9622b15cb3dSCy Schubert DPRINTF(1, ("%s using '%s'\n", 9632d4e511cSCy Schubert refnumtoa(&peer->srcadr), up->lb_buf)); 9642b15cb3dSCy Schubert 9652b15cb3dSCy Schubert /* Data will be accepted. Update stats & log data. */ 9662b15cb3dSCy Schubert up->tally.accepted++; 9672d4e511cSCy Schubert refclock_save_lcode(pp, up->lb_buf, up->lb_len); 9682b15cb3dSCy Schubert pp->lastrec = rd_timestamp; 969c0b746e5SOllivier Robert 9702d4e511cSCy Schubert /* If we have PPS augmented receive time, we *must* have a 9712d4e511cSCy Schubert * working PPS source and we must set the flags accordingly. 972224ba2bdSOllivier Robert */ 9732d4e511cSCy Schubert # ifdef HAVE_PPSAPI 9742d4e511cSCy Schubert if (withpps) { 9752b15cb3dSCy Schubert up->ppsapi_gate = TRUE; 9762b15cb3dSCy Schubert peer->precision = PPS_PRECISION; 9772d4e511cSCy Schubert if (tabsdiffd(rd_reftime, rd_timestamp) < 0.5) { 9782d4e511cSCy Schubert if ( ! (peer->ttl & NMEA_QUIETPPS_MASK)) 9792b15cb3dSCy Schubert peer->flags |= FLAG_PPS; 9802b15cb3dSCy Schubert DPRINTF(2, ("%s PPS_RELATE_PHASE\n", 9812b15cb3dSCy Schubert refnumtoa(&peer->srcadr))); 9822b15cb3dSCy Schubert up->tally.pps_used++; 9832d4e511cSCy Schubert } else { 9842b15cb3dSCy Schubert DPRINTF(2, ("%s PPS_RELATE_EDGE\n", 9852b15cb3dSCy Schubert refnumtoa(&peer->srcadr))); 9862d4e511cSCy Schubert } 9872d4e511cSCy Schubert /* !Note! 'FLAG_PPS' is reset in 'nmea_poll()' */ 988c0b746e5SOllivier Robert } 989224ba2bdSOllivier Robert # endif /* HAVE_PPSAPI */ 9902d4e511cSCy Schubert /* Whether the receive time stamp is PPS-augmented or not, 9912d4e511cSCy Schubert * the proper fudge offset is already applied. There's no 9922d4e511cSCy Schubert * residual fudge to process. 9932d4e511cSCy Schubert */ 9942d4e511cSCy Schubert refclock_process_offset(pp, rd_reftime, rd_timestamp, 0.0); 9952d4e511cSCy Schubert up->rcvtout = 2; 996c0b746e5SOllivier Robert } 997c0b746e5SOllivier Robert 9982d4e511cSCy Schubert /* 9992d4e511cSCy Schubert * ------------------------------------------------------------------- 10002d4e511cSCy Schubert * nmea_receive - receive data from the serial interface 10012d4e511cSCy Schubert * 10022d4e511cSCy Schubert * With serial IO only, a single call to 'refclock_gtlin()' to get the 10032d4e511cSCy Schubert * string would suffice to get the NMEA data. When using NMEAD, this 10042d4e511cSCy Schubert * does unfortunately no longer hold, since TCP is stream oriented and 10052d4e511cSCy Schubert * not line oriented, and there's no one to do the line-splitting work 10062d4e511cSCy Schubert * of the TTY driver in line/cooked mode. 10072d4e511cSCy Schubert * 10082d4e511cSCy Schubert * So we have to do this manually here, and we have to live with the 10092d4e511cSCy Schubert * fact that there could be more than one sentence in a receive buffer. 10102d4e511cSCy Schubert * Likewise, there can be partial messages on either end. (Strictly 10112d4e511cSCy Schubert * speaking, a receive buffer could also contain just a single fragment, 10122d4e511cSCy Schubert * though that's unlikely.) 10132d4e511cSCy Schubert * 10142d4e511cSCy Schubert * We deal with that by scanning the input buffer, copying bytes from 10152d4e511cSCy Schubert * the receive buffer to the assembly buffer as we go and calling the 10162d4e511cSCy Schubert * record processor every time we hit a CR/LF, provided the resulting 10172d4e511cSCy Schubert * line is not empty. Any leftovers are kept for the next round. 10182d4e511cSCy Schubert * 10192d4e511cSCy Schubert * Note: When used with a serial data stream, there's no change to the 10202d4e511cSCy Schubert * previous line-oriented input: One line is copied to the buffer and 10212d4e511cSCy Schubert * processed per call. Only with NMEAD the behavior changes, and the 10222d4e511cSCy Schubert * timing is badly affected unless a PPS channel is also associated with 10232d4e511cSCy Schubert * the clock instance. TCP leaves us nothing to improve on here. 10242d4e511cSCy Schubert * ------------------------------------------------------------------- 10252d4e511cSCy Schubert */ 10262d4e511cSCy Schubert static void 10272d4e511cSCy Schubert nmea_receive( 10282d4e511cSCy Schubert struct recvbuf * rbufp 10292d4e511cSCy Schubert ) 10302d4e511cSCy Schubert { 10312d4e511cSCy Schubert /* declare & init control structure pointers */ 10322d4e511cSCy Schubert struct peer * const peer = rbufp->recv_peer; 10332d4e511cSCy Schubert struct refclockproc * const pp = peer->procptr; 10342d4e511cSCy Schubert nmea_unit * const up = (nmea_unit*)pp->unitptr; 10352d4e511cSCy Schubert 10362d4e511cSCy Schubert const char *sp, *se; 10372d4e511cSCy Schubert char *dp, *de; 10382d4e511cSCy Schubert 10392d4e511cSCy Schubert /* paranoia check: */ 10402d4e511cSCy Schubert if (up->lb_len >= sizeof(up->lb_buf)) 10412d4e511cSCy Schubert up->lb_len = 0; 10422d4e511cSCy Schubert 10432d4e511cSCy Schubert /* pick up last assembly position; leave room for NUL */ 10442d4e511cSCy Schubert dp = up->lb_buf + up->lb_len; 10452d4e511cSCy Schubert de = up->lb_buf + sizeof(up->lb_buf) - 1; 10462d4e511cSCy Schubert /* set up input range */ 10472d4e511cSCy Schubert sp = (const char *)rbufp->recv_buffer; 10482d4e511cSCy Schubert se = sp + rbufp->recv_length; 10492d4e511cSCy Schubert 10502d4e511cSCy Schubert /* walk over the input data, dropping parity bits and control 10512d4e511cSCy Schubert * chars as we go, and calling the record processor for each 10522d4e511cSCy Schubert * complete non-empty line. 10532d4e511cSCy Schubert */ 10542d4e511cSCy Schubert while (sp != se) { 10552d4e511cSCy Schubert char ch = (*sp++ & 0x7f); 10562d4e511cSCy Schubert if (dp == up->lb_buf) { 10572d4e511cSCy Schubert if (ch == '$') 10582d4e511cSCy Schubert *dp++ = ch; 10592d4e511cSCy Schubert } else if (dp > de) { 10602d4e511cSCy Schubert dp = up->lb_buf; 10612d4e511cSCy Schubert } else if (ch == '\n' || ch == '\r') { 10622d4e511cSCy Schubert *dp = '\0'; 10632d4e511cSCy Schubert up->lb_len = (int)(dp - up->lb_buf); 10642d4e511cSCy Schubert dp = up->lb_buf; 10652d4e511cSCy Schubert nmea_procrec(peer, rbufp->recv_time); 10662d4e511cSCy Schubert } else if (ch >= 0x20 && ch < 0x7f) { 10672d4e511cSCy Schubert *dp++ = ch; 10682d4e511cSCy Schubert } 10692d4e511cSCy Schubert } 10702d4e511cSCy Schubert /* update state to keep for next round */ 10712d4e511cSCy Schubert *dp = '\0'; 10722d4e511cSCy Schubert up->lb_len = (int)(dp - up->lb_buf); 10732d4e511cSCy Schubert } 1074224ba2bdSOllivier Robert 1075c0b746e5SOllivier Robert /* 10762b15cb3dSCy Schubert * ------------------------------------------------------------------- 1077c0b746e5SOllivier Robert * nmea_poll - called by the transmit procedure 1078c0b746e5SOllivier Robert * 10792b15cb3dSCy Schubert * Does the necessary bookkeeping stuff to keep the reported state of 10802b15cb3dSCy Schubert * the clock in sync with reality. 10812b15cb3dSCy Schubert * 10822b15cb3dSCy Schubert * We go to great pains to avoid changing state here, since there may 10832b15cb3dSCy Schubert * be more than one eavesdropper receiving the same timecode. 10842b15cb3dSCy Schubert * ------------------------------------------------------------------- 1085c0b746e5SOllivier Robert */ 1086c0b746e5SOllivier Robert static void 1087c0b746e5SOllivier Robert nmea_poll( 1088c0b746e5SOllivier Robert int unit, 1089c0b746e5SOllivier Robert struct peer * peer 1090c0b746e5SOllivier Robert ) 1091c0b746e5SOllivier Robert { 10922b15cb3dSCy Schubert struct refclockproc * const pp = peer->procptr; 10932b15cb3dSCy Schubert nmea_unit * const up = (nmea_unit *)pp->unitptr; 1094c0b746e5SOllivier Robert 1095c0b746e5SOllivier Robert /* 10962b15cb3dSCy Schubert * Process median filter samples. If none received, declare a 10972b15cb3dSCy Schubert * timeout and keep going. 1098c0b746e5SOllivier Robert */ 10992b15cb3dSCy Schubert # ifdef HAVE_PPSAPI 11002b15cb3dSCy Schubert /* 11012b15cb3dSCy Schubert * If we don't have PPS pulses and time stamps, turn PPS down 11022b15cb3dSCy Schubert * for now. 11032b15cb3dSCy Schubert */ 11042b15cb3dSCy Schubert if (!up->ppsapi_gate) { 11052b15cb3dSCy Schubert peer->flags &= ~FLAG_PPS; 11062b15cb3dSCy Schubert peer->precision = PRECISION; 11072b15cb3dSCy Schubert } else { 11082b15cb3dSCy Schubert up->ppsapi_gate = FALSE; 11092b15cb3dSCy Schubert } 11102b15cb3dSCy Schubert # endif /* HAVE_PPSAPI */ 1111c0b746e5SOllivier Robert 11122b15cb3dSCy Schubert /* 11132b15cb3dSCy Schubert * If the median filter is empty, claim a timeout. Else process 11142b15cb3dSCy Schubert * the input data and keep the stats going. 11152b15cb3dSCy Schubert */ 11162b15cb3dSCy Schubert if (pp->coderecv == pp->codeproc) { 11172d4e511cSCy Schubert peer->flags &= ~FLAG_PPS; 11182d4e511cSCy Schubert if (pp->currentstatus < CEVNT_TIMEOUT) 11192b15cb3dSCy Schubert refclock_report(peer, CEVNT_TIMEOUT); 11202d4e511cSCy Schubert memset(&up->last_gpsdate, 0, sizeof(up->last_gpsdate)); 11212b15cb3dSCy Schubert } else { 11222b15cb3dSCy Schubert pp->polls++; 11232b15cb3dSCy Schubert pp->lastref = pp->lastrec; 11242b15cb3dSCy Schubert refclock_receive(peer); 11252d4e511cSCy Schubert if (pp->currentstatus > CEVNT_NOMINAL) 11262d4e511cSCy Schubert refclock_report(peer, CEVNT_NOMINAL); 1127c0b746e5SOllivier Robert } 1128c0b746e5SOllivier Robert 1129c0b746e5SOllivier Robert /* 11302b15cb3dSCy Schubert * If extended logging is required, write the tally stats to the 11312b15cb3dSCy Schubert * clockstats file; otherwise just do a normal clock stats 11322b15cb3dSCy Schubert * record. Clear the tally stats anyway. 11332b15cb3dSCy Schubert */ 11342b15cb3dSCy Schubert if (peer->ttl & NMEA_EXTLOG_MASK) { 11352b15cb3dSCy Schubert /* Log & reset counters with extended logging */ 11362b15cb3dSCy Schubert const char *nmea = pp->a_lastcode; 11372b15cb3dSCy Schubert if (*nmea == '\0') nmea = "(none)"; 11382b15cb3dSCy Schubert mprintf_clock_stats( 11392b15cb3dSCy Schubert &peer->srcadr, "%s %u %u %u %u %u %u", 11402b15cb3dSCy Schubert nmea, 11412b15cb3dSCy Schubert up->tally.total, up->tally.accepted, 11422b15cb3dSCy Schubert up->tally.rejected, up->tally.malformed, 11432b15cb3dSCy Schubert up->tally.filtered, up->tally.pps_used); 11442b15cb3dSCy Schubert } else { 11452b15cb3dSCy Schubert record_clock_stats(&peer->srcadr, pp->a_lastcode); 11462b15cb3dSCy Schubert } 11472b15cb3dSCy Schubert ZERO(up->tally); 11482b15cb3dSCy Schubert } 11492b15cb3dSCy Schubert 11502b15cb3dSCy Schubert #if NMEA_WRITE_SUPPORT 11512b15cb3dSCy Schubert /* 11522b15cb3dSCy Schubert * ------------------------------------------------------------------- 1153c0b746e5SOllivier Robert * gps_send(fd, cmd, peer) Sends a command to the GPS receiver. 11542b15cb3dSCy Schubert * as in gps_send(fd, "rqts,u", peer); 1155c0b746e5SOllivier Robert * 11562b15cb3dSCy Schubert * If 'cmd' starts with a '$' it is assumed that this command is in raw 11572b15cb3dSCy Schubert * format, that is, starts with '$', ends with '<cr><lf>' and that any 11582b15cb3dSCy Schubert * checksum is correctly provided; the command will be send 'as is' in 11592b15cb3dSCy Schubert * that case. Otherwise the function will create the necessary frame 11602b15cb3dSCy Schubert * (start char, chksum, final CRLF) on the fly. 1161c0b746e5SOllivier Robert * 11622b15cb3dSCy Schubert * We don't currently send any data, but would like to send RTCM SC104 11632b15cb3dSCy Schubert * messages for differential positioning. It should also give us better 11642b15cb3dSCy Schubert * time. Without a PPS output, we're Just fooling ourselves because of 11652b15cb3dSCy Schubert * the serial code paths 11662b15cb3dSCy Schubert * ------------------------------------------------------------------- 1167c0b746e5SOllivier Robert */ 1168c0b746e5SOllivier Robert static void 1169c0b746e5SOllivier Robert gps_send( 1170c0b746e5SOllivier Robert int fd, 1171c0b746e5SOllivier Robert const char * cmd, 1172c0b746e5SOllivier Robert struct peer * peer 1173c0b746e5SOllivier Robert ) 1174c0b746e5SOllivier Robert { 11752b15cb3dSCy Schubert /* $...*xy<CR><LF><NUL> add 7 */ 11762b15cb3dSCy Schubert char buf[NMEA_PROTO_MAXLEN + 7]; 11772b15cb3dSCy Schubert int len; 11782b15cb3dSCy Schubert u_char dcs; 11792b15cb3dSCy Schubert const u_char *beg, *end; 1180c0b746e5SOllivier Robert 11812b15cb3dSCy Schubert if (*cmd != '$') { 11822b15cb3dSCy Schubert /* get checksum and length */ 11832b15cb3dSCy Schubert beg = end = (const u_char*)cmd; 11842b15cb3dSCy Schubert dcs = 0; 11852b15cb3dSCy Schubert while (*end >= ' ' && *end != '*') 11862b15cb3dSCy Schubert dcs ^= *end++; 11872b15cb3dSCy Schubert len = end - beg; 11882b15cb3dSCy Schubert /* format into output buffer with overflow check */ 11892b15cb3dSCy Schubert len = snprintf(buf, sizeof(buf), "$%.*s*%02X\r\n", 11902b15cb3dSCy Schubert len, beg, dcs); 11912b15cb3dSCy Schubert if ((size_t)len >= sizeof(buf)) { 11922b15cb3dSCy Schubert DPRINTF(1, ("%s gps_send: buffer overflow for command '%s'\n", 11932b15cb3dSCy Schubert refnumtoa(&peer->srcadr), cmd)); 11942b15cb3dSCy Schubert return; /* game over player 1 */ 11952b15cb3dSCy Schubert } 11962b15cb3dSCy Schubert cmd = buf; 11972b15cb3dSCy Schubert } else { 11982b15cb3dSCy Schubert len = strlen(cmd); 11992b15cb3dSCy Schubert } 12002b15cb3dSCy Schubert 12012b15cb3dSCy Schubert DPRINTF(1, ("%s gps_send: '%.*s'\n", refnumtoa(&peer->srcadr), 12022b15cb3dSCy Schubert len - 2, cmd)); 12032b15cb3dSCy Schubert 12042b15cb3dSCy Schubert /* send out the whole stuff */ 1205a466cc55SCy Schubert if (refclock_fdwrite(peer, fd, cmd, len) != len) 1206c0b746e5SOllivier Robert refclock_report(peer, CEVNT_FAULT); 1207c0b746e5SOllivier Robert } 12082b15cb3dSCy Schubert #endif /* NMEA_WRITE_SUPPORT */ 12092b15cb3dSCy Schubert 12102b15cb3dSCy Schubert /* 12112b15cb3dSCy Schubert * ------------------------------------------------------------------- 12122b15cb3dSCy Schubert * helpers for faster field splitting 12132b15cb3dSCy Schubert * ------------------------------------------------------------------- 12142b15cb3dSCy Schubert * 12152b15cb3dSCy Schubert * set up a field record, check syntax and verify checksum 12162b15cb3dSCy Schubert * 12172b15cb3dSCy Schubert * format is $XXXXX,1,2,3,4*ML 12182b15cb3dSCy Schubert * 12192b15cb3dSCy Schubert * 8-bit XOR of characters between $ and * noninclusive is transmitted 12202b15cb3dSCy Schubert * in last two chars M and L holding most and least significant nibbles 12212b15cb3dSCy Schubert * in hex representation such as: 12222b15cb3dSCy Schubert * 12232b15cb3dSCy Schubert * $GPGLL,5057.970,N,00146.110,E,142451,A*27 12242b15cb3dSCy Schubert * $GPVTG,089.0,T,,,15.2,N,,*7F 12252b15cb3dSCy Schubert * 12262b15cb3dSCy Schubert * Some other constraints: 12272d4e511cSCy Schubert * + The field name must be at least 5 upcase characters or digits and 12282d4e511cSCy Schubert * must start with a character. 12292b15cb3dSCy Schubert * + The checksum (if present) must be uppercase hex digits. 12302b15cb3dSCy Schubert * + The length of a sentence is limited to 80 characters (not including 12312b15cb3dSCy Schubert * the final CR/LF nor the checksum, but including the leading '$') 12322b15cb3dSCy Schubert * 12332b15cb3dSCy Schubert * Return values: 12342b15cb3dSCy Schubert * + CHECK_INVALID 12352b15cb3dSCy Schubert * The data does not form a valid NMEA sentence or a checksum error 12362b15cb3dSCy Schubert * occurred. 12372b15cb3dSCy Schubert * + CHECK_VALID 12382b15cb3dSCy Schubert * The data is a valid NMEA sentence but contains no checksum. 12392b15cb3dSCy Schubert * + CHECK_CSVALID 12402b15cb3dSCy Schubert * The data is a valid NMEA sentence and passed the checksum test. 12412b15cb3dSCy Schubert * ------------------------------------------------------------------- 12422b15cb3dSCy Schubert */ 12432b15cb3dSCy Schubert static int 12442b15cb3dSCy Schubert field_init( 12452b15cb3dSCy Schubert nmea_data * data, /* context structure */ 12462b15cb3dSCy Schubert char * cptr, /* start of raw data */ 12472b15cb3dSCy Schubert int dlen /* data len, not counting trailing NUL */ 12482b15cb3dSCy Schubert ) 12492b15cb3dSCy Schubert { 12502b15cb3dSCy Schubert u_char cs_l; /* checksum local computed */ 12512b15cb3dSCy Schubert u_char cs_r; /* checksum remote given */ 12522b15cb3dSCy Schubert char * eptr; /* buffer end end pointer */ 12532b15cb3dSCy Schubert char tmp; /* char buffer */ 12542b15cb3dSCy Schubert 12552b15cb3dSCy Schubert cs_l = 0; 12562b15cb3dSCy Schubert cs_r = 0; 12572b15cb3dSCy Schubert /* some basic input constraints */ 12582b15cb3dSCy Schubert if (dlen < 0) 12592b15cb3dSCy Schubert dlen = 0; 12602b15cb3dSCy Schubert eptr = cptr + dlen; 12612b15cb3dSCy Schubert *eptr = '\0'; 12622b15cb3dSCy Schubert 12632b15cb3dSCy Schubert /* load data context */ 12642b15cb3dSCy Schubert data->base = cptr; 12652b15cb3dSCy Schubert data->cptr = cptr; 12662b15cb3dSCy Schubert data->cidx = 0; 12672b15cb3dSCy Schubert data->blen = dlen; 12682b15cb3dSCy Schubert 12692b15cb3dSCy Schubert /* syntax check follows here. check allowed character 12702b15cb3dSCy Schubert * sequences, updating the local computed checksum as we go. 12712b15cb3dSCy Schubert * 12722b15cb3dSCy Schubert * regex equiv: '^\$[A-Z][A-Z0-9]{4,}[^*]*(\*[0-9A-F]{2})?$' 12732b15cb3dSCy Schubert */ 12742b15cb3dSCy Schubert 12752b15cb3dSCy Schubert /* -*- start character: '^\$' */ 12762b15cb3dSCy Schubert if (*cptr == '\0') 12772b15cb3dSCy Schubert return CHECK_EMPTY; 12782b15cb3dSCy Schubert if (*cptr++ != '$') 12792b15cb3dSCy Schubert return CHECK_INVALID; 12802b15cb3dSCy Schubert 12812b15cb3dSCy Schubert /* -*- advance context beyond start character */ 12822b15cb3dSCy Schubert data->base++; 12832b15cb3dSCy Schubert data->cptr++; 12842b15cb3dSCy Schubert data->blen--; 12852b15cb3dSCy Schubert 12862b15cb3dSCy Schubert /* -*- field name: '[A-Z][A-Z0-9]{4,},' */ 12872b15cb3dSCy Schubert if (*cptr < 'A' || *cptr > 'Z') 12882b15cb3dSCy Schubert return CHECK_INVALID; 12892b15cb3dSCy Schubert cs_l ^= *cptr++; 12902b15cb3dSCy Schubert while ((*cptr >= 'A' && *cptr <= 'Z') || 12912b15cb3dSCy Schubert (*cptr >= '0' && *cptr <= '9') ) 12922b15cb3dSCy Schubert cs_l ^= *cptr++; 12932b15cb3dSCy Schubert if (*cptr != ',' || (cptr - data->base) < NMEA_PROTO_IDLEN) 12942b15cb3dSCy Schubert return CHECK_INVALID; 12952b15cb3dSCy Schubert cs_l ^= *cptr++; 12962b15cb3dSCy Schubert 12972b15cb3dSCy Schubert /* -*- data: '[^*]*' */ 12982b15cb3dSCy Schubert while (*cptr && *cptr != '*') 12992b15cb3dSCy Schubert cs_l ^= *cptr++; 13002b15cb3dSCy Schubert 13012b15cb3dSCy Schubert /* -*- checksum field: (\*[0-9A-F]{2})?$ */ 13022b15cb3dSCy Schubert if (*cptr == '\0') 13032b15cb3dSCy Schubert return CHECK_VALID; 13042b15cb3dSCy Schubert if (*cptr != '*' || cptr != eptr - 3 || 13052b15cb3dSCy Schubert (cptr - data->base) >= NMEA_PROTO_MAXLEN) 13062b15cb3dSCy Schubert return CHECK_INVALID; 13072b15cb3dSCy Schubert 13082b15cb3dSCy Schubert for (cptr++; (tmp = *cptr) != '\0'; cptr++) { 13092b15cb3dSCy Schubert if (tmp >= '0' && tmp <= '9') 13102b15cb3dSCy Schubert cs_r = (cs_r << 4) + (tmp - '0'); 13112b15cb3dSCy Schubert else if (tmp >= 'A' && tmp <= 'F') 13122b15cb3dSCy Schubert cs_r = (cs_r << 4) + (tmp - 'A' + 10); 13132b15cb3dSCy Schubert else 13142b15cb3dSCy Schubert break; 1315c0b746e5SOllivier Robert } 1316c0b746e5SOllivier Robert 13172b15cb3dSCy Schubert /* -*- make sure we are at end of string and csum matches */ 13182b15cb3dSCy Schubert if (cptr != eptr || cs_l != cs_r) 13192b15cb3dSCy Schubert return CHECK_INVALID; 13202b15cb3dSCy Schubert 13212b15cb3dSCy Schubert return CHECK_CSVALID; 13222b15cb3dSCy Schubert } 13232b15cb3dSCy Schubert 13242b15cb3dSCy Schubert /* 13252b15cb3dSCy Schubert * ------------------------------------------------------------------- 13262b15cb3dSCy Schubert * fetch a data field by index, zero being the name field. If this 13272b15cb3dSCy Schubert * function is called repeatedly with increasing indices, the total load 13282b15cb3dSCy Schubert * is O(n), n being the length of the string; if it is called with 13292b15cb3dSCy Schubert * decreasing indices, the total load is O(n^2). Try not to go backwards 13302b15cb3dSCy Schubert * too often. 13312b15cb3dSCy Schubert * ------------------------------------------------------------------- 13322b15cb3dSCy Schubert */ 1333c0b746e5SOllivier Robert static char * 1334c0b746e5SOllivier Robert field_parse( 13352b15cb3dSCy Schubert nmea_data * data, 1336c0b746e5SOllivier Robert int fn 1337c0b746e5SOllivier Robert ) 1338c0b746e5SOllivier Robert { 13392b15cb3dSCy Schubert char tmp; 1340c0b746e5SOllivier Robert 13412b15cb3dSCy Schubert if (fn < data->cidx) { 13422b15cb3dSCy Schubert data->cidx = 0; 13432b15cb3dSCy Schubert data->cptr = data->base; 1344c0b746e5SOllivier Robert } 13452b15cb3dSCy Schubert while ((fn > data->cidx) && (tmp = *data->cptr) != '\0') { 13462b15cb3dSCy Schubert data->cidx += (tmp == ','); 13472b15cb3dSCy Schubert data->cptr++; 13482b15cb3dSCy Schubert } 13492b15cb3dSCy Schubert return data->cptr; 13502b15cb3dSCy Schubert } 13512b15cb3dSCy Schubert 13522b15cb3dSCy Schubert /* 13532b15cb3dSCy Schubert * ------------------------------------------------------------------- 13542b15cb3dSCy Schubert * Wipe (that is, overwrite with '_') data fields and the checksum in 13552b15cb3dSCy Schubert * the last timecode. The list of field indices is given as integers 13562d4e511cSCy Schubert * in a varargs list, preferably in ascending order, in any case 13572b15cb3dSCy Schubert * terminated by a negative field index. 13582b15cb3dSCy Schubert * 13592b15cb3dSCy Schubert * A maximum number of 8 fields can be overwritten at once to guard 13602b15cb3dSCy Schubert * against runaway (that is, unterminated) argument lists. 13612b15cb3dSCy Schubert * 13622b15cb3dSCy Schubert * This function affects what a remote user can see with 13632b15cb3dSCy Schubert * 13642b15cb3dSCy Schubert * ntpq -c clockvar <server> 13652b15cb3dSCy Schubert * 13662b15cb3dSCy Schubert * Note that this also removes the wiped fields from any clockstats 13672b15cb3dSCy Schubert * log. Some NTP operators monitor their NMEA GPS using the change in 13682b15cb3dSCy Schubert * location in clockstats over time as as a proxy for the quality of 13692b15cb3dSCy Schubert * GPS reception and thereby time reported. 13702b15cb3dSCy Schubert * ------------------------------------------------------------------- 13712b15cb3dSCy Schubert */ 13722b15cb3dSCy Schubert static void 13732b15cb3dSCy Schubert field_wipe( 13742b15cb3dSCy Schubert nmea_data * data, 13752b15cb3dSCy Schubert ... 13762b15cb3dSCy Schubert ) 13772b15cb3dSCy Schubert { 13782b15cb3dSCy Schubert va_list va; /* vararg index list */ 13792b15cb3dSCy Schubert int fcnt; /* safeguard against runaway arglist */ 13802b15cb3dSCy Schubert int fidx; /* field to nuke, or -1 for checksum */ 13812b15cb3dSCy Schubert char * cp; /* overwrite destination */ 13822b15cb3dSCy Schubert 13832b15cb3dSCy Schubert fcnt = 8; 13842b15cb3dSCy Schubert cp = NULL; 13852b15cb3dSCy Schubert va_start(va, data); 13862b15cb3dSCy Schubert do { 13872b15cb3dSCy Schubert fidx = va_arg(va, int); 13882b15cb3dSCy Schubert if (fidx >= 0 && fidx <= NMEA_PROTO_FIELDS) { 13892b15cb3dSCy Schubert cp = field_parse(data, fidx); 13902b15cb3dSCy Schubert } else { 13912b15cb3dSCy Schubert cp = data->base + data->blen; 13922b15cb3dSCy Schubert if (data->blen >= 3 && cp[-3] == '*') 13932b15cb3dSCy Schubert cp -= 2; 13942b15cb3dSCy Schubert } 13952b15cb3dSCy Schubert for ( ; '\0' != *cp && '*' != *cp && ',' != *cp; cp++) 13962b15cb3dSCy Schubert if ('.' != *cp) 13972b15cb3dSCy Schubert *cp = '_'; 13982b15cb3dSCy Schubert } while (fcnt-- && fidx >= 0); 13992b15cb3dSCy Schubert va_end(va); 14002b15cb3dSCy Schubert } 14012b15cb3dSCy Schubert 14022b15cb3dSCy Schubert /* 14032b15cb3dSCy Schubert * ------------------------------------------------------------------- 14042b15cb3dSCy Schubert * PARSING HELPERS 14052b15cb3dSCy Schubert * ------------------------------------------------------------------- 14062d4e511cSCy Schubert */ 14072d4e511cSCy Schubert typedef unsigned char const UCC; 14082d4e511cSCy Schubert 14092d4e511cSCy Schubert static char const * const s_eof_chars = ",*\r\n"; 14102d4e511cSCy Schubert 1411*f5f40dd6SCy Schubert #ifdef DEBUG 14122d4e511cSCy Schubert static int field_length(UCC *cp, unsigned int nfields) 14132d4e511cSCy Schubert { 14142d4e511cSCy Schubert char const * ep = (char const*)cp; 14152d4e511cSCy Schubert ep = strpbrk(ep, s_eof_chars); 14162d4e511cSCy Schubert if (ep && nfields) 14172d4e511cSCy Schubert while (--nfields && ep && *ep == ',') 14182d4e511cSCy Schubert ep = strpbrk(ep + 1, s_eof_chars); 14192d4e511cSCy Schubert return (ep) 14202d4e511cSCy Schubert ? (int)((UCC*)ep - cp) 14212d4e511cSCy Schubert : (int)strlen((char const*)cp); 14222d4e511cSCy Schubert } 1423*f5f40dd6SCy Schubert #endif /* DEBUG */ 14242d4e511cSCy Schubert 14252d4e511cSCy Schubert /* /[,*\r\n]/ --> skip */ 14262d4e511cSCy Schubert static int _parse_eof(UCC *cp, UCC ** ep) 14272d4e511cSCy Schubert { 14282d4e511cSCy Schubert int rc = (strchr(s_eof_chars, *(char const*)cp) != NULL); 14292d4e511cSCy Schubert *ep = cp + rc; 14302d4e511cSCy Schubert return rc; 14312d4e511cSCy Schubert } 14322d4e511cSCy Schubert 14332d4e511cSCy Schubert /* /,/ --> skip */ 14342d4e511cSCy Schubert static int _parse_sep(UCC *cp, UCC ** ep) 14352d4e511cSCy Schubert { 14362d4e511cSCy Schubert int rc = (*cp == ','); 14372d4e511cSCy Schubert *ep = cp + rc; 14382d4e511cSCy Schubert return rc; 14392d4e511cSCy Schubert } 14402d4e511cSCy Schubert 14412d4e511cSCy Schubert /* /[[:digit:]]{2}/ --> uint16_t */ 14422d4e511cSCy Schubert static int _parse_num2d(UCC *cp, UCC ** ep, uint16_t *into) 14432d4e511cSCy Schubert { 14442d4e511cSCy Schubert int rc = FALSE; 14452d4e511cSCy Schubert 14462d4e511cSCy Schubert if (isdigit(cp[0]) && isdigit(cp[1])) { 14472d4e511cSCy Schubert *into = (cp[0] - '0') * 10 + (cp[1] - '0'); 14482d4e511cSCy Schubert cp += 2; 14492d4e511cSCy Schubert rc = TRUE; 14502d4e511cSCy Schubert } 14512d4e511cSCy Schubert *ep = cp; 14522d4e511cSCy Schubert return rc; 14532d4e511cSCy Schubert } 14542d4e511cSCy Schubert 14552d4e511cSCy Schubert /* /[[:digit:]]+/ --> uint16_t */ 14562d4e511cSCy Schubert static int _parse_u16(UCC *cp, UCC **ep, uint16_t *into, unsigned int ndig) 14572d4e511cSCy Schubert { 14582d4e511cSCy Schubert uint16_t num = 0; 14592d4e511cSCy Schubert int rc = FALSE; 14602d4e511cSCy Schubert if (isdigit(*cp) && ndig) { 14612d4e511cSCy Schubert rc = TRUE; 14622d4e511cSCy Schubert do 14632d4e511cSCy Schubert num = (num * 10) + (*cp - '0'); 14642d4e511cSCy Schubert while (isdigit(*++cp) && --ndig); 14652d4e511cSCy Schubert *into = num; 14662d4e511cSCy Schubert } 14672d4e511cSCy Schubert *ep = cp; 14682d4e511cSCy Schubert return rc; 14692d4e511cSCy Schubert } 14702d4e511cSCy Schubert 14712d4e511cSCy Schubert /* /[[:digit:]]+/ --> uint32_t */ 14722d4e511cSCy Schubert static int _parse_u32(UCC *cp, UCC **ep, uint32_t *into, unsigned int ndig) 14732d4e511cSCy Schubert { 14742d4e511cSCy Schubert uint32_t num = 0; 14752d4e511cSCy Schubert int rc = FALSE; 14762d4e511cSCy Schubert if (isdigit(*cp) && ndig) { 14772d4e511cSCy Schubert rc = TRUE; 14782d4e511cSCy Schubert do 14792d4e511cSCy Schubert num = (num * 10) + (*cp - '0'); 14802d4e511cSCy Schubert while (isdigit(*++cp) && --ndig); 14812d4e511cSCy Schubert *into = num; 14822d4e511cSCy Schubert } 14832d4e511cSCy Schubert *ep = cp; 14842d4e511cSCy Schubert return rc; 14852d4e511cSCy Schubert } 14862d4e511cSCy Schubert 14872d4e511cSCy Schubert /* /(\.[[:digit:]]*)?/ --> l_fp{0, f} 14882d4e511cSCy Schubert * read fractional seconds, convert to l_fp 14892b15cb3dSCy Schubert * 14902d4e511cSCy Schubert * Only the first 9 decimal digits are evaluated; any excess is parsed 14912d4e511cSCy Schubert * away but silently ignored. (--> truncation to 1 nanosecond) 14922d4e511cSCy Schubert */ 14932d4e511cSCy Schubert static int _parse_frac(UCC *cp, UCC **ep, l_fp *into) 14942d4e511cSCy Schubert { 14952d4e511cSCy Schubert static const uint32_t powtab[10] = { 14962d4e511cSCy Schubert 0, 14972d4e511cSCy Schubert 100000000, 10000000, 1000000, 14982d4e511cSCy Schubert 100000, 10000, 1000, 14992d4e511cSCy Schubert 100, 10, 1 15002d4e511cSCy Schubert }; 15012d4e511cSCy Schubert 15022d4e511cSCy Schubert struct timespec ts; 15032d4e511cSCy Schubert ZERO(ts); 15042d4e511cSCy Schubert if (*cp == '.') { 15052d4e511cSCy Schubert uint32_t fval = 0; 15062d4e511cSCy Schubert UCC * sp = cp + 1; 15072d4e511cSCy Schubert if (_parse_u32(sp, &cp, &fval, 9)) 15082d4e511cSCy Schubert ts.tv_nsec = fval * powtab[(size_t)(cp - sp)]; 15092d4e511cSCy Schubert while (isdigit(*cp)) 15102d4e511cSCy Schubert ++cp; 15112d4e511cSCy Schubert } 15122d4e511cSCy Schubert 15132d4e511cSCy Schubert *ep = cp; 15142d4e511cSCy Schubert *into = tspec_intv_to_lfp(ts); 15152d4e511cSCy Schubert return TRUE; 15162d4e511cSCy Schubert } 15172d4e511cSCy Schubert 15182d4e511cSCy Schubert /* /[[:digit:]]{6}/ --> time-of-day 15192d4e511cSCy Schubert * parses a number string representing 'HHMMSS' 15202d4e511cSCy Schubert */ 15212d4e511cSCy Schubert static int _parse_time(UCC *cp, UCC ** ep, TCivilDate *into) 15222d4e511cSCy Schubert { 15232d4e511cSCy Schubert uint16_t s, m, h; 15242d4e511cSCy Schubert int rc; 15252d4e511cSCy Schubert UCC * xp = cp; 15262d4e511cSCy Schubert 15272d4e511cSCy Schubert rc = _parse_num2d(cp, &cp, &h) && (h < 24) 15282d4e511cSCy Schubert && _parse_num2d(cp, &cp, &m) && (m < 60) 15292d4e511cSCy Schubert && _parse_num2d(cp, &cp, &s) && (s < 61); /* leap seconds! */ 15302d4e511cSCy Schubert 15312d4e511cSCy Schubert if (rc) { 15322d4e511cSCy Schubert into->hour = (uint8_t)h; 15332d4e511cSCy Schubert into->minute = (uint8_t)m; 15342d4e511cSCy Schubert into->second = (uint8_t)s; 15352d4e511cSCy Schubert *ep = cp; 15362d4e511cSCy Schubert } else { 15372d4e511cSCy Schubert *ep = xp; 15382d4e511cSCy Schubert DPRINTF(1, ("nmea: invalid time code: '%.*s'\n", 15392d4e511cSCy Schubert field_length(xp, 1), xp)); 15402d4e511cSCy Schubert } 15412d4e511cSCy Schubert return rc; 15422d4e511cSCy Schubert } 15432d4e511cSCy Schubert 15442d4e511cSCy Schubert /* /[[:digit:]]{6}/ --> civil date 15452d4e511cSCy Schubert * parses a number string representing 'ddmmyy' 15462d4e511cSCy Schubert */ 15472d4e511cSCy Schubert static int _parse_date1(UCC *cp, UCC **ep, TCivilDate *into) 15482d4e511cSCy Schubert { 15492d4e511cSCy Schubert unsigned short d, m, y; 15502d4e511cSCy Schubert int rc; 15512d4e511cSCy Schubert UCC * xp = cp; 15522d4e511cSCy Schubert 15532d4e511cSCy Schubert rc = _parse_num2d(cp, &cp, &d) && (d - 1 < 31) 15542d4e511cSCy Schubert && _parse_num2d(cp, &cp, &m) && (m - 1 < 12) 15552d4e511cSCy Schubert && _parse_num2d(cp, &cp, &y) 15562d4e511cSCy Schubert && _parse_eof(cp, ep); 15572d4e511cSCy Schubert if (rc) { 15582d4e511cSCy Schubert into->monthday = (uint8_t )d; 15592d4e511cSCy Schubert into->month = (uint8_t )m; 15602d4e511cSCy Schubert into->year = (uint16_t)y; 15612d4e511cSCy Schubert *ep = cp; 15622d4e511cSCy Schubert } else { 15632d4e511cSCy Schubert *ep = xp; 15642d4e511cSCy Schubert DPRINTF(1, ("nmea: invalid date code: '%.*s'\n", 15652d4e511cSCy Schubert field_length(xp, 1), xp)); 15662d4e511cSCy Schubert } 15672d4e511cSCy Schubert return rc; 15682d4e511cSCy Schubert } 15692d4e511cSCy Schubert 15702d4e511cSCy Schubert /* /[[:digit:]]+,[[:digit:]]+,[[:digit:]]+/ --> civil date 15712d4e511cSCy Schubert * parses three successive numeric fields as date: day,month,year 15722d4e511cSCy Schubert */ 15732d4e511cSCy Schubert static int _parse_date3(UCC *cp, UCC **ep, TCivilDate *into) 15742d4e511cSCy Schubert { 15752d4e511cSCy Schubert uint16_t d, m, y; 15762d4e511cSCy Schubert int rc; 15772d4e511cSCy Schubert UCC * xp = cp; 15782d4e511cSCy Schubert 15792d4e511cSCy Schubert rc = _parse_u16(cp, &cp, &d, 2) && (d - 1 < 31) 15802d4e511cSCy Schubert && _parse_sep(cp, &cp) 15812d4e511cSCy Schubert && _parse_u16(cp, &cp, &m, 2) && (m - 1 < 12) 15822d4e511cSCy Schubert && _parse_sep(cp, &cp) 15832d4e511cSCy Schubert && _parse_u16(cp, &cp, &y, 4) && (y > 1980) 15842d4e511cSCy Schubert && _parse_eof(cp, ep); 15852d4e511cSCy Schubert if (rc) { 15862d4e511cSCy Schubert into->monthday = (uint8_t )d; 15872d4e511cSCy Schubert into->month = (uint8_t )m; 15882d4e511cSCy Schubert into->year = (uint16_t)y; 15892d4e511cSCy Schubert *ep = cp; 15902d4e511cSCy Schubert } else { 15912d4e511cSCy Schubert *ep = xp; 15922d4e511cSCy Schubert DPRINTF(1, ("nmea: invalid date code: '%.*s'\n", 15932d4e511cSCy Schubert field_length(xp, 3), xp)); 15942d4e511cSCy Schubert } 15952d4e511cSCy Schubert return rc; 15962d4e511cSCy Schubert } 15972d4e511cSCy Schubert 15982d4e511cSCy Schubert /* 15992d4e511cSCy Schubert * ------------------------------------------------------------------- 16002b15cb3dSCy Schubert * Check sync status 16012b15cb3dSCy Schubert * 16022b15cb3dSCy Schubert * If the character at the data field start matches the tag value, 16032b15cb3dSCy Schubert * return LEAP_NOWARNING and LEAP_NOTINSYNC otherwise. If the 'inverted' 16042b15cb3dSCy Schubert * flag is given, just the opposite value is returned. If there is no 16052b15cb3dSCy Schubert * data field (*cp points to the NUL byte) the result is LEAP_NOTINSYNC. 16062b15cb3dSCy Schubert * ------------------------------------------------------------------- 16072b15cb3dSCy Schubert */ 16082b15cb3dSCy Schubert static u_char 16092b15cb3dSCy Schubert parse_qual( 16102b15cb3dSCy Schubert nmea_data * rd, 16112b15cb3dSCy Schubert int idx, 16122b15cb3dSCy Schubert char tag, 16132b15cb3dSCy Schubert int inv 16142b15cb3dSCy Schubert ) 16152b15cb3dSCy Schubert { 16162d4e511cSCy Schubert static const u_char table[2] = { 16172d4e511cSCy Schubert LEAP_NOTINSYNC, LEAP_NOWARNING }; 16182b15cb3dSCy Schubert 16192d4e511cSCy Schubert char * dp = field_parse(rd, idx); 16202b15cb3dSCy Schubert 16212b15cb3dSCy Schubert return table[ *dp && ((*dp == tag) == !inv) ]; 16222b15cb3dSCy Schubert } 16232b15cb3dSCy Schubert 16242b15cb3dSCy Schubert /* 16252b15cb3dSCy Schubert * ------------------------------------------------------------------- 16262b15cb3dSCy Schubert * Parse a time stamp in HHMMSS[.sss] format with error checking. 16272b15cb3dSCy Schubert * 16282b15cb3dSCy Schubert * returns 1 on success, 0 on failure 16292b15cb3dSCy Schubert * ------------------------------------------------------------------- 16302b15cb3dSCy Schubert */ 16312b15cb3dSCy Schubert static int 16322b15cb3dSCy Schubert parse_time( 16332b15cb3dSCy Schubert struct calendar * jd, /* result calendar pointer */ 16342d4e511cSCy Schubert l_fp * fofs, /* storage for nsec fraction */ 16352b15cb3dSCy Schubert nmea_data * rd, 16362b15cb3dSCy Schubert int idx 16372b15cb3dSCy Schubert ) 16382b15cb3dSCy Schubert { 16392d4e511cSCy Schubert UCC * dp = (UCC*)field_parse(rd, idx); 16402b15cb3dSCy Schubert 16412d4e511cSCy Schubert return _parse_time(dp, &dp, jd) 16422d4e511cSCy Schubert && _parse_frac(dp, &dp, fofs) 16432d4e511cSCy Schubert && _parse_eof (dp, &dp); 16442b15cb3dSCy Schubert } 16452b15cb3dSCy Schubert 16462b15cb3dSCy Schubert /* 16472b15cb3dSCy Schubert * ------------------------------------------------------------------- 16482b15cb3dSCy Schubert * Parse a date string from an NMEA sentence. This could either be a 16492b15cb3dSCy Schubert * partial date in DDMMYY format in one field, or DD,MM,YYYY full date 16502b15cb3dSCy Schubert * spec spanning three fields. This function does some extensive error 16512b15cb3dSCy Schubert * checking to make sure the date string was consistent. 16522b15cb3dSCy Schubert * 16532b15cb3dSCy Schubert * returns 1 on success, 0 on failure 16542b15cb3dSCy Schubert * ------------------------------------------------------------------- 16552b15cb3dSCy Schubert */ 16562b15cb3dSCy Schubert static int 16572b15cb3dSCy Schubert parse_date( 16582b15cb3dSCy Schubert struct calendar * jd, /* result pointer */ 16592b15cb3dSCy Schubert nmea_data * rd, 16602b15cb3dSCy Schubert int idx, 16612b15cb3dSCy Schubert enum date_fmt fmt 16622b15cb3dSCy Schubert ) 16632b15cb3dSCy Schubert { 16642d4e511cSCy Schubert UCC * dp = (UCC*)field_parse(rd, idx); 16652b15cb3dSCy Schubert 16662b15cb3dSCy Schubert switch (fmt) { 16672b15cb3dSCy Schubert case DATE_1_DDMMYY: 16682d4e511cSCy Schubert return _parse_date1(dp, &dp, jd); 16692b15cb3dSCy Schubert case DATE_3_DDMMYYYY: 16702d4e511cSCy Schubert return _parse_date3(dp, &dp, jd); 16712b15cb3dSCy Schubert default: 16722b15cb3dSCy Schubert DPRINTF(1, ("nmea: invalid parse format: %d\n", fmt)); 16732d4e511cSCy Schubert break; 16742b15cb3dSCy Schubert } 16752b15cb3dSCy Schubert return FALSE; 16762b15cb3dSCy Schubert } 16772b15cb3dSCy Schubert 16782b15cb3dSCy Schubert /* 16792b15cb3dSCy Schubert * ------------------------------------------------------------------- 16802b15cb3dSCy Schubert * Parse GPS week time info from an NMEA sentence. This info contains 16812b15cb3dSCy Schubert * the GPS week number, the GPS time-of-week and the leap seconds GPS 16822b15cb3dSCy Schubert * to UTC. 16832b15cb3dSCy Schubert * 16842b15cb3dSCy Schubert * returns 1 on success, 0 on failure 16852b15cb3dSCy Schubert * ------------------------------------------------------------------- 16862b15cb3dSCy Schubert */ 16872b15cb3dSCy Schubert static int 16882d4e511cSCy Schubert parse_gpsw( 16892d4e511cSCy Schubert TGpsDatum * wd, 16902b15cb3dSCy Schubert nmea_data * rd, 16912b15cb3dSCy Schubert int weekidx, 16922b15cb3dSCy Schubert int timeidx, 16932b15cb3dSCy Schubert int leapidx 16942b15cb3dSCy Schubert ) 16952b15cb3dSCy Schubert { 16962d4e511cSCy Schubert uint32_t secs; 16972d4e511cSCy Schubert uint16_t week, leap = 0; 16982d4e511cSCy Schubert l_fp fofs; 16992d4e511cSCy Schubert int rc; 17002b15cb3dSCy Schubert 17012d4e511cSCy Schubert UCC * dpw = (UCC*)field_parse(rd, weekidx); 17022d4e511cSCy Schubert UCC * dps = (UCC*)field_parse(rd, timeidx); 17032d4e511cSCy Schubert 17042d4e511cSCy Schubert rc = _parse_u16 (dpw, &dpw, &week, 5) 17052d4e511cSCy Schubert && _parse_eof (dpw, &dpw) 17062d4e511cSCy Schubert && _parse_u32 (dps, &dps, &secs, 9) 17072d4e511cSCy Schubert && _parse_frac(dps, &dps, &fofs) 17082d4e511cSCy Schubert && _parse_eof (dps, &dps) 17092d4e511cSCy Schubert && (secs < 7*SECSPERDAY); 17102d4e511cSCy Schubert if (rc && leapidx > 0) { 17112d4e511cSCy Schubert UCC * dpl = (UCC*)field_parse(rd, leapidx); 17122d4e511cSCy Schubert rc = _parse_u16 (dpl, &dpl, &leap, 5) 17132d4e511cSCy Schubert && _parse_eof (dpl, &dpl); 17142b15cb3dSCy Schubert } 17152d4e511cSCy Schubert if (rc) { 17162d4e511cSCy Schubert fofs.l_ui -= leap; 17172d4e511cSCy Schubert *wd = gpscal_from_gpsweek(week, secs, fofs); 17182d4e511cSCy Schubert } else { 17192d4e511cSCy Schubert DPRINTF(1, ("nmea: parse_gpsw: invalid weektime spec\n")); 17202d4e511cSCy Schubert } 17212d4e511cSCy Schubert return rc; 17222b15cb3dSCy Schubert } 17232b15cb3dSCy Schubert 17242d4e511cSCy Schubert 17252d4e511cSCy Schubert #ifdef HAVE_PPSAPI 17262d4e511cSCy Schubert static double 17272d4e511cSCy Schubert tabsdiffd( 17282d4e511cSCy Schubert l_fp t1, 17292d4e511cSCy Schubert l_fp t2 17302b15cb3dSCy Schubert ) 17312b15cb3dSCy Schubert { 17322d4e511cSCy Schubert double dd; 17332d4e511cSCy Schubert L_SUB(&t1, &t2); 17342d4e511cSCy Schubert LFPTOD(&t1, dd); 17352d4e511cSCy Schubert return fabs(dd); 17362b15cb3dSCy Schubert } 17372d4e511cSCy Schubert #endif /* HAVE_PPSAPI */ 17382b15cb3dSCy Schubert 17392b15cb3dSCy Schubert /* 17402b15cb3dSCy Schubert * =================================================================== 17412b15cb3dSCy Schubert * 17422b15cb3dSCy Schubert * NMEAD support 17432b15cb3dSCy Schubert * 17442b15cb3dSCy Schubert * original nmead support added by Jon Miner (cp_n18@yahoo.com) 17452b15cb3dSCy Schubert * 17462b15cb3dSCy Schubert * See http://home.hiwaay.net/~taylorc/gps/nmea-server/ 17472b15cb3dSCy Schubert * for information about nmead 17482b15cb3dSCy Schubert * 17492b15cb3dSCy Schubert * To use this, you need to create a link from /dev/gpsX to 17502b15cb3dSCy Schubert * the server:port where nmead is running. Something like this: 17512b15cb3dSCy Schubert * 17522b15cb3dSCy Schubert * ln -s server:port /dev/gps1 17532b15cb3dSCy Schubert * 17542b15cb3dSCy Schubert * Split into separate function by Juergen Perlinger 17552b15cb3dSCy Schubert * (perlinger-at-ntp-dot-org) 17562b15cb3dSCy Schubert * 17572b15cb3dSCy Schubert * =================================================================== 17582b15cb3dSCy Schubert */ 17592b15cb3dSCy Schubert static int 17602b15cb3dSCy Schubert nmead_open( 17612b15cb3dSCy Schubert const char * device 17622b15cb3dSCy Schubert ) 17632b15cb3dSCy Schubert { 17642b15cb3dSCy Schubert int fd = -1; /* result file descriptor */ 17652b15cb3dSCy Schubert 17662b15cb3dSCy Schubert # ifdef HAVE_READLINK 17672b15cb3dSCy Schubert char host[80]; /* link target buffer */ 17682b15cb3dSCy Schubert char * port; /* port name or number */ 17692b15cb3dSCy Schubert int rc; /* result code (several)*/ 17702b15cb3dSCy Schubert int sh; /* socket handle */ 17712b15cb3dSCy Schubert struct addrinfo ai_hint; /* resolution hint */ 17722b15cb3dSCy Schubert struct addrinfo *ai_list; /* resolution result */ 17732b15cb3dSCy Schubert struct addrinfo *ai; /* result scan ptr */ 17742b15cb3dSCy Schubert 17752b15cb3dSCy Schubert fd = -1; 17762b15cb3dSCy Schubert 17772b15cb3dSCy Schubert /* try to read as link, make sure no overflow occurs */ 17782b15cb3dSCy Schubert rc = readlink(device, host, sizeof(host)); 17792b15cb3dSCy Schubert if ((size_t)rc >= sizeof(host)) 17802b15cb3dSCy Schubert return fd; /* error / overflow / truncation */ 17812b15cb3dSCy Schubert host[rc] = '\0'; /* readlink does not place NUL */ 17822b15cb3dSCy Schubert 17832b15cb3dSCy Schubert /* get port */ 17842b15cb3dSCy Schubert port = strchr(host, ':'); 17852b15cb3dSCy Schubert if (!port) 17862b15cb3dSCy Schubert return fd; /* not 'host:port' syntax ? */ 17872b15cb3dSCy Schubert *port++ = '\0'; /* put in separator */ 17882b15cb3dSCy Schubert 17892b15cb3dSCy Schubert /* get address infos and try to open socket 17902b15cb3dSCy Schubert * 17912b15cb3dSCy Schubert * This getaddrinfo() is naughty in ntpd's nonblocking main 17922b15cb3dSCy Schubert * thread, but you have to go out of your wary to use this code 17932b15cb3dSCy Schubert * and typically the blocking is at startup where its impact is 17942b15cb3dSCy Schubert * reduced. The same holds for the 'connect()', as it is 17952b15cb3dSCy Schubert * blocking, too... 17962b15cb3dSCy Schubert */ 17972b15cb3dSCy Schubert ZERO(ai_hint); 17982b15cb3dSCy Schubert ai_hint.ai_protocol = IPPROTO_TCP; 17992b15cb3dSCy Schubert ai_hint.ai_socktype = SOCK_STREAM; 18002b15cb3dSCy Schubert if (getaddrinfo(host, port, &ai_hint, &ai_list)) 18012b15cb3dSCy Schubert return fd; 18022b15cb3dSCy Schubert 18032b15cb3dSCy Schubert for (ai = ai_list; ai && (fd == -1); ai = ai->ai_next) { 18042b15cb3dSCy Schubert sh = socket(ai->ai_family, ai->ai_socktype, 18052b15cb3dSCy Schubert ai->ai_protocol); 18062b15cb3dSCy Schubert if (INVALID_SOCKET == sh) 18072b15cb3dSCy Schubert continue; 18082b15cb3dSCy Schubert rc = connect(sh, ai->ai_addr, ai->ai_addrlen); 18092b15cb3dSCy Schubert if (-1 != rc) 18102b15cb3dSCy Schubert fd = sh; 18112b15cb3dSCy Schubert else 18122b15cb3dSCy Schubert close(sh); 18132b15cb3dSCy Schubert } 18142b15cb3dSCy Schubert freeaddrinfo(ai_list); 18152d4e511cSCy Schubert if (fd != -1) 18162d4e511cSCy Schubert make_socket_nonblocking(fd); 18172b15cb3dSCy Schubert # else 18182b15cb3dSCy Schubert fd = -1; 18192b15cb3dSCy Schubert # endif 18202b15cb3dSCy Schubert 18212b15cb3dSCy Schubert return fd; 1822c0b746e5SOllivier Robert } 1823c0b746e5SOllivier Robert #else 18242b15cb3dSCy Schubert NONEMPTY_TRANSLATION_UNIT 18252b15cb3dSCy Schubert #endif /* REFCLOCK && CLOCK_NMEA */ 1826