PageRenderTime 224ms CodeModel.GetById 193ms app.highlight 26ms RepoModel.GetById 1ms app.codeStats 0ms

/contrib/ntp/util/tg.c

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