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