Annotation of embedaddon/ntp/ntpd/refclock_pst.c, revision 1.1.1.1
1.1 misho 1: /*
2: * refclock_pst - clock driver for PSTI/Traconex WWV/WWVH receivers
3: */
4:
5: #ifdef HAVE_CONFIG_H
6: #include <config.h>
7: #endif
8:
9: #if defined(REFCLOCK) && defined(CLOCK_PST)
10:
11: #include "ntpd.h"
12: #include "ntp_io.h"
13: #include "ntp_refclock.h"
14: #include "ntp_stdlib.h"
15:
16: #include <stdio.h>
17: #include <ctype.h>
18:
19: /*
20: * This driver supports the PSTI 1010 and Traconex 1020 WWV/WWVH
21: * Receivers. No specific claim of accuracy is made for these receiver,
22: * but actual experience suggests that 10 ms would be a conservative
23: * assumption.
24: *
25: * The DIPswitches should be set for 9600 bps line speed, 24-hour day-
26: * of-year format and UTC time zone. Automatic correction for DST should
27: * be disabled. It is very important that the year be set correctly in
28: * the DIPswitches; otherwise, the day of year will be incorrect after
29: * 28 April of a normal or leap year. The propagation delay DIPswitches
30: * should be set according to the distance from the transmitter for both
31: * WWV and WWVH, as described in the instructions. While the delay can
32: * be set only to within 11 ms, the fudge time1 parameter can be used
33: * for vernier corrections.
34: *
35: * Using the poll sequence QTQDQM, the response timecode is in three
36: * sections totalling 50 ASCII printing characters, as concatenated by
37: * the driver, in the following format:
38: *
39: * ahh:mm:ss.fffs<cr> yy/dd/mm/ddd<cr> frdzycchhSSFTttttuuxx<cr>
40: *
41: * on-time = first <cr>
42: * hh:mm:ss.fff = hours, minutes, seconds, milliseconds
43: * a = AM/PM indicator (' ' for 24-hour mode)
44: * yy = year (from internal switches)
45: * dd/mm/ddd = day of month, month, day of year
46: * s = daylight-saving indicator (' ' for 24-hour mode)
47: * f = frequency enable (O = all frequencies enabled)
48: * r = baud rate (3 = 1200, 6 = 9600)
49: * d = features indicator (@ = month/day display enabled)
50: * z = time zone (0 = UTC)
51: * y = year (5 = 91)
52: * cc = WWV propagation delay (52 = 22 ms)
53: * hh = WWVH propagation delay (81 = 33 ms)
54: * SS = status (80 or 82 = operating correctly)
55: * F = current receive frequency (4 = 15 MHz)
56: * T = transmitter (C = WWV, H = WWVH)
57: * tttt = time since last update (0000 = minutes)
58: * uu = flush character (03 = ^c)
59: * xx = 94 (unknown)
60: *
61: * The alarm condition is indicated by other than '8' at A, which occurs
62: * during initial synchronization and when received signal is lost for
63: * an extended period; unlock condition is indicated by other than
64: * "0000" in the tttt subfield at Q.
65: *
66: * Fudge Factors
67: *
68: * There are no special fudge factors other than the generic.
69: */
70:
71: /*
72: * Interface definitions
73: */
74: #define DEVICE "/dev/wwv%d" /* device name and unit */
75: #define SPEED232 B9600 /* uart speed (9600 baud) */
76: #define PRECISION (-10) /* precision assumed (about 1 ms) */
77: #define WWVREFID "WWV\0" /* WWV reference ID */
78: #define WWVHREFID "WWVH" /* WWVH reference ID */
79: #define DESCRIPTION "PSTI/Traconex WWV/WWVH Receiver" /* WRU */
80: #define PST_PHI (10e-6) /* max clock oscillator offset */
81: #define LENPST 46 /* min timecode length */
82:
83: /*
84: * Unit control structure
85: */
86: struct pstunit {
87: int tcswitch; /* timecode switch */
88: char *lastptr; /* pointer to timecode data */
89: };
90:
91: /*
92: * Function prototypes
93: */
94: static int pst_start (int, struct peer *);
95: static void pst_shutdown (int, struct peer *);
96: static void pst_receive (struct recvbuf *);
97: static void pst_poll (int, struct peer *);
98:
99: /*
100: * Transfer vector
101: */
102: struct refclock refclock_pst = {
103: pst_start, /* start up driver */
104: pst_shutdown, /* shut down driver */
105: pst_poll, /* transmit poll message */
106: noentry, /* not used (old pst_control) */
107: noentry, /* initialize driver */
108: noentry, /* not used (old pst_buginfo) */
109: NOFLAGS /* not used */
110: };
111:
112:
113: /*
114: * pst_start - open the devices and initialize data for processing
115: */
116: static int
117: pst_start(
118: int unit,
119: struct peer *peer
120: )
121: {
122: register struct pstunit *up;
123: struct refclockproc *pp;
124: int fd;
125: char device[20];
126:
127: /*
128: * Open serial port. Use CLK line discipline, if available.
129: */
130: snprintf(device, sizeof(device), DEVICE, unit);
131: if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
132: return (0);
133:
134: /*
135: * Allocate and initialize unit structure
136: */
137: up = emalloc(sizeof(*up));
138: memset(up, 0, sizeof(*up));
139: pp = peer->procptr;
140: pp->io.clock_recv = pst_receive;
141: pp->io.srcclock = (caddr_t)peer;
142: pp->io.datalen = 0;
143: pp->io.fd = fd;
144: if (!io_addclock(&pp->io)) {
145: close(fd);
146: pp->io.fd = -1;
147: free(up);
148: return (0);
149: }
150: pp->unitptr = (caddr_t)up;
151:
152: /*
153: * Initialize miscellaneous variables
154: */
155: peer->precision = PRECISION;
156: pp->clockdesc = DESCRIPTION;
157: memcpy((char *)&pp->refid, WWVREFID, 4);
158: peer->burst = MAXSTAGE;
159: return (1);
160: }
161:
162:
163: /*
164: * pst_shutdown - shut down the clock
165: */
166: static void
167: pst_shutdown(
168: int unit,
169: struct peer *peer
170: )
171: {
172: register struct pstunit *up;
173: struct refclockproc *pp;
174:
175: pp = peer->procptr;
176: up = (struct pstunit *)pp->unitptr;
177: if (-1 != pp->io.fd)
178: io_closeclock(&pp->io);
179: if (NULL != up)
180: free(up);
181: }
182:
183:
184: /*
185: * pst_receive - receive data from the serial interface
186: */
187: static void
188: pst_receive(
189: struct recvbuf *rbufp
190: )
191: {
192: register struct pstunit *up;
193: struct refclockproc *pp;
194: struct peer *peer;
195: l_fp trtmp;
196: u_long ltemp;
197: char ampmchar; /* AM/PM indicator */
198: char daychar; /* standard/daylight indicator */
199: char junque[10]; /* "yy/dd/mm/" discard */
200: char info[14]; /* "frdzycchhSSFT" clock info */
201:
202: /*
203: * Initialize pointers and read the timecode and timestamp
204: */
205: peer = (struct peer *)rbufp->recv_srcclock;
206: pp = peer->procptr;
207: up = (struct pstunit *)pp->unitptr;
208: up->lastptr += refclock_gtlin(rbufp, up->lastptr, pp->a_lastcode
209: + BMAX - 2 - up->lastptr, &trtmp);
210: *up->lastptr++ = ' ';
211: *up->lastptr = '\0';
212:
213: /*
214: * Note we get a buffer and timestamp for each <cr>, but only
215: * the first timestamp is retained.
216: */
217: if (up->tcswitch == 0)
218: pp->lastrec = trtmp;
219: up->tcswitch++;
220: pp->lencode = up->lastptr - pp->a_lastcode;
221: if (up->tcswitch < 3)
222: return;
223:
224: /*
225: * We get down to business, check the timecode format and decode
226: * its contents. If the timecode has invalid length or is not in
227: * proper format, we declare bad format and exit.
228: */
229: if (pp->lencode < LENPST) {
230: refclock_report(peer, CEVNT_BADREPLY);
231: return;
232: }
233:
234: /*
235: * Timecode format:
236: * "ahh:mm:ss.fffs yy/dd/mm/ddd frdzycchhSSFTttttuuxx"
237: */
238: if (sscanf(pp->a_lastcode,
239: "%c%2d:%2d:%2d.%3ld%c %9s%3d%13s%4ld",
240: &mchar, &pp->hour, &pp->minute, &pp->second, &pp->nsec,
241: &daychar, junque, &pp->day, info, <emp) != 10) {
242: refclock_report(peer, CEVNT_BADREPLY);
243: return;
244: }
245: pp->nsec *= 1000000;
246:
247: /*
248: * Decode synchronization, quality and last update. If
249: * unsynchronized, set the leap bits accordingly and exit. Once
250: * synchronized, the dispersion depends only on when the clock
251: * was last heard, which depends on the time since last update,
252: * as reported by the clock.
253: */
254: if (info[9] != '8')
255: pp->leap = LEAP_NOTINSYNC;
256: if (info[12] == 'H')
257: memcpy((char *)&pp->refid, WWVHREFID, 4);
258: else
259: memcpy((char *)&pp->refid, WWVREFID, 4);
260: if (peer->stratum <= 1)
261: peer->refid = pp->refid;
262: if (ltemp == 0)
263: pp->lastref = pp->lastrec;
264: pp->disp = PST_PHI * ltemp * 60;
265:
266: /*
267: * Process the new sample in the median filter and determine the
268: * timecode timestamp.
269: */
270: if (!refclock_process(pp))
271: refclock_report(peer, CEVNT_BADTIME);
272: else if (peer->disp > MAXDISTANCE)
273: refclock_receive(peer);
274: }
275:
276:
277: /*
278: * pst_poll - called by the transmit procedure
279: */
280: static void
281: pst_poll(
282: int unit,
283: struct peer *peer
284: )
285: {
286: register struct pstunit *up;
287: struct refclockproc *pp;
288:
289: /*
290: * Time to poll the clock. The PSTI/Traconex clock responds to a
291: * "QTQDQMT" by returning a timecode in the format specified
292: * above. Note there is no checking on state, since this may not
293: * be the only customer reading the clock. Only one customer
294: * need poll the clock; all others just listen in. If the clock
295: * becomes unreachable, declare a timeout and keep going.
296: */
297: pp = peer->procptr;
298: up = (struct pstunit *)pp->unitptr;
299: up->tcswitch = 0;
300: up->lastptr = pp->a_lastcode;
301: if (write(pp->io.fd, "QTQDQMT", 6) != 6)
302: refclock_report(peer, CEVNT_FAULT);
303: if (peer->burst > 0)
304: return;
305: if (pp->coderecv == pp->codeproc) {
306: refclock_report(peer, CEVNT_TIMEOUT);
307: return;
308: }
309: refclock_receive(peer);
310: record_clock_stats(&peer->srcadr, pp->a_lastcode);
311: #ifdef DEBUG
312: if (debug)
313: printf("pst: timecode %d %s\n", pp->lencode,
314: pp->a_lastcode);
315: #endif
316: peer->burst = MAXSTAGE;
317: pp->polls++;
318: }
319:
320: #else
321: int refclock_pst_int;
322: #endif /* REFCLOCK */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>