📄 refclock_hpgps.c
字号:
/* * refclock_hpgps - clock driver for HP 58503A GPS receiver */#ifdef HAVE_CONFIG_H# include <config.h>#endif#if defined(REFCLOCK) && defined(CLOCK_HPGPS)#include "ntpd.h"#include "ntp_io.h"#include "ntp_refclock.h"#include "ntp_stdlib.h"#include <stdio.h>#include <ctype.h>/* Version 0.1 April 1, 1995 * 0.2 April 25, 1995 * tolerant of missing timecode response prompt and sends * clear status if prompt indicates error; * can use either local time or UTC from receiver; * can get receiver status screen via flag4 * * WARNING!: This driver is UNDER CONSTRUCTION * Everything in here should be treated with suspicion. * If it looks wrong, it probably is. * * Comments and/or questions to: Dave Vitanye * Hewlett Packard Company * dave@scd.hp.com * (408) 553-2856 * * Thanks to the author of the PST driver, which was the starting point for * this one. * * This driver supports the HP 58503A Time and Frequency Reference Receiver. * This receiver uses HP SmartClock (TM) to implement an Enhanced GPS receiver. * The receiver accuracy when locked to GPS in normal operation is better * than 1 usec. The accuracy when operating in holdover is typically better * than 10 usec. per day. * * The same driver also handles the HP Z3801A which is available surplus * from the cell phone industry. It's popular with hams. * It needs a different line setup: 19200 baud, 7 data bits, odd parity * That is selected by adding "mode 1" to the server line in ntp.conf * 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 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: * T#yyyymmddhhmmssMFLRVcc<cr><lf>scpi > * * The driver processes the response at the <cr> and <lf>, so what the * driver sees is the prompt from the previous poll, followed by this * timecode. The prompt from the current poll is (usually) left unread until * the next poll. So (except on the very first poll) the driver sees this: * * scpi > T#yyyymmddhhmmssMFLRVcc<cr><lf> * * The T is the on-time character, at 980 msec. before the next 1PPS edge. * The # is the timecode format type. We look for format 2. * Without any of the CLK or PPS stuff, then, the receiver buffer timestamp * at the <cr> is 24 characters later, which is about 25 msec. at 9600 bps, * so the first approximation for fudge time1 is nominally -0.955 seconds. * This number probably needs adjusting for each machine / OS type, so far: * -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. * *//* * Fudge Factors * * Fudge time1 is used to accomodate the timecode serial interface adjustment. * Fudge flag4 can be set to request a receiver status screen summary, which * is recorded in the clockstats file. *//* * Interface definitions */#define DEVICE "/dev/hpgps%d" /* device name and unit */#define SPEED232 B9600 /* uart speed (9600 baud) */#define SPEED232Z B19200 /* uart speed (19200 baud) */#define PRECISION (-10) /* precision assumed (about 1 ms) */#define REFID "GPS\0" /* reference ID */#define DESCRIPTION "HP 58503A 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 */#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 */ char *lastptr; /* pointer to receiver response data */ char statscrn[SMAX]; /* receiver status screen buffer */};/* * Function prototypes */static int hpgps_start P((int, struct peer *));static void hpgps_shutdown P((int, struct peer *));static void hpgps_receive P((struct recvbuf *));static void hpgps_poll P((int, struct peer *));/* * Transfer vector */struct refclock refclock_hpgps = { hpgps_start, /* start up driver */ hpgps_shutdown, /* shut down driver */ hpgps_poll, /* transmit poll message */ noentry, /* not used (old hpgps_control) */ noentry, /* initialize driver */ noentry, /* not used (old hpgps_buginfo) */ NOFLAGS /* not used */};/* * hpgps_start - open the devices and initialize data for processing */static inthpgps_start( int unit, struct peer *peer ){ register struct hpgpsunit *up; struct refclockproc *pp; int fd; char device[20]; /* * Open serial port. Use CLK line discipline, if available. * Default is HP 58503A, mode arg selects HP Z3801A */ (void)sprintf(device, DEVICE, unit); /* mode parameter to server config line shares ttl slot */ if ((peer->ttl == 1)) { if (!(fd = refclock_open(device, SPEED232Z, LDISC_CLK | LDISC_7O1))) return (0); } else { if (!(fd = refclock_open(device, SPEED232, LDISC_CLK))) return (0); } /* * Allocate and initialize unit structure */ if (!(up = (struct hpgpsunit *) emalloc(sizeof(struct hpgpsunit)))) { (void) close(fd); return (0); } memset((char *)up, 0, sizeof(struct hpgpsunit)); pp = peer->procptr; pp->io.clock_recv = hpgps_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->tzhour = 0; up->tzminute = 0; *up->statscrn = '\0'; up->lastptr = up->statscrn; up->pollcnt = 2; /* * 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); return (1);}/* * hpgps_shutdown - shut down the clock */static voidhpgps_shutdown( int unit, struct peer *peer ){ register struct hpgpsunit *up; struct refclockproc *pp; pp = peer->procptr; up = (struct hpgpsunit *)pp->unitptr; io_closeclock(&pp->io); free(up);}/* * hpgps_receive - receive data from the serial interface */static voidhpgps_receive( struct recvbuf *rbufp ){ register 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 */ /* * Initialize pointers and read the receiver response */ peer = (struct peer *)rbufp->recv_srcclock; pp = peer->procptr; up = (struct hpgpsunit *)pp->unitptr; *pp->a_lastcode = '\0'; pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);#ifdef DEBUG if (debug) printf("hpgps: lencode: %d timecode:%s\n", pp->lencode, pp->a_lastcode);#endif /* * If there's no characters in the reply, we can quit now */ if (pp->lencode == 0) return; /* * If linecnt is greater than zero, we are getting information only, * such as the receiver identification string or the receiver status * screen, so put the receiver response at the end of the status * 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 (up->linecnt-- > 0) { if ((int)(pp->lencode + 2) <= (SMAX - (up->lastptr - up->statscrn))) { *up->lastptr++ = '\n'; (void)strcpy(up->lastptr, pp->a_lastcode); up->lastptr += pp->lencode; } if (up->linecnt == 0) record_clock_stats(&peer->srcadr, up->statscrn); return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -