Annotation of embedaddon/ntp/ntpd/refclock_as2201.c, revision 1.1.1.1
1.1 misho 1: /*
2: * refclock_as2201 - clock driver for the Austron 2201A GPS
3: * Timing Receiver
4: */
5: #ifdef HAVE_CONFIG_H
6: #include <config.h>
7: #endif
8:
9: #if defined(REFCLOCK) && defined(CLOCK_AS2201)
10:
11: #include "ntpd.h"
12: #include "ntp_io.h"
13: #include "ntp_refclock.h"
14: #include "ntp_unixtime.h"
15: #include "ntp_stdlib.h"
16:
17: #include <stdio.h>
18: #include <ctype.h>
19:
20: /*
21: * This driver supports the Austron 2200A/2201A GPS Receiver with
22: * Buffered RS-232-C Interface Module. Note that the original 2200/2201
23: * receivers will not work reliably with this driver, since the older
24: * design cannot accept input commands at any reasonable data rate.
25: *
26: * The program sends a "*toc\r" to the radio and expects a response of
27: * the form "yy:ddd:hh:mm:ss.mmm\r" where yy = year of century, ddd =
28: * day of year, hh:mm:ss = second of day and mmm = millisecond of
29: * second. Then, it sends statistics commands to the radio and expects
30: * a multi-line reply showing the corresponding statistics or other
31: * selected data. Statistics commands are sent in order as determined by
32: * a vector of commands; these might have to be changed with different
33: * radio options. If flag4 of the fudge configuration command is set to
34: * 1, the statistics data are written to the clockstats file for later
35: * processing.
36: *
37: * In order for this code to work, the radio must be placed in non-
38: * interactive mode using the "off" command and with a single <cr>
39: * response using the "term cr" command. The setting of the "echo"
40: * and "df" commands does not matter. The radio should select UTC
41: * timescale using the "ts utc" command.
42: *
43: * There are two modes of operation for this driver. The first with
44: * default configuration is used with stock kernels and serial-line
45: * drivers and works with almost any machine. In this mode the driver
46: * assumes the radio captures a timestamp upon receipt of the "*" that
47: * begins the driver query. Accuracies in this mode are in the order of
48: * a millisecond or two and the receiver can be connected to only one
49: * host.
50: *
51: * The second mode of operation can be used for SunOS kernels that have
52: * been modified with the ppsclock streams module included in this
53: * distribution. The mode is enabled if flag3 of the fudge configuration
54: * command has been set to 1. In this mode a precise timestamp is
55: * available using a gadget box and 1-pps signal from the receiver. This
56: * improves the accuracy to the order of a few tens of microseconds. In
57: * addition, the serial output and 1-pps signal can be bussed to more
58: * than one hosts, but only one of them should be connected to the
59: * radio input data line.
60: */
61:
62: /*
63: * GPS Definitions
64: */
65: #define SMAX 200 /* statistics buffer length */
66: #define DEVICE "/dev/gps%d" /* device name and unit */
67: #define SPEED232 B9600 /* uart speed (9600 baud) */
68: #define PRECISION (-20) /* precision assumed (about 1 us) */
69: #define REFID "GPS\0" /* reference ID */
70: #define DESCRIPTION "Austron 2201A GPS Receiver" /* WRU */
71:
72: #define LENTOC 19 /* yy:ddd:hh:mm:ss.mmm timecode lngth */
73:
74: /*
75: * AS2201 unit control structure.
76: */
77: struct as2201unit {
78: char *lastptr; /* statistics buffer pointer */
79: char stats[SMAX]; /* statistics buffer */
80: int linect; /* count of lines remaining */
81: int index; /* current statistics command */
82: };
83:
84: /*
85: * Radio commands to extract statitistics
86: *
87: * A command consists of an ASCII string terminated by a <cr> (\r). The
88: * command list consist of a sequence of commands terminated by a null
89: * string ("\0"). One command from the list is sent immediately
90: * following each received timecode (*toc\r command) and the ASCII
91: * strings received from the radio are saved along with the timecode in
92: * the clockstats file. Subsequent commands are sent at each timecode,
93: * with the last one in the list followed by the first one. The data
94: * received from the radio consist of ASCII strings, each terminated by
95: * a <cr> (\r) character. The number of strings for each command is
96: * specified as the first line of output as an ASCII-encode number. Note
97: * that the ETF command requires the Input Buffer Module and the LORAN
98: * commands require the LORAN Assist Module. However, if these modules
99: * are not installed, the radio and this driver will continue to operate
100: * successfuly, but no data will be captured for these commands.
101: */
102: static char stat_command[][30] = {
103: "ITF\r", /* internal time/frequency */
104: "ETF\r", /* external time/frequency */
105: "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
106: "LORAN TDATA\r", /* LORAN signal data */
107: "ID;OPT;VER\r", /* model; options; software version */
108:
109: "ITF\r", /* internal time/frequency */
110: "ETF\r", /* external time/frequency */
111: "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
112: "TRSTAT\r", /* satellite tracking status */
113: "POS;PPS;PPSOFF\r", /* position, pps source, offsets */
114:
115: "ITF\r", /* internal time/frequency */
116: "ETF\r", /* external time/frequency */
117: "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
118: "LORAN TDATA\r", /* LORAN signal data */
119: "UTC\r", /* UTC leap info */
120:
121: "ITF\r", /* internal time/frequency */
122: "ETF\r", /* external time/frequency */
123: "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
124: "TRSTAT\r", /* satellite tracking status */
125: "OSC;ET;TEMP\r", /* osc type; tune volts; oven temp */
126: "\0" /* end of table */
127: };
128:
129: /*
130: * Function prototypes
131: */
132: static int as2201_start (int, struct peer *);
133: static void as2201_shutdown (int, struct peer *);
134: static void as2201_receive (struct recvbuf *);
135: static void as2201_poll (int, struct peer *);
136:
137: /*
138: * Transfer vector
139: */
140: struct refclock refclock_as2201 = {
141: as2201_start, /* start up driver */
142: as2201_shutdown, /* shut down driver */
143: as2201_poll, /* transmit poll message */
144: noentry, /* not used (old as2201_control) */
145: noentry, /* initialize driver (not used) */
146: noentry, /* not used (old as2201_buginfo) */
147: NOFLAGS /* not used */
148: };
149:
150:
151: /*
152: * as2201_start - open the devices and initialize data for processing
153: */
154: static int
155: as2201_start(
156: int unit,
157: struct peer *peer
158: )
159: {
160: register struct as2201unit *up;
161: struct refclockproc *pp;
162: int fd;
163: char gpsdev[20];
164:
165: /*
166: * Open serial port. Use CLK line discipline, if available.
167: */
168: snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit);
169: if (!(fd = refclock_open(gpsdev, SPEED232, LDISC_CLK)))
170: return (0);
171:
172: /*
173: * Allocate and initialize unit structure
174: */
175: up = emalloc(sizeof(*up));
176: memset(up, 0, sizeof(*up));
177: pp = peer->procptr;
178: pp->io.clock_recv = as2201_receive;
179: pp->io.srcclock = (caddr_t)peer;
180: pp->io.datalen = 0;
181: pp->io.fd = fd;
182: if (!io_addclock(&pp->io)) {
183: close(fd);
184: pp->io.fd = -1;
185: free(up);
186: return (0);
187: }
188: pp->unitptr = (caddr_t)up;
189:
190: /*
191: * Initialize miscellaneous variables
192: */
193: peer->precision = PRECISION;
194: peer->burst = NSTAGE;
195: pp->clockdesc = DESCRIPTION;
196: memcpy((char *)&pp->refid, REFID, 4);
197: up->lastptr = up->stats;
198: up->index = 0;
199: return (1);
200: }
201:
202:
203: /*
204: * as2201_shutdown - shut down the clock
205: */
206: static void
207: as2201_shutdown(
208: int unit,
209: struct peer *peer
210: )
211: {
212: register struct as2201unit *up;
213: struct refclockproc *pp;
214:
215: pp = peer->procptr;
216: up = (struct as2201unit *)pp->unitptr;
217: if (-1 != pp->io.fd)
218: io_closeclock(&pp->io);
219: if (NULL != up)
220: free(up);
221: }
222:
223:
224: /*
225: * as2201__receive - receive data from the serial interface
226: */
227: static void
228: as2201_receive(
229: struct recvbuf *rbufp
230: )
231: {
232: register struct as2201unit *up;
233: struct refclockproc *pp;
234: struct peer *peer;
235: l_fp trtmp;
236:
237: /*
238: * Initialize pointers and read the timecode and timestamp.
239: */
240: peer = (struct peer *)rbufp->recv_srcclock;
241: pp = peer->procptr;
242: up = (struct as2201unit *)pp->unitptr;
243: pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
244: #ifdef DEBUG
245: if (debug)
246: printf("gps: timecode %d %d %s\n",
247: up->linect, pp->lencode, pp->a_lastcode);
248: #endif
249: if (pp->lencode == 0)
250: return;
251:
252: /*
253: * If linect is greater than zero, we must be in the middle of a
254: * statistics operation, so simply tack the received data at the
255: * end of the statistics string. If not, we could either have
256: * just received the timecode itself or a decimal number
257: * indicating the number of following lines of the statistics
258: * reply. In the former case, write the accumulated statistics
259: * data to the clockstats file and continue onward to process
260: * the timecode; in the later case, save the number of lines and
261: * quietly return.
262: */
263: if (pp->sloppyclockflag & CLK_FLAG2)
264: pp->lastrec = trtmp;
265: if (up->linect > 0) {
266: up->linect--;
267: if ((int)(up->lastptr - up->stats + pp->lencode) > SMAX - 2)
268: return;
269: *up->lastptr++ = ' ';
270: (void)strcpy(up->lastptr, pp->a_lastcode);
271: up->lastptr += pp->lencode;
272: return;
273: } else {
274: if (pp->lencode == 1) {
275: up->linect = atoi(pp->a_lastcode);
276: return;
277: } else {
278: record_clock_stats(&peer->srcadr, up->stats);
279: #ifdef DEBUG
280: if (debug)
281: printf("gps: stat %s\n", up->stats);
282: #endif
283: }
284: }
285: up->lastptr = up->stats;
286: *up->lastptr = '\0';
287:
288: /*
289: * We get down to business, check the timecode format and decode
290: * its contents. If the timecode has invalid length or is not in
291: * proper format, we declare bad format and exit.
292: */
293: if (pp->lencode < LENTOC) {
294: refclock_report(peer, CEVNT_BADREPLY);
295: return;
296: }
297:
298: /*
299: * Timecode format: "yy:ddd:hh:mm:ss.mmm"
300: */
301: if (sscanf(pp->a_lastcode, "%2d:%3d:%2d:%2d:%2d.%3ld", &pp->year,
302: &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->nsec)
303: != 6) {
304: refclock_report(peer, CEVNT_BADREPLY);
305: return;
306: }
307: pp->nsec *= 1000000;
308:
309: /*
310: * Test for synchronization (this is a temporary crock).
311: */
312: if (pp->a_lastcode[2] != ':')
313: pp->leap = LEAP_NOTINSYNC;
314: else
315: pp->leap = LEAP_NOWARNING;
316:
317: /*
318: * Process the new sample in the median filter and determine the
319: * timecode timestamp.
320: */
321: if (!refclock_process(pp)) {
322: refclock_report(peer, CEVNT_BADTIME);
323: return;
324: }
325:
326: /*
327: * If CLK_FLAG4 is set, initialize the statistics buffer and
328: * send the next command. If not, simply write the timecode to
329: * the clockstats file.
330: */
331: (void)strcpy(up->lastptr, pp->a_lastcode);
332: up->lastptr += pp->lencode;
333: if (pp->sloppyclockflag & CLK_FLAG4) {
334: *up->lastptr++ = ' ';
335: (void)strcpy(up->lastptr, stat_command[up->index]);
336: up->lastptr += strlen(stat_command[up->index]);
337: up->lastptr--;
338: *up->lastptr = '\0';
339: (void)write(pp->io.fd, stat_command[up->index],
340: strlen(stat_command[up->index]));
341: up->index++;
342: if (*stat_command[up->index] == '\0')
343: up->index = 0;
344: }
345: }
346:
347:
348: /*
349: * as2201_poll - called by the transmit procedure
350: *
351: * We go to great pains to avoid changing state here, since there may be
352: * more than one eavesdropper receiving the same timecode.
353: */
354: static void
355: as2201_poll(
356: int unit,
357: struct peer *peer
358: )
359: {
360: struct refclockproc *pp;
361:
362: /*
363: * Send a "\r*toc\r" to get things going. We go to great pains
364: * to avoid changing state, since there may be more than one
365: * eavesdropper watching the radio.
366: */
367: pp = peer->procptr;
368: if (write(pp->io.fd, "\r*toc\r", 6) != 6) {
369: refclock_report(peer, CEVNT_FAULT);
370: } else {
371: pp->polls++;
372: if (!(pp->sloppyclockflag & CLK_FLAG2))
373: get_systime(&pp->lastrec);
374: }
375: if (peer->burst > 0)
376: return;
377: if (pp->coderecv == pp->codeproc) {
378: refclock_report(peer, CEVNT_TIMEOUT);
379: return;
380: }
381: refclock_receive(peer);
382: peer->burst = NSTAGE;
383: }
384:
385: #else
386: int refclock_as2201_bs;
387: #endif /* REFCLOCK */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>