1/* $NetBSD: tg.c,v 1.1.1.1 2009/12/13 16:57:31 kardel 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 */ 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