~ubuntu-branches/debian/squeeze/ntp/squeeze-201010051545

« back to all changes in this revision

Viewing changes to ntpd/refclock_zyfer.c

  • Committer: Bazaar Package Importer
  • Author(s): Matt Zimmerman
  • Date: 2004-10-11 16:10:27 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20041011161027-icyjbji8ujym633o
Tags: 1:4.2.0a-10ubuntu2
Use ntp.ubuntulinux.org instead of pool.ntp.org

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * refclock_zyfer - clock driver for the Zyfer GPSTarplus Clock
 
3
 *
 
4
 * Harlan Stenn, Jan 2002
 
5
 */
 
6
 
 
7
#ifdef HAVE_CONFIG_H
 
8
#include <config.h>
 
9
#endif
 
10
 
 
11
#if defined(REFCLOCK) && defined(CLOCK_ZYFER)
 
12
 
 
13
#include "ntpd.h"
 
14
#include "ntp_io.h"
 
15
#include "ntp_refclock.h"
 
16
#include "ntp_stdlib.h"
 
17
#include "ntp_unixtime.h"
 
18
 
 
19
#include <stdio.h>
 
20
#include <ctype.h>
 
21
 
 
22
#ifdef HAVE_SYS_TERMIOS_H
 
23
# include <sys/termios.h>
 
24
#endif
 
25
#ifdef HAVE_SYS_PPSCLOCK_H
 
26
# include <sys/ppsclock.h>
 
27
#endif
 
28
 
 
29
/*
 
30
 * This driver provides support for the TOD serial port of a Zyfer GPStarplus.
 
31
 * This clock also provides PPS as well as IRIG outputs.
 
32
 * Precision is limited by the serial driver, etc.
 
33
 *
 
34
 * If I was really brave I'd hack/generalize the serial driver to deal
 
35
 * with arbitrary on-time characters.  This clock *begins* the stream with
 
36
 * `!`, the on-time character, and the string is *not* EOL-terminated.
 
37
 *
 
38
 * Configure the beast for 9600, 8N1.  While I see leap-second stuff
 
39
 * in the documentation, the published specs on the TOD format only show
 
40
 * the seconds going to '59'.  I see no leap warning in the TOD format.
 
41
 *
 
42
 * The clock sends the following message once per second:
 
43
 *
 
44
 *      !TIME,2002,017,07,59,32,2,4,1
 
45
 *            YYYY DDD HH MM SS m T O
 
46
 *
 
47
 *      !               On-time character
 
48
 *      YYYY            Year
 
49
 *      DDD     001-366 Day of Year
 
50
 *      HH      00-23   Hour
 
51
 *      MM      00-59   Minute
 
52
 *      SS      00-59   Second (probably 00-60)
 
53
 *      m       1-5     Time Mode:
 
54
 *                      1 = GPS time
 
55
 *                      2 = UTC time
 
56
 *                      3 = LGPS time (Local GPS)
 
57
 *                      4 = LUTC time (Local UTC)
 
58
 *                      5 = Manual time
 
59
 *      T       4-9     Time Figure Of Merit:
 
60
 *                      4         x <= 1us
 
61
 *                      5   1us < x <= 10 us
 
62
 *                      6  10us < x <= 100us
 
63
 *                      7 100us < x <= 1ms
 
64
 *                      8   1ms < x <= 10ms
 
65
 *                      9  10ms < x
 
66
 *      O       0-4     Operation Mode:
 
67
 *                      0 Warm-up
 
68
 *                      1 Time Locked
 
69
 *                      2 Coasting
 
70
 *                      3 Recovering
 
71
 *                      4 Manual
 
72
 *
 
73
 */
 
74
 
 
75
/*
 
76
 * Interface definitions
 
77
 */
 
78
#define DEVICE          "/dev/zyfer%d" /* device name and unit */
 
79
#define SPEED232        B9600   /* uart speed (9600 baud) */
 
80
#define PRECISION       (-20)   /* precision assumed (about 1 us) */
 
81
#define REFID           "GPS\0" /* reference ID */
 
82
#define DESCRIPTION     "Zyfer GPStarplus" /* WRU */
 
83
 
 
84
#define LENZYFER        29      /* timecode length */
 
85
 
 
86
/*
 
87
 * Unit control structure
 
88
 */
 
89
struct zyferunit {
 
90
        u_char  Rcvbuf[LENZYFER + 1];
 
91
        u_char  polled;         /* poll message flag */
 
92
        int     pollcnt;
 
93
        l_fp    tstamp;         /* timestamp of last poll */
 
94
        int     Rcvptr;
 
95
};
 
96
 
 
97
/*
 
98
 * Function prototypes
 
99
 */
 
