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 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 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 */ 575 void 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 */ 599 void 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 */ 639 void 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 } 653