diff -ruN ntp-4.2.4p4.orig/configure.ac ntp-4.2.4p4/configure.ac --- ntp-4.2.4p4.orig/configure.ac 2006-12-28 06:01:44.000000000 -0600 +++ ntp-4.2.4p4/configure.ac 2008-07-06 17:18:30.000000000 -0500 @@ -2405,6 +2405,16 @@ fi AC_MSG_RESULT($ntp_ok) +AC_MSG_CHECKING(Fury GPS) +AC_ARG_ENABLE(FURY, + AC_HELP_STRING([--enable-FURY], [+ Fury GPS]), + [ntp_ok=$enableval], [ntp_ok=$ntp_eac]) +if test "$ntp_ok" = "yes"; then + ntp_refclock=yes + AC_DEFINE(CLOCK_FURY, 1, [Fury GPS Receiver?]) +fi +AC_MSG_RESULT($ntp_ok) + # Requires modem control AC_MSG_CHECKING(Heath GC-1000 WWV/WWVH receiver) AC_ARG_ENABLE(HEATH, diff -ruN ntp-4.2.4p4.orig/include/ntp.h ntp-4.2.4p4/include/ntp.h --- ntp-4.2.4p4.orig/include/ntp.h 2006-12-28 06:03:04.000000000 -0600 +++ ntp-4.2.4p4/include/ntp.h 2008-07-06 17:19:03.000000000 -0500 @@ -501,7 +501,8 @@ #define REFCLK_ZYFER 42 /* Zyfer GPStarplus receiver */ #define REFCLK_RIPENCC 43 /* RIPE NCC Trimble driver */ #define REFCLK_NEOCLOCK4X 44 /* NeoClock4X DCF77 or TDF receiver */ -#define REFCLK_MAX 44 /* NeoClock4X DCF77 or TDF receiver */ +#define REFCLK_FURY 45 /* Fury GPS receiver */ +#define REFCLK_MAX 45 /* Fury GPS receiver */ /* * Macro for sockaddr_storage structures operations diff -ruN ntp-4.2.4p4.orig/libntp/clocktypes.c ntp-4.2.4p4/libntp/clocktypes.c --- ntp-4.2.4p4.orig/libntp/clocktypes.c 2006-06-06 15:16:26.000000000 -0500 +++ ntp-4.2.4p4/libntp/clocktypes.c 2008-07-06 17:18:30.000000000 -0500 @@ -100,6 +100,8 @@ "GPS_RIPENCC" }, { REFCLK_NEOCLOCK4X, "NeoClock4X DCF77 / TDF receiver (44)", "NEOCLK4X"}, + { REFCLK_FURY, "Fury GPS receiver (45)", + "GPS_FURY"}, { -1, "", "" } }; diff -ruN ntp-4.2.4p4.orig/ntpd/Makefile.am ntp-4.2.4p4/ntpd/Makefile.am --- ntp-4.2.4p4.orig/ntpd/Makefile.am 2007-04-14 06:06:59.000000000 -0500 +++ ntp-4.2.4p4/ntpd/Makefile.am 2008-07-06 17:18:30.000000000 -0500 @@ -59,7 +59,7 @@ refclock_fg.c refclock_gpsvme.c refclock_heath.c refclock_hopfser.c \ refclock_hopfpci.c refclock_hpgps.c refclock_irig.c refclock_jjy.c \ refclock_jupiter.c refclock_leitch.c refclock_local.c \ - refclock_mx4200.c refclock_neoclock4x.c \ + refclock_mx4200.c refclock_neoclock4x.c refclock_fury.c \ refclock_nmea.c refclock_oncore.c refclock_palisade.c \ refclock_palisade.h refclock_parse.c \ refclock_pcf.c refclock_pst.c refclock_ripencc.c refclock_shm.c \ diff -ruN ntp-4.2.4p4.orig/ntpd/ntp_control.c ntp-4.2.4p4/ntpd/ntp_control.c --- ntp-4.2.4p4.orig/ntpd/ntp_control.c 2006-12-28 06:03:27.000000000 -0600 +++ ntp-4.2.4p4/ntpd/ntp_control.c 2008-07-06 17:18:30.000000000 -0500 @@ -406,6 +406,7 @@ CTL_SST_TS_UHF, /* REFCLK_ZYFER (42) */ CTL_SST_TS_UHF, /* REFCLK_RIPENCC (43) */ CTL_SST_TS_UHF, /* REFCLK_NEOCLOCK4X (44) */ + CTL_SST_TS_UHF, /* REFCLK_FURY (45) */ }; diff -ruN ntp-4.2.4p4.orig/ntpd/refclock_conf.c ntp-4.2.4p4/ntpd/refclock_conf.c --- ntp-4.2.4p4.orig/ntpd/refclock_conf.c 2006-12-28 06:03:44.000000000 -0600 +++ ntp-4.2.4p4/ntpd/refclock_conf.c 2008-07-06 17:18:30.000000000 -0500 @@ -258,6 +258,12 @@ #define refclock_neoclock4x refclock_none #endif +#ifdef CLOCK_FURY +extern struct refclock refclock_fury; +#else +#define refclock_fury refclock_none +#endif + /* * Order is clock_start(), clock_shutdown(), clock_poll(), * clock_control(), clock_init(), clock_buginfo, clock_flags; @@ -309,7 +315,8 @@ &refclock_tt560, /* 41 REFCLK_TT560 */ &refclock_zyfer, /* 42 REFCLK_ZYFER */ &refclock_ripencc, /* 43 REFCLK_RIPENCC */ - &refclock_neoclock4x /* 44 REFCLK_NEOCLOCK4X */ + &refclock_neoclock4x, /* 44 REFCLK_NEOCLOCK4X */ + &refclock_fury /* 45 REFCLK_FURY */ }; u_char num_refclock_conf = sizeof(refclock_conf)/sizeof(struct refclock *); diff -ruN ntp-4.2.4p4.orig/ntpd/refclock_fury.c ntp-4.2.4p4/ntpd/refclock_fury.c --- ntp-4.2.4p4.orig/ntpd/refclock_fury.c 1969-12-31 18:00:00.000000000 -0600 +++ ntp-4.2.4p4/ntpd/refclock_fury.c 2008-07-06 21:46:07.000000000 -0500 @@ -0,0 +1,600 @@ +/* + * refclock_fury.c - clock driver for an FURY GPS CLOCK + * Scott Mace 6/20/2008 + * based on refclock_nmea.c + */ +#ifdef HAVE_CONFIG_H +#include +#endif + +#if defined(SYS_WINNT) +#undef close +#define close closesocket +#endif + +#if defined(REFCLOCK) && defined(CLOCK_FURY) + + +#include +#include + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_unixtime.h" +#include "ntp_refclock.h" +#include "ntp_stdlib.h" + +/* + * This driver supports the FURY GPS Receiver with + * + * Based on refclock_nmea.c, some code from reflock_hpgps.c + * + * The Fury supports a partial SCPI command set, which is useful + * for gather various health and performance stats + * The only on-time marker is the NMEA GPGGA sentence that is generated + * once per second when the Fury is placed in NMEA mode + * + * Other interesting data is collected from GPS?,SYNC?,DIAG?,and MEAS? + * + */ + +/* + * Definitions + */ +#ifdef SYS_WINNT +# define DEVICE "COM%d:" /* COM 1 - 3 supported */ +#else +# define DEVICE "/dev/gps%d" /* name of radio device */ +#endif +#define SPEED232 B115200 /* uart speed (115200 bps) */ +#define PRECISION (-9) /* precision assumed (about 2 ms) */ +#define PPS_PRECISION (-20) /* precision assumed (about 1 us) */ +#define REFID "GPS\0" /* reference id */ +#define DESCRIPTION "FURY GPS Clock" /* who we are */ +#define NANOSECOND 1000000000 /* one second (ns) */ +#define RANGEGATE 500000 /* range gate (ns) */ + +#define LENNMEA 75 /* min timecode length */ + +/* + * Tables to compute the ddd of year form icky dd/mm timecode. Viva la + * leap. + */ +static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +/* + * Unit control structure + */ +struct furyunit { + int pollcnt; /* poll message counter */ + int polled; /* Hand in a sample? */ + l_fp tstamp; /* timestamp of last poll */ + int tscnt; + float stat_efc; + unsigned long stat_life; + int stat_ppslock; + unsigned long stat_holddur; + int stat_holdover; + float stat_fee; + float stat_tint; + float stat_temp; + float stat_amp; + float stat_volt; + int stat_track; + int stat_vis; + int stat_pulsestat; + int stat_pulseacc; + int stat_pulsesaw; +}; + +/* + * Function prototypes + */ +static int fury_start P((int, struct peer *)); +static void fury_shutdown P((int, struct peer *)); +static void fury_receive P((struct recvbuf *)); +static void fury_poll P((int, struct peer *)); +static void gps_send P((int, const char *, struct peer *)); +static char *field_parse P((char *, int)); + +/* + * Transfer vector + */ +struct refclock refclock_fury = { + fury_start, /* start up driver */ + fury_shutdown, /* shut down driver */ + fury_poll, /* transmit poll message */ + noentry, /* fudge control */ + noentry, /* initialize driver */ + noentry, /* buginfo */ + NOFLAGS /* not used */ +}; + +/* + * fury_start - open the GPS devices and initialize data for processing + */ +static int +fury_start( + int unit, + struct peer *peer + ) +{ + register struct furyunit *up; + struct refclockproc *pp; + int fd; + char device[20]; + + /* + * Open serial port. Use CLK line discipline, if available. + */ + (void)sprintf(device, DEVICE, unit); + + fd = refclock_open(device, SPEED232, LDISC_CLK); + if (fd <= 0) { + return (0); + } + + /* + * Allocate and initialize unit structure + */ + up = (struct furyunit *)emalloc(sizeof(struct furyunit)); + if (up == NULL) { + (void) close(fd); + return (0); + } + memset((char *)up, 0, sizeof(struct furyunit)); + pp = peer->procptr; + pp->io.clock_recv = fury_receive; + pp->io.srcclock = (caddr_t)peer; + pp->io.datalen = 0; + pp->io.fd = fd; + if (!io_addclock(&pp->io)) { + (void) close(fd); + free(up); + return (0); + } + pp->unitptr = (caddr_t)up; + + /* + * Initialize miscellaneous variables + */ + peer->precision = PRECISION; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, REFID, 4); + up->pollcnt = 2; + up->tscnt=0; + + /* turn off command echo */ + gps_send(pp->io.fd,"SYST:COMM:SER:ECHO OFF\r", peer); + + /* turn off prompt */ + gps_send(pp->io.fd,"SYST:COMM:SER:PRO OFF\r", peer); + + /* query the receiver ID */ + gps_send(pp->io.fd,"*IDN?\r", peer); + + /* place the fury in NMEA mode */ + gps_send(pp->io.fd,"GPS:GPGGA 1\r", peer); + + return (1); +} + +/* + * fury_shutdown - shut down a GPS clock + */ +static void +fury_shutdown( + int unit, + struct peer *peer + ) +{ + register struct furyunit *up; + struct refclockproc *pp; + + pp = peer->procptr; + up = (struct furyunit *)pp->unitptr; + io_closeclock(&pp->io); + free(up); +} + +/* + * fury_receive - receive data from the serial interface + */ +static void +fury_receive( + struct recvbuf *rbufp + ) +{ + register struct furyunit *up; + struct refclockproc *pp; + struct peer *peer; + int month, day; + int i,m; + char *cp, *dp; + char *tcp; + int cmdtype; + /* Use these variables to hold data until we decide its worth keeping */ + char rd_lastcode[BMAX]; + l_fp rd_tmp; + u_short rd_lencode; + static char logbuf[1024]; /* logging string buffer */ + + + /* + * Initialize pointers and read the timecode and timestamp + */ + peer = (struct peer *)rbufp->recv_srcclock; + pp = peer->procptr; + up = (struct furyunit *)pp->unitptr; + rd_lencode = (u_short)refclock_gtlin(rbufp, rd_lastcode, BMAX, &rd_tmp); + + /* + * There is a case that a gives back a "blank" line + */ + if (rd_lencode == 0) + return; + +#ifdef DEBUG + if (debug) + printf("fury: gpsread %d %s\n", rd_lencode, rd_lastcode); +#endif + + pp->lencode = rd_lencode; + strcpy(pp->a_lastcode,rd_lastcode); + cp = pp->a_lastcode; + + pp->lastrec = up->tstamp = rd_tmp; + up->pollcnt = 2; + + + + /* clean off a leading "scpi > " if needed */ + if (strrchr(pp->a_lastcode,'>')==0) { + tcp = cp; + } else { + tcp = cp+7; + } + + /* do not process null lines after prompt stripping */ + if (strlen(tcp)==0) { + return; + } + + /* handle command errors */ + if (strstr(tcp,"Command Error")){ +#ifdef DEBUG + if (debug) + printf("fury: error indicated in prompt: %s\n", tcp); +#endif + if (write(pp->io.fd, "*CLS\r\r", 6) != 6) + refclock_report(peer, CEVNT_FAULT); + } + + if(strncmp(tcp,"$GPGGA",6)==0) { + up->tscnt++; + dp = field_parse(tcp,1); + } else if (strncmp(tcp,"EFControl Absolute:",19)==0) { + m = sscanf(tcp,"EFControl Absolute: %f",&up->stat_efc); + return; + } else if (strncmp(tcp,"Lifetime :",10)==0) { + m = sscanf(tcp,"Lifetime : %lu",&up->stat_life); + return; + } else if (strncmp(tcp,"1PPS LOCK STATUS :",19)==0) { + m = sscanf(tcp,"1PPS LOCK STATUS : %d",&up->stat_ppslock); + return; + } else if (strncmp(tcp,"LAST HOLDOVER DURATION",22)==0) { + m = sscanf(tcp,"LAST HOLDOVER DURATION %lu,%d", + &up->stat_holddur,&up->stat_holdover); + return; + } else if (strncmp(tcp,"FREQ ERROR ESTIMATE",19)==0) { + m = sscanf(tcp,"FREQ ERROR ESTIMATE %f",&up->stat_fee); + return; + } else if (strncmp(tcp,"TIME INTERVAL DIFFERENCE",24)==0) { + m = sscanf(tcp,"TIME INTERVAL DIFFERENCE %f",&up->stat_tint); + return; + } else if (strncmp(tcp,"OCXO Temperature:",16)==0) { + m = sscanf(tcp,"OCXO Temperature: %f",&up->stat_temp); + return; + } else if (strncmp(tcp,"OCXO Current:",13)==0) { + m = sscanf(tcp,"OCXO Current: %f",&up->stat_amp); + return; + } else if (strncmp(tcp,"OCXO Voltage:",13)==0) { + m = sscanf(tcp,"OCXO Voltage: %f",&up->stat_volt); + return; + } else if (strncmp(tcp,"TRACKED SATS :",14)==0) { + m = sscanf(tcp,"TRACKED SATS : %d",&up->stat_track); + return; + } else if (strncmp(tcp,"VISIBLE SATS :",14)==0) { + m = sscanf(tcp,"VISIBLE SATS : %d",&up->stat_vis); + return; + } else if (strncmp(tcp,"PULSE STATUS:",13)==0) { + m = sscanf(tcp,"PULSE STATUS:%d",&up->stat_pulsestat); + return; + } else if (strncmp(tcp,"PULSE ACCURACY:",15)==0) { + m = sscanf(tcp,"PULSE ACCURACY:%d",&up->stat_pulseacc); + return; + } else if (strncmp(tcp,"PULSE SAWTOOTH:",15)==0) { + m = sscanf(tcp,"PULSE SAWTOOTH:%d",&up->stat_pulsesaw); + return; + } else if (strncmp(tcp,"59",2)==0) { + pp->leap = LEAP_DELSECOND; +#ifdef DEBUG + if (debug) + printf("fury: leap del pending\n"); +#endif + return; + } else if (strncmp(tcp,"60",2)==0) { + pp->leap = LEAP_NOWARNING; +#ifdef DEBUG + if (debug) + printf("fury: leap no warning\n"); +#endif + return; + } else if (strncmp(tcp,"61",2)==0) { + pp->leap = LEAP_ADDSECOND; +#ifdef DEBUG + if (debug) + printf("fury: leap add pending\n"); +#endif + return; + } else if (strncmp(tcp,"Jackson",7)==0) { + sprintf(logbuf, "IDN: %s", tcp); + record_clock_stats(&peer->srcadr, logbuf); + return; + } + else { + /* unwanted response */ +#ifdef DEBUG + if (debug) + printf("fury: unknown code %d %s\n", strlen(tcp),tcp); +#endif + return; + } + +#ifdef DEBUG + if (debug) + printf("fury: timecode %d %s\n", strlen(tcp),tcp); +#endif + + + /* + * Check time code format of NMEA + */ + + if( !isdigit((int)dp[0]) || + !isdigit((int)dp[1]) || + !isdigit((int)dp[2]) || + !isdigit((int)dp[3]) || + !isdigit((int)dp[4]) || + !isdigit((int)dp[5]) + ) { + refclock_report(peer, CEVNT_BADREPLY); + return; + } + + /* initial holdover */ + if ((up->stat_ppslock == 1) && (up->stat_holdover == 1) && (up->stat_holddur <= 86400)) { + sprintf(logbuf, "HOLDOVER INITIAL ppslock: %d holdstate: %d holddur: %lu", + up->stat_ppslock, up->stat_holdover, up->stat_holddur); + record_clock_stats(&peer->srcadr, logbuf); + } + + /* holdover threshold exceeded */ + if ((up->stat_holdover == 1) && (up->stat_holddur > 86400)) { + pp->leap = LEAP_NOTINSYNC; + sprintf(logbuf, "HOLDOVER EXCEEDED NOTINSYNC ppslock: %d holdstate: %d holddur: %lu", + up->stat_ppslock, up->stat_holdover, up->stat_holddur); + record_clock_stats(&peer->srcadr, logbuf); + } + + /* check if we are unlocked */ + if (up->stat_ppslock == 0) { + /* check if we are in holdover recovery */ + if ((up->stat_holddur > 0) && (up->stat_pulsestat == 1)) { + sprintf(logbuf, "HOLDOVER RECOVERY ppslock: %d holdstate: %d holddur: %lu", + up->stat_ppslock, up->stat_holdover, up->stat_holddur); + record_clock_stats(&peer->srcadr, logbuf); + /* check if we are in normal holdover */ + } else if ((up->stat_holdover == 1) && (up->stat_holddur <= 86400)) { + sprintf(logbuf, "HOLDOVER ppslock: %d holdstate: %d holddur: %lu", + up->stat_ppslock, up->stat_holdover, up->stat_holddur); + record_clock_stats(&peer->srcadr, logbuf); + /* we are not synced at all */ + } else { + pp->leap = LEAP_NOTINSYNC; + sprintf(logbuf, "NOTINSYNC ppslock: %d holdstate: %d holddur: %lu", + up->stat_ppslock, up->stat_holdover, up->stat_holddur); + record_clock_stats(&peer->srcadr, logbuf); + } + } + + + /* + * Convert time and check values. + */ + pp->hour = ((dp[0] - '0') * 10) + dp[1] - '0'; + pp->minute = ((dp[2] - '0') * 10) + dp[3] - '0'; + pp->second = ((dp[4] - '0') * 10) + dp[5] - '0'; + /* Default to 0 milliseconds, if decimal convert milliseconds in + one, two or three digits + */ + pp->nsec = 0; + if (dp[6] == '.') { + if (isdigit((int)dp[7])) { + pp->nsec = (dp[7] - '0') * 100000000; + if (isdigit((int)dp[8])) { + pp->nsec += (dp[8] - '0') * 10000000; + if (isdigit((int)dp[9])) { + pp->nsec += (dp[9] - '0') * 1000000; + } + } + } + } + + if (pp->hour > 23 || pp->minute > 59 || pp->second > 59 + || pp->nsec > 1000000000) { + refclock_report(peer, CEVNT_BADTIME); + return; + } + + + /* + * Convert date and check values. + */ + /* only time */ + time_t tt = time(NULL); + struct tm * t = gmtime(&tt); + day = t->tm_mday; + month = t->tm_mon + 1; + pp->year= t->tm_year; + + if (month < 1 || month > 12 || day < 1) { + refclock_report(peer, CEVNT_BADTIME); + return; + } + + /* Hmmmm this will be a nono for 2100,2200,2300 but I don't think I'll be here */ + /* good thing that 2000 is a leap year */ + /* pp->year will be 00-99 if read from GPS, 00-> (years since 1900) from tm_year */ + if (pp->year % 4) { + if (day > day1tab[month - 1]) { + refclock_report(peer, CEVNT_BADTIME); + return; + } + for (i = 0; i < month - 1; i++) + day += day1tab[i]; + } else { + if (day > day2tab[month - 1]) { + refclock_report(peer, CEVNT_BADTIME); + return; + } + for (i = 0; i < month - 1; i++) + day += day2tab[i]; + } + pp->day = day; + + /* + * Process the new sample in the median filter and determine the + * reference clock offset and dispersion. We use lastrec as both + * the reference time and receive time, in order to avoid being + * cute, like setting the reference time later than the receive + * time, which may cause a paranoid protocol module to chuck out + * the data. + */ + + if (!refclock_process(pp)) { + refclock_report(peer, CEVNT_BADTIME); + return; + } + + /* + * Only go on if we had been polled. + */ + if (up->polled) { + up->polled = 0; + pp->lastref = pp->lastrec; + refclock_receive(peer); + + /* If we get here - what we got from the clock is OK, so say so */ + refclock_report(peer, CEVNT_NOMINAL); + + if (pp->sloppyclockflag & CLK_FLAG4){ + record_clock_stats(&peer->srcadr, tcp); + } + } + + if (pp->sloppyclockflag & CLK_FLAG3){ + if ((up->tscnt == 1) && (up->stat_efc > 0)) { + sprintf(logbuf, "%lu %d %lu %d %.2e %.2e %.2f %f %f %.2f %d %d %d %d %d", + up->stat_life, up->stat_ppslock, up->stat_holddur, + up->stat_holdover, up->stat_fee, up->stat_tint, up->stat_temp, + up->stat_efc, up->stat_amp, up->stat_volt, up->stat_track, + up->stat_vis, up->stat_pulsestat, up->stat_pulseacc, up->stat_pulsesaw); + record_clock_stats(&peer->srcadr, logbuf); + } + if (up->tscnt == 10) { + gps_send(pp->io.fd,"DIAG?\r", peer); + gps_send(pp->io.fd,"MEAS?\r", peer); + up->tscnt=0; + } + } + + /* send these for holdover parsing */ + gps_send(pp->io.fd,"SYNC?\r", peer); + gps_send(pp->io.fd,"GPS?\r", peer); + +} + +/* + * fury_poll - called by the transmit procedure + * + * We go to great pains to avoid changing state here, since there may be + * more than one eavesdropper receiving the same timecode. + */ +static void +fury_poll( + int unit, + struct peer *peer + ) +{ + register struct furyunit *up; + struct refclockproc *pp; + + pp = peer->procptr; + up = (struct furyunit *)pp->unitptr; + if (up->pollcnt == 0) + refclock_report(peer, CEVNT_TIMEOUT); + else + up->pollcnt--; + pp->polls++; + up->polled = 1; + + /* + * send cmd to check for leap second + * 59 del + * 60 nowarn + * 61 add + */ + gps_send(pp->io.fd,"PTIME:LEAP:DUR?\r", peer); +} + +/* + * + * gps_send(fd,cmd, peer) Sends a command to the GPS receiver. + * + */ +static void +gps_send( + int fd, + const char *cmd, + struct peer *peer + ) +{ + + if (write(fd, cmd, strlen(cmd)) == -1) { + refclock_report(peer, CEVNT_FAULT); + } + usleep(50); +} + +static char * +field_parse( + char *cp, + int fn + ) +{ + char *tp; + int i = fn; + + for (tp = cp; *tp != '\0'; tp++) { + if (*tp == ',') + i--; + if (i == 0) + break; + } + return (++tp); +} +#else +int refclock_fury_bs; +#endif /* REFCLOCK */