]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ntp/ntpd/refclock_ulink.c
This commit was generated by cvs2svn to compensate for changes in r58310,
[FreeBSD/FreeBSD.git] / contrib / ntp / ntpd / refclock_ulink.c
1 /*
2  * refclock_ulink - clock driver for Ultralink Model 320 WWVB receivers
3  * By Dave Strout <dstrout@linuxfoundary.com>
4  *
5  * Latest version is always on www.linuxfoundary.com
6  *
7  * Based on the Spectracom driver
8  */
9
10 #ifdef HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13
14 #if defined(REFCLOCK) && defined(CLOCK_ULINK)
15
16 #include <stdio.h>
17 #include <ctype.h>
18 #include <sys/time.h>
19 #include <time.h>
20
21 #include "ntpd.h"
22 #include "ntp_io.h"
23 #include "ntp_refclock.h"
24 #include "ntp_calendar.h"
25 #include "ntp_stdlib.h"
26
27 /*
28  * This driver supports the Ultralink Model 320 WWVB receiver.  The Model 320 is
29  * an RS-232 powered unit which consists of two parts:  a DB-25 shell that contains
30  * a microprocessor, and an approx 2"x4" plastic box that contains the antenna.
31  * The two are connected by a 6-wire RJ-25 cable of length up to 1000'.  The 
32  * microprocessor steals power from the RS-232 port, which means that the port must 
33  * be kept open all of the time.  The unit also has an internal clock for loss of signal
34  * periods.  Claimed accuracy is 0.1 sec.
35  * 
36  * The timecode format is:
37  *
38  *  <cr><lf>SQRYYYYDDD+HH:MM:SS.mmLT<cr>
39  *
40  * where:
41  *
42  * S = 'S' -- sync'd in last hour, '0'-'9' - hours x 10 since last update, else '?'
43  * Q = Number of correlating time-frames, from 0 to 5
44  * R = 'R' -- reception in progress, 'N' -- Noisy reception, ' ' -- standby mode
45  * YYYY = year from 1990 to 2089
46  * DDD = current day from 1 to 366
47  * + = '+' if current year is a leap year, else ' '
48  * HH = UTC hour 0 to 23
49  * MM = Minutes of current hour from 0 to 59
50  * SS = Seconds of current minute from 0 to 59
51  * mm = 10's milliseconds of the current second from 00 to 99
52  * L  = Leap second pending at end of month -- 'I' = inset, 'D'=delete
53  * T  = DST <-> STD transition indicators
54  *
55  * Note that this driver does not do anything with the L or T flags.
56  *
57  * The M320 also has a 'U' command which returns UT1 correction information.  It
58  * is not used in this driver.
59  *
60  */
61
62 /*
63  * Interface definitions
64  */
65 #define DEVICE          "/dev/ulink%d" /* device name and unit */
66 #define SPEED232        B9600   /* uart speed (9600 baud) */
67 #define PRECISION       (-13)   /* precision assumed (about 100 us) */
68 #define REFID           "M320"  /* reference ID */
69 #define DESCRIPTION     "Ultralink WWVB Receiver" /* WRU */
70
71 #define LENWWVB0        28      /* format 0 timecode length */
72 #define LENWWVB2        24      /* format 2 timecode length */
73 #define LENWWVB3        29      /* format 3 timecode length */
74
75 #define MONLIN          15      /* number of monitoring lines */
76
77 /*
78  * ULINK unit control structure
79  */
80 struct ulinkunit {
81         u_char  tcswitch;       /* timecode switch */
82         l_fp    laststamp;      /* last receive timestamp */
83         u_char  lasthour;       /* last hour (for monitor) */
84         u_char  linect;         /* count ignored lines (for monitor */
85 };
86
87 /*
88  * Function prototypes
89  */
90 static  int     ulink_start     P((int, struct peer *));
91 static  void    ulink_shutdown  P((int, struct peer *));
92 static  void    ulink_receive   P((struct recvbuf *));
93 static  void    ulink_poll      P((int, struct peer *));
94 static  int     fd; /* We need to keep the serial port open to power the ULM320 */
95
96 /*
97  * Transfer vector
98  */
99 struct  refclock refclock_ulink = {
100         ulink_start,            /* start up driver */
101         ulink_shutdown,         /* shut down driver */
102         ulink_poll,             /* transmit poll message */
103         noentry,                /* not used (old wwvb_control) */
104         noentry,                /* initialize driver (not used) */
105         noentry,                /* not used (old wwvb_buginfo) */
106         NOFLAGS                 /* not used */
107 };
108
109
110 /*
111  * ulink_start - open the devices and initialize data for processing
112  */
113 static int
114 ulink_start(
115         int unit,
116         struct peer *peer
117         )
118 {
119         register struct ulinkunit *up;
120         struct refclockproc *pp;
121         char device[20];
122         fprintf(stderr, "Starting Ulink driver\n");
123         /*
124          * Open serial port. Use CLK line discipline, if available.
125          */
126         (void)sprintf(device, DEVICE, unit);
127         if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
128                 return (0);
129
130         /*
131          * Allocate and initialize unit structure
132          */
133         if (!(up = (struct ulinkunit *)
134               emalloc(sizeof(struct ulinkunit)))) {
135                 (void) close(fd);
136                 return (0);
137         }
138         memset((char *)up, 0, sizeof(struct ulinkunit));
139         pp = peer->procptr;
140         pp->unitptr = (caddr_t)up;
141         pp->io.clock_recv = ulink_receive;
142         pp->io.srcclock = (caddr_t)peer;
143         pp->io.datalen = 0;
144         pp->io.fd = fd;
145         if (!io_addclock(&pp->io)) {
146                 (void) close(fd);
147                 free(up);
148                 return (0);
149         }
150
151         /*
152          * Initialize miscellaneous variables
153          */
154         peer->precision = PRECISION;
155         peer->flags |= FLAG_BURST;
156         peer->burst = NSTAGE;
157         pp->clockdesc = DESCRIPTION;
158         memcpy((char *)&pp->refid, REFID, 4);
159         return (1);
160 }
161
162
163 /*
164  * ulink_shutdown - shut down the clock
165  */
166 static void
167 ulink_shutdown(
168         int unit,
169         struct peer *peer
170         )
171 {
172         register struct ulinkunit *up;
173         struct refclockproc *pp;
174
175         pp = peer->procptr;
176         up = (struct ulinkunit *)pp->unitptr;
177         io_closeclock(&pp->io);
178         free(up);
179         close(fd);
180 }
181
182
183 /*
184  * ulink_receive - receive data from the serial interface
185  */
186 static void
187 ulink_receive(
188         struct recvbuf *rbufp
189         )
190 {
191         struct ulinkunit *up;
192         struct refclockproc *pp;
193         struct peer *peer;
194
195         l_fp    trtmp;          /* arrival timestamp */
196         char    syncchar;       /* synchronization indicator */
197         char    qualchar;       /* quality indicator */
198         char    modechar;       /* Modes: 'R'=rx, 'N'=noise, ' '=standby */
199         char    leapchar;       /* leap indicator */
200         int     temp;           /* int temp */
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 ulinkunit *)pp->unitptr;
208         temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
209
210         /*
211          * Note we get a buffer and timestamp for both a <cr> and <lf>,
212          * but only the <cr> timestamp is retained. Note: in format 0 on
213          * a Netclock/2 or upgraded 8170 the start bit is delayed 100
214          * +-50 us relative to the pps; however, on an unmodified 8170
215          * the start bit can be delayed up to 10 ms. In format 2 the
216          * reading precision is only to the millisecond. Thus, unless
217          * you have a pps gadget and don't have to have the year, format
218          * 0 provides the lowest jitter.
219          */
220         if (temp == 0) {
221                 if (up->tcswitch == 0) {
222                         up->tcswitch = 1;
223                         up->laststamp = trtmp;
224                 } else
225                     up->tcswitch = 0;
226                 return;
227         }
228         pp->lencode = temp;
229         pp->lastrec = up->laststamp;
230         up->laststamp = trtmp;
231         up->tcswitch = 1;
232 #ifdef DEBUG
233         if (debug)
234                 printf("ulink: timecode %d %s\n", pp->lencode,
235                     pp->a_lastcode);
236 #endif
237
238         /*
239          * We get down to business, check the timecode format and decode
240          * its contents. This code uses the timecode length to determine
241          * whether format 0 or format 2. If the timecode has invalid
242          * length or is not in proper format, we declare bad format and
243          * exit.
244          */
245         syncchar = qualchar = leapchar = ' ';
246         pp->msec = 0;
247         
248         /*
249          * Timecode format SQRYYYYDDD+HH:MM:SS.mmLT
250          */
251         sscanf(pp->a_lastcode, "%c%c%c%4d%3d%c%2d:%2d:%2d.%2d",
252                &syncchar, &qualchar, &modechar, &pp->year, &pp->day,
253                &leapchar,&pp->hour, &pp->minute, &pp->second,&pp->msec);
254
255         pp->msec *= 10; /* M320 returns 10's of msecs */
256         qualchar = ' ';
257
258         /*
259          * Decode synchronization, quality and leap characters. If
260          * unsynchronized, set the leap bits accordingly and exit.
261          * Otherwise, set the leap bits according to the leap character.
262          * Once synchronized, the dispersion depends only on the
263          * quality character.
264          */
265         pp->disp = .001;
266         pp->leap = LEAP_NOWARNING;
267
268         /*
269          * Process the new sample in the median filter and determine the
270          * timecode timestamp.
271          */
272         if (!refclock_process(pp))
273                 refclock_report(peer, CEVNT_BADTIME);
274 }
275
276
277 /*
278  * ulink_poll - called by the transmit procedure
279  */
280 static void
281 ulink_poll(
282         int unit,
283         struct peer *peer
284         )
285 {
286         register struct ulinkunit *up;
287         struct refclockproc *pp;
288         char pollchar;
289
290         pp = peer->procptr;
291         up = (struct ulinkunit *)pp->unitptr;
292         pollchar = 'T';
293         if (write(pp->io.fd, &pollchar, 1) != 1)
294                 refclock_report(peer, CEVNT_FAULT);
295         else
296                 pp->polls++;
297         if (peer->burst > 0)
298                 return;
299         if (pp->coderecv == pp->codeproc) {
300                 refclock_report(peer, CEVNT_TIMEOUT);
301                 return;
302         }
303         record_clock_stats(&peer->srcadr, pp->a_lastcode);
304         refclock_receive(peer);
305         peer->burst = NSTAGE;
306
307         /*
308          * If the monitor flag is set (flag4), we dump the internal
309          * quality table at the first timecode beginning the day.
310          */
311         if (pp->sloppyclockflag & CLK_FLAG4 && pp->hour <
312             (int)up->lasthour)
313                 up->linect = MONLIN;
314         up->lasthour = pp->hour;
315 }
316
317 #else
318 int refclock_ulink_bs;
319 #endif /* REFCLOCK */