[Git][NTPsec/ntpsec][master] 5 commits: Squish compiler warning

Hal Murray (@hal.murray) gitlab at mg.gitlab.com
Sat Aug 23 22:16:11 UTC 2025



Hal Murray pushed to branch master at NTPsec / ntpsec


Commits:
386e8c76 by Hal Murray at 2025-08-12T00:43:04-07:00
Squish compiler warning

A maybe-uninitialized that showed up with -O3

- - - - -
5dd22f1b by Hal Murray at 2025-08-21T23:32:27-07:00
Fix sandbox to build on Raspbian 9 (stretch)

- - - - -
ad76338d by Hal Murray at 2025-08-21T23:45:15-07:00
move fix_WNRO so hpgps driver can use it

no functional changes

- - - - -
0539c4c9 by Hal Murray at 2025-08-21T23:54:07-07:00
Major update to HPGPS driver

  Fix gps WNRO glitch in Z3801A (2025-Aug-17 => 2006-Jan-01)
  "scpi >" eliminated from clockstats
  several internal variables added to clockstats

- - - - -
83fa4924 by Hal Murray at 2025-08-21T23:58:57-07:00
Minor tweaks to HOWTO-OpenSSL

- - - - -


9 changed files:

- HOWTO-OpenSSL
- NEWS.adoc
- docs/driver_hpgps.adoc
- include/ntp_refclock.h
- ntpd/ntp_refclock.c
- ntpd/ntp_sandbox.c
- ntpd/ntp_signd.c
- ntpd/refclock_hpgps.c
- ntpd/refclock_nmea.c


Changes:

