xref: /netbsd-src/external/bsd/ntp/dist/util/tg.c (revision cdfa2a7ef92791ba9db70a584a1d904730e6fb46)
1 /*	$NetBSD: tg.c,v 1.5 2020/05/25 20:47:37 christos Exp $	*/
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  */
84 int 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  */
95 int 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  */
110 struct 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  */
130 struct 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  */
197 struct 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  */
213 struct 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  */
229 void	sec(int);		/* send second */
230 void	digit(int);		/* encode digit */
231 void	peep(int, int, int);	/* send cycles */
232 void	delay(int);		/* delay samples */
233 
234 /*
235  * Global variables
236  */
237 char	buffer[BUFLNG];		/* output buffer */
238 int	bufcnt = 0;		/* buffer counter */
239 int	second = 0;		/* seconds counter */
240 int	fd;			/* audio codec file descriptor */
241 int	tone = 1000;		/* WWV sync frequency */
242 int	level = AUDIO_MAX_GAIN / 8; /* output level */
243 int	port = AUDIO_LINE_OUT;	/* output port */
244 int	encode = WWV;		/* encoder select */
245 int	leap = 0;		/* leap indicator */
246 int	dst = 0;		/* winter/summer time */
247 int	dut1 = 0;		/* DUT1 correction (sign, magnitude) */
248 int	utc = 0;		/* option epoch */
249 
250 /*
251  * Main program
252  */
253 int
main(int argc,char ** argv)254 main(
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 	strlcpy(device, DEVICE, sizeof(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 			strlcpy(device, optarg, sizeof(device));
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 		snprintf(code, sizeof(code), "%01d%03d%02d%02d%01d",
380 		    year / 10, day, 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 				snprintf(code, sizeof(code),
437 				    "%01d%03d%02d%02d%01d", year / 10,
438 				    day, hour, minute, year % 10);
439 				printf("\n%s\n", code);
440 				ptr = 8;
441 			}
442 		}
443 		if (encode == IRIG) {
444 			snprintf(code, sizeof(code),
445 			    "%04x%04d%06d%02d%02d%02d", 0, year, day,
446 			    hour, minute, second);
447 			printf("%s\n", code);
448 			ptr = 19;
449 		}
450 
451 		/*
452 		 * Generate data for the second
453 		 */
454 		switch(encode) {
455 
456 		/*
457 		 * The IRIG second consists of 20 BCD digits of width-
458 		 * modulateod pulses at 2, 5 and 8 ms and modulated 50
459 		 * percent on the 1000-Hz carrier.
460 		 */
461 		case IRIG:
462 			for (i = 0; i < 100; i++) {
463 				if (i < 10) {
464 					sw = progz[i].sw;
465 					arg = progz[i].arg;
466 				} else {
467 					sw = progy[i % 10].sw;
468 					arg = progy[i % 10].arg;
469 				}
470 				switch(sw) {
471 
472 				case COEF:	/* send BCD bit */
473 					if (code[ptr] & arg) {
474 						peep(M5, 1000, HIGH);
475 						peep(M5, 1000, LOW);
476 						printf("1");
477 					} else {
478 						peep(M2, 1000, HIGH);
479 						peep(M8, 1000, LOW);
480 						printf("0");
481 					}
482 					break;
483 
484 				case DEC:	/* send IM/PI bit */
485 					ptr--;
486 					printf(" ");
487 					peep(arg, 1000, HIGH);
488 					peep(10 - arg, 1000, LOW);
489 					break;
490 
491 				case MIN:	/* send data bit */
492 					peep(arg, 1000, HIGH);
493 					peep(10 - arg, 1000, LOW);
494 					printf("M ");
495 					break;
496 				}
497 				if (ptr < 0)
498 					break;
499 			}
500 			printf("\n");
501 			break;
502 
503 		/*
504 		 * The WWV/H second consists of 9 BCD digits of width-
505 		 * modulateod pulses 200, 500 and 800 ms at 100-Hz.
506 		 */
507 		case WWV:
508 			sw = progx[second].sw;
509 			arg = progx[second].arg;
510 			switch(sw) {
511 
512 			case DATA:		/* send data bit */
513 				sec(arg);
514 				break;
515 
516 			case COEF:		/* send BCD bit */
517 				if (code[ptr] & arg) {
518 					sec(DATA1);
519 					printf("1");
520 				} else {
521 					sec(DATA0);
522 					printf("0");
523 				}
524 				break;
525 
526 			case LEAP:		/* send leap bit */
527 				if (leap) {
528 					sec(DATA1);
529 					printf("L ");
530 				} else {
531 					sec(DATA0);
532 					printf("  ");
533 				}
534 				break;
535 
536 			case DEC:		/* send data bit */
537 				ptr--;
538 				sec(arg);
539 				printf(" ");
540 				break;
541 
542 			case MIN:		/* send minute sync */
543 				peep(arg, tone, HIGH);
544 				peep(1000 - arg, tone, OFF);
545 				break;
546 
547 			case DUT1:		/* send DUT1 bits */
548 				if (dut1 & arg)
549 					sec(DATA1);
550 				else
551 					sec(DATA0);
552 				break;
553 
554 			case DST1:		/* send DST1 bit */
555 				ptr--;
556 				if (dst)
557 					sec(DATA1);
558 				else
559 					sec(DATA0);
560 				printf(" ");
561 				break;
562 
563 			case DST2:		/* send DST2 bit */
564 				if (dst)
565 					sec(DATA1);
566 				else
567 					sec(DATA0);
568 				break;
569 			}
570 		}
571 	}
572 }
573 
574 
575 /*
576  * Generate WWV/H 0 or 1 data pulse.
577  */
sec(int code)578 void sec(
579 	int	code		/* DATA0, DATA1, PI */
580 	)
581 {
582 	/*
583 	 * The WWV data pulse begins with 5 ms of 1000 Hz follwed by a
584 	 * guard time of 25 ms. The data pulse is 170, 570 or 770 ms at
585 	 * 100 Hz corresponding to 0, 1 or position indicator (PI),
586 	 * respectively. Note the 100-Hz data pulses are transmitted 6
587 	 * dB below the 1000-Hz sync pulses. Originally the data pulses
588 	 * were transmited 10 dB below the sync pulses, but the station
589 	 * engineers increased that to 6 dB because the Heath GC-1000
590 	 * WWV/H radio clock worked much better.
591 	 */
592 	peep(5, tone, HIGH);		/* send seconds tick */
593 	peep(25, tone, OFF);
594 	peep(code - 30, 100, LOW);	/* send data */
595 	peep(1000 - code, 100, OFF);
596 }
597 
598 
599 /*
600  * Generate cycles of 100 Hz or any multiple of 100 Hz.
601  */
peep(int pulse,int freq,int amp)602 void peep(
603 	int	pulse,		/* pulse length (ms) */
604 	int	freq,		/* frequency (Hz) */
605 	int	amp		/* amplitude */
606 	)
607 {
608 	int	increm;		/* phase increment */
609 	int	i, j;
610 
611 	if (amp == OFF || freq == 0)
612 		increm = 10;
613 	else
614 		increm = freq / 100;
615 	j = 0;
616 	for (i = 0 ; i < pulse * 8; i++) {
617 		switch (amp) {
618 
619 		case HIGH:
620 			buffer[bufcnt++] = ~c6000[j];
621 			break;
622 
623 		case LOW:
624 			buffer[bufcnt++] = ~c3000[j];
625 			break;
626 
627 		default:
628 			buffer[bufcnt++] = ~0;
629 		}
630 		if (bufcnt >= BUFLNG) {
631 			write(fd, buffer, BUFLNG);
632 			bufcnt = 0;
633 		}
634 		j = (j + increm) % 80;
635 	}
636 }
637 
638 
639 /*
640  * Delay for initial phasing
641  */
delay(int delay)642 void delay (
643 	int	delay		/* delay in samples */
644 	)
645 {
646 	int	samples;	/* samples remaining */
647 
648 	samples = delay;
649 	memset(buffer, 0, BUFLNG);
650 	while (samples >= BUFLNG) {
651 		write(fd, buffer, BUFLNG);
652 		samples -= BUFLNG;
653 	}
654 		write(fd, buffer, samples);
655 }
656