1181834Sroberto/* 2181834Sroberto * tg.c generate WWV or IRIG signals for test 3181834Sroberto */ 4181834Sroberto/* 5181834Sroberto * This program can generate audio signals that simulate the WWV/H 6181834Sroberto * broadcast timecode. Alternatively, it can generate the IRIG-B 7181834Sroberto * timecode commonly used to synchronize laboratory equipment. It is 8181834Sroberto * intended to test the WWV/H driver (refclock_wwv.c) and the IRIG 9181834Sroberto * driver (refclock_irig.c) in the NTP driver collection. 10181834Sroberto * 11181834Sroberto * Besides testing the drivers themselves, this program can be used to 12181834Sroberto * synchronize remote machines over audio transmission lines or program 13181834Sroberto * feeds. The program reads the time on the local machine and sets the 14181834Sroberto * initial epoch of the signal generator within one millisecond. 15181834Sroberto * Alernatively, the initial epoch can be set to an arbitrary time. This 16181834Sroberto * is useful when searching for bugs and testing for correct response to 17181834Sroberto * a leap second in UTC. Note however, the ultimate accuracy is limited 18181834Sroberto * by the intrinsic frequency error of the codec sample clock, which can 19181834Sroberto # reach well over 100 PPM. 20181834Sroberto * 21181834Sroberto * The default is to route generated signals to the line output 22181834Sroberto * jack; the s option on the command line routes these signals to the 23181834Sroberto * internal speaker as well. The v option controls the speaker volume 24181834Sroberto * over the range 0-255. The signal generator by default uses WWV 25181834Sroberto * format; the h option switches to WWVH format and the i option 26181834Sroberto * switches to IRIG-B format. 27181834Sroberto * 28181834Sroberto * Once started the program runs continuously. The default initial epoch 29181834Sroberto * for the signal generator is read from the computer system clock when 30181834Sroberto * the program starts. The y option specifies an alternate epoch using a 31181834Sroberto * string yydddhhmmss, where yy is the year of century, ddd the day of 32181834Sroberto * year, hh the hour of day and mm the minute of hour. For instance, 33181834Sroberto * 1946Z on 1 January 2006 is 060011946. The l option lights the leap 34181834Sroberto * warning bit in the WWV/H timecode, so is handy to check for correct 35181834Sroberto * behavior at the next leap second epoch. The remaining options are 36181834Sroberto * specified below under the Parse Options heading. Most of these are 37181834Sroberto * for testing. 38181834Sroberto * 39181834Sroberto * During operation the program displays the WWV/H timecode (9 digits) 40181834Sroberto * or IRIG timecode (20 digits) as each new string is constructed. The 41181834Sroberto * display is followed by the BCD binary bits as transmitted. Note that 42181834Sroberto * the transmissionorder is low-order first as the frame is processed 43181834Sroberto * left to right. For WWV/H The leap warning L preceeds the first bit. 44181834Sroberto * For IRIG the on-time marker M preceeds the first (units) bit, so its 45181834Sroberto * code is delayed one bit and the next digit (tens) needs only three 46181834Sroberto * bits. 47181834Sroberto * 48181834Sroberto * The program has been tested with the Sun Blade 1500 running Solaris 49181834Sroberto * 10, but not yet with other machines. It uses no special features and 50181834Sroberto * should be readily portable to other hardware and operating systems. 51181834Sroberto */ 52181834Sroberto#include <stdio.h> 53181834Sroberto#include <stdlib.h> 54181834Sroberto#include <time.h> 55181834Sroberto#include <sys/audio.h> 56181834Sroberto#include <math.h> 57181834Sroberto#include <errno.h> 58181834Sroberto#include <sys/types.h> 59181834Sroberto#include <sys/stat.h> 60181834Sroberto#include <fcntl.h> 61181834Sroberto#include <string.h> 62181834Sroberto#include <unistd.h> 63181834Sroberto 64181834Sroberto#define SECOND 8000 /* one second of 125-us samples */ 65181834Sroberto#define BUFLNG 400 /* buffer size */ 66181834Sroberto#define DEVICE "/dev/audio" /* default audio device */ 67181834Sroberto#define WWV 0 /* WWV encoder */ 68181834Sroberto#define IRIG 1 /* IRIG-B encoder */ 69181834Sroberto#define OFF 0 /* zero amplitude */ 70181834Sroberto#define LOW 1 /* low amplitude */ 71181834Sroberto#define HIGH 2 /* high amplitude */ 72181834Sroberto#define DATA0 200 /* WWV/H 0 pulse */ 73181834Sroberto#define DATA1 500 /* WWV/H 1 pulse */ 74181834Sroberto#define PI 800 /* WWV/H PI pulse */ 75181834Sroberto#define M2 2 /* IRIG 0 pulse */ 76181834Sroberto#define M5 5 /* IRIG 1 pulse */ 77181834Sroberto#define M8 8 /* IRIG PI pulse */ 78181834Sroberto 79181834Sroberto/* 80181834Sroberto * Companded sine table amplitude 3000 units 81181834Sroberto */ 82181834Srobertoint c3000[] = {1, 48, 63, 70, 78, 82, 85, 89, 92, 94, /* 0-9 */ 83181834Sroberto 96, 98, 99, 100, 101, 101, 102, 103, 103, 103, /* 10-19 */ 84181834Sroberto 103, 103, 103, 103, 102, 101, 101, 100, 99, 98, /* 20-29 */ 85181834Sroberto 96, 94, 92, 89, 85, 82, 78, 70, 63, 48, /* 30-39 */ 86181834Sroberto 129, 176, 191, 198, 206, 210, 213, 217, 220, 222, /* 40-49 */ 87181834Sroberto 224, 226, 227, 228, 229, 229, 230, 231, 231, 231, /* 50-59 */ 88181834Sroberto 231, 231, 231, 231, 230, 229, 229, 228, 227, 226, /* 60-69 */ 89181834Sroberto 224, 222, 220, 217, 213, 210, 206, 198, 191, 176}; /* 70-79 */ 90181834Sroberto/* 91181834Sroberto * Companded sine table amplitude 6000 units 92181834Sroberto */ 93181834Srobertoint c6000[] = {1, 63, 78, 86, 93, 98, 101, 104, 107, 110, /* 0-9 */ 94181834Sroberto 112, 113, 115, 116, 117, 117, 118, 118, 119, 119, /* 10-19 */ 95181834Sroberto 119, 119, 119, 118, 118, 117, 117, 116, 115, 113, /* 20-29 */ 96181834Sroberto 112, 110, 107, 104, 101, 98, 93, 86, 78, 63, /* 30-39 */ 97181834Sroberto 129, 191, 206, 214, 221, 226, 229, 232, 235, 238, /* 40-49 */ 98181834Sroberto 240, 241, 243, 244, 245, 245, 246, 246, 247, 247, /* 50-59 */ 99181834Sroberto 247, 247, 247, 246, 246, 245, 245, 244, 243, 241, /* 60-69 */ 100181834Sroberto 240, 238, 235, 232, 229, 226, 221, 214, 206, 191}; /* 70-79 */ 101181834Sroberto 102181834Sroberto/* 103181834Sroberto * Decoder operations at the end of each second are driven by a state 104181834Sroberto * machine. The transition matrix consists of a dispatch table indexed 105181834Sroberto * by second number. Each entry in the table contains a case switch 106181834Sroberto * number and argument. 107181834Sroberto */ 108181834Srobertostruct progx { 109181834Sroberto int sw; /* case switch number */ 110181834Sroberto int arg; /* argument */ 111181834Sroberto}; 112181834Sroberto 113181834Sroberto/* 114181834Sroberto * Case switch numbers 115181834Sroberto */ 116181834Sroberto#define DATA 0 /* send data (0, 1, PI) */ 117181834Sroberto#define COEF 1 /* send BCD bit */ 118181834Sroberto#define DEC 2 /* decrement to next digit */ 119181834Sroberto#define MIN 3 /* minute pulse */ 120181834Sroberto#define LEAP 4 /* leap warning */ 121181834Sroberto#define DUT1 5 /* DUT1 bits */ 122181834Sroberto#define DST1 6 /* DST1 bit */ 123181834Sroberto#define DST2 7 /* DST2 bit */ 124181834Sroberto 125181834Sroberto/* 126181834Sroberto * WWV/H format (100-Hz, 9 digits, 1 m frame) 127181834Sroberto */ 128181834Srobertostruct progx progx[] = { 129181834Sroberto {MIN, 800}, /* 0 minute sync pulse */ 130181834Sroberto {DATA, DATA0}, /* 1 */ 131181834Sroberto {DST2, 0}, /* 2 DST2 */ 132181834Sroberto {LEAP, 0}, /* 3 leap warning */ 133181834Sroberto {COEF, 1}, /* 4 1 year units */ 134181834Sroberto {COEF, 2}, /* 5 2 */ 135181834Sroberto {COEF, 4}, /* 6 4 */ 136181834Sroberto {COEF, 8}, /* 7 8 */ 137181834Sroberto {DEC, DATA0}, /* 8 */ 138181834Sroberto {DATA, PI}, /* 9 p1 */ 139181834Sroberto {COEF, 1}, /* 10 1 minute units */ 140181834Sroberto {COEF, 2}, /* 11 2 */ 141181834Sroberto {COEF, 4}, /* 12 4 */ 142181834Sroberto {COEF, 8}, /* 13 8 */ 143181834Sroberto {DEC, DATA0}, /* 14 */ 144181834Sroberto {COEF, 1}, /* 15 10 minute tens */ 145181834Sroberto {COEF, 2}, /* 16 20 */ 146181834Sroberto {COEF, 4}, /* 17 40 */ 147181834Sroberto {COEF, 8}, /* 18 80 (not used) */ 148181834Sroberto {DEC, PI}, /* 19 p2 */ 149181834Sroberto {COEF, 1}, /* 20 1 hour units */ 150181834Sroberto {COEF, 2}, /* 21 2 */ 151181834Sroberto {COEF, 4}, /* 22 4 */ 152181834Sroberto {COEF, 8}, /* 23 8 */ 153181834Sroberto {DEC, DATA0}, /* 24 */ 154181834Sroberto {COEF, 1}, /* 25 10 hour tens */ 155181834Sroberto {COEF, 2}, /* 26 20 */ 156181834Sroberto {COEF, 4}, /* 27 40 (not used) */ 157181834Sroberto {COEF, 8}, /* 28 80 (not used) */ 158181834Sroberto {DEC, PI}, /* 29 p3 */ 159181834Sroberto {COEF, 1}, /* 30 1 day units */ 160181834Sroberto {COEF, 2}, /* 31 2 */ 161181834Sroberto {COEF, 4}, /* 32 4 */ 162181834Sroberto {COEF, 8}, /* 33 8 */ 163181834Sroberto {DEC, DATA0}, /* 34 not used */ 164181834Sroberto {COEF, 1}, /* 35 10 day tens */ 165181834Sroberto {COEF, 2}, /* 36 20 */ 166181834Sroberto {COEF, 4}, /* 37 40 */ 167181834Sroberto {COEF, 8}, /* 38 80 */ 168181834Sroberto {DEC, PI}, /* 39 p4 */ 169181834Sroberto {COEF, 1}, /* 40 100 day hundreds */ 170181834Sroberto {COEF, 2}, /* 41 200 */ 171181834Sroberto {COEF, 4}, /* 42 400 (not used) */ 172181834Sroberto {COEF, 8}, /* 43 800 (not used) */ 173181834Sroberto {DEC, DATA0}, /* 44 */ 174181834Sroberto {DATA, DATA0}, /* 45 */ 175181834Sroberto {DATA, DATA0}, /* 46 */ 176181834Sroberto {DATA, DATA0}, /* 47 */ 177181834Sroberto {DATA, DATA0}, /* 48 */ 178181834Sroberto {DATA, PI}, /* 49 p5 */ 179181834Sroberto {DUT1, 8}, /* 50 DUT1 sign */ 180181834Sroberto {COEF, 1}, /* 51 10 year tens */ 181181834Sroberto {COEF, 2}, /* 52 20 */ 182181834Sroberto {COEF, 4}, /* 53 40 */ 183181834Sroberto {COEF, 8}, /* 54 80 */ 184181834Sroberto {DST1, 0}, /* 55 DST1 */ 185181834Sroberto {DUT1, 1}, /* 56 0.1 DUT1 fraction */ 186181834Sroberto {DUT1, 2}, /* 57 0.2 */ 187181834Sroberto {DUT1, 4}, /* 58 0.4 */ 188181834Sroberto {DATA, PI}, /* 59 p6 */ 189181834Sroberto {DATA, DATA0}, /* 60 leap */ 190181834Sroberto}; 191181834Sroberto 192181834Sroberto/* 193181834Sroberto * IRIG format except first frame (1000 Hz, 20 digits, 1 s frame) 194181834Sroberto */ 195181834Srobertostruct progx progy[] = { 196181834Sroberto {COEF, 1}, /* 0 1 units */ 197181834Sroberto {COEF, 2}, /* 1 2 */ 198181834Sroberto {COEF, 4}, /* 2 4 */ 199181834Sroberto {COEF, 8}, /* 3 8 */ 200181834Sroberto {DEC, M2}, /* 4 im */ 201181834Sroberto {COEF, 1}, /* 5 10 tens */ 202181834Sroberto {COEF, 2}, /* 6 20 */ 203181834Sroberto {COEF, 4}, /* 7 40 */ 204181834Sroberto {COEF, 8}, /* 8 80 */ 205181834Sroberto {DEC, M8}, /* 9 pi */ 206181834Sroberto}; 207181834Sroberto 208181834Sroberto/* 209181834Sroberto * IRIG format first frame (1000 Hz, 20 digits, 1 s frame) 210181834Sroberto */ 211181834Srobertostruct progx progz[] = { 212181834Sroberto {MIN, M8}, /* 0 pi (second) */ 213181834Sroberto {COEF, 1}, /* 1 1 units */ 214181834Sroberto {COEF, 2}, /* 2 2 */ 215181834Sroberto {COEF, 4}, /* 3 4 */ 216181834Sroberto {COEF, 8}, /* 4 8 */ 217181834Sroberto {DEC, M2}, /* 5 im */ 218181834Sroberto {COEF, 1}, /* 6 10 tens */ 219181834Sroberto {COEF, 2}, /* 7 20 */ 220181834Sroberto {COEF, 4}, /* 8 40 */ 221181834Sroberto {DEC, M8}, /* 9 pi */ 222181834Sroberto}; 223181834Sroberto 224181834Sroberto/* 225181834Sroberto * Forward declarations 226181834Sroberto */ 227181834Srobertovoid sec(int); /* send second */ 228181834Srobertovoid digit(int); /* encode digit */ 229181834Srobertovoid peep(int, int, int); /* send cycles */ 230181834Srobertovoid delay(int); /* delay samples */ 231181834Sroberto 232181834Sroberto/* 233181834Sroberto * Global variables 234181834Sroberto */ 235181834Srobertochar buffer[BUFLNG]; /* output buffer */ 236181834Srobertoint bufcnt = 0; /* buffer counter */ 237181834Srobertoint second = 0; /* seconds counter */ 238181834Srobertoint fd; /* audio codec file descriptor */ 239181834Srobertoint tone = 1000; /* WWV sync frequency */ 240181834Srobertoint level = AUDIO_MAX_GAIN / 8; /* output level */ 241181834Srobertoint port = AUDIO_LINE_OUT; /* output port */ 242181834Srobertoint encode = WWV; /* encoder select */ 243181834Srobertoint leap = 0; /* leap indicator */ 244181834Srobertoint dst = 0; /* winter/summer time */ 245181834Srobertoint dut1 = 0; /* DUT1 correction (sign, magnitude) */ 246181834Srobertoint utc = 0; /* option epoch */ 247181834Sroberto 248181834Sroberto/* 249181834Sroberto * Main program 250181834Sroberto */ 251181834Srobertoint 252181834Srobertomain( 253181834Sroberto int argc, /* command line options */ 254181834Sroberto char **argv /* poiniter to list of tokens */ 255181834Sroberto ) 256181834Sroberto{ 257181834Sroberto struct timeval tv; /* system clock at startup */ 258181834Sroberto audio_info_t info; /* Sun audio structure */ 259181834Sroberto struct tm *tm = NULL; /* structure returned by gmtime */ 260181834Sroberto char device[50]; /* audio device */ 261181834Sroberto char code[100]; /* timecode */ 262181834Sroberto int rval, temp, arg, sw, ptr; 263181834Sroberto int minute, hour, day, year; 264181834Sroberto int i; 265181834Sroberto 266181834Sroberto /* 267181834Sroberto * Parse options 268181834Sroberto */ 269285612Sdelphij strlcpy(device, DEVICE, sizeof(device)); 270181834Sroberto year = 0; 271181834Sroberto while ((temp = getopt(argc, argv, "a:dhilsu:v:y:")) != -1) { 272181834Sroberto switch (temp) { 273181834Sroberto 274181834Sroberto case 'a': /* specify audio device (/dev/audio) */ 275285612Sdelphij strlcpy(device, optarg, sizeof(device)); 276181834Sroberto break; 277181834Sroberto 278181834Sroberto case 'd': /* set DST for summer (WWV/H only) */ 279181834Sroberto dst++; 280181834Sroberto break; 281181834Sroberto 282181834Sroberto case 'h': /* select WWVH sync frequency */ 283181834Sroberto tone = 1200; 284181834Sroberto break; 285181834Sroberto 286181834Sroberto case 'i': /* select irig format */ 287181834Sroberto encode = IRIG; 288181834Sroberto break; 289181834Sroberto 290181834Sroberto case 'l': /* set leap warning bit (WWV/H only) */ 291181834Sroberto leap++; 292181834Sroberto break; 293181834Sroberto 294181834Sroberto case 's': /* enable speaker */ 295181834Sroberto port |= AUDIO_SPEAKER; 296181834Sroberto break; 297181834Sroberto 298181834Sroberto case 'u': /* set DUT1 offset (-7 to +7) */ 299181834Sroberto sscanf(optarg, "%d", &dut1); 300181834Sroberto if (dut1 < 0) 301181834Sroberto dut1 = abs(dut1); 302181834Sroberto else 303181834Sroberto dut1 |= 0x8; 304181834Sroberto break; 305181834Sroberto 306181834Sroberto case 'v': /* set output level (0-255) */ 307181834Sroberto sscanf(optarg, "%d", &level); 308181834Sroberto break; 309181834Sroberto 310181834Sroberto case 'y': /* set initial date and time */ 311181834Sroberto sscanf(optarg, "%2d%3d%2d%2d", &year, &day, 312181834Sroberto &hour, &minute); 313181834Sroberto utc++; 314181834Sroberto break; 315181834Sroberto 316181834Sroberto defult: 317181834Sroberto printf("invalid option %c\n", temp); 318181834Sroberto break; 319181834Sroberto } 320181834Sroberto } 321181834Sroberto 322181834Sroberto /* 323181834Sroberto * Open audio device and set options 324181834Sroberto */ 325181834Sroberto fd = open("/dev/audio", O_WRONLY); 326181834Sroberto if (fd <= 0) { 327181834Sroberto printf("audio open %s\n", strerror(errno)); 328181834Sroberto exit(1); 329181834Sroberto } 330181834Sroberto rval = ioctl(fd, AUDIO_GETINFO, &info); 331181834Sroberto if (rval < 0) { 332181834Sroberto printf("audio control %s\n", strerror(errno)); 333181834Sroberto exit(0); 334181834Sroberto } 335181834Sroberto info.play.port = port; 336181834Sroberto info.play.gain = level; 337181834Sroberto info.play.sample_rate = SECOND; 338181834Sroberto info.play.channels = 1; 339181834Sroberto info.play.precision = 8; 340181834Sroberto info.play.encoding = AUDIO_ENCODING_ULAW; 341181834Sroberto printf("port %d gain %d rate %d chan %d prec %d encode %d\n", 342181834Sroberto info.play.port, info.play.gain, info.play.sample_rate, 343181834Sroberto info.play.channels, info.play.precision, 344181834Sroberto info.play.encoding); 345181834Sroberto ioctl(fd, AUDIO_SETINFO, &info); 346181834Sroberto 347181834Sroberto /* 348181834Sroberto * Unless specified otherwise, read the system clock and 349181834Sroberto * initialize the time. 350181834Sroberto */ 351181834Sroberto if (!utc) { 352181834Sroberto gettimeofday(&tv, NULL); 353181834Sroberto tm = gmtime(&tv.tv_sec); 354181834Sroberto minute = tm->tm_min; 355181834Sroberto hour = tm->tm_hour; 356181834Sroberto day = tm->tm_yday + 1; 357181834Sroberto year = tm->tm_year % 100; 358181834Sroberto second = tm->tm_sec; 359181834Sroberto 360181834Sroberto /* 361181834Sroberto * Delay the first second so the generator is accurately 362181834Sroberto * aligned with the system clock within one sample (125 363181834Sroberto * microseconds ). 364181834Sroberto */ 365181834Sroberto delay(SECOND - tv.tv_usec * 8 / 1000); 366181834Sroberto } 367181834Sroberto memset(code, 0, sizeof(code)); 368181834Sroberto switch (encode) { 369181834Sroberto 370181834Sroberto /* 371181834Sroberto * For WWV/H and default time, carefully set the signal 372181834Sroberto * generator seconds number to agree with the current time. 373181834Sroberto */ 374181834Sroberto case WWV: 375181834Sroberto printf("year %d day %d time %02d:%02d:%02d tone %d\n", 376181834Sroberto year, day, hour, minute, second, tone); 377285612Sdelphij snprintf(code, sizeof(code), "%01d%03d%02d%02d%01d", 378285612Sdelphij year / 10, day, hour, minute, year % 10); 379181834Sroberto printf("%s\n", code); 380181834Sroberto ptr = 8; 381181834Sroberto for (i = 0; i <= second; i++) { 382181834Sroberto if (progx[i].sw == DEC) 383181834Sroberto ptr--; 384181834Sroberto } 385181834Sroberto break; 386181834Sroberto 387181834Sroberto /* 388181834Sroberto * For IRIG the signal generator runs every second, so requires 389181834Sroberto * no additional alignment. 390181834Sroberto */ 391181834Sroberto case IRIG: 392181834Sroberto printf("sbs %x year %d day %d time %02d:%02d:%02d\n", 393181834Sroberto 0, year, day, hour, minute, second); 394181834Sroberto break; 395181834Sroberto } 396181834Sroberto 397181834Sroberto /* 398181834Sroberto * Run the signal generator to generate new timecode strings 399181834Sroberto * once per minute for WWV/H and once per second for IRIG. 400181834Sroberto */ 401181834Sroberto while(1) { 402181834Sroberto 403181834Sroberto /* 404181834Sroberto * Crank the state machine to propagate carries to the 405181834Sroberto * year of century. Note that we delayed up to one 406181834Sroberto * second for alignment after reading the time, so this 407181834Sroberto * is the next second. 408181834Sroberto */ 409181834Sroberto second = (second + 1) % 60; 410181834Sroberto if (second == 0) { 411181834Sroberto minute++; 412181834Sroberto if (minute >= 60) { 413181834Sroberto minute = 0; 414181834Sroberto hour++; 415181834Sroberto } 416181834Sroberto if (hour >= 24) { 417181834Sroberto hour = 0; 418181834Sroberto day++; 419181834Sroberto } 420181834Sroberto 421181834Sroberto /* 422181834Sroberto * At year rollover check for leap second. 423181834Sroberto */ 424181834Sroberto if (day >= (year & 0x3 ? 366 : 367)) { 425181834Sroberto if (leap) { 426181834Sroberto sec(DATA0); 427181834Sroberto printf("\nleap!"); 428181834Sroberto leap = 0; 429181834Sroberto } 430181834Sroberto day = 1; 431181834Sroberto year++; 432181834Sroberto } 433181834Sroberto if (encode == WWV) { 434285612Sdelphij snprintf(code, sizeof(code), 435285612Sdelphij "%01d%03d%02d%02d%01d", year / 10, 436285612Sdelphij day, hour, minute, year % 10); 437181834Sroberto printf("\n%s\n", code); 438181834Sroberto ptr = 8; 439181834Sroberto } 440181834Sroberto } 441181834Sroberto if (encode == IRIG) { 442285612Sdelphij snprintf(code, sizeof(code), 443285612Sdelphij "%04x%04d%06d%02d%02d%02d", 0, year, day, 444285612Sdelphij hour, minute, second); 445181834Sroberto printf("%s\n", code); 446181834Sroberto ptr = 19; 447181834Sroberto } 448181834Sroberto 449181834Sroberto /* 450181834Sroberto * Generate data for the second 451181834Sroberto */ 452181834Sroberto switch(encode) { 453181834Sroberto 454181834Sroberto /* 455181834Sroberto * The IRIG second consists of 20 BCD digits of width- 456181834Sroberto * modulateod pulses at 2, 5 and 8 ms and modulated 50 457181834Sroberto * percent on the 1000-Hz carrier. 458181834Sroberto */ 459181834Sroberto case IRIG: 460181834Sroberto for (i = 0; i < 100; i++) { 461181834Sroberto if (i < 10) { 462181834Sroberto sw = progz[i].sw; 463181834Sroberto arg = progz[i].arg; 464181834Sroberto } else { 465181834Sroberto sw = progy[i % 10].sw; 466181834Sroberto arg = progy[i % 10].arg; 467181834Sroberto } 468181834Sroberto switch(sw) { 469181834Sroberto 470181834Sroberto case COEF: /* send BCD bit */ 471181834Sroberto if (code[ptr] & arg) { 472181834Sroberto peep(M5, 1000, HIGH); 473181834Sroberto peep(M5, 1000, LOW); 474181834Sroberto printf("1"); 475181834Sroberto } else { 476181834Sroberto peep(M2, 1000, HIGH); 477181834Sroberto peep(M8, 1000, LOW); 478181834Sroberto printf("0"); 479181834Sroberto } 480181834Sroberto break; 481181834Sroberto 482181834Sroberto case DEC: /* send IM/PI bit */ 483181834Sroberto ptr--; 484181834Sroberto printf(" "); 485181834Sroberto peep(arg, 1000, HIGH); 486181834Sroberto peep(10 - arg, 1000, LOW); 487181834Sroberto break; 488181834Sroberto 489181834Sroberto case MIN: /* send data bit */ 490181834Sroberto peep(arg, 1000, HIGH); 491181834Sroberto peep(10 - arg, 1000, LOW); 492181834Sroberto printf("M "); 493181834Sroberto break; 494181834Sroberto } 495181834Sroberto if (ptr < 0) 496181834Sroberto break; 497181834Sroberto } 498181834Sroberto printf("\n"); 499181834Sroberto break; 500181834Sroberto 501181834Sroberto /* 502181834Sroberto * The WWV/H second consists of 9 BCD digits of width- 503181834Sroberto * modulateod pulses 200, 500 and 800 ms at 100-Hz. 504181834Sroberto */ 505181834Sroberto case WWV: 506181834Sroberto sw = progx[second].sw; 507181834Sroberto arg = progx[second].arg; 508181834Sroberto switch(sw) { 509181834Sroberto 510181834Sroberto case DATA: /* send data bit */ 511181834Sroberto sec(arg); 512181834Sroberto break; 513181834Sroberto 514181834Sroberto case COEF: /* send BCD bit */ 515181834Sroberto if (code[ptr] & arg) { 516181834Sroberto sec(DATA1); 517181834Sroberto printf("1"); 518181834Sroberto } else { 519181834Sroberto sec(DATA0); 520181834Sroberto printf("0"); 521181834Sroberto } 522181834Sroberto break; 523181834Sroberto 524181834Sroberto case LEAP: /* send leap bit */ 525181834Sroberto if (leap) { 526181834Sroberto sec(DATA1); 527181834Sroberto printf("L "); 528181834Sroberto } else { 529181834Sroberto sec(DATA0); 530181834Sroberto printf(" "); 531181834Sroberto } 532181834Sroberto break; 533181834Sroberto 534181834Sroberto case DEC: /* send data bit */ 535181834Sroberto ptr--; 536181834Sroberto sec(arg); 537181834Sroberto printf(" "); 538181834Sroberto break; 539181834Sroberto 540181834Sroberto case MIN: /* send minute sync */ 541181834Sroberto peep(arg, tone, HIGH); 542181834Sroberto peep(1000 - arg, tone, OFF); 543181834Sroberto break; 544181834Sroberto 545181834Sroberto case DUT1: /* send DUT1 bits */ 546181834Sroberto if (dut1 & arg) 547181834Sroberto sec(DATA1); 548181834Sroberto else 549181834Sroberto sec(DATA0); 550181834Sroberto break; 551181834Sroberto 552181834Sroberto case DST1: /* send DST1 bit */ 553181834Sroberto ptr--; 554181834Sroberto if (dst) 555181834Sroberto sec(DATA1); 556181834Sroberto else 557181834Sroberto sec(DATA0); 558181834Sroberto printf(" "); 559181834Sroberto break; 560181834Sroberto 561181834Sroberto case DST2: /* send DST2 bit */ 562181834Sroberto if (dst) 563181834Sroberto sec(DATA1); 564181834Sroberto else 565181834Sroberto sec(DATA0); 566181834Sroberto break; 567181834Sroberto } 568181834Sroberto } 569181834Sroberto } 570181834Sroberto} 571181834Sroberto 572181834Sroberto 573181834Sroberto/* 574181834Sroberto * Generate WWV/H 0 or 1 data pulse. 575181834Sroberto */ 576181834Srobertovoid sec( 577181834Sroberto int code /* DATA0, DATA1, PI */ 578181834Sroberto ) 579181834Sroberto{ 580181834Sroberto /* 581181834Sroberto * The WWV data pulse begins with 5 ms of 1000 Hz follwed by a 582181834Sroberto * guard time of 25 ms. The data pulse is 170, 570 or 770 ms at 583181834Sroberto * 100 Hz corresponding to 0, 1 or position indicator (PI), 584181834Sroberto * respectively. Note the 100-Hz data pulses are transmitted 6 585181834Sroberto * dB below the 1000-Hz sync pulses. Originally the data pulses 586181834Sroberto * were transmited 10 dB below the sync pulses, but the station 587181834Sroberto * engineers increased that to 6 dB because the Heath GC-1000 588181834Sroberto * WWV/H radio clock worked much better. 589181834Sroberto */ 590181834Sroberto peep(5, tone, HIGH); /* send seconds tick */ 591181834Sroberto peep(25, tone, OFF); 592181834Sroberto peep(code - 30, 100, LOW); /* send data */ 593181834Sroberto peep(1000 - code, 100, OFF); 594181834Sroberto} 595181834Sroberto 596181834Sroberto 597181834Sroberto/* 598181834Sroberto * Generate cycles of 100 Hz or any multiple of 100 Hz. 599181834Sroberto */ 600181834Srobertovoid peep( 601181834Sroberto int pulse, /* pulse length (ms) */ 602181834Sroberto int freq, /* frequency (Hz) */ 603181834Sroberto int amp /* amplitude */ 604181834Sroberto ) 605181834Sroberto{ 606181834Sroberto int increm; /* phase increment */ 607181834Sroberto int i, j; 608181834Sroberto 609181834Sroberto if (amp == OFF || freq == 0) 610181834Sroberto increm = 10; 611181834Sroberto else 612181834Sroberto increm = freq / 100; 613181834Sroberto j = 0; 614181834Sroberto for (i = 0 ; i < pulse * 8; i++) { 615181834Sroberto switch (amp) { 616181834Sroberto 617181834Sroberto case HIGH: 618181834Sroberto buffer[bufcnt++] = ~c6000[j]; 619181834Sroberto break; 620181834Sroberto 621181834Sroberto case LOW: 622181834Sroberto buffer[bufcnt++] = ~c3000[j]; 623181834Sroberto break; 624181834Sroberto 625181834Sroberto default: 626181834Sroberto buffer[bufcnt++] = ~0; 627181834Sroberto } 628181834Sroberto if (bufcnt >= BUFLNG) { 629181834Sroberto write(fd, buffer, BUFLNG); 630181834Sroberto bufcnt = 0; 631181834Sroberto } 632181834Sroberto j = (j + increm) % 80; 633181834Sroberto } 634181834Sroberto} 635181834Sroberto 636181834Sroberto 637181834Sroberto/* 638181834Sroberto * Delay for initial phasing 639181834Sroberto */ 640181834Srobertovoid delay ( 641181834Sroberto int delay /* delay in samples */ 642181834Sroberto ) 643181834Sroberto{ 644181834Sroberto int samples; /* samples remaining */ 645181834Sroberto 646181834Sroberto samples = delay; 647181834Sroberto memset(buffer, 0, BUFLNG); 648181834Sroberto while (samples >= BUFLNG) { 649181834Sroberto write(fd, buffer, BUFLNG); 650181834Sroberto samples -= BUFLNG; 651181834Sroberto } 652181834Sroberto write(fd, buffer, samples); 653181834Sroberto} 654