100
static  int     zyfer_start     P((int, struct peer *));
 
101
static  void    zyfer_shutdown  P((int, struct peer *));
 
102
static  void    zyfer_receive   P((struct recvbuf *));
 
103
static  void    zyfer_poll      P((int, struct peer *));
 
104
 
 
105
/*
 
106
 * Transfer vector
 
107
 */
 
108
struct  refclock refclock_zyfer = {
 
109
        zyfer_start,            /* start up driver */
 
110
        zyfer_shutdown,         /* shut down driver */
 
111
        zyfer_poll,             /* transmit poll message */
 
112
        noentry,                /* not used (old zyfer_control) */
 
113
        noentry,                /* initialize driver (not used) */
 
114
        noentry,                /* not used (old zyfer_buginfo) */
 
115
        NOFLAGS                 /* not used */
 
116
};
 
117
 
 
118
 
 
119
/*
 
120
 * zyfer_start - open the devices and initialize data for processing
 
121
 */
 
122
static int
 
123
zyfer_start(
 
124
        int unit,
 
125
        struct peer *peer
 
126
        )
 
127
{
 
128
        register struct zyferunit *up;
 
129
        struct refclockproc *pp;
 
130
        int fd;
 
131
        char device[20];
 
132
 
 
133
        /*
 
134
         * Open serial port.
 
135
         * Something like LDISC_ACTS that looked for ! would be nice...
 
136
         */
 
137
        (void)sprintf(device, DEVICE, unit);
 
138
        if ( !(fd = refclock_open(device, SPEED232, LDISC_RAW)) )
 
139
            return (0);
 
140
 
 
141
        msyslog(LOG_NOTICE, "zyfer(%d) fd: %d dev <%s>", unit, fd, device);
 
142
 
 
143
        /*
 
144
         * Allocate and initialize unit structure
 
145
         */
 
146
        if (!(up = (struct zyferunit *)
 
147
              emalloc(sizeof(struct zyferunit)))) {
 
148
                (void) close(fd);
 
149
                return (0);
 
150
        }
 
151
        memset((char *)up, 0, sizeof(struct zyferunit));
 
152
        pp = peer->procptr;
 
153
        pp->io.clock_recv = zyfer_receive;
 
154
        pp->io.srcclock = (caddr_t)peer;
 
155
        pp->io.datalen = 0;
 
156
        pp->io.fd = fd;
 
157
        if (!io_addclock(&pp->io)) {
 
158
                (void) close(fd);
 
159
                free(up);
 
160
                return (0);
 
161
        }
 
162
        pp->unitptr = (caddr_t)up;
 
163
 
 
164
        /*
 
165
         * Initialize miscellaneous variables
 
166
         */
 
167
        peer->precision = PRECISION;
 
168
        pp->clockdesc = DESCRIPTION;
 
169
        memcpy((char *)&pp->refid, REFID, 4);
 
170
        up->pollcnt = 2;
 
171
        up->polled = 0;         /* May not be needed... */
 
172
 
 
173
        return (1);
 
174
}
 
175
 
 
176
 
 
177
/*
 
178
 * zyfer_shutdown - shut down the clock
 
179
 */
 
180
static void
 
181
zyfer_shutdown(
 
182
        int unit,
 
183
        struct peer *peer
 
184
        )
 
185
{
 
186
        register struct zyferunit *up;
 
187
        struct refclockproc *pp;
 
188
 
 
189
        pp = peer->procptr;
 
190
        up = (struct zyferunit *)pp->unitptr;
 
191
        io_closeclock(&pp->io);
 
192
        free(up);
 
193
}
 
194
 
 
195
 
 
196
/*
 
197
 * zyfer_receive - receive data from the serial interface
 
198
 */
 
199
static void
 
200
zyfer_receive(
 
201
        struct recvbuf *rbufp
 
202
        )
 
