1ea906c41SOllivier Robert /*
2ea906c41SOllivier Robert * tg.c generate WWV or IRIG signals for test
3ea906c41SOllivier Robert */
4ea906c41SOllivier Robert /*
5ea906c41SOllivier Robert * This program can generate audio signals that simulate the WWV/H
6ea906c41SOllivier Robert * broadcast timecode. Alternatively, it can generate the IRIG-B
7ea906c41SOllivier Robert * timecode commonly used to synchronize laboratory equipment. It is
8ea906c41SOllivier Robert * intended to test the WWV/H driver (refclock_wwv.c) and the IRIG
9ea906c41SOllivier Robert * driver (refclock_irig.c) in the NTP driver collection.
10ea906c41SOllivier Robert *
11ea906c41SOllivier Robert * Besides testing the drivers themselves, this program can be used to
12ea906c41SOllivier Robert * synchronize remote machines over audio transmission lines or program
13ea906c41SOllivier Robert * feeds. The program reads the time on the local machine and sets the
14ea906c41SOllivier Robert * initial epoch of the signal generator within one millisecond.
15ea906c41SOllivier Robert * Alernatively, the initial epoch can be set to an arbitrary time. This
16ea906c41SOllivier Robert * is useful when searching for bugs and testing for correct response to
17ea906c41SOllivier Robert * a leap second in UTC. Note however, the ultimate accuracy is limited
18ea906c41SOllivier Robert * by the intrinsic frequency error of the codec sample clock, which can
19ea906c41SOllivier Robert # reach well over 100 PPM.
20ea906c41SOllivier Robert *
21ea906c41SOllivier Robert * The default is to route generated signals to the line output
22ea906c41SOllivier Robert * jack; the s option on the command line routes these signals to the
23ea906c41SOllivier Robert * internal speaker as well. The v option controls the speaker volume
24ea906c41SOllivier Robert * over the range 0-255. The signal generator by default uses WWV
25ea906c41SOllivier Robert * format; the h option switches to WWVH format and the i option
26ea906c41SOllivier Robert * switches to IRIG-B format.
27ea906c41SOllivier Robert *
28ea906c41SOllivier Robert * Once started the program runs continuously. The default initial epoch
29ea906c41SOllivier Robert * for the signal generator is read from the computer system clock when
30ea906c41SOllivier Robert * the program starts. The y option specifies an alternate epoch using a
31ea906c41SOllivier Robert * string yydddhhmmss, where yy is the year of century, ddd the day of
32ea906c41SOllivier Robert * year, hh the hour of day and mm the minute of hour. For instance,
33ea906c41SOllivier Robert * 1946Z on 1 January 2006 is 060011946. The l option lights the leap
34ea906c41SOllivier Robert * warning bit in the WWV/H timecode, so is handy to check for correct
35ea906c41SOllivier Robert * behavior at the next leap second epoch. The remaining options are
36ea906c41SOllivier Robert * specified below under the Parse Options heading. Most of these are
37ea906c41SOllivier Robert * for testing.
38ea906c41SOllivier Robert *
39ea906c41SOllivier Robert * During operation the program displays the WWV/H timecode (9 digits)
40ea906c41SOllivier Robert * or IRIG timecode (20 digits) as each new string is constructed. The
41ea906c41SOllivier Robert * display is followed by the BCD binary bits as transmitted. Note that
42ea906c41SOllivier Robert * the transmissionorder is low-order first as the frame is processed
43ea906c41SOllivier Robert * left to right. For WWV/H The leap warning L preceeds the first bit.
44ea906c41SOllivier Robert * For IRIG the on-time marker M preceeds the first (units) bit, so its
45ea906c41SOllivier Robert * code is delayed one bit and the next digit (tens) needs only three
46ea906c41SOllivier Robert * bits.
47ea906c41SOllivier Robert *
48ea906c41SOllivier Robert * The program has been tested with the Sun Blade 1500 running Solaris
49ea906c41SOllivier Robert * 10, but not yet with other machines. It uses no special features and
50ea906c41SOllivier Robert * should be readily portable to other hardware and operating systems.
51ea906c41SOllivier Robert */
52ea906c41SOllivier Robert #include <stdio.h>
53ea906c41SOllivier Robert #include <stdlib.h>
54ea906c41SOllivier Robert #include <time.h>
55ea906c41SOllivier Robert #include <sys/audio.h>
56ea906c41SOllivier Robert #include <math.h>
57ea906c41SOllivier Robert #include <errno.h>
58ea906c41SOllivier Robert #include <sys/types.h>
59ea906c41SOllivier Robert #include <sys/stat.h>
60ea906c41SOllivier Robert #include <fcntl.h>
61ea906c41SOllivier Robert #include <string.h>
62ea906c41SOllivier Robert #include <unistd.h>
63ea906c41SOllivier Robert
64ea906c41SOllivier Robert #define SECOND 8000 /* one second of 125-us samples */
65ea906c41SOllivier Robert #define BUFLNG 400 /* buffer size */
66ea906c41SOllivier Robert #define DEVICE "/dev/audio" /* default audio device */
67ea906c41SOllivier Robert #define WWV 0 /* WWV encoder */
68ea906c41SOllivier Robert #define IRIG 1 /* IRIG-B encoder */
69ea906c41SOllivier Robert #define OFF 0 /* zero amplitude */
70ea906c41SOllivier Robert #define LOW 1 /* low amplitude */
71ea906c41SOllivier Robert #define HIGH 2 /* high amplitude */
72ea906c41SOllivier Robert #define DATA0 200 /* WWV/H 0 pulse */
73ea906c41SOllivier Robert #define DATA1 500 /* WWV/H 1 pulse */
74ea906c41SOllivier Robert #define PI 800 /* WWV/H PI pulse */
75ea906c41SOllivier Robert #define M2 2 /* IRIG 0 pulse */
76ea906c41SOllivier Robert #define M5 5 /* IRIG 1 pulse */
77ea906c41SOllivier Robert #define M8 8 /* IRIG PI pulse */
78ea906c41SOllivier Robert
79ea906c41SOllivier Robert /*
80ea906c41SOllivier Robert * Companded sine table amplitude 3000 units
81ea906c41SOllivier Robert */
82ea906c41SOllivier Robert int c3000[] = {1, 48, 63, 70, 78, 82, 85, 89, 92, 94, /* 0-9 */
83ea906c41SOllivier Robert 96, 98, 99, 100, 101, 101, 102, 103, 103, 103, /* 10-19 */
84ea906c41SOllivier Robert 103, 103, 103, 103, 102, 101, 101, 100, 99, 98, /* 20-29 */
85ea906c41SOllivier Robert 96, 94, 92, 89, 85, 82, 78, 70, 63, 48, /* 30-39 */
86ea906c41SOllivier Robert 129, 176, 191, 198, 206, 210, 213, 217, 220, 222, /* 40-49 */
87ea906c41SOllivier Robert 224, 226, 227, 228, 229, 229, 230, 231, 231, 231, /* 50-59 */
88ea906c41SOllivier Robert 231, 231, 231, 231, 230, 229, 229, 228, 227, 226, /* 60-69 */
89ea906c41SOllivier Robert 224, 222, 220, 217, 213, 210, 206, 198, 191, 176}; /* 70-79 */
90ea906c41SOllivier Robert /*
91ea906c41SOllivier Robert * Companded sine table amplitude 6000 units
92ea906c41SOllivier Robert */
93ea906c41SOllivier Robert int c6000[] = {1, 63, 78, 86, 93, 98, 101, 104, 107, 110, /* 0-9 */
94ea906c41SOllivier Robert 112, 113, 115, 116, 117, 117, 118, 118, 119, 119, /* 10-19 */
95ea906c41SOllivier Robert 119, 119, 119, 118, 118, 117, 117, 116, 115, 113, /* 20-29 */
96ea906c41SOllivier Robert 112, 110, 107, 104, 101, 98, 93, 86, 78, 63, /* 30-39 */
97ea906c41SOllivier Robert 129, 191, 206, 214, 221, 226, 229, 232, 235, 238, /* 40-49 */
98ea906c41SOllivier Robert 240, 241, 243, 244, 245, 245, 246, 246, 247, 247, /* 50-59 */
99ea906c41SOllivier Robert 247, 247, 247, 246, 246, 245, 245, 244, 243, 241, /* 60-69 */
100ea906c41SOllivier Robert 240, 238, 235, 232, 229, 226, 221, 214, 206, 191}; /* 70-79 */
101ea906c41SOllivier Robert
102ea906c41SOllivier Robert /*
103ea906c41SOllivier Robert * Decoder operations at the end of each second are driven by a state
104ea906c41SOllivier Robert * machine. The transition matrix consists of a dispatch table indexed
105ea906c41SOllivier Robert * by second number. Each entry in the table contains a case switch
106ea906c41SOllivier Robert * number and argument.
107ea906c41SOllivier Robert */
108ea906c41SOllivier Robert struct progx {
109ea906c41SOllivier Robert int sw; /* case switch number */
110ea906c41SOllivier Robert int arg; /* argument */
111ea906c41SOllivier Robert };
112ea906c41SOllivier Robert
113ea906c41SOllivier Robert /*
114ea906c41SOllivier Robert * Case switch numbers
115ea906c41SOllivier Robert */
116ea906c41SOllivier Robert #define DATA 0 /* send data (0, 1, PI) */
117ea906c41SOllivier Robert #define COEF 1 /* send BCD bit */
118ea906c41SOllivier Robert #define DEC 2 /* decrement to next digit */
119ea906c41SOllivier Robert #define MIN 3 /* minute pulse */
120ea906c41SOllivier Robert #define LEAP 4 /* leap warning */
121ea906c41SOllivier Robert #define DUT1 5 /* DUT1 bits */
122ea906c41SOllivier Robert #define DST1 6 /* DST1 bit */
123ea906c41SOllivier Robert #define DST2 7 /* DST2 bit */
124ea906c41SOllivier Robert
125ea906c41SOllivier Robert /*
126ea906c41SOllivier Robert * WWV/H format (100-Hz, 9 digits, 1 m frame)
127ea906c41SOllivier Robert */
128ea906c41SOllivier Robert struct progx progx[] = {
129ea906c41SOllivier Robert {MIN, 800}, /* 0 minute sync pulse */
130ea906c41SOllivier Robert {DATA, DATA0}, /* 1 */
131ea906c41SOllivier Robert {DST2, 0}, /* 2 DST2 */
132ea906c41SOllivier Robert {LEAP, 0}, /* 3 leap warning */
133ea906c41SOllivier Robert {COEF, 1}, /* 4 1 year units */
134ea906c41SOllivier Robert {COEF, 2}, /* 5 2 */
135ea906c41SOllivier Robert {COEF, 4}, /* 6 4 */
136ea906c41SOllivier Robert {COEF, 8}, /* 7 8 */
137ea906c41SOllivier Robert {DEC, DATA0}, /* 8 */
138ea906c41SOllivier Robert {DATA, PI}, /* 9 p1 */
139ea906c41SOllivier Robert {COEF, 1}, /* 10 1 minute units */
140ea906c41SOllivier Robert {COEF, 2}, /* 11 2 */
141ea906c41SOllivier Robert {COEF, 4}, /* 12 4 */
142ea906c41SOllivier Robert {COEF, 8}, /* 13 8 */
143ea906c41SOllivier Robert {DEC, DATA0}, /* 14 */
144ea906c41SOllivier Robert {COEF, 1}, /* 15 10 minute tens */
145ea906c41SOllivier Robert {COEF, 2}, /* 16 20 */
146ea906c41SOllivier Robert {COEF, 4}, /* 17 40 */
147ea906c41SOllivier Robert {COEF, 8}, /* 18 80 (not used) */
148ea906c41SOllivier Robert {DEC, PI}, /* 19 p2 */
149ea906c41SOllivier Robert {COEF, 1}, /* 20 1 hour units */
150ea906c41SOllivier Robert {COEF, 2}, /* 21 2 */
151ea906c41SOllivier Robert {COEF, 4}, /* 22 4 */
152ea906c41SOllivier Robert {COEF, 8}, /* 23 8 */
153ea906c41SOllivier Robert {DEC, DATA0}, /* 24 */
154ea906c41SOllivier Robert {COEF, 1}, /* 25 10 hour tens */
155ea906c41SOllivier Robert {COEF, 2}, /* 26 20 */
156ea906c41SOllivier Robert {COEF, 4}, /* 27 40 (not used) */
157ea906c41SOllivier Robert {COEF, 8}, /* 28 80 (not used) */
158ea906c41SOllivier Robert {DEC, PI}, /* 29 p3 */
159ea906c41SOllivier Robert {COEF, 1}, /* 30 1 day units */
160ea906c41SOllivier Robert {COEF, 2}, /* 31 2 */
161ea906c41SOllivier Robert {COEF, 4}, /* 32 4 */
162ea906c41SOllivier Robert {COEF, 8}, /* 33 8 */
163ea906c41SOllivier Robert {DEC, DATA0}, /* 34 not used */
164ea906c41SOllivier Robert {COEF, 1}, /* 35 10 day tens */
165ea906c41SOllivier Robert {COEF, 2}, /* 36 20 */
166ea906c41SOllivier Robert {COEF, 4}, /* 37 40 */
167ea906c41SOllivier Robert {COEF, 8}, /* 38 80 */
168ea906c41SOllivier Robert {DEC, PI}, /* 39 p4 */
169ea906c41SOllivier Robert {COEF, 1}, /* 40 100 day hundreds */
170ea906c41SOllivier Robert {COEF, 2}, /* 41 200 */
171ea906c41SOllivier Robert {COEF, 4}, /* 42 400 (not used) */
172ea906c41SOllivier Robert {COEF, 8}, /* 43 800 (not used) */
173ea906c41SOllivier Robert {DEC, DATA0}, /* 44 */
174ea906c41SOllivier Robert {DATA, DATA0}, /* 45 */
175ea906c41SOllivier Robert {DATA, DATA0}, /* 46 */
176ea906c41SOllivier Robert {DATA, DATA0}, /* 47 */
177ea906c41SOllivier Robert {DATA, DATA0}, /* 48 */
178ea906c41SOllivier Robert {DATA, PI}, /* 49 p5 */
179ea906c41SOllivier Robert {DUT1, 8}, /* 50 DUT1 sign */
180ea906c41SOllivier Robert {COEF, 1}, /* 51 10 year tens */
181ea906c41SOllivier Robert {COEF, 2}, /* 52 20 */
182ea906c41SOllivier Robert {COEF, 4}, /* 53 40 */
183ea906c41SOllivier Robert {COEF, 8}, /* 54 80 */
184ea906c41SOllivier Robert {DST1, 0}, /* 55 DST1 */
185ea906c41SOllivier Robert {DUT1, 1}, /* 56 0.1 DUT1 fraction */
186ea906c41SOllivier Robert {DUT1, 2}, /* 57 0.2 */
187ea906c41SOllivier Robert {DUT1, 4}, /* 58 0.4 */
188ea906c41SOllivier Robert {DATA, PI}, /* 59 p6 */
189ea906c41SOllivier Robert {DATA, DATA0}, /* 60 leap */
190ea906c41SOllivier Robert };
191ea906c41SOllivier Robert
192ea906c41SOllivier Robert /*
193ea906c41SOllivier Robert * IRIG format except first frame (1000 Hz, 20 digits, 1 s frame)
194ea906c41SOllivier Robert */
195ea906c41SOllivier Robert struct progx progy[] = {
196ea906c41SOllivier Robert {COEF, 1}, /* 0 1 units */
197ea906c41SOllivier Robert {COEF, 2}, /* 1 2 */
198ea906c41SOllivier Robert {COEF, 4}, /* 2 4 */
199ea906c41SOllivier Robert {COEF, 8}, /* 3 8 */
200ea906c41SOllivier Robert {DEC, M2}, /* 4 im */
201ea906c41SOllivier Robert {COEF, 1}, /* 5 10 tens */
202ea906c41SOllivier Robert {COEF, 2}, /* 6 20 */
203ea906c41SOllivier Robert {COEF, 4}, /* 7 40 */
204ea906c41SOllivier Robert {COEF, 8}, /* 8 80 */
205ea906c41SOllivier Robert {DEC, M8}, /* 9 pi */
206ea906c41SOllivier Robert };
207ea906c41SOllivier Robert
208ea906c41SOllivier Robert /*
209ea906c41SOllivier Robert * IRIG format first frame (1000 Hz, 20 digits, 1 s frame)
210ea906c41SOllivier Robert */
211ea906c41SOllivier Robert struct progx progz[] = {
212ea906c41SOllivier Robert {MIN, M8}, /* 0 pi (second) */
213ea906c41SOllivier Robert {COEF, 1}, /* 1 1 units */
214ea906c41SOllivier Robert {COEF, 2}, /* 2 2 */
215ea906c41SOllivier Robert {COEF, 4}, /* 3 4 */
216ea906c41SOllivier Robert {COEF, 8}, /* 4 8 */
217ea906c41SOllivier Robert {DEC, M2}, /* 5 im */
218ea906c41SOllivier Robert {COEF, 1}, /* 6 10 tens */
219ea906c41SOllivier Robert {COEF, 2}, /* 7 20 */
220ea906c41SOllivier Robert {COEF, 4}, /* 8 40 */
221ea906c41SOllivier Robert {DEC, M8}, /* 9 pi */
222ea906c41SOllivier Robert };
223ea906c41SOllivier Robert
224ea906c41SOllivier Robert /*
225ea906c41SOllivier Robert * Forward declarations
226ea906c41SOllivier Robert */
227ea906c41SOllivier Robert void sec(int); /* send second */
228ea906c41SOllivier Robert void digit(int); /* encode digit */
229ea906c41SOllivier Robert void peep(int, int, int); /* send cycles */
230ea906c41SOllivier Robert void delay(int); /* delay samples */
231ea906c41SOllivier Robert
232ea906c41SOllivier Robert /*
233ea906c41SOllivier Robert * Global variables
234ea906c41SOllivier Robert */
235ea906c41SOllivier Robert char buffer[BUFLNG]; /* output buffer */
236ea906c41SOllivier Robert int bufcnt = 0; /* buffer counter */
237ea906c41SOllivier Robert int second = 0; /* seconds counter */
238ea906c41SOllivier Robert int fd; /* audio codec file descriptor */
239ea906c41SOllivier Robert int tone = 1000; /* WWV sync frequency */
240ea906c41SOllivier Robert int level = AUDIO_MAX_GAIN / 8; /* output level */
241ea906c41SOllivier Robert int port = AUDIO_LINE_OUT; /* output port */
242ea906c41SOllivier Robert int encode = WWV; /* encoder select */
243ea906c41SOllivier Robert int leap = 0; /* leap indicator */
244ea906c41SOllivier Robert int dst = 0; /* winter/summer time */
245ea906c41SOllivier Robert int dut1 = 0; /* DUT1 correction (sign, magnitude) */
246ea906c41SOllivier Robert int utc = 0; /* option epoch */
247ea906c41SOllivier Robert
248ea906c41SOllivier Robert /*
249ea906c41SOllivier Robert * Main program
250ea906c41SOllivier Robert */
251ea906c41SOllivier Robert int
main(int argc,char ** argv)252ea906c41SOllivier Robert main(
253ea906c41SOllivier Robert int argc, /* command line options */
254ea906c41SOllivier Robert char **argv /* poiniter to list of tokens */
255ea906c41SOllivier Robert )
256ea906c41SOllivier Robert {
257ea906c41SOllivier Robert struct timeval tv; /* system clock at startup */
258ea906c41SOllivier Robert audio_info_t info; /* Sun audio structure */
259ea906c41SOllivier Robert struct tm *tm = NULL; /* structure returned by gmtime */
260ea906c41SOllivier Robert char device[50]; /* audio device */
261ea906c41SOllivier Robert char code[100]; /* timecode */
262ea906c41SOllivier Robert int rval, temp, arg, sw, ptr;
263ea906c41SOllivier Robert int minute, hour, day, year;
264ea906c41SOllivier Robert int i;
265ea906c41SOllivier Robert
266ea906c41SOllivier Robert /*
267ea906c41SOllivier Robert * Parse options
268ea906c41SOllivier Robert */
269*2b15cb3dSCy Schubert strlcpy(device, DEVICE, sizeof(device));
270ea906c41SOllivier Robert year = 0;
271ea906c41SOllivier Robert while ((temp = getopt(argc, argv, "a:dhilsu:v:y:")) != -1) {
272ea906c41SOllivier Robert switch (temp) {
273ea906c41SOllivier Robert
274ea906c41SOllivier Robert case 'a': /* specify audio device (/dev/audio) */
275*2b15cb3dSCy Schubert strlcpy(device, optarg, sizeof(device));
276ea906c41SOllivier Robert break;
277ea906c41SOllivier Robert
278ea906c41SOllivier Robert case 'd': /* set DST for summer (WWV/H only) */
279ea906c41SOllivier Robert dst++;
280ea906c41SOllivier Robert break;
281ea906c41SOllivier Robert
282ea906c41SOllivier Robert case 'h': /* select WWVH sync frequency */
283ea906c41SOllivier Robert tone = 1200;
284ea906c41SOllivier Robert break;
285ea906c41SOllivier Robert
286ea906c41SOllivier Robert case 'i': /* select irig format */
287ea906c41SOllivier Robert encode = IRIG;
288ea906c41SOllivier Robert break;
289ea906c41SOllivier Robert
290ea906c41SOllivier Robert case 'l': /* set leap warning bit (WWV/H only) */
291ea906c41SOllivier Robert leap++;
292ea906c41SOllivier Robert break;
293ea906c41SOllivier Robert
294ea906c41SOllivier Robert case 's': /* enable speaker */
295ea906c41SOllivier Robert port |= AUDIO_SPEAKER;
296ea906c41SOllivier Robert break;
297ea906c41SOllivier Robert
298ea906c41SOllivier Robert case 'u': /* set DUT1 offset (-7 to +7) */
299ea906c41SOllivier Robert sscanf(optarg, "%d", &dut1);
300ea906c41SOllivier Robert if (dut1 < 0)
301ea906c41SOllivier Robert dut1 = abs(dut1);
302ea906c41SOllivier Robert else
303ea906c41SOllivier Robert dut1 |= 0x8;
304ea906c41SOllivier Robert break;
305ea906c41SOllivier Robert
306ea906c41SOllivier Robert case 'v': /* set output level (0-255) */
307ea906c41SOllivier Robert sscanf(optarg, "%d", &level);
308ea906c41SOllivier Robert break;
309ea906c41SOllivier Robert
310ea906c41SOllivier Robert case 'y': /* set initial date and time */
311ea906c41SOllivier Robert sscanf(optarg, "%2d%3d%2d%2d", &year, &day,
312ea906c41SOllivier Robert &hour, &minute);
313ea906c41SOllivier Robert utc++;
314ea906c41SOllivier Robert break;
315ea906c41SOllivier Robert
316ea906c41SOllivier Robert defult:
317ea906c41SOllivier Robert printf("invalid option %c\n", temp);
318ea906c41SOllivier Robert break;
319ea906c41SOllivier Robert }
320ea906c41SOllivier Robert }
321ea906c41SOllivier Robert
322ea906c41SOllivier Robert /*
323ea906c41SOllivier Robert * Open audio device and set options
324ea906c41SOllivier Robert */
325ea906c41SOllivier Robert fd = open("/dev/audio", O_WRONLY);
326ea906c41SOllivier Robert if (fd <= 0) {
327ea906c41SOllivier Robert printf("audio open %s\n", strerror(errno));
328ea906c41SOllivier Robert exit(1);
329ea906c41SOllivier Robert }
330ea906c41SOllivier Robert rval = ioctl(fd, AUDIO_GETINFO, &info);
331ea906c41SOllivier Robert if (rval < 0) {
332ea906c41SOllivier Robert printf("audio control %s\n", strerror(errno));
333ea906c41SOllivier Robert exit(0);
334ea906c41SOllivier Robert }
335ea906c41SOllivier Robert info.play.port = port;
336ea906c41SOllivier Robert info.play.gain = level;
337ea906c41SOllivier Robert info.play.sample_rate = SECOND;
338ea906c41SOllivier Robert info.play.channels = 1;
339ea906c41SOllivier Robert info.play.precision = 8;
340ea906c41SOllivier Robert info.play.encoding = AUDIO_ENCODING_ULAW;
341ea906c41SOllivier Robert printf("port %d gain %d rate %d chan %d prec %d encode %d\n",
342ea906c41SOllivier Robert info.play.port, info.play.gain, info.play.sample_rate,
343ea906c41SOllivier Robert info.play.channels, info.play.precision,
344ea906c41SOllivier Robert info.play.encoding);
345ea906c41SOllivier Robert ioctl(fd, AUDIO_SETINFO, &info);
346ea906c41SOllivier Robert
347ea906c41SOllivier Robert /*
348ea906c41SOllivier Robert * Unless specified otherwise, read the system clock and
349ea906c41SOllivier Robert * initialize the time.
350ea906c41SOllivier Robert */
351ea906c41SOllivier Robert if (!utc) {
352ea906c41SOllivier Robert gettimeofday(&tv, NULL);
353ea906c41SOllivier Robert tm = gmtime(&tv.tv_sec);
354ea906c41SOllivier Robert minute = tm->tm_min;
355ea906c41SOllivier Robert hour = tm->tm_hour;
356ea906c41SOllivier Robert day = tm->tm_yday + 1;
357ea906c41SOllivier Robert year = tm->tm_year % 100;
358ea906c41SOllivier Robert second = tm->tm_sec;
359ea906c41SOllivier Robert
360ea906c41SOllivier Robert /*
361ea906c41SOllivier Robert * Delay the first second so the generator is accurately
362ea906c41SOllivier Robert * aligned with the system clock within one sample (125
363ea906c41SOllivier Robert * microseconds ).
364ea906c41SOllivier Robert */
365ea906c41SOllivier Robert delay(SECOND - tv.tv_usec * 8 / 1000);
366ea906c41SOllivier Robert }
367ea906c41SOllivier Robert memset(code, 0, sizeof(code));
368ea906c41SOllivier Robert switch (encode) {
369ea906c41SOllivier Robert
370ea906c41SOllivier Robert /*
371ea906c41SOllivier Robert * For WWV/H and default time, carefully set the signal
372ea906c41SOllivier Robert * generator seconds number to agree with the current time.
373ea906c41SOllivier Robert */
374ea906c41SOllivier Robert case WWV:
375ea906c41SOllivier Robert printf("year %d day %d time %02d:%02d:%02d tone %d\n",
376ea906c41SOllivier Robert year, day, hour, minute, second, tone);
377*2b15cb3dSCy Schubert snprintf(code, sizeof(code), "%01d%03d%02d%02d%01d",
378*2b15cb3dSCy Schubert year / 10, day, hour, minute, year % 10);
379ea906c41SOllivier Robert printf("%s\n", code);
380ea906c41SOllivier Robert ptr = 8;
381ea906c41SOllivier Robert for (i = 0; i <= second; i++) {
382ea906c41SOllivier Robert if (progx[i].sw == DEC)
383ea906c41SOllivier Robert ptr--;
384ea906c41SOllivier Robert }
385ea906c41SOllivier Robert break;
386ea906c41SOllivier Robert
387ea906c41SOllivier Robert /*
388ea906c41SOllivier Robert * For IRIG the signal generator runs every second, so requires
389ea906c41SOllivier Robert * no additional alignment.
390ea906c41SOllivier Robert */
391ea906c41SOllivier Robert case IRIG:
392ea906c41SOllivier Robert printf("sbs %x year %d day %d time %02d:%02d:%02d\n",
393ea906c41SOllivier Robert 0, year, day, hour, minute, second);
394ea906c41SOllivier Robert break;
395ea906c41SOllivier Robert }
396ea906c41SOllivier Robert
397ea906c41SOllivier Robert /*
398ea906c41SOllivier Robert * Run the signal generator to generate new timecode strings
399ea906c41SOllivier Robert * once per minute for WWV/H and once per second for IRIG.
400ea906c41SOllivier Robert */
401ea906c41SOllivier Robert while(1) {
402ea906c41SOllivier Robert
403ea906c41SOllivier Robert /*
404ea906c41SOllivier Robert * Crank the state machine to propagate carries to the
405ea906c41SOllivier Robert * year of century. Note that we delayed up to one
406ea906c41SOllivier Robert * second for alignment after reading the time, so this
407ea906c41SOllivier Robert * is the next second.
408ea906c41SOllivier Robert */
409ea906c41SOllivier Robert second = (second + 1) % 60;
410ea906c41SOllivier Robert if (second == 0) {
411ea906c41SOllivier Robert minute++;
412ea906c41SOllivier Robert if (minute >= 60) {
413ea906c41SOllivier Robert minute = 0;
414ea906c41SOllivier Robert hour++;
415ea906c41SOllivier Robert }
416ea906c41SOllivier Robert if (hour >= 24) {
417ea906c41SOllivier Robert hour = 0;
418ea906c41SOllivier Robert day++;
419ea906c41SOllivier Robert }
420ea906c41SOllivier Robert
421ea906c41SOllivier Robert /*
422ea906c41SOllivier Robert * At year rollover check for leap second.
423ea906c41SOllivier Robert */
424ea906c41SOllivier Robert if (day >= (year & 0x3 ? 366 : 367)) {
425ea906c41SOllivier Robert if (leap) {
426ea906c41SOllivier Robert sec(DATA0);
427ea906c41SOllivier Robert printf("\nleap!");
428ea906c41SOllivier Robert leap = 0;
429ea906c41SOllivier Robert }
430ea906c41SOllivier Robert day = 1;
431ea906c41SOllivier Robert year++;
432ea906c41SOllivier Robert }
433ea906c41SOllivier Robert if (encode == WWV) {
434*2b15cb3dSCy Schubert snprintf(code, sizeof(code),
435*2b15cb3dSCy Schubert "%01d%03d%02d%02d%01d", year / 10,
436*2b15cb3dSCy Schubert day, hour, minute, year % 10);
437ea906c41SOllivier Robert printf("\n%s\n", code);
438ea906c41SOllivier Robert ptr = 8;
439ea906c41SOllivier Robert }
440ea906c41SOllivier Robert }
441ea906c41SOllivier Robert if (encode == IRIG) {
442*2b15cb3dSCy Schubert snprintf(code, sizeof(code),
443*2b15cb3dSCy Schubert "%04x%04d%06d%02d%02d%02d", 0, year, day,
444*2b15cb3dSCy Schubert hour, minute, second);
445ea906c41SOllivier Robert printf("%s\n", code);
446ea906c41SOllivier Robert ptr = 19;
447ea906c41SOllivier Robert }
448ea906c41SOllivier Robert
449ea906c41SOllivier Robert /*
450ea906c41SOllivier Robert * Generate data for the second
451ea906c41SOllivier Robert */
452ea906c41SOllivier Robert switch(encode) {
453ea906c41SOllivier Robert
454ea906c41SOllivier Robert /*
455ea906c41SOllivier Robert * The IRIG second consists of 20 BCD digits of width-
456ea906c41SOllivier Robert * modulateod pulses at 2, 5 and 8 ms and modulated 50
457ea906c41SOllivier Robert * percent on the 1000-Hz carrier.
458ea906c41SOllivier Robert */
459ea906c41SOllivier Robert case IRIG:
460ea906c41SOllivier Robert for (i = 0; i < 100; i++) {
461ea906c41SOllivier Robert if (i < 10) {
462ea906c41SOllivier Robert sw = progz[i].sw;
463ea906c41SOllivier Robert arg = progz[i].arg;
464ea906c41SOllivier Robert } else {
465ea906c41SOllivier Robert sw = progy[i % 10].sw;
466ea906c41SOllivier Robert arg = progy[i % 10].arg;
467ea906c41SOllivier Robert }
468ea906c41SOllivier Robert switch(sw) {
469ea906c41SOllivier Robert
470ea906c41SOllivier Robert case COEF: /* send BCD bit */
471ea906c41SOllivier Robert if (code[ptr] & arg) {
472ea906c41SOllivier Robert peep(M5, 1000, HIGH);
473ea906c41SOllivier Robert peep(M5, 1000, LOW);
474ea906c41SOllivier Robert printf("1");
475ea906c41SOllivier Robert } else {
476ea906c41SOllivier Robert peep(M2, 1000, HIGH);
477ea906c41SOllivier Robert peep(M8, 1000, LOW);
478ea906c41SOllivier Robert printf("0");
479ea906c41SOllivier Robert }
480ea906c41SOllivier Robert break;
481ea906c41SOllivier Robert
482ea906c41SOllivier Robert case DEC: /* send IM/PI bit */
483ea906c41SOllivier Robert ptr--;
484ea906c41SOllivier Robert printf(" ");
485ea906c41SOllivier Robert peep(arg, 1000, HIGH);
486ea906c41SOllivier Robert peep(10 - arg, 1000, LOW);
487ea906c41SOllivier Robert break;
488ea906c41SOllivier Robert
489ea906c41SOllivier Robert case MIN: /* send data bit */
490ea906c41SOllivier Robert peep(arg, 1000, HIGH);
491ea906c41SOllivier Robert peep(10 - arg, 1000, LOW);
492ea906c41SOllivier Robert printf("M ");
493ea906c41SOllivier Robert break;
494ea906c41SOllivier Robert }
495ea906c41SOllivier Robert if (ptr < 0)
496ea906c41SOllivier Robert break;
497ea906c41SOllivier Robert }
498ea906c41SOllivier Robert printf("\n");
499ea906c41SOllivier Robert break;
500ea906c41SOllivier Robert
501ea906c41SOllivier Robert /*
502ea906c41SOllivier Robert * The WWV/H second consists of 9 BCD digits of width-
503ea906c41SOllivier Robert * modulateod pulses 200, 500 and 800 ms at 100-Hz.
504ea906c41SOllivier Robert */
505ea906c41SOllivier Robert case WWV:
506ea906c41SOllivier Robert sw = progx[second].sw;
507ea906c41SOllivier Robert arg = progx[second].arg;
508ea906c41SOllivier Robert switch(sw) {
509ea906c41SOllivier Robert
510ea906c41SOllivier Robert case DATA: /* send data bit */
511ea906c41SOllivier Robert sec(arg);
512ea906c41SOllivier Robert break;
513ea906c41SOllivier Robert
514ea906c41SOllivier Robert case COEF: /* send BCD bit */
515ea906c41SOllivier Robert if (code[ptr] & arg) {
516ea906c41SOllivier Robert sec(DATA1);
517ea906c41SOllivier Robert printf("1");
518ea906c41SOllivier Robert } else {
519ea906c41SOllivier Robert sec(DATA0);
520ea906c41SOllivier Robert printf("0");
521ea906c41SOllivier Robert }
522ea906c41SOllivier Robert break;
523ea906c41SOllivier Robert
524ea906c41SOllivier Robert case LEAP: /* send leap bit */
525ea906c41SOllivier Robert if (leap) {
526ea906c41SOllivier Robert sec(DATA1);
527ea906c41SOllivier Robert printf("L ");
528ea906c41SOllivier Robert } else {
529ea906c41SOllivier Robert sec(DATA0);
530ea906c41SOllivier Robert printf(" ");
531ea906c41SOllivier Robert }
532ea906c41SOllivier Robert break;
533ea906c41SOllivier Robert
534ea906c41SOllivier Robert case DEC: /* send data bit */
535ea906c41SOllivier Robert ptr--;
536ea906c41SOllivier Robert sec(arg);
537ea906c41SOllivier Robert printf(" ");
538ea906c41SOllivier Robert break;
539ea906c41SOllivier Robert
540ea906c41SOllivier Robert case MIN: /* send minute sync */
541ea906c41SOllivier Robert peep(arg, tone, HIGH);
542ea906c41SOllivier Robert peep(1000 - arg, tone, OFF);
543ea906c41SOllivier Robert break;
544ea906c41SOllivier Robert
545ea906c41SOllivier Robert case DUT1: /* send DUT1 bits */
546ea906c41SOllivier Robert if (dut1 & arg)
547ea906c41SOllivier Robert sec(DATA1);
548ea906c41SOllivier Robert else
549ea906c41SOllivier Robert sec(DATA0);
550ea906c41SOllivier Robert break;
551ea906c41SOllivier Robert
552ea906c41SOllivier Robert case DST1: /* send DST1 bit */
553ea906c41SOllivier Robert ptr--;
554ea906c41SOllivier Robert if (dst)
555ea906c41SOllivier Robert sec(DATA1);
556ea906c41SOllivier Robert else
557ea906c41SOllivier Robert sec(DATA0);
558ea906c41SOllivier Robert printf(" ");
559ea906c41SOllivier Robert break;
560ea906c41SOllivier Robert
561ea906c41SOllivier Robert case DST2: /* send DST2 bit */
562ea906c41SOllivier Robert if (dst)
563ea906c41SOllivier Robert sec(DATA1);
564ea906c41SOllivier Robert else
565ea906c41SOllivier Robert sec(DATA0);
566ea906c41SOllivier Robert break;
567ea906c41SOllivier Robert }
568ea906c41SOllivier Robert }
569ea906c41SOllivier Robert }
570ea906c41SOllivier Robert }
571ea906c41SOllivier Robert
572ea906c41SOllivier Robert
573ea906c41SOllivier Robert /*
574ea906c41SOllivier Robert * Generate WWV/H 0 or 1 data pulse.
575ea906c41SOllivier Robert */
sec(int code)576ea906c41SOllivier Robert void sec(
577ea906c41SOllivier Robert int code /* DATA0, DATA1, PI */
578ea906c41SOllivier Robert )
579ea906c41SOllivier Robert {
580ea906c41SOllivier Robert /*
581ea906c41SOllivier Robert * The WWV data pulse begins with 5 ms of 1000 Hz follwed by a
582ea906c41SOllivier Robert * guard time of 25 ms. The data pulse is 170, 570 or 770 ms at
583ea906c41SOllivier Robert * 100 Hz corresponding to 0, 1 or position indicator (PI),
584ea906c41SOllivier Robert * respectively. Note the 100-Hz data pulses are transmitted 6
585ea906c41SOllivier Robert * dB below the 1000-Hz sync pulses. Originally the data pulses
586ea906c41SOllivier Robert * were transmited 10 dB below the sync pulses, but the station
587ea906c41SOllivier Robert * engineers increased that to 6 dB because the Heath GC-1000
588ea906c41SOllivier Robert * WWV/H radio clock worked much better.
589ea906c41SOllivier Robert */
590ea906c41SOllivier Robert peep(5, tone, HIGH); /* send seconds tick */
591ea906c41SOllivier Robert peep(25, tone, OFF);
592ea906c41SOllivier Robert peep(code - 30, 100, LOW); /* send data */
593ea906c41SOllivier Robert peep(1000 - code, 100, OFF);
594ea906c41SOllivier Robert }
595ea906c41SOllivier Robert
596ea906c41SOllivier Robert
597ea906c41SOllivier Robert /*
598ea906c41SOllivier Robert * Generate cycles of 100 Hz or any multiple of 100 Hz.
599ea906c41SOllivier Robert */
peep(int pulse,int freq,int amp)600ea906c41SOllivier Robert void peep(
601ea906c41SOllivier Robert int pulse, /* pulse length (ms) */
602ea906c41SOllivier Robert int freq, /* frequency (Hz) */
603ea906c41SOllivier Robert int amp /* amplitude */
604ea906c41SOllivier Robert )
605ea906c41SOllivier Robert {
606ea906c41SOllivier Robert int increm; /* phase increment */
607ea906c41SOllivier Robert int i, j;
608ea906c41SOllivier Robert
609ea906c41SOllivier Robert if (amp == OFF || freq == 0)
610ea906c41SOllivier Robert increm = 10;
611ea906c41SOllivier Robert else
612ea906c41SOllivier Robert increm = freq / 100;
613ea906c41SOllivier Robert j = 0;
614ea906c41SOllivier Robert for (i = 0 ; i < pulse * 8; i++) {
615ea906c41SOllivier Robert switch (amp) {
616ea906c41SOllivier Robert
617ea906c41SOllivier Robert case HIGH:
618ea906c41SOllivier Robert buffer[bufcnt++] = ~c6000[j];
619ea906c41SOllivier Robert break;
620ea906c41SOllivier Robert
621ea906c41SOllivier Robert case LOW:
622ea906c41SOllivier Robert buffer[bufcnt++] = ~c3000[j];
623ea906c41SOllivier Robert break;
624ea906c41SOllivier Robert
625ea906c41SOllivier Robert default:
626ea906c41SOllivier Robert buffer[bufcnt++] = ~0;
627ea906c41SOllivier Robert }
628ea906c41SOllivier Robert if (bufcnt >= BUFLNG) {
629ea906c41SOllivier Robert write(fd, buffer, BUFLNG);
630ea906c41SOllivier Robert bufcnt = 0;
631ea906c41SOllivier Robert }
632ea906c41SOllivier Robert j = (j + increm) % 80;
633ea906c41SOllivier Robert }
634ea906c41SOllivier Robert }
635ea906c41SOllivier Robert
636ea906c41SOllivier Robert
637ea906c41SOllivier Robert /*
638ea906c41SOllivier Robert * Delay for initial phasing
639ea906c41SOllivier Robert */
delay(int delay)640ea906c41SOllivier Robert void delay (
641ea906c41SOllivier Robert int delay /* delay in samples */
642ea906c41SOllivier Robert )
643ea906c41SOllivier Robert {
644ea906c41SOllivier Robert int samples; /* samples remaining */
645ea906c41SOllivier Robert
646ea906c41SOllivier Robert samples = delay;
647ea906c41SOllivier Robert memset(buffer, 0, BUFLNG);
648ea906c41SOllivier Robert while (samples >= BUFLNG) {
649ea906c41SOllivier Robert write(fd, buffer, BUFLNG);
650ea906c41SOllivier Robert samples -= BUFLNG;
651ea906c41SOllivier Robert }
652ea906c41SOllivier Robert write(fd, buffer, samples);
653ea906c41SOllivier Robert }
654