[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