203
{
 
204
        register struct zyferunit *up;
 
205
        struct refclockproc *pp;
 
206
        struct peer *peer;
 
207
        int tmode;              /* Time mode */
 
208
        int tfom;               /* Time Figure Of Merit */
 
209
        int omode;              /* Operation mode */
 
210
        u_char *p;
 
211
#ifdef PPS
 
212
        struct ppsclockev ppsev;
 
213
        int request;
 
214
#ifdef HAVE_CIOGETEV
 
215
        request = CIOGETEV;
 
216
#endif
 
217
#ifdef HAVE_TIOCGPPSEV
 
218
        request = TIOCGPPSEV;
 
219
#endif
 
220
#endif /* PPS */
 
221
 
 
222
        peer = (struct peer *)rbufp->recv_srcclock;
 
223
        pp = peer->procptr;
 
224
        up = (struct zyferunit *)pp->unitptr;
 
225
        p = (u_char *) &rbufp->recv_space;
 
226
        /*
 
227
         * If lencode is 0:
 
228
         * - if *rbufp->recv_space is !
 
229
         * - - call refclock_gtlin to get things going
 
230
         * - else flush
 
231
         * else stuff it on the end of lastcode
 
232
         * If we don't have LENZYFER bytes
 
233
         * - wait for more data
 
234
         * Crack the beast, and if it's OK, process it.
 
235
         *
 
236
         * We use refclock_gtlin() because we might use LDISC_CLK.
 
237
         *
 
238
         * Under FreeBSD, we get the ! followed by two 14-byte packets.
 
239
         */
 
240
 
 
241
        if (pp->lencode >= LENZYFER)
 
242
                pp->lencode = 0;
 
243
 
 
244
        if (!pp->lencode) {
 
245
                if (*p == '!')
 
246
                        pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode,
 
247
                                                     BMAX, &pp->lastrec);
 
248
                else
 
249
                        return;
 
250
        } else {
 
251
                memcpy(pp->a_lastcode + pp->lencode, p, rbufp->recv_length);
 
252
                pp->lencode += rbufp->recv_length;
 
253
                pp->a_lastcode[pp->lencode] = '\0';
 
254
        }
 
255
 
 
256
        if (pp->lencode < LENZYFER)
 
257
                return;
 
258
 
 
259
        record_clock_stats(&peer->srcadr, pp->a_lastcode);
 
260
 
 
261
        /*
 
262
         * We get down to business, check the timecode format and decode
 
263
         * its contents. If the timecode has invalid length or is not in
 
264
         * proper format, we declare bad format and exit.
 
265
         */
 
266
 
 
267
        if (pp->lencode != LENZYFER) {
 
268
                refclock_report(peer, CEVNT_BADTIME);
 
269
                return;
 
270
        }
 
271
 
 
272
        /*
 
273
         * Timecode sample: "!TIME,2002,017,07,59,32,2,4,1"
 
274
         */
 
275
        if (sscanf(pp->a_lastcode, "!TIME,%4d,%3d,%2d,%2d,%2d,%d,%d,%d",
 
276
                   &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second,
 
277
                   &tmode, &tfom, &omode) != 8) {
 
278
                refclock_report(peer, CEVNT_BADREPLY);
 
279
                return;
 
280
        }
 
281
 
 
282
        if (tmode != 2) {
 
283
                refclock_report(peer, CEVNT_BADTIME);
 
284
                return;
 
285
        }
 
286
 
 
287
        /* Should we make sure tfom is 4? */
 
288
 
 
289
        if (omode != 1) {
 
290
                pp->leap = LEAP_NOTINSYNC;
 
291
                return;
 
292
        }
 
293
#ifdef PPS
 
294
        if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) {
 
295
                ppsev.tv.tv_sec += (u_int32) JAN_1970;
 
296
                TVTOTS(&ppsev.tv,&up->tstamp);
 
297
        }
 
298
        /* record the last ppsclock event time stamp */
 
299
        pp->lastrec = up->tstamp;
 
300
#endif /* PPS */
 
301
        if (!refclock_process(pp)) {
 
302
                refclock_report(peer, CEVNT_BADTIME);
 
303
                return;
 
304
        }
 
305
 
 
306
        /*
 
307
         * Good place for record_clock_stats()
 
308
         */
 
309
        up->pollcnt = 2;
 
310
 
 
311
        if (up->polled) {
 
312
                up->polled = 0;
 
313
                refclock_receive(peer);
 
314
        }
 
315
}
 
316
 
 
317
 
 
318
/*
 
319
 * zyfer_poll - called by the transmit procedure
 
320
 */
 
321
static void
 
322
zyfer_poll(
 
323
        int unit,
 
324
        struct peer *peer
 
325
        )
 
326
{
 
327
        register struct zyferunit *up;
 
328
        struct refclockproc *pp;
 
329
 
 
330
        /*
 
331
         * We don't really do anything here, except arm the receiving
 
332
         * side to capture a sample and check for timeouts.
 
333
         */
 
334
        pp = peer->procptr;
 
335
        up = (struct zyferunit *)pp->unitptr;
 
336
        if (!up->pollcnt)
 
337
                refclock_report(peer, CEVNT_TIMEOUT);
 
338
        else
 
339
                up->pollcnt--;
 
340
        pp->polls++;
 
341
        up->polled = 1;
 
342
}
 
343
 
 
344
#else
 
345
int refclock_zyfer_bs;
 
346
#endif /* REFCLOCK */