=====================================
HOWTO-OpenSSL
=====================================
@@ -86,10 +86,10 @@ time make -j4         |& tee make.log
   # ./apps/openssl without the wrapper will use the installed libraries
 The above check is broken on Fedora
   (I think Fedora needs some old/weak crypto that default OpenSSL
-   no longer provides.  I haven't gone donw this rabbit hole.)
+   no longer provides.  I haven't gone down this rabbit hole.)
 It works on Debian, Ubuntu and FreeBSD.
 
-time make test        |& tee test.log
+time make test -j4    |& tee test.log
 time make build_docs  |& tee docs.log
 sudo make install     |& tee install.log
 


=====================================
NEWS.adoc
=====================================
@@ -12,6 +12,11 @@ on user-visible changes.
 
 ## Repository Head
 
+  Major update to HPGPS driver
+    Fix gps WNRO glitch in Z3801A
+    "scpi >" eliminated from clockstats
+    several internal variables added to clockstats
+
 ## 2025-04-18: 1.2.4
 
 * waf has been upgraded to version 2.1.4


=====================================
docs/driver_hpgps.adoc
=====================================
@@ -11,12 +11,10 @@ Serial Port: /dev/hpgps__u__; 9600 bps 8N1, 19200 bps 7N2 for the HP Z3801A
 
 == Warning
 
-As of September 2017 we have reports that the internal clock on a
-Z3801A was observed to roll over to 1998 (see
-link:rollover.html[Rollover issues in time sources]). Users should
-audit for rollover before deploying any of these devices. One or more "g"
-suffixes on your 'time1' option may be useful as a workaround if
-your device does not support setting the era date.
+Old gear, like the Z3801A has GPS WNRO problems (see
+link:rollover.html[Rollover issues in time sources]).
+The driver should adjust the time forward by N*1024 weeks
+to get a sane time.
 
 == Description
 
@@ -34,10 +32,17 @@ off in the thousands.
 They use HP SmartClock (TM) to implement an Enhanced GPS receiver. The
 receiver accuracy when locked to GPS in normal operation is better
 than 1 µs. The accuracy when operating in holdover is typically
-better than 10 µs per day. The receiver should be operated with
-factory default settings. Initial driver operation: expects the
-receiver to be already locked to GPS, configured and able to output
-timecode format 2 messages.
+better than 10 µs per day.
+
+The driver expects the receiver to be running in UTC mode
+and T2 mode.  You can set that up using:
+
+-------------------------------------
+  :ptime:tcode:format F2
+  :diag:gps:utc 1
+-------------------------------------
+
+The latter isn't documented and needs a reboot to take effect.
 
 The driver uses the poll sequence +:PTIME:TCODE?+ to get a response from
 the receiver. The receiver responds with a timecode string of ASCII
@@ -103,9 +108,9 @@ your documentation for details.
 +flag2 {0 | 1}+::
    Not used by this driver.
 +flag3 {0 | 1}+::
-   Not used by this driver.
+   Adds extra stats to the clockstats line
 +flag4 {0 | 1}+::
-   Not used by this driver.
+   Adds a status page to clockstats.
 +subtype+::
    Setting mode 1 configures for the HP Z3801A.
 +mode+::
@@ -117,6 +122,38 @@ your documentation for details.
 +baud+ 'number'::
   Overrides the default baud rate.
 
+== Monitor Data   
+
+The last T2 sentence that is accepted or rejected is written to the
+clockstats file and available with +ntpq -c clockvar+. If +flag3+ is set,
+several more internal variables are logged.
+
+----------------------------------------------------------------------------
+60908 28302.174 HPGPS(0) T220060105075143300003B  64 0  +3 +1.07000E+002 0 +1.5E-006 0 +660669 +2.60128E+001
+----------------------------------------------------------------------------
+
+.Clockstats
+[cols="10%,20%,70%",options="header"]
+|=============================================================================
+|Column|Sample          |Meaning
+|1     |60908           |MJD
+|2     |28302.174       |Time of day in seconds
+|3     |HPGPS(0)        |Driver type and unit.
+|4     |T2...           |T2 sentence from unit
+|5     |64              |Number of sentences accepted and used for timekeeping
+|6     |0               |Number of sentences rejected
+|||Below only if flag3 is on
+|7     |+3              | result from :GPS:SAT:TRAC:COUNT?
+|8     |+1.07000E+002   | result from :ROSC:HOLD:DUR? (duration of this or last holdover)
+|9     |0               | 1 if in holdover
+|10    |+1.5E-006       | result from :ROSC:HOLD:TUNC:PRED?
+|11    |0               | ???
+|12    |+660669         | result from DIAG:ROSC:EFC:ABS? (DAC value)
+|13    |+2.60128E+001   | result from :DIAG:ROSC:EFC:REL? (-100 to 100)
+|=============================================================================
+
+
+
 == Configuration Example
 
 ----------------------------------------------------------------------------


=====================================
include/ntp_refclock.h
=====================================
@@ -195,6 +195,10 @@ extern	size_t	refclock_gtraw	(struct recvbuf *, char *, size_t, l_fp *);
 extern	bool	indicate_refclock_packet(struct refclockio *,
 					 struct recvbuf *);
 
+extern void     fix_WNRO        (struct timespec *dt, int *wnro,
+			const struct peer *peer);
+
+
 extern struct refclock refclock_none;
 
 #ifdef CLOCK_ARBITER


=====================================
ntpd/ntp_refclock.c
=====================================
@@ -12,6 +12,7 @@
 #include "lib_strbuf.h"
 #include "ntp_calendar.h"
 #include "timespecops.h"
+#include "PIVOT.h"
 
 #include <stdio.h>
 
@@ -1087,4 +1088,36 @@ refclock_catcher(
 	return PPS_OK;
 }
 #endif /* HAVE_PPSAPI */
+
+/* Early GPS has a 10 bit week number field.
+ * That's a bit less than 20 years.
+ * GPS started in 1980.  We have now wrapped twice: Aug 1999 and Apr 2019.
+ * https://en.wikipedia.org/wiki/GPS_week_number_rollover
+ *
+ * Some firmware fixes the date to be at least the firmware build date.
+ * That gives valid time for 20 years from the build date.
+ * It also means that old GPS units can break at any time,
+ *   not just on 1024 week boundaries.
+ *
+ * This code wraps based on our build date.
+ * But using a build date would break repeatable builds.
+ * So we use a pivot date that gets updated at release time.
+ * So our code should work for 1024 weeks from the release date.
+ *
+ * Modern GPS satellites have added 3 more bits.
+ * Old firmware doesn't know about them.
+ */
+
+void fix_WNRO (struct timespec *dt, int *wnro, const struct peer *peer) {
+  int i;
+  for (i=0; dt->tv_sec < RELEASE_DATE; i++) {
+    dt->tv_sec += 1024*7*86400;
+  }
+  if (*wnro != i) {
+    *wnro = i;
+    msyslog(LOG_INFO, "REFCLOCK: %s date advanced by %d weeks, WNRO", \
+      refclock_name(peer), *wnro*1024);
+  }
+};
+
 #endif /* REFCLOCK */


=====================================
ntpd/ntp_sandbox.c
=====================================
@@ -406,7 +406,10 @@ int scmp_sc[] = {
  * rather than generate a trap.
  */
 	SCMP_SYS(clone),	/* threads */
+#ifdef __NR_clone3
+	/* Doesn't exist on 4.19.66, Raspbian 9 (stretch) */
 	SCMP_SYS(clone3),
+#endif
 	SCMP_SYS(kill),		/* generate signal */
 	SCMP_SYS(madvise),
 	SCMP_SYS(mprotect),


=====================================
ntpd/ntp_signd.c
=====================================
@@ -298,6 +298,7 @@ send_via_ntp_signd(
 
 	{
 		uint32_t op_reply = 0;
+		samba_reply.op = 0;	/* keep compiler happy */
 		memcpy(&samba_reply, reply, header_length > reply_len ? reply_len: header_length);
 		op_reply = ntohl(samba_reply.op);
 		if (reply_len < header_length) {


=====================================
ntpd/refclock_hpgps.c
=====================================
@@ -3,12 +3,13 @@
  */
 
 #include "config.h"
-#include "ntp.h"
 #include "ntpd.h"
 #include "ntp_io.h"
+#include "ntp_assert.h"
 #include "ntp_calendar.h"
 #include "ntp_refclock.h"
 #include "ntp_stdlib.h"
+#include "timespecops.h"
 
 #include <stdio.h>
 #include <ctype.h>
@@ -45,11 +46,10 @@
  * HP Z3801A code from Jeff Mock added by Hal Murray, Sep 2005
  *
  *
- * The receiver should be operated with factory default settings.
  * Initial driver operation: expects the receiver to be already locked
  * to GPS, configured and able to output timecode format 2 messages.
  *
- * The driver uses the poll sequence :PTIME:TCODE? to get a response from
+ * The driver uses the poll sequence :PTIME:CODE? to get a response from
  * the receiver. The receiver responds with a timecode string of ASCII
  * printing characters, followed by a <cr><lf>, followed by a prompt string
  * issued by the receiver, in the following format:
@@ -71,9 +71,20 @@
  *  -0.955000 on an HP 9000 Model 712/80 HP-UX 9.05
  *  -0.953175 on an HP 9000 Model 370    HP-UX 9.10
  *
- * This receiver also provides a 1PPS signal, but I haven't figured out
- * how to deal with any of the CLK or PPS stuff yet. Stay tuned.
+ * This receiver also provides a 1PPS signal.
+ * Use the ATOM driver (22) to take advantage of it.
+ * That requires kernel support.
  *
+ * There should always be one request in the pipeline.
+ * Whenever a message is received, another one is asked for.
+ * Normally, the next time messages arrives a second later.
+ * The status page takes 2 seconds.  (even at 19200)
+ * The timer routine will recover if a message gets dropped
+ * due to an error or an unplugged cable or loss of power or ...
+ * If flag4 is set, each polling interval does a dance to get
+ * the status page.  The receive routine asks for status
+ * rather than time, and the status collection code asks for
+ * the time after it has collected the whole status message.
  */
 
 /*
@@ -82,6 +93,8 @@
  * is recorded in the clockstats file.
  */
 
+#define HPDEBUG false
+
 /*
  * Interface definitions
  */
@@ -93,29 +106,34 @@
 #define NAME		"HPGPS"		/* shortname */
 #define	DESCRIPTION	"HP GPS Time and Frequency Reference Receiver"
 
-#define SMAX            23*80+1 /* for :SYSTEM:PRINT? status screen response */
 
-#define MTZONE          2       /* number of fields in timezone reply */
+/* Size of buffer for status screen from :SYSTEM:STATUS?
+ * (This code used to use :SYSTEM:PRINT?
+ *  I can't find any doc for that.  Hal, 2025-Aug-19.)
+ * The Z3801A manual shows 22 lines.
+ * Newer versions of firmware have 23 lines of up to 79 characters.
+ * Not all lines were full.    Hal Murray, Nov 2023
+ */
+#define LMAX		24	/* lines in status screen, plus spare */
+#define SMAX            LMAX*80+1 /* characters */
+
 #define MTCODET2        12      /* number of fields in timecode format T2 */
 #define NTCODET2        21      /* number of chars to checksum in format T2 */
 
-/*
- * Tables to compute the day of year from yyyymmdd 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 hpgpsunit {
-	int	pollcnt;	/* poll message counter */
-	int     tzhour;         /* timezone offset, hours */
-	int     tzminute;       /* timezone offset, minutes */
-	int     linecnt;        /* set for expected multiple line responses */
+	int	idlesec;	/* seconds since last message */
+	bool	didpoll;	/* poll called recently */
+	unsigned int     cmndcnt;        /* collecting data */
+	int     linecnt;        /* collecting text for status screen */
 	char	*lastptr;	/* pointer to receiver response data */
+	int	wnro;
 	char    statscrn[SMAX]; /* receiver status screen buffer */
+/* Statisitics since last poll: */
+	int	timecnt;	/* Valid time code messages received */
+	int	errorcnt;	/* Errors in received messages */
 };
 
 /*
@@ -124,6 +142,9 @@ struct hpgpsunit {
 static	bool	hpgps_start	(int, struct peer *);
 static	void	hpgps_receive	(struct recvbuf *);
 static	void	hpgps_poll	(int, struct peer *);
+static	void	hpgps_timer	(int, struct peer *);
+static	void	hpgps_write(struct peer *peer, const char *msg);
+static	bool	hpgps_receive_T2(struct peer *const peer);
 
 /*
  * Transfer vector
@@ -135,18 +156,31 @@ struct	refclock refclock_hpgps = {
 	hpgps_poll,		/* transmit poll message */
 	NULL,			/* not used (old hpgps_control) */
 	NULL,			/* initialize driver */
-	NULL			/* timer - not used */
+	hpgps_timer		/* called once per second */
 };
 
+/* commands to get extra info */
+const char *commands[] = {
+  ":GPS:SAT:TRAC:COUNT?\r",       /* sats being tracked */
+  ":ROSC:HOLD:DUR?\r",            /* duration,state 1 if in holdover */
+  ":ROSC:HOLD:TUNC:PRED?\r",      /* 1 day holdover estimate */
+  ":DIAG:ROSC:EFC:ABS?\r",        /* DAC value */
+  ":DIAG:ROSC:EFC:REL?\r"         /* -100 to 100 */
+  };
+/* Get E-230 if data not available
+ * Test case is PTIME:LEAP:GPST? (hex time of next leap)
+ *   PTIME:LEAP:ACC?\n:SYST:ERR?
+ *     scpi > +18
+ *     scpi > +0,"No error"
+ *   :PTIME:LEAP:GPST?\n:SYST:ERR?
+ *     scpi > E-230> -230,"Data corrupt or stale"
+//":PTIME:TINT?\r*CLS\r",         /x error if no sats, no data */
+//"DIAG:LIF:COUN?\n",             /x uptime in units of 3 hours */
 
 /*
  * hpgps_start - open the devices and initialize data for processing
  */
-static bool
-hpgps_start(
-	int unit,
-	struct peer *peer
-	)
+bool hpgps_start(int unit, struct peer *peer)
 {
 	struct hpgpsunit *up;
 	struct refclockproc *pp;
@@ -155,11 +189,19 @@ hpgps_start(
 	unsigned int speed;
 	char device[20];
 
+	snprintf(device, sizeof(device), DEVICE, unit);
+	/* refclock_open flushes junk, but a quick restart may leave
+	 * a time-request in the pipeline.
+	 * waits are evil, but this doesn't get called during normal operations.
+	 */
+	sleep(2);
 	/*
 	 * Open serial port. Use CLK line discipline, if available.
 	 * Default is HP 58503A, mode arg selects HP Z3801A
 	 */
-	snprintf(device, sizeof(device), DEVICE, unit);
+	/* mode parameter to server config line shares ttl slot */
+	/* Need mode rather than flag because this was called
+	 * before following fudge line was even parsed. */
 	ldisc = LDISC_STD;
 	speed = SPEED232;
 	/* subtype parameter to server config line shares mode slot */
@@ -197,20 +239,20 @@ hpgps_start(
 	pp->clockdesc = DESCRIPTION;
 	memcpy((char *)&pp->refid, REFID, REFIDLEN);
 	peer->sstclktype = CTL_SST_TS_UHF;
-	up->tzhour = 0;
-	up->tzminute = 0;
 
 	*up->statscrn = '\0';
 	up->lastptr = up->statscrn;
-	up->pollcnt = 2;
+
+	up->didpoll = false;
+	up->idlesec = 0;
+	up->timecnt = 0;
+	up->errorcnt = 0;
 
 	/*
 	 * Get the identifier string, which is logged but otherwise ignored,
-	 * and get the local timezone information
 	 */
 	up->linecnt = 1;
-	if (write(pp->io.fd, "*IDN?\r:PTIME:TZONE?\r", 20) != 20)
-	    refclock_report(peer, CEVNT_FAULT);
+	hpgps_write(peer, "*IDN?\r");
 
 	return true;
 }
@@ -219,47 +261,85 @@ hpgps_start(
 /*
  * hpgps_receive - receive data from the serial interface
  */
-static void
-hpgps_receive(
-	struct recvbuf *rbufp
-	)
+void hpgps_receive(struct recvbuf *rbufp)
 {
-	struct hpgpsunit *up;
-	struct refclockproc *pp;
-	struct peer *peer;
-	l_fp trtmp;
-	char tcodechar1;        /* identifies timecode format */
-	char tcodechar2;        /* identifies timecode format */
-	char timequal;          /* time figure of merit: 0-9 */
-	char freqqual;          /* frequency figure of merit: 0-3 */
-	char leapchar;          /* leapsecond: + or 0 or - */
-	char servchar;          /* request for service: 0 = no, 1 = yes */
-	char syncchar;          /* time info is invalid: 0 = no, 1 = yes */
-	short expectedsm;       /* expected timecode byte checksum */
-	short tcodechksm;       /* computed timecode byte checksum */
-	int i,m,n;
-	int month, day, lastday;
-	char *tcp;              /* timecode pointer (skips over the prompt) */
-	char prompt[BMAX];      /* prompt in response from receiver */
+	struct peer		* const peer = rbufp->recv_peer;
+	struct refclockproc	* const pp   = peer->procptr;
+	struct hpgpsunit	* const up   = pp->unitptr;
+
+	l_fp rd_timestamp;
 
 	/*
-	 * Initialize pointers and read the receiver response
+	 * read the receiver response
 	 */
-	peer = rbufp->recv_peer;
-	pp = peer->procptr;
-	up = pp->unitptr;
 	*pp->a_lastcode = '\0';
-	pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
+	pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &rd_timestamp);
 
 	DPRINT(1, ("hpgps: lencode: %d timecode:%s\n",
 		   pp->lencode, pp->a_lastcode));
 
+if (HPDEBUG) {
+  printf("HP in:%3d %s\n", pp->lencode, pp->a_lastcode);
+}
 	/*
 	 * If there's no characters in the reply, we can quit now
 	 */
 	if (pp->lencode == 0)
 	    return;
 
+	/* Strip off leading prompt to cleanup log files. */
+	while (1) {
+		if (strstr(pp->a_lastcode, "scpi > ") == pp->a_lastcode) {
+			pp->lencode -= 7;
+			memmove(pp->a_lastcode, pp->a_lastcode+7, pp->lencode+1);
+			continue;
+		}
+		if (pp->a_lastcode[0] == 'E' &&
+		    pp->a_lastcode[1] == '-' &&
+		    pp->a_lastcode[5] == '>' &&
+		    pp->a_lastcode[6] == ' ') {
+			/* "E-nnn> " Error code */
+			msyslog(LOG_ERR, "HPGPS(%d) error: %s",
+				pp->refclkunit, pp->a_lastcode);
+			DPRINT(0, ("hpgps: error: %s\n", pp->a_lastcode));
+			hpgps_write(peer, "*CLS\r\r");
+			pp->lencode -= 7;
+			memmove(pp->a_lastcode, pp->a_lastcode+7, pp->lencode+1);
+			continue;
+		}
+		break;
+	}
+
+	if (up->cmndcnt > 0) {
+		/* some values are 2 part: +1.17000E+002,0
+		 * split them here to avoid postprocessing before gnuplot
+		 */ 
+		char *comma = strchr(pp->a_lastcode, ',');
+		if (NULL != comma) {
+		  *comma = ' ';
+		}
+		*up->lastptr++ = ' ';
+		memcpy(up->lastptr, pp->a_lastcode, (size_t)pp->lencode);
+		up->lastptr += pp->lencode;
+		if (up->cmndcnt < COUNTOF(commands)) {
+		  hpgps_write(peer, commands[up->cmndcnt++]);
+		  return;
+		}
+		*up->lastptr++ = 0;
+if (HPDEBUG) {
+  printf("HPlog: %s\n", up->statscrn);
+}
+		record_clock_stats(peer, up->statscrn);
+		up->cmndcnt = 0;
+		if ((pp->sloppyclockflag & CLK_FLAG4) ) {
+		  up->linecnt = LMAX;
+		  hpgps_write(peer, ":SYSTEM:STATUS?\r");
+		} else {
+		  hpgps_write(peer, ":PTIME:TCODE?\r");
+		}
+		return;
+	}
+
 	/*
 	 * If linecnt is greater than zero, we are getting information only,
 	 * such as the receiver identification string or the receiver status
@@ -267,73 +347,107 @@ hpgps_receive(
 	 * screen buffer. When we have the last line, write the buffer to
 	 * the clockstats file and return without further processing.
 	 *
-	 * If linecnt is zero, we are expecting either the timezone
-	 * or a timecode. At this point, also write the response
-	 * to the clockstats file, and go on to process the prompt (if any),
-	 * timezone, or timecode and timestamp.
+	 * If linecnt is zero, we are expecting a timecode.
 	 */
 
 
-	if (up->linecnt-- > 0) {
+	if (up->linecnt > 0) {
+		up->linecnt--;
+		/* Silently drop whole line if it doesn't fit. */
 		if ((int)(pp->lencode + 2) <= (SMAX - (up->lastptr - up->statscrn))) {
-			*up->lastptr++ = '\n';
+			if ( (up->lastptr != up->statscrn) || (up->linecnt > 0) )
+				/* ID string stays on same line */
+				*up->lastptr++ = '\n';
 			memcpy(up->lastptr, pp->a_lastcode, (size_t)pp->lencode);
 			up->lastptr += pp->lencode;
 		}
-		if (up->linecnt == 0) {
-		    record_clock_stats(peer, up->statscrn);
+		/* Status screen is 22 or 23 lines */
+		if ( (up->linecnt == 0) ||
+		     (strstr(pp->a_lastcode, "Self Test:") == pp->a_lastcode) ) {
+			up->linecnt = 0;
+			record_clock_stats(peer, up->statscrn);
+			hpgps_write(peer, ":PTIME:TCODE?\r");
 		}
-
 		return;
 	}
 
-	record_clock_stats(peer, pp->a_lastcode);
-	pp->lastrec = trtmp;
+	pp->lastrec = rd_timestamp;
+
+	up->idlesec = 0;
+
+	if (hpgps_receive_T2(peer)) return;
 
+	if (!up->didpoll) {
+		/* error ?? */
+		return;
+	}
+
+	REQUIRE(up->didpoll);
+	up->didpoll = false;
 	up->lastptr = up->statscrn;
 	*up->lastptr = '\0';
-	up->pollcnt = 2;
+	/* Do FLAG3 first.  End of FLAG3 processing starts FLAG4 */
+	if (pp->sloppyclockflag & CLK_FLAG3) {
+		up->lastptr += snprintf(up->statscrn, sizeof(up->statscrn),
+		   "%s  %d %d ",  pp->a_lastcode, up->timecnt, up->errorcnt);
+		up->timecnt = up->errorcnt = 0;
+		up->cmndcnt = 0;
+		hpgps_write(peer, commands[up->cmndcnt++]);
+	} else if (pp->sloppyclockflag & CLK_FLAG4) {
+		up->linecnt = LMAX;
+		hpgps_write(peer, ":SYSTEM:STATUS?\r");
+	} else {
+		mprintf_clock_stats(peer,
+		   "%s  %d %d",  pp->a_lastcode, up->timecnt, up->errorcnt);
+		up->timecnt = up->errorcnt = 0;
+		hpgps_write(peer, ":PTIME:TCODE?\r");
+	}
+}
 
-	/*
-	 * We get down to business: get a prompt if one is there, issue
-	 * a clear status command if it contains an error indication.
-	 * Next, check for either the timezone reply or the timecode reply
-	 * and decode it.  If we don't recognize the reply, or don't get the
-	 * proper number of decoded fields, or get an out of range timezone,
-	 * or if the timecode checksum is bad, then we declare bad format
-	 * and exit.
-	 *
-	 * Timezone format (including nominal prompt):
-	 * scpi > -H,-M<cr><lf>
+/* return true if all OK
+ * false is error or didpoll
+ */
+bool hpgps_receive_T2(struct peer *const peer)
+{
+	struct refclockproc	* const pp   = peer->procptr;
+	struct hpgpsunit	* const up   = pp->unitptr;
+
+	l_fp rd_reftime;
+	char tcodechar1;        /* identifies timecode format */
+	char tcodechar2;        /* identifies timecode format */
+	char timequal;          /* time figure of merit: 0-9 */
+	char freqqual;          /* frequency figure of merit: 0-3 */
+	char leapchar;          /* leapsecond: + or 0 or - */
+	char servchar;          /* request for service: 0 = no, 1 = yes */
+	char syncchar;          /* time info is invalid: 0 = no, 1 = yes */
+	short expectedsm;       /* expected timecode byte checksum */
+	short tcodechksm;       /* computed timecode byte checksum */
+	int m, n;
+	struct tm tm;		/* temp storage for parsed data */
+	struct timespec date;	/* time stamp derived from serial port */
+	char *tcp;              /* timecode pointer (skips over the prompt) */
+
+	/* We get down to business:
+	 * Check for a timecode reply and decode it.
+	 * If we don't recognize the reply, or don't get the proper
+	 * number of decoded fields, or if the timecode checksum is bad,
+	 * then we declare bad format and exit.
 	 *
-	 * Timecode format (including nominal prompt):
-	 * scpi > T2yyyymmddhhmmssMFLRVcc<cr><lf>
+	 * Timecode format (after removing prompt):
+	 * T2yyyymmddhhmmssTFLRVcc<cr><lf>
 	 *
 	 */
 
-	strlcpy(prompt, pp->a_lastcode, sizeof(prompt));
-	tcp = strrchr(pp->a_lastcode,'>');
-	if (tcp == NULL) {
-	    tcp = pp->a_lastcode;
-	} else {
-	    tcp++;
-	}
-	prompt[tcp - pp->a_lastcode] = '\0';
-	while ((*tcp == ' ') || (*tcp == '\t')) {
-	    tcp++;
+	tcp = pp->a_lastcode;
+	/* Not expected to happen. Beware of filling up log files. */
+	if (*tcp == ' ') {
+	    msyslog(LOG_INFO, "HPGPS(%d) Leading space: '%s'",
+		pp->refclkunit, pp->a_lastcode);
 	}
+	while ((*tcp == ' ') || (*tcp == '\t')) tcp++;
 
 	/*
-	 * deal with an error indication in the prompt here
-	 */
-	if (strrchr(prompt,'E') > strrchr(prompt,'s')){
-	        DPRINT(1, ("hpgps: error indicated in prompt: %s\n", prompt));
-		if (write(pp->io.fd, "*CLS\r\r", 6) != 6)
-		    refclock_report(peer, CEVNT_FAULT);
-	}
-
-	/*
-	 * make sure we got a timezone or timecode format and
+	 * make sure we got a timecode format and
 	 * then process accordingly
 	 */
 	m = sscanf(tcp,"%c%c", &tcodechar1, &tcodechar2);
@@ -341,63 +455,32 @@ hpgps_receive(
 	if (m != 2){
 	        DPRINT(1, ("hpgps: no format indicator\n"));
 		refclock_report(peer, CEVNT_BADREPLY);
-		return;
+		up->errorcnt++;
+		return(false);
 	}
 
-	switch (tcodechar1) {
-
-	    case '+':
-	    case '-':
-		m = sscanf(tcp,"%d,%d", &up->tzhour, &up->tzminute);
-		if (m != MTZONE) {
-		        DPRINT(1, ("hpgps: only %d fields recognized in timezone\n", m));
-			refclock_report(peer, CEVNT_BADREPLY);
-			return;
-		}
-		if ((up->tzhour < -12) || (up->tzhour > 13) ||
-		    (up->tzminute < -59) || (up->tzminute > 59)){
-		        DPRINT(1, ("hpgps: timezone %d, %d out of range\n",
-				   up->tzhour, up->tzminute));
-			refclock_report(peer, CEVNT_BADREPLY);
-			return;
-		}
-		return;
-
-	    case 'T':
-		break;
-
-	    default:
-	        DPRINT(1, ("hpgps: unrecognized reply format %c%c\n",
+	if ('T' != tcodechar1 || '2' != tcodechar2) {
+		DPRINT(1, ("hpgps: unrecognized reply format %c%c\n",
 			   tcodechar1, tcodechar2));
 		refclock_report(peer, CEVNT_BADREPLY);
-		return;
-	} /* end of tcodechar1 switch */
-
-
-	switch (tcodechar2) {
+		up->errorcnt++;
+		return(false);
+	}
 
-	    case '2':
-		m = sscanf(tcp,"%*c%*c%4d%2d%2d%2d%2d%2d%c%c%c%c%c%2hx",
-			   &pp->year, &month, &day, &pp->hour, &pp->minute,
-                           &pp->second,
-			   &timequal, &freqqual, &leapchar, &servchar,
-                           &syncchar,
-			   (short unsigned int*)&expectedsm);
-		n = NTCODET2;
 
-		if (m != MTCODET2){
-		        DPRINT(1, ("hpgps: only %d fields recognized in timecode\n", m));
-			refclock_report(peer, CEVNT_BADREPLY);
-			return;
-		}
-		break;
+	m = sscanf(tcp,"%*c%*c%4d%2d%2d%2d%2d%2d%c%c%c%c%c%2hx",
+		&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+		&tm.tm_hour, &tm.tm_min, &tm.tm_sec,
+		&timequal, &freqqual, &leapchar, &servchar, &syncchar,
+		(short unsigned int*)&expectedsm);
 
-	    default:
-	        DPRINT(1, ("hpgps: unrecognized timecode format %c%c\n",
-			   tcodechar1, tcodechar2));
+	if (m != MTCODET2){
+	        DPRINT(1, ("hpgps: only %d fields recognized in timecode\n", m));
 		refclock_report(peer, CEVNT_BADREPLY);
-		return;
-	} /* end of tcodechar2 format switch */
+		up->errorcnt++;
+		return(false);
+	}
+
 
 	/*
 	 * Compute and verify the checksum.
@@ -405,6 +488,7 @@ hpgps_receive(
 	 * before the expected checksum.  Bail out if incorrect.
 	 */
 	tcodechksm = 0;
+	n = NTCODET2;
 	while (n-- > 0) {
 		tcodechksm += *tcp++;
 	}
@@ -414,81 +498,20 @@ hpgps_receive(
 	        DPRINT(1, ("hpgps: checksum %2hX doesn't match %2hX expected\n",
 			   tcodechksm, expectedsm));
 		refclock_report(peer, CEVNT_BADREPLY);
-		return;
-	}
-
-	/*
-	 * Compute the day of year from the yyyymmdd format.
-	 */
-	if (month < 1 || month > 12 || day < 1) {
-		refclock_report(peer, CEVNT_BADTIME);
-		return;
-	}
-
-	if ( ! is_leapyear(pp->year) ) {			/* Y2KFixes */
-		/* not a leap year */
-		if (day > day1tab[month - 1]) {
-			refclock_report(peer, CEVNT_BADTIME);
-			return;
-		}
-		for (i = 0; i < month - 1; i++) {
-			day += day1tab[i];
-		}
-		lastday = 365;
-	} else {
-		/* a leap year */
-		if (day > day2tab[month - 1]) {
-			refclock_report(peer, CEVNT_BADTIME);
-			return;
-		}
-		for (i = 0; i < month - 1; i++) {
-			day += day2tab[i];
-		}
-		lastday = 366;
-	}
-
-	/*
-	 * Deal with the timezone offset here. The receiver timecode is in
-	 * local time = UTC + :PTIME:TZONE, so SUBTRACT the timezone values.
-	 * For example, Pacific Standard Time is -8 hours , 0 minutes.
-	 * Deal with the underflows and overflows.
-	 */
-	pp->minute -= up->tzminute;
-	pp->hour -= up->tzhour;
-
-	if (pp->minute < 0) {
-		pp->minute += 60;
-		pp->hour--;
-	}
-	if (pp->minute > 59) {
-		pp->minute -= 60;
-		pp->hour++;
-	}
-	if (pp->hour < 0)  {
-		pp->hour += 24;
-		day--;
-		if (day < 1) {
-			pp->year--;
-			if ( is_leapyear(pp->year) )		/* Y2KFixes */
-			    day = 366;
-			else
-			    day = 365;
-		}
+		up->errorcnt++;
+		return(false);
 	}
 
-	if (pp->hour > 23) {
-		pp->hour -= 24;
-		day++;
-		if (day > lastday) {
-			pp->year++;
-			day = 1;
-		}
+	if (timequal > '4') {
+		DPRINT(0, ("hpgps: TFOM %c too big\n", timequal));
+		refclock_report(peer, CEVNT_BADREPLY);
+		up->errorcnt++;
+		return(false);
 	}
 
-	pp->yday = day;
 
 	/*
-	 * Decode the MFLRV indicators.
+	 * Decode the TFLRV indicators.
 	 * NEED TO FIGURE OUT how to deal with the request for service,
 	 * time quality, and frequency quality indicators some day.
 	 */
@@ -508,12 +531,12 @@ hpgps_receive(
 		     * but that seems too likely to introduce other bugs.
 		     */
 		    case '+':
-			if ((month==6) || (month==12))
+			if ((tm.tm_mon==6) || (tm.tm_mon==12))
 			    pp->leap = LEAP_ADDSECOND;
 			break;
 
 		    case '-':
-			if ((month==6) || (month==12))
+			if ((tm.tm_mon==6) || (tm.tm_mon==12))
 			    pp->leap = LEAP_DELSECOND;
 			break;
 
@@ -521,7 +544,8 @@ hpgps_receive(
 			DPRINT(1, ("hpgps: unrecognized leap indicator: %c\n",
 				   leapchar));
 			refclock_report(peer, CEVNT_BADTIME);
-			return;
+			up->errorcnt++;
+			return(false);
 		} /* end of leapchar switch */
 	}
 
@@ -533,55 +557,85 @@ hpgps_receive(
 	 * time, which may cause a paranoid protocol module to chuck out
 	 * the data.
 	 */
-	if (!refclock_process(pp)) {
-		refclock_report(peer, CEVNT_BADTIME);
-		return;
-	}
-	pp->lastref = pp->lastrec;
-	refclock_receive(peer);
+	tm.tm_year -= 1900;
+	tm.tm_mon -= 1;
+	date.tv_nsec = 0;
+	date.tv_sec = timegm(&tm);	/* No error checking */
 
-	/*
-	 * If CLK_FLAG4 is set, ask for the status screen response.
-	 */
-	if (pp->sloppyclockflag & CLK_FLAG4){
-		up->linecnt = 22;
-		if (write(pp->io.fd, ":SYSTEM:PRINT?\r", 15) != 15)
-		    refclock_report(peer, CEVNT_FAULT);
+	/* Z3801A broke 2025-Aug-17 => 2006-Jam-01 */
+	fix_WNRO(&date, &up->wnro, peer);
+
+	rd_reftime = tspec_stamp_to_lfp(date);
+	refclock_process_offset(pp, rd_reftime, pp->lastrec, pp->fudgetime1);
+	up->timecnt++;
+
+	if(up->didpoll) {
+		return(false);
 	}
+
+	hpgps_write(peer, ":PTIME:TCODE?\r");
+	return(true);
 }
 
 
 /*
  * hpgps_poll - called by the transmit procedure
  */
-static void
-hpgps_poll(
-	int unit,
-	struct peer *peer
-	)
+void hpgps_poll(int unit, struct peer *peer)
 {
+	UNUSED_ARG(unit);
+	struct refclockproc *pp;
 	struct hpgpsunit *up;
+
+	pp = peer->procptr;
+	up = (struct hpgpsunit *)pp->unitptr;
+
+	pp->lastref = pp->lastrec;
+	refclock_receive(peer);
+
+	up->didpoll = true;
+
+	pp->polls++;
+}
+ 
+/*
+ * hpgps_timer - called once per second
+ */
+static void hpgps_timer(int unit, struct peer *peer)
+{
 	struct refclockproc *pp;
+	struct hpgpsunit *up;
 
 	UNUSED_ARG(unit);
 
-	/*
-	 * Time to poll the clock. The HP 58503A responds to a
-	 * ":PTIME:TCODE?" by returning a timecode in the format specified
-	 * above. If nothing is heard from the clock for two polls,
-	 * declare a timeout and keep going.
-	 */
 	pp = peer->procptr;
 	up = pp->unitptr;
-	if (up->pollcnt == 0) {
-		refclock_report(peer, CEVNT_TIMEOUT);
-	} else {
-		up->pollcnt--;
+
+	if (up->idlesec++ == 5)
+	    refclock_report(peer, CEVNT_TIMEOUT);
+	if (up->idlesec >= 5) {
+		/* FIXME: logging (happens on some commands) */
+		/* Timeout.  Poke it again.
+		 * This recovers from the cable being unplugged for a while.
+		 */
+		hpgps_write(peer, ":PTIME:TCODE?\r");
+		up->cmndcnt = 0;
+		up->linecnt = 0;
 	}
-	if (write(pp->io.fd, ":PTIME:TCODE?\r", 14) != 14) {
+}
+
+
+static void hpgps_write(struct peer *peer, const char *msg) {
+	struct refclockproc *pp = peer->procptr;
+	int len = strlen(msg);
+if (HPDEBUG) {
+  static int counter = 0;
+  char copy[64];  /* msg ends with \r */
+  strlcpy(copy, msg, sizeof(copy));
+  *strstr(copy, "\r") = 0;
+  printf("HPout: %d %s\n", counter++, copy);
+}
+	if (write(pp->io.fd, msg, len) != len)
 		refclock_report(peer, CEVNT_FAULT);
-	}
-	else
-	    pp->polls++;
 }
 


=====================================
ntpd/refclock_nmea.c
=====================================
@@ -31,7 +31,6 @@
 #include "ntp_refclock.h"
 #include "ntp_stdlib.h"
 #include "timespecops.h"
-#include "PIVOT.h"
 
 #ifdef HAVE_PPSAPI
 # include "ppsapi_timepps.h"
@@ -273,8 +272,6 @@ static bool	parse_time	(struct timespec *dt, nmea_data *, int idx);
 static bool	parse_date	(struct timespec *dt, nmea_data*,
 					int idx, enum date_fmt fmt);
 static bool	kludge_day	(struct timespec *dt);
-static bool	fix_WNRO	(struct timespec *dt, int *wnro, \
-					const struct peer *peer);
 
 static void     save_ltc        (struct refclockproc * const, const char * const,
 				 size_t);
@@ -1684,36 +1681,4 @@ static bool kludge_day (struct timespec *dt) {
   return true;
 }
 
-/* Early GPS has a 10 bit week number field.
- * That's a bit less than 20 years.
- * GPS started in 1980.  We have now wrapped twice: Aug 1999 and Apr 2019.
- * https://en.wikipedia.org/wiki/GPS_week_number_rollover
- *
- * Some firmware fixes the date to be at least the firmware build date.
- * That gives valid time for 20 years from the build date.
- * It also means that old GPS units can break at any time,
- *   not just on 1024 week boundaries.
- *
- * This code wraps based on our build date.
- * But using a build date would break repeatable builds.
- * So we use a pivot date that gets updated at release time.
- * So our code should work for 1024 weeks from the release date.
- *
- * Modern GPS satellites have added 3 more bits.
- * Old firmware doesn't know about them.
- */
-
-static bool fix_WNRO (struct timespec *dt, int *wnro, const struct peer *peer) {
-  int i;
-  for (i=0; dt->tv_sec < RELEASE_DATE; i++) {
-    dt->tv_sec += 1024*7*86400;
-  }
-  if (*wnro != i) {
-    *wnro = i;
-    msyslog(LOG_INFO, "REFCLOCK: %s date advanced by %d weeks, WNRO", \
-      refclock_name(peer), *wnro*1024);
-  }
-  return true;
-};
-
 // end



View it on GitLab: https://gitlab.com/NTPsec/ntpsec/-/compare/16dbe2b07bc70b37e73aa76f850db2705f7c5dd9...83fa4924b8f656bf9e5a729bc1e1bb2f942a59fe

-- 
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/-/compare/16dbe2b07bc70b37e73aa76f850db2705f7c5dd9...83fa4924b8f656bf9e5a729bc1e1bb2f942a59fe
You're receiving this email because of your account on gitlab.com.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.ntpsec.org/pipermail/vc/attachments/20250823/288e811e/attachment-0001.htm>


More information about the vc mailing list