1 | /* |
---|
2 | * refclock_irig - clock driver for the IRIG audio decoder |
---|
3 | */ |
---|
4 | #ifdef HAVE_CONFIG_H |
---|
5 | #include <config.h> |
---|
6 | #endif |
---|
7 | |
---|
8 | #if defined(REFCLOCK) && defined(IRIG) |
---|
9 | |
---|
10 | #include <stdio.h> |
---|
11 | #include <ctype.h> |
---|
12 | #include <sys/time.h> |
---|
13 | #include <sys/ioccom.h> |
---|
14 | |
---|
15 | #include "ntpd.h" |
---|
16 | #include "ntp_io.h" |
---|
17 | #include "ntp_refclock.h" |
---|
18 | #include "ntp_unixtime.h" |
---|
19 | #include <sys/bsd_audioirig.h> |
---|
20 | #include "ntp_stdlib.h" |
---|
21 | |
---|
22 | /* |
---|
23 | * This driver supports the IRIG audio decoder. This clever gadget uses |
---|
24 | * a modified BSD audio driver for the Sun SPARCstation which provides |
---|
25 | * a timestamp, raw binary timecode, status byte and decoded ASCII |
---|
26 | * timecode. The data are represented in the structure in the |
---|
27 | * sys/bsd_audioirig.h header file: |
---|
28 | * |
---|
29 | * struct irig_time { |
---|
30 | * struct timeval stamp; timestamp |
---|
31 | * u_char bits[13]; 100 IRIG data bits |
---|
32 | * u_char status; status byte |
---|
33 | * char time[14]; time string (null terminated) |
---|
34 | * |
---|
35 | * where stamp represents a timestamp at the zero crossing of the index |
---|
36 | * marker at the second's epoch, bits is a 13-octet, zero-padded binary- |
---|
37 | * coded string representing code elements 1 through 100 in the IRIG-B |
---|
38 | * code format, and status is a status bute, The decoded timestamp is a |
---|
39 | * 13-octet, null-terminated ASCII string "ddd hh:mm:ss*", where ddd is |
---|
40 | * the day of year, hh:mm:ss the time of day and * is a status |
---|
41 | * indicator, with " " indicating valid time and "?" indicating |
---|
42 | * something wrong. |
---|
43 | * |
---|
44 | * The timestamp is in Unix timeval format, consisting of two 32-bit |
---|
45 | * words, the first of which is the seconds since 1970 and the second is |
---|
46 | * the fraction of the second in microseconds. The status byte is zero |
---|
47 | * if (a) the input signal is within amplitude tolerances, (b) the raw |
---|
48 | * binary timecode contains only valid code elements, (c) 11 position |
---|
49 | * identifiers have been found at the expected element positions, (d) |
---|
50 | * the clock status byte contained in the timecode is valid, and (e) a |
---|
51 | * time determination has been made since the last read() system call. |
---|
52 | * |
---|
53 | * The 100 elements of the IRIG-B timecode are numbered from 0 through |
---|
54 | * 99. Position identifiers occur at elements 0, 9, 19 and every ten |
---|
55 | * thereafter to 99. The control function (CF) elements begin at element |
---|
56 | * 50 (CF 1) and extend to element 78 (CF 27). The straight-binary- |
---|
57 | * seconds (SBS) field, which encodes the seconds of the UTC day, begins |
---|
58 | * at element 80 (CF 28) and extends to element 97 (CF 44). The encoding |
---|
59 | * of elements 50 (CF 1) through 78 (CF 27) is device dependent. This |
---|
60 | * driver presently does not use the CF elements. |
---|
61 | * |
---|
62 | * Where feasible, the interface should be operated with signature |
---|
63 | * control, so that, if the IRIG signal is lost or malformed, the |
---|
64 | * interface produces an unmodulated signal, rather than possibly random |
---|
65 | * digits. The driver will declare "unsynchronized" in this case. |
---|
66 | * |
---|
67 | * Spectracom Netclock/2 WWVB Synchronized Clock |
---|
68 | * |
---|
69 | * Element CF Function |
---|
70 | * ------------------------------------- |
---|
71 | * 55 6 time sync status |
---|
72 | * 60-63 10-13 bcd year units |
---|
73 | * 65-68 15-18 bcd year tens |
---|
74 | * |
---|
75 | */ |
---|
76 | |
---|
77 | /* |
---|
78 | * IRIG interface definitions |
---|
79 | */ |
---|
80 | #define DEVICE "/dev/irig%d" /* device name and unit */ |
---|
81 | #define PRECISION (-13) /* precision assumed (100 us) */ |
---|
82 | #define REFID "IRIG" /* reference ID */ |
---|
83 | #define DESCRIPTION "IRIG Audio Decoder" /* WRU */ |
---|
84 | |
---|
85 | #define NSAMPLES 3 /* stages of median filter */ |
---|
86 | #define IRIG_FORMAT 1 /* IRIG timestamp format */ |
---|
87 | |
---|
88 | /* |
---|
89 | * Imported from ntp_timer module |
---|
90 | */ |
---|
91 | extern u_long current_time; /* current time (s) */ |
---|
92 | |
---|
93 | /* |
---|
94 | * Imported from ntpd module |
---|
95 | */ |
---|
96 | extern int debug; /* global debug flag */ |
---|
97 | |
---|
98 | /* |
---|
99 | * Function prototypes |
---|
100 | */ |
---|
101 | static int irig_start P((int, struct peer *)); |
---|
102 | static void irig_shutdown P((int, struct peer *)); |
---|
103 | static void irig_poll P((int, struct peer *)); |
---|
104 | |
---|
105 | /* |
---|
106 | * Transfer vector |
---|
107 | */ |
---|
108 | struct refclock refclock_irig = { |
---|
109 | irig_start, /* start up driver */ |
---|
110 | irig_shutdown, /* shut down driver */ |
---|
111 | irig_poll, /* transmit poll message */ |
---|
112 | noentry, /* not used (old irig_control) */ |
---|
113 | noentry, /* initialize driver (not used) */ |
---|
114 | noentry, /* not used (old irig_buginfo) */ |
---|
115 | NOFLAGS /* not used */ |
---|
116 | }; |
---|
117 | |
---|
118 | |
---|
119 | /* |
---|
120 | * irig_start - open the device and initialize data for processing |
---|
121 | */ |
---|
122 | static int |
---|
123 | irig_start(unit, peer) |
---|
124 | int unit; |
---|
125 | struct peer *peer; |
---|
126 | { |
---|
127 | register struct refclockproc *pp; |
---|
128 | char device[20]; |
---|
129 | int fd; |
---|
130 | int format = IRIG_FORMAT; |
---|
131 | |
---|
132 | /* |
---|
133 | * Open audio device and set format |
---|
134 | */ |
---|
135 | (void)sprintf(device, DEVICE, unit); |
---|
136 | fd = open(device, O_RDONLY | O_NDELAY, 0777); |
---|
137 | if (fd == -1) { |
---|
138 | msyslog(LOG_ERR, "irig_start: open of %s: %m", device); |
---|
139 | return (0); |
---|
140 | } |
---|
141 | if (ioctl(fd, AUDIO_IRIG_OPEN, 0) < 0) { |
---|
142 | msyslog(LOG_ERR, "irig_start: AUDIO_IRIG_OPEN %m"); |
---|
143 | close(fd); |
---|
144 | return (0); |
---|
145 | } |
---|
146 | if (ioctl(fd, AUDIO_IRIG_SETFORMAT, (char *)&format) < 0) { |
---|
147 | msyslog(LOG_ERR, "irig_start: AUDIO_IRIG_SETFORMAT %m", |
---|
148 | DEVICE); |
---|
149 | close(fd); |
---|
150 | return (0); |
---|
151 | } |
---|
152 | pp = peer->procptr; |
---|
153 | pp->io.clock_recv = noentry; |
---|
154 | pp->io.srcclock = (caddr_t)peer; |
---|
155 | pp->io.datalen = 0; |
---|
156 | pp->io.fd = fd; |
---|
157 | |
---|
158 | /* |
---|
159 | * Initialize miscellaneous variables |
---|
160 | */ |
---|
161 | peer->precision = PRECISION; |
---|
162 | pp->clockdesc = DESCRIPTION; |
---|
163 | memcpy((char *)&pp->refid, REFID, 4); |
---|
164 | return (1); |
---|
165 | } |
---|
166 | |
---|
167 | |
---|
168 | /* |
---|
169 | * irig_shutdown - shut down the clock |
---|
170 | */ |
---|
171 | static void |
---|
172 | irig_shutdown(unit, peer) |
---|
173 | int unit; |
---|
174 | struct peer *peer; |
---|
175 | { |
---|
176 | struct refclockproc *pp; |
---|
177 | |
---|
178 | pp = peer->procptr; |
---|
179 | io_closeclock(&pp->io); |
---|
180 | } |
---|
181 | |
---|
182 | |
---|
183 | /* |
---|
184 | * irig_poll - called by the transmit procedure |
---|
185 | */ |
---|
186 | static void |
---|
187 | irig_poll(unit, peer) |
---|
188 | int unit; |
---|
189 | struct peer *peer; |
---|
190 | { |
---|
191 | |
---|
192 | struct refclockproc *pp; |
---|
193 | struct irig_time buf; |
---|
194 | char *cp, *dp; |
---|
195 | u_char *dpt; |
---|
196 | int i; |
---|
197 | |
---|
198 | pp = peer->procptr; |
---|
199 | if (read(pp->io.fd, (char *) &buf, sizeof(buf)) != sizeof(buf)) { |
---|
200 | refclock_report(peer, CEVNT_FAULT); |
---|
201 | return; |
---|
202 | } |
---|
203 | pp->polls++; |
---|
204 | |
---|
205 | #ifdef DEBUG |
---|
206 | if (debug) { |
---|
207 | dpt = (u_char *)&buf; |
---|
208 | printf("irig: "); |
---|
209 | for (i = 0; i < sizeof(buf); i++) |
---|
210 | printf("%02x", *dpt++); |
---|
211 | printf("\n"); |
---|
212 | } |
---|
213 | #endif |
---|
214 | |
---|
215 | buf.stamp.tv_sec += JAN_1970; |
---|
216 | TVTOTS(&buf.stamp, &pp->lastrec); |
---|
217 | cp = buf.time; |
---|
218 | dp = pp->a_lastcode; |
---|
219 | for (i = 0; i < sizeof(buf.time); i++) |
---|
220 | *dp++ = *cp++; |
---|
221 | *--dp = '\0'; |
---|
222 | pp->lencode = dp - pp->a_lastcode; |
---|
223 | |
---|
224 | #ifdef DEBUG |
---|
225 | if (debug) |
---|
226 | printf("irig: time %s timecode %d %s\n", |
---|
227 | ulfptoa(&pp->lastrec, 6), pp->lencode, |
---|
228 | pp->a_lastcode); |
---|
229 | #endif |
---|
230 | record_clock_stats(&peer->srcadr, pp->a_lastcode); |
---|
231 | |
---|
232 | /* |
---|
233 | * Get IRIG time and convert to timestamp format |
---|
234 | */ |
---|
235 | if (sscanf(pp->a_lastcode, "%3d %2d:%2d:%2d", |
---|
236 | &pp->day, &pp->hour, &pp->minute, &pp->second) != 4) { |
---|
237 | refclock_report(peer, CEVNT_BADREPLY); |
---|
238 | return; |
---|
239 | } |
---|
240 | if (pp->a_lastcode[12] != ' ') { |
---|
241 | pp->leap = LEAP_NOTINSYNC; |
---|
242 | } else { |
---|
243 | pp->leap = 0; |
---|
244 | pp->lasttime = current_time; |
---|
245 | } |
---|
246 | |
---|
247 | /* |
---|
248 | * Process the new sample in the median filter and determine the |
---|
249 | * reference clock offset and dispersion. We use lastrec as both |
---|
250 | * the reference time and receive time in order to avoid being |
---|
251 | * cute, like setting the reference time later than the receive |
---|
252 | * time, which may cause a paranoid protocol module to chuck out |
---|
253 | * the data. |
---|
254 | */ |
---|
255 | if (!refclock_process(pp, NSAMPLES, NSAMPLES)) { |
---|
256 | refclock_report(peer, CEVNT_BADTIME); |
---|
257 | return; |
---|
258 | } |
---|
259 | refclock_receive(peer, &pp->offset, 0, pp->dispersion, |
---|
260 | &pp->lastrec, &pp->lastrec, pp->leap); |
---|
261 | } |
---|
262 | |
---|
263 | #else /* not (REFCLOCK && IRIG) */ |
---|
264 | int refclock_irig_bs; |
---|
265 | #endif /* not (REFCLOCK && IRIG) */ |
---|