1181834Sroberto/*
2181834Sroberto * tg.c generate WWV or IRIG signals for test
3181834Sroberto */
4181834Sroberto/*
5181834Sroberto * This program can generate audio signals that simulate the WWV/H
6181834Sroberto * broadcast timecode. Alternatively, it can generate the IRIG-B
7181834Sroberto * timecode commonly used to synchronize laboratory equipment. It is
8181834Sroberto * intended to test the WWV/H driver (refclock_wwv.c) and the IRIG
9181834Sroberto * driver (refclock_irig.c) in the NTP driver collection.
10181834Sroberto *
11181834Sroberto * Besides testing the drivers themselves, this program can be used to
12181834Sroberto * synchronize remote machines over audio transmission lines or program
13181834Sroberto * feeds. The program reads the time on the local machine and sets the
14181834Sroberto * initial epoch of the signal generator within one millisecond.
15181834Sroberto * Alernatively, the initial epoch can be set to an arbitrary time. This
16181834Sroberto * is useful when searching for bugs and testing for correct response to
17181834Sroberto * a leap second in UTC. Note however, the ultimate accuracy is limited
18181834Sroberto * by the intrinsic frequency error of the codec sample clock, which can
19181834Sroberto # reach well over 100 PPM.
20181834Sroberto *
21181834Sroberto * The default is to route generated signals to the line output
22181834Sroberto * jack; the s option on the command line routes these signals to the
23181834Sroberto * internal speaker as well. The v option controls the speaker volume
24181834Sroberto * over the range 0-255. The signal generator by default uses WWV
25181834Sroberto * format; the h option switches to WWVH format and the i option
26181834Sroberto * switches to IRIG-B format.
27181834Sroberto *
28181834Sroberto * Once started the program runs continuously. The default initial epoch
29181834Sroberto * for the signal generator is read from the computer system clock when
30181834Sroberto * the program starts. The y option specifies an alternate epoch using a
31181834Sroberto * string yydddhhmmss, where yy is the year of century, ddd the day of
32181834Sroberto * year, hh the hour of day and mm the minute of hour. For instance,
33181834Sroberto * 1946Z on 1 January 2006 is 060011946. The l option lights the leap
34181834Sroberto * warning bit in the WWV/H timecode, so is handy to check for correct
35181834Sroberto * behavior at the next leap second epoch. The remaining options are
36181834Sroberto * specified below under the Parse Options heading. Most of these are
37181834Sroberto * for testing.
38181834Sroberto *
39181834Sroberto * During operation the program displays the WWV/H timecode (9 digits)
40181834Sroberto * or IRIG timecode (20 digits) as each new string is constructed. The
41181834Sroberto * display is followed by the BCD binary bits as transmitted. Note that
42181834Sroberto * the transmissionorder is low-order first as the frame is processed
43181834Sroberto * left to right. For WWV/H The leap warning L preceeds the first bit.
44181834Sroberto * For IRIG the on-time marker M preceeds the first (units) bit, so its
45181834Sroberto * code is delayed one bit and the next digit (tens) needs only three
46181834Sroberto * bits.
47181834Sroberto *
48181834Sroberto * The program has been tested with the Sun Blade 1500 running Solaris
49181834Sroberto * 10, but not yet with other machines. It uses no special features and
50181834Sroberto * should be readily portable to other hardware and operating systems.
51181834Sroberto */
52181834Sroberto#include <stdio.h>
53181834Sroberto#include <stdlib.h>
54181834Sroberto#include <time.h>
55181834Sroberto#include <sys/audio.h>
56181834Sroberto#include <math.h>
57181834Sroberto#include <errno.h>
58181834Sroberto#include <sys/types.h>
59181834Sroberto#include <sys/stat.h>
60181834Sroberto#include <fcntl.h>
61181834Sroberto#include <string.h>
62181834Sroberto#include <unistd.h>
63181834Sroberto
64181834Sroberto#define	SECOND	8000		/* one second of 125-us samples */
65181834Sroberto#define BUFLNG	400		/* buffer size */
66181834Sroberto#define	DEVICE	"/dev/audio"	/* default audio device */
67181834Sroberto#define	WWV	0		/* WWV encoder */
68181834Sroberto#define	IRIG	1		/* IRIG-B encoder */
69181834Sroberto#define	OFF	0		/* zero amplitude */
70181834Sroberto#define	LOW	1		/* low amplitude */
71181834Sroberto#define	HIGH	2		/* high amplitude */
72181834Sroberto#define	DATA0	200		/* WWV/H 0 pulse */
73181834Sroberto#define	DATA1	500		/* WWV/H 1 pulse */
74181834Sroberto#define PI	800		/* WWV/H PI pulse */
75181834Sroberto#define	M2	2		/* IRIG 0 pulse */
76181834Sroberto#define	M5	5		/* IRIG 1 pulse */
77181834Sroberto#define	M8	8		/* IRIG PI pulse */
78181834Sroberto
79181834Sroberto/*
80181834Sroberto * Companded sine table amplitude 3000 units
81181834Sroberto */
82181834Srobertoint c3000[] = {1, 48, 63, 70, 78, 82, 85, 89, 92, 94,	/* 0-9 */
83181834Sroberto     96,  98,  99, 100, 101, 101, 102, 103, 103, 103,	/* 10-19 */
84181834Sroberto    103, 103, 103, 103, 102, 101, 101, 100,  99,  98,	/* 20-29 */
85181834Sroberto     96,  94,  92,  89,  85,  82,  78,  70,  63,  48,	/* 30-39 */
86181834Sroberto    129, 176, 191, 198, 206, 210, 213, 217, 220, 222,	/* 40-49 */
87181834Sroberto    224, 226, 227, 228, 229, 229, 230, 231, 231, 231, 	/* 50-59 */
88181834Sroberto    231, 231, 231, 231, 230, 229, 229, 228, 227, 226,	/* 60-69 */
89181834Sroberto    224, 222, 220, 217, 213, 210, 206, 198, 191, 176}; 	/* 70-79 */
90181834Sroberto/*
91181834Sroberto * Companded sine table amplitude 6000 units
92181834Sroberto */
93181834Srobertoint c6000[] = {1, 63, 78, 86, 93, 98, 101, 104, 107, 110, /* 0-9 */
94181834Sroberto    112, 113, 115, 116, 117, 117, 118, 118, 119, 119,	/* 10-19 */
95181834Sroberto    119, 119, 119, 118, 118, 117, 117, 116, 115, 113,	/* 20-29 */
96181834Sroberto    112, 110, 107, 104, 101,  98,  93,  86,  78,  63,	/* 30-39 */
97181834Sroberto    129, 191, 206, 214, 221, 226, 229, 232, 235, 238,	/* 40-49 */
98181834Sroberto    240, 241, 243, 244, 245, 245, 246, 246, 247, 247, 	/* 50-59 */
99181834Sroberto    247, 247, 247, 246, 246, 245, 245, 244, 243, 241,	/* 60-69 */
100181834Sroberto    240, 238, 235, 232, 229, 226, 221, 214, 206, 191}; 	/* 70-79 */
101181834Sroberto
102181834Sroberto/*
103181834Sroberto * Decoder operations at the end of each second are driven by a state
104181834Sroberto * machine. The transition matrix consists of a dispatch table indexed
105181834Sroberto * by second number. Each entry in the table contains a case switch
106181834Sroberto * number and argument.
107181834Sroberto */
108181834Srobertostruct progx {
109181834Sroberto	int sw;			/* case switch number */
110181834Sroberto	int arg;		/* argument */
111181834Sroberto};
112181834Sroberto
113181834Sroberto/*
114181834Sroberto * Case switch numbers
115181834Sroberto */
116181834Sroberto#define DATA	0		/* send data (0, 1, PI) */
117181834Sroberto#define COEF	1		/* send BCD bit */
118181834Sroberto#define	DEC	2		/* decrement to next digit */
119181834Sroberto#define	MIN	3		/* minute pulse */
120181834Sroberto#define	LEAP	4		/* leap warning */
121181834Sroberto#define	DUT1	5		/* DUT1 bits */
122181834Sroberto#define	DST1	6		/* DST1 bit */
123181834Sroberto#define	DST2	7		/* DST2 bit */
124181834Sroberto
125181834Sroberto/*
126181834Sroberto * WWV/H format (100-Hz, 9 digits, 1 m frame)
127181834Sroberto */
128181834Srobertostruct progx progx[] = {
129181834Sroberto	{MIN,	800},		/* 0 minute sync pulse */
130181834Sroberto	{DATA,	DATA0},		/* 1 */
131181834Sroberto	{DST2,	0},		/* 2 DST2 */
132181834Sroberto	{LEAP,	0},		/* 3 leap warning */
133181834Sroberto	{COEF,	1},		/* 4 1 year units */
134181834Sroberto	{COEF,	2},		/* 5 2 */
135181834Sroberto	{COEF,	4},		/* 6 4 */
136181834Sroberto	{COEF,	8},		/* 7 8 */
137181834Sroberto	{DEC,	DATA0},		/* 8 */
138181834Sroberto	{DATA,	PI},		/* 9 p1 */
139181834Sroberto	{COEF,	1},		/* 10 1 minute units */
140181834Sroberto	{COEF,	2},		/* 11 2 */
141181834Sroberto	{COEF,	4},		/* 12 4 */
142181834Sroberto	{COEF,	8},		/* 13 8 */
143181834Sroberto	{DEC,	DATA0},		/* 14 */
144181834Sroberto	{COEF,	1},		/* 15 10 minute tens */
145181834Sroberto	{COEF,	2},		/* 16 20 */
146181834Sroberto	{COEF,	4},		/* 17 40 */
147181834Sroberto	{COEF,	8},		/* 18 80 (not used) */
148181834Sroberto	{DEC,	PI},		/* 19 p2 */
149181834Sroberto	{COEF,	1},		/* 20 1 hour units */
150181834Sroberto	{COEF,	2},		/* 21 2 */
151181834Sroberto	{COEF,	4},		/* 22 4 */
152181834Sroberto	{COEF,	8},		/* 23 8 */
153181834Sroberto	{DEC,	DATA0},		/* 24 */
154181834Sroberto	{COEF,	1},		/* 25 10 hour tens */
155181834Sroberto	{COEF,	2},		/* 26 20 */
156181834Sroberto	{COEF,	4},		/* 27 40 (not used) */
157181834Sroberto	{COEF,	8},		/* 28 80 (not used) */
158181834Sroberto	{DEC,	PI},		/* 29 p3 */
159181834Sroberto	{COEF,	1},		/* 30 1 day units */
160181834Sroberto	{COEF,	2},		/* 31 2 */
161181834Sroberto	{COEF,	4},		/* 32 4 */
162181834Sroberto	{COEF,	8},		/* 33 8 */
163181834Sroberto	{DEC,	DATA0},		/* 34 not used */
164181834Sroberto	{COEF,	1},		/* 35 10 day tens */
165181834Sroberto	{COEF,	2},		/* 36 20 */
166181834Sroberto	{COEF,	4},		/* 37 40 */
167181834Sroberto	{COEF,	8},		/* 38 80 */
168181834Sroberto	{DEC,	PI},		/* 39 p4 */
169181834Sroberto	{COEF,	1},		/* 40 100 day hundreds */
170181834Sroberto	{COEF,	2},		/* 41 200 */
171181834Sroberto	{COEF,	4},		/* 42 400 (not used) */
172181834Sroberto	{COEF,	8},		/* 43 800 (not used) */
173181834Sroberto	{DEC,	DATA0},		/* 44 */
174181834Sroberto	{DATA,	DATA0},		/* 45 */
175181834Sroberto	{DATA,	DATA0},		/* 46 */
176181834Sroberto	{DATA,	DATA0},		/* 47 */
177181834Sroberto	{DATA,	DATA0},		/* 48 */
178181834Sroberto	{DATA,	PI},		/* 49 p5 */
179181834Sroberto	{DUT1,	8},		/* 50 DUT1 sign */
180181834Sroberto	{COEF,	1},		/* 51 10 year tens */
181181834Sroberto	{COEF,	2},		/* 52 20 */
182181834Sroberto	{COEF,	4},		/* 53 40 */
183181834Sroberto	{COEF,	8},		/* 54 80 */
184181834Sroberto	{DST1,	0},		/* 55 DST1 */
185181834Sroberto	{DUT1,	1},		/* 56 0.1 DUT1 fraction */
186181834Sroberto	{DUT1,	2},		/* 57 0.2 */
187181834Sroberto	{DUT1,	4},		/* 58 0.4 */
188181834Sroberto	{DATA,	PI},		/* 59 p6 */
189181834Sroberto	{DATA,	DATA0},		/* 60 leap */
190181834Sroberto};
191181834Sroberto
192181834Sroberto/*
193181834Sroberto * IRIG format except first frame (1000 Hz, 20 digits, 1 s frame)
194181834Sroberto */
195181834Srobertostruct progx progy[] = {
196181834Sroberto	{COEF,	1},		/* 0 1 units */
197181834Sroberto	{COEF,	2},		/* 1 2 */
198181834Sroberto	{COEF,	4},		/* 2 4 */
199181834Sroberto	{COEF,	8},		/* 3 8 */
200181834Sroberto	{DEC,	M2},		/* 4 im */
201181834Sroberto	{COEF,	1},		/* 5 10 tens */
202181834Sroberto	{COEF,	2},		/* 6 20 */
203181834Sroberto	{COEF,	4},		/* 7 40 */
204181834Sroberto	{COEF,	8},		/* 8 80 */
205181834Sroberto	{DEC,	M8},		/* 9 pi */
206181834Sroberto};
207181834Sroberto
208181834Sroberto/*
209181834Sroberto * IRIG format first frame (1000 Hz, 20 digits, 1 s frame)
210181834Sroberto */
211181834Srobertostruct progx progz[] = {
212181834Sroberto	{MIN,	M8},		/* 0 pi (second) */
213181834Sroberto	{COEF,	1},		/* 1 1 units */
214181834Sroberto	{COEF,	2},		/* 2 2 */
215181834Sroberto	{COEF,	4},		/* 3 4 */
216181834Sroberto	{COEF,	8},		/* 4 8 */
217181834Sroberto	{DEC,	M2},		/* 5 im */
218181834Sroberto	{COEF,	1},		/* 6 10 tens */
219181834Sroberto	{COEF,	2},		/* 7 20 */
220181834Sroberto	{COEF,	4},		/* 8 40 */
221181834Sroberto	{DEC,	M8},		/* 9 pi */
222181834Sroberto};
223181834Sroberto
224181834Sroberto/*
225181834Sroberto * Forward declarations
226181834Sroberto */
227181834Srobertovoid	sec(int);		/* send second */
228181834Srobertovoid	digit(int);		/* encode digit */
229181834Srobertovoid	peep(int, int, int);	/* send cycles */
230181834Srobertovoid	delay(int);		/* delay samples */
231181834Sroberto
232181834Sroberto/*
233181834Sroberto * Global variables
234181834Sroberto */
235181834Srobertochar	buffer[BUFLNG];		/* output buffer */
236181834Srobertoint	bufcnt = 0;		/* buffer counter */
237181834Srobertoint	second = 0;		/* seconds counter */
238181834Srobertoint	fd;			/* audio codec file descriptor */
239181834Srobertoint	tone = 1000;		/* WWV sync frequency */
240181834Srobertoint	level = AUDIO_MAX_GAIN / 8; /* output level */
241181834Srobertoint	port = AUDIO_LINE_OUT;	/* output port */
242181834Srobertoint	encode = WWV;		/* encoder select */
243181834Srobertoint	leap = 0;		/* leap indicator */
244181834Srobertoint	dst = 0;		/* winter/summer time */
245181834Srobertoint	dut1 = 0;		/* DUT1 correction (sign, magnitude) */
246181834Srobertoint	utc = 0;		/* option epoch */
247181834Sroberto
248181834Sroberto/*
249181834Sroberto * Main program
250181834Sroberto */
251181834Srobertoint
252181834Srobertomain(
253181834Sroberto	int	argc,		/* command line options */
254181834Sroberto	char	**argv		/* poiniter to list of tokens */
255181834Sroberto	)
256181834Sroberto{
257181834Sroberto	struct timeval tv;	/* system clock at startup */
258181834Sroberto	audio_info_t info;	/* Sun audio structure */
259181834Sroberto	struct tm *tm = NULL;	/* structure returned by gmtime */
260181834Sroberto	char	device[50];	/* audio device */
261181834Sroberto	char	code[100];	/* timecode */
262181834Sroberto	int	rval, temp, arg, sw, ptr;
263181834Sroberto	int	minute, hour, day, year;
264181834Sroberto	int	i;
265181834Sroberto
266181834Sroberto	/*
267181834Sroberto	 * Parse options
268181834Sroberto	 */
269285612Sdelphij	strlcpy(device, DEVICE, sizeof(device));
270181834Sroberto	year = 0;
271181834Sroberto	while ((temp = getopt(argc, argv, "a:dhilsu:v:y:")) != -1) {
272181834Sroberto		switch (temp) {
273181834Sroberto
274181834Sroberto		case 'a':	/* specify audio device (/dev/audio) */
275285612Sdelphij			strlcpy(device, optarg, sizeof(device));
276181834Sroberto			break;
277181834Sroberto
278181834Sroberto		case 'd':	/* set DST for summer (WWV/H only) */
279181834Sroberto			dst++;
280181834Sroberto			break;
281181834Sroberto
282181834Sroberto		case 'h':	/* select WWVH sync frequency */
283181834Sroberto			tone = 1200;
284181834Sroberto			break;
285181834Sroberto
286181834Sroberto		case 'i':	/* select irig format */
287181834Sroberto			encode = IRIG;
288181834Sroberto			break;
289181834Sroberto
290181834Sroberto		case 'l':	/* set leap warning bit (WWV/H only) */
291181834Sroberto			leap++;
292181834Sroberto			break;
293181834Sroberto
294181834Sroberto		case 's':	/* enable speaker */
295181834Sroberto			port |= AUDIO_SPEAKER;
296181834Sroberto			break;
297181834Sroberto
298181834Sroberto		case 'u':	/* set DUT1 offset (-7 to +7) */
299181834Sroberto			sscanf(optarg, "%d", &dut1);
300181834Sroberto			if (dut1 < 0)
301181834Sroberto				dut1 = abs(dut1);
302181834Sroberto			else
303181834Sroberto				dut1 |= 0x8;
304181834Sroberto			break;
305181834Sroberto
306181834Sroberto		case 'v':	/* set output level (0-255) */
307181834Sroberto			sscanf(optarg, "%d", &level);
308181834Sroberto			break;
309181834Sroberto
310181834Sroberto		case 'y':	/* set initial date and time */
311181834Sroberto			sscanf(optarg, "%2d%3d%2d%2d", &year, &day,
312181834Sroberto			    &hour, &minute);
313181834Sroberto			utc++;
314181834Sroberto			break;
315181834Sroberto
316181834Sroberto		defult:
317181834Sroberto			printf("invalid option %c\n", temp);
318181834Sroberto			break;
319181834Sroberto		}
320181834Sroberto	}
321181834Sroberto
322181834Sroberto	/*
323181834Sroberto	 * Open audio device and set options
324181834Sroberto	 */
325181834Sroberto	fd = open("/dev/audio", O_WRONLY);
326181834Sroberto	if (fd <= 0) {
327181834Sroberto		printf("audio open %s\n", strerror(errno));
328181834Sroberto		exit(1);
329181834Sroberto	}
330181834Sroberto	rval = ioctl(fd, AUDIO_GETINFO, &info);
331181834Sroberto	if (rval < 0) {
332181834Sroberto		printf("audio control %s\n", strerror(errno));
333181834Sroberto		exit(0);
334181834Sroberto	}
335181834Sroberto	info.play.port = port;
336181834Sroberto	info.play.gain = level;
337181834Sroberto	info.play.sample_rate = SECOND;
338181834Sroberto	info.play.channels = 1;
339181834Sroberto	info.play.precision = 8;
340181834Sroberto	info.play.encoding = AUDIO_ENCODING_ULAW;
341181834Sroberto	printf("port %d gain %d rate %d chan %d prec %d encode %d\n",
342181834Sroberto	    info.play.port, info.play.gain, info.play.sample_rate,
343181834Sroberto	    info.play.channels, info.play.precision,
344181834Sroberto	    info.play.encoding);
345181834Sroberto	ioctl(fd, AUDIO_SETINFO, &info);
346181834Sroberto
347181834Sroberto 	/*
348181834Sroberto	 * Unless specified otherwise, read the system clock and
349181834Sroberto	 * initialize the time.
350181834Sroberto	 */
351181834Sroberto	if (!utc) {
352181834Sroberto		gettimeofday(&tv, NULL);
353181834Sroberto		tm = gmtime(&tv.tv_sec);
354181834Sroberto		minute = tm->tm_min;
355181834Sroberto		hour = tm->tm_hour;
356181834Sroberto		day = tm->tm_yday + 1;
357181834Sroberto		year = tm->tm_year % 100;
358181834Sroberto		second = tm->tm_sec;
359181834Sroberto
360181834Sroberto		/*
361181834Sroberto		 * Delay the first second so the generator is accurately
362181834Sroberto		 * aligned with the system clock within one sample (125
363181834Sroberto		 * microseconds ).
364181834Sroberto		 */
365181834Sroberto		delay(SECOND - tv.tv_usec * 8 / 1000);
366181834Sroberto	}
367181834Sroberto	memset(code, 0, sizeof(code));
368181834Sroberto	switch (encode) {
369181834Sroberto
370181834Sroberto	/*
371181834Sroberto	 * For WWV/H and default time, carefully set the signal
372181834Sroberto	 * generator seconds number to agree with the current time.
373181834Sroberto	 */
374181834Sroberto	case WWV:
375181834Sroberto		printf("year %d day %d time %02d:%02d:%02d tone %d\n",
376181834Sroberto		    year, day, hour, minute, second, tone);
377285612Sdelphij		snprintf(code, sizeof(code), "%01d%03d%02d%02d%01d",
378285612Sdelphij		    year / 10, day, hour, minute, year % 10);
379181834Sroberto		printf("%s\n", code);
380181834Sroberto		ptr = 8;
381181834Sroberto		for (i = 0; i <= second; i++) {
382181834Sroberto			if (progx[i].sw == DEC)
383181834Sroberto				ptr--;
384181834Sroberto		}
385181834Sroberto		break;
386181834Sroberto
387181834Sroberto	/*
388181834Sroberto	 * For IRIG the signal generator runs every second, so requires
389181834Sroberto	 * no additional alignment.
390181834Sroberto	 */
391181834Sroberto	case IRIG:
392181834Sroberto		printf("sbs %x year %d day %d time %02d:%02d:%02d\n",
393181834Sroberto		    0, year, day, hour, minute, second);
394181834Sroberto		break;
395181834Sroberto	}
396181834Sroberto
397181834Sroberto	/*
398181834Sroberto	 * Run the signal generator to generate new timecode strings
399181834Sroberto	 * once per minute for WWV/H and once per second for IRIG.
400181834Sroberto	 */
401181834Sroberto	while(1) {
402181834Sroberto
403181834Sroberto		/*
404181834Sroberto		 * Crank the state machine to propagate carries to the
405181834Sroberto		 * year of century. Note that we delayed up to one
406181834Sroberto		 * second for alignment after reading the time, so this
407181834Sroberto		 * is the next second.
408181834Sroberto		 */
409181834Sroberto		second = (second + 1) % 60;
410181834Sroberto		if (second == 0) {
411181834Sroberto			minute++;
412181834Sroberto			if (minute >= 60) {
413181834Sroberto				minute = 0;
414181834Sroberto				hour++;
415181834Sroberto			}
416181834Sroberto			if (hour >= 24) {
417181834Sroberto				hour = 0;
418181834Sroberto				day++;
419181834Sroberto			}
420181834Sroberto
421181834Sroberto			/*
422181834Sroberto			 * At year rollover check for leap second.
423181834Sroberto			 */
424181834Sroberto			if (day >= (year & 0x3 ? 366 : 367)) {
425181834Sroberto				if (leap) {
426181834Sroberto					sec(DATA0);
427181834Sroberto					printf("\nleap!");
428181834Sroberto					leap = 0;
429181834Sroberto				}
430181834Sroberto				day = 1;
431181834Sroberto				year++;
432181834Sroberto			}
433181834Sroberto			if (encode == WWV) {
434285612Sdelphij				snprintf(code, sizeof(code),
435285612Sdelphij				    "%01d%03d%02d%02d%01d", year / 10,
436285612Sdelphij				    day, hour, minute, year % 10);
437181834Sroberto				printf("\n%s\n", code);
438181834Sroberto				ptr = 8;
439181834Sroberto			}
440181834Sroberto		}
441181834Sroberto		if (encode == IRIG) {
442285612Sdelphij			snprintf(code, sizeof(code),
443285612Sdelphij			    "%04x%04d%06d%02d%02d%02d", 0, year, day,
444285612Sdelphij			    hour, minute, second);
445181834Sroberto			printf("%s\n", code);
446181834Sroberto			ptr = 19;
447181834Sroberto		}
448181834Sroberto
449181834Sroberto		/*
450181834Sroberto		 * Generate data for the second
451181834Sroberto		 */
452181834Sroberto		switch(encode) {
453181834Sroberto
454181834Sroberto		/*
455181834Sroberto		 * The IRIG second consists of 20 BCD digits of width-
456181834Sroberto		 * modulateod pulses at 2, 5 and 8 ms and modulated 50
457181834Sroberto		 * percent on the 1000-Hz carrier.
458181834Sroberto		 */
459181834Sroberto		case IRIG:
460181834Sroberto			for (i = 0; i < 100; i++) {
461181834Sroberto				if (i < 10) {
462181834Sroberto					sw = progz[i].sw;
463181834Sroberto					arg = progz[i].arg;
464181834Sroberto				} else {
465181834Sroberto					sw = progy[i % 10].sw;
466181834Sroberto					arg = progy[i % 10].arg;
467181834Sroberto				}
468181834Sroberto				switch(sw) {
469181834Sroberto
470181834Sroberto				case COEF:	/* send BCD bit */
471181834Sroberto					if (code[ptr] & arg) {
472181834Sroberto						peep(M5, 1000, HIGH);
473181834Sroberto						peep(M5, 1000, LOW);
474181834Sroberto						printf("1");
475181834Sroberto					} else {
476181834Sroberto						peep(M2, 1000, HIGH);
477181834Sroberto						peep(M8, 1000, LOW);
478181834Sroberto						printf("0");
479181834Sroberto					}
480181834Sroberto					break;
481181834Sroberto
482181834Sroberto				case DEC:	/* send IM/PI bit */
483181834Sroberto					ptr--;
484181834Sroberto					printf(" ");
485181834Sroberto					peep(arg, 1000, HIGH);
486181834Sroberto					peep(10 - arg, 1000, LOW);
487181834Sroberto					break;
488181834Sroberto
489181834Sroberto				case MIN:	/* send data bit */
490181834Sroberto					peep(arg, 1000, HIGH);
491181834Sroberto					peep(10 - arg, 1000, LOW);
492181834Sroberto					printf("M ");
493181834Sroberto					break;
494181834Sroberto				}
495181834Sroberto				if (ptr < 0)
496181834Sroberto					break;
497181834Sroberto			}
498181834Sroberto			printf("\n");
499181834Sroberto			break;
500181834Sroberto
501181834Sroberto		/*
502181834Sroberto		 * The WWV/H second consists of 9 BCD digits of width-
503181834Sroberto		 * modulateod pulses 200, 500 and 800 ms at 100-Hz.
504181834Sroberto		 */
505181834Sroberto		case WWV:
506181834Sroberto			sw = progx[second].sw;
507181834Sroberto			arg = progx[second].arg;
508181834Sroberto			switch(sw) {
509181834Sroberto
510181834Sroberto			case DATA:		/* send data bit */
511181834Sroberto				sec(arg);
512181834Sroberto				break;
513181834Sroberto
514181834Sroberto			case COEF:		/* send BCD bit */
515181834Sroberto				if (code[ptr] & arg) {
516181834Sroberto					sec(DATA1);
517181834Sroberto					printf("1");
518181834Sroberto				} else {
519181834Sroberto					sec(DATA0);
520181834Sroberto					printf("0");
521181834Sroberto				}
522181834Sroberto				break;
523181834Sroberto
524181834Sroberto			case LEAP:		/* send leap bit */
525181834Sroberto				if (leap) {
526181834Sroberto					sec(DATA1);
527181834Sroberto					printf("L ");
528181834Sroberto				} else {
529181834Sroberto					sec(DATA0);
530181834Sroberto					printf("  ");
531181834Sroberto				}
532181834Sroberto				break;
533181834Sroberto
534181834Sroberto			case DEC:		/* send data bit */
535181834Sroberto				ptr--;
536181834Sroberto				sec(arg);
537181834Sroberto				printf(" ");
538181834Sroberto				break;
539181834Sroberto
540181834Sroberto			case MIN:		/* send minute sync */
541181834Sroberto				peep(arg, tone, HIGH);
542181834Sroberto				peep(1000 - arg, tone, OFF);
543181834Sroberto				break;
544181834Sroberto
545181834Sroberto			case DUT1:		/* send DUT1 bits */
546181834Sroberto				if (dut1 & arg)
547181834Sroberto					sec(DATA1);
548181834Sroberto				else
549181834Sroberto					sec(DATA0);
550181834Sroberto				break;
551181834Sroberto
552181834Sroberto			case DST1:		/* send DST1 bit */
553181834Sroberto				ptr--;
554181834Sroberto				if (dst)
555181834Sroberto					sec(DATA1);
556181834Sroberto				else
557181834Sroberto					sec(DATA0);
558181834Sroberto				printf(" ");
559181834Sroberto				break;
560181834Sroberto
561181834Sroberto			case DST2:		/* send DST2 bit */
562181834Sroberto				if (dst)
563181834Sroberto					sec(DATA1);
564181834Sroberto				else
565181834Sroberto					sec(DATA0);
566181834Sroberto				break;
567181834Sroberto			}
568181834Sroberto		}
569181834Sroberto	}
570181834Sroberto}
571181834Sroberto
572181834Sroberto
573181834Sroberto/*
574181834Sroberto * Generate WWV/H 0 or 1 data pulse.
575181834Sroberto */
576181834Srobertovoid sec(
577181834Sroberto	int	code		/* DATA0, DATA1, PI */
578181834Sroberto	)
579181834Sroberto{
580181834Sroberto	/*
581181834Sroberto	 * The WWV data pulse begins with 5 ms of 1000 Hz follwed by a
582181834Sroberto	 * guard time of 25 ms. The data pulse is 170, 570 or 770 ms at
583181834Sroberto	 * 100 Hz corresponding to 0, 1 or position indicator (PI),
584181834Sroberto	 * respectively. Note the 100-Hz data pulses are transmitted 6
585181834Sroberto	 * dB below the 1000-Hz sync pulses. Originally the data pulses
586181834Sroberto	 * were transmited 10 dB below the sync pulses, but the station
587181834Sroberto	 * engineers increased that to 6 dB because the Heath GC-1000
588181834Sroberto	 * WWV/H radio clock worked much better.
589181834Sroberto	 */
590181834Sroberto	peep(5, tone, HIGH);		/* send seconds tick */
591181834Sroberto	peep(25, tone, OFF);
592181834Sroberto	peep(code - 30, 100, LOW);	/* send data */
593181834Sroberto	peep(1000 - code, 100, OFF);
594181834Sroberto}
595181834Sroberto
596181834Sroberto
597181834Sroberto/*
598181834Sroberto * Generate cycles of 100 Hz or any multiple of 100 Hz.
599181834Sroberto */
600181834Srobertovoid peep(
601181834Sroberto	int	pulse,		/* pulse length (ms) */
602181834Sroberto	int	freq,		/* frequency (Hz) */
603181834Sroberto	int	amp		/* amplitude */
604181834Sroberto	)
605181834Sroberto{
606181834Sroberto	int	increm;		/* phase increment */
607181834Sroberto	int	i, j;
608181834Sroberto
609181834Sroberto	if (amp == OFF || freq == 0)
610181834Sroberto		increm = 10;
611181834Sroberto	else
612181834Sroberto		increm = freq / 100;
613181834Sroberto	j = 0;
614181834Sroberto	for (i = 0 ; i < pulse * 8; i++) {
615181834Sroberto		switch (amp) {
616181834Sroberto
617181834Sroberto		case HIGH:
618181834Sroberto			buffer[bufcnt++] = ~c6000[j];
619181834Sroberto			break;
620181834Sroberto
621181834Sroberto		case LOW:
622181834Sroberto			buffer[bufcnt++] = ~c3000[j];
623181834Sroberto			break;
624181834Sroberto
625181834Sroberto		default:
626181834Sroberto			buffer[bufcnt++] = ~0;
627181834Sroberto		}
628181834Sroberto		if (bufcnt >= BUFLNG) {
629181834Sroberto			write(fd, buffer, BUFLNG);
630181834Sroberto			bufcnt = 0;
631181834Sroberto		}
632181834Sroberto		j = (j + increm) % 80;
633181834Sroberto	}
634181834Sroberto}
635181834Sroberto
636181834Sroberto
637181834Sroberto/*
638181834Sroberto * Delay for initial phasing
639181834Sroberto */
640181834Srobertovoid delay (
641181834Sroberto	int	delay		/* delay in samples */
642181834Sroberto	)
643181834Sroberto{
644181834Sroberto	int	samples;	/* samples remaining */
645181834Sroberto
646181834Sroberto	samples = delay;
647181834Sroberto	memset(buffer, 0, BUFLNG);
648181834Sroberto	while (samples >= BUFLNG) {
649181834Sroberto		write(fd, buffer, BUFLNG);
650181834Sroberto		samples -= BUFLNG;
651181834Sroberto	}
652181834Sroberto		write(fd, buffer, samples);
653181834Sroberto}
654