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