1275970Scy/* 2275970Scy * tg.c generate WWV or IRIG signals for test 3275970Scy */ 4275970Scy/* 5275970Scy * This program can generate audio signals that simulate the WWV/H 6275970Scy * broadcast timecode. Alternatively, it can generate the IRIG-B 7275970Scy * timecode commonly used to synchronize laboratory equipment. It is 8275970Scy * intended to test the WWV/H driver (refclock_wwv.c) and the IRIG 9275970Scy * driver (refclock_irig.c) in the NTP driver collection. 10275970Scy * 11275970Scy * Besides testing the drivers themselves, this program can be used to 12275970Scy * synchronize remote machines over audio transmission lines or program 13275970Scy * feeds. The program reads the time on the local machine and sets the 14275970Scy * initial epoch of the signal generator within one millisecond. 15275970Scy * Alernatively, the initial epoch can be set to an arbitrary time. This 16275970Scy * is useful when searching for bugs and testing for correct response to 17275970Scy * a leap second in UTC. Note however, the ultimate accuracy is limited 18275970Scy * by the intrinsic frequency error of the codec sample clock, which can 19275970Scy # reach well over 100 PPM. 20275970Scy * 21275970Scy * The default is to route generated signals to the line output 22275970Scy * jack; the s option on the command line routes these signals to the 23275970Scy * internal speaker as well. The v option controls the speaker volume 24275970Scy * over the range 0-255. The signal generator by default uses WWV 25275970Scy * format; the h option switches to WWVH format and the i option 26275970Scy * switches to IRIG-B format. 27275970Scy * 28275970Scy * Once started the program runs continuously. The default initial epoch 29275970Scy * for the signal generator is read from the computer system clock when 30275970Scy * the program starts. The y option specifies an alternate epoch using a 31275970Scy * string yydddhhmmss, where yy is the year of century, ddd the day of 32275970Scy * year, hh the hour of day and mm the minute of hour. For instance, 33275970Scy * 1946Z on 1 January 2006 is 060011946. The l option lights the leap 34275970Scy * warning bit in the WWV/H timecode, so is handy to check for correct 35275970Scy * behavior at the next leap second epoch. The remaining options are 36275970Scy * specified below under the Parse Options heading. Most of these are 37275970Scy * for testing. 38275970Scy * 39275970Scy * During operation the program displays the WWV/H timecode (9 digits) 40275970Scy * or IRIG timecode (20 digits) as each new string is constructed. The 41275970Scy * display is followed by the BCD binary bits as transmitted. Note that 42275970Scy * the transmissionorder is low-order first as the frame is processed 43275970Scy * left to right. For WWV/H The leap warning L preceeds the first bit. 44275970Scy * For IRIG the on-time marker M preceeds the first (units) bit, so its 45275970Scy * code is delayed one bit and the next digit (tens) needs only three 46275970Scy * bits. 47275970Scy * 48275970Scy * The program has been tested with the Sun Blade 1500 running Solaris 49275970Scy * 10, but not yet with other machines. It uses no special features and 50275970Scy * should be readily portable to other hardware and operating systems. 51275970Scy * 52275970Scy * $Log: tg.c,v $ 53275970Scy * Revision 1.28 2007/02/12 23:57:45 dmw 54275970Scy * v0.23 2007-02-12 dmw: 55275970Scy * - Changed statistics to include calculated error 56275970Scy * of frequency, based on number of added or removed 57275970Scy * cycles over time. 58275970Scy * 59275970Scy * Revision 1.27 2007/02/09 02:28:59 dmw 60275970Scy * v0.22 2007-02-08 dmw: 61275970Scy * - Changed default for rate correction to "enabled", "-j" switch now disables. 62275970Scy * - Adjusted help message accordingly. 63275970Scy * - Added "2007" to modifications note at end of help message. 64275970Scy * 65275970Scy * Revision 1.26 2007/02/08 03:36:17 dmw 66275970Scy * v0.21 2007-02-07 dmw: 67275970Scy * - adjusted strings for shorten and lengthen to make 68275970Scy * fit on smaller screen. 69275970Scy * 70275970Scy * Revision 1.25 2007/02/01 06:08:09 dmw 71275970Scy * v0.20 2007-02-01 dmw: 72275970Scy * - Added periodic display of running time along with legend on IRIG-B, allows tracking how 73275970Scy * close IRIG output is to actual clock time. 74275970Scy * 75275970Scy * Revision 1.24 2007/01/31 19:24:11 dmw 76275970Scy * v0.19 2007-01-31 dmw: 77275970Scy * - Added tracking of how many seconds have been adjusted, 78275970Scy * how many cycles added (actually in milliseconds), how 79275970Scy * many cycles removed, print periodically if verbose is 80275970Scy * active. 81275970Scy * - Corrected lack of lengthen or shorten of minute & hour 82275970Scy * pulses for WWV format. 83275970Scy * 84275970Scy * Revision 1.23 2007/01/13 07:09:12 dmw 85275970Scy * v0.18 2007-01-13 dmw: 86275970Scy * - added -k option, which allows force of long or short 87275970Scy * cycles, to test against IRIG-B decoder. 88275970Scy * 89275970Scy * Revision 1.22 2007/01/08 16:27:23 dmw 90275970Scy * v0.17 2007-01-08 dmw: 91275970Scy * - Changed -j option to **enable** rate correction, not disable. 92275970Scy * 93275970Scy * Revision 1.21 2007/01/08 06:22:36 dmw 94275970Scy * v0.17 2007-01-08 dmw: 95275970Scy * - Run stability check versus ongoing system clock (assume NTP correction) 96275970Scy * and adjust time code rate to try to correct, if gets too far out of sync. 97275970Scy * Disable this algorithm with -j option. 98275970Scy * 99275970Scy * Revision 1.20 2006/12/19 04:59:04 dmw 100275970Scy * v0.16 2006-12-18 dmw 101275970Scy * - Corrected print of setting of output frequency, always 102275970Scy * showed 8000 samples/sec, now as specified on command line. 103275970Scy * - Modified to reflect new employer Norscan. 104275970Scy * 105275970Scy * Revision 1.19 2006/12/19 03:45:38 dmw 106275970Scy * v0.15 2006-12-18 dmw: 107275970Scy * - Added count of number of seconds to output then exit, 108275970Scy * default zero for forever. 109275970Scy * 110275970Scy * Revision 1.18 2006/12/18 05:43:36 dmw 111275970Scy * v0.14 2006-12-17 dmw: 112275970Scy * - Corrected WWV(H) signal to leave "tick" sound off of 29th and 59th second of minute. 113275970Scy * - Adjusted verbose output format for WWV(H). 114275970Scy * 115275970Scy * Revision 1.17 2006/12/18 02:31:33 dmw 116275970Scy * v0.13 2006-12-17 dmw: 117275970Scy * - Put SPARC code back in, hopefully will work, but I don't have 118275970Scy * a SPARC to try it on... 119275970Scy * - Reworked Verbose mode, different flag to initiate (x not v) 120275970Scy * and actually implement turn off of verbosity when this flag used. 121275970Scy * - Re-claimed v flag for output level. 122275970Scy * - Note that you must define OSS_MODS to get OSS to compile, 123275970Scy * otherwise will expect to compile using old SPARC options, as 124275970Scy * it used to be. 125275970Scy * 126275970Scy * Revision 1.16 2006/10/26 19:08:43 dmw 127275970Scy * v0.12 2006-10-26 dmw: 128275970Scy * - Reversed output binary dump for IRIG, makes it easier to read the numbers. 129275970Scy * 130275970Scy * Revision 1.15 2006/10/24 15:57:09 dmw 131275970Scy * v0.11 2006-10-24 dmw: 132275970Scy * - another tweak. 133275970Scy * 134275970Scy * Revision 1.14 2006/10/24 15:55:53 dmw 135275970Scy * v0.11 2006-10-24 dmw: 136275970Scy * - Curses a fix to the fix to the fix of the usaeg. 137275970Scy * 138275970Scy * Revision 1.13 2006/10/24 15:53:25 dmw 139275970Scy * v0.11 (still) 2006-10-24 dmw: 140275970Scy * - Messed with usage message that's all. 141275970Scy * 142275970Scy * Revision 1.12 2006/10/24 15:50:05 dmw 143275970Scy * v0.11 2006-10-24 dmw: 144275970Scy * - oops, needed to note "hours" in usage of that offset. 145275970Scy * 146275970Scy * Revision 1.11 2006/10/24 15:49:09 dmw 147275970Scy * v0.11 2006-10-24 dmw: 148275970Scy * - Added ability to offset actual time sent, from the UTC time 149275970Scy * as per the computer. 150275970Scy * 151275970Scy * Revision 1.10 2006/10/24 03:25:55 dmw 152275970Scy * v0.10 2006-10-23 dmw: 153275970Scy * - Corrected polarity of correction of offset when going into or out of DST. 154275970Scy * - Ensure that zero offset is always positive (pet peeve). 155275970Scy * 156275970Scy * Revision 1.9 2006/10/24 00:00:35 dmw 157275970Scy * v0.9 2006-10-23 dmw: 158275970Scy * - Shift time offset when DST in or out. 159275970Scy * 160275970Scy * Revision 1.8 2006/10/23 23:49:28 dmw 161275970Scy * v0.8 2006-10-23 dmw: 162275970Scy * - made offset of zero default positive. 163275970Scy * 164275970Scy * Revision 1.7 2006/10/23 23:44:13 dmw 165275970Scy * v0.7 2006-10-23 dmw: 166275970Scy * - Added unmodulated and inverted unmodulated output. 167275970Scy * 168275970Scy * Revision 1.6 2006/10/23 18:10:37 dmw 169275970Scy * v0.6 2006-10-23 dmw: 170275970Scy * - Cleaned up usage message. 171275970Scy * - Require at least one option, or prints usage message and exits. 172275970Scy * 173275970Scy * Revision 1.5 2006/10/23 16:58:10 dmw 174275970Scy * v0.5 2006-10-23 dmw: 175275970Scy * - Finally added a usage message. 176275970Scy * - Added leap second pending and DST change pending into IEEE 1344. 177275970Scy * - Default code type is now IRIG-B with IEEE 1344. 178275970Scy * 179275970Scy * Revision 1.4 2006/10/23 03:27:25 dmw 180275970Scy * v0.4 2006-10-22 dmw: 181275970Scy * - Added leap second addition and deletion. 182275970Scy * - Added DST changing forward and backward. 183275970Scy * - Changed date specification to more conventional year, month, and day of month 184275970Scy * (rather than day of year). 185275970Scy * 186275970Scy * Revision 1.3 2006/10/22 21:04:12 dmw 187275970Scy * v0.2 2006-10-22 dmw: 188275970Scy * - Corrected format of legend line. 189275970Scy * 190275970Scy * Revision 1.2 2006/10/22 21:01:07 dmw 191275970Scy * v0.1 2006-10-22 dmw: 192275970Scy * - Added some more verbose output (as is my style) 193275970Scy * - Corrected frame format - there were markers in the 194275970Scy * middle of frames, now correctly as "zero" bits. 195275970Scy * - Added header line to show fields of output. 196275970Scy * - Added straight binary seconds, were not implemented 197275970Scy * before. 198275970Scy * - Added IEEE 1344 with parity. 199275970Scy * 200275970Scy * 201275970Scy */ 202275970Scy#include <stdio.h> 203275970Scy#include <stdlib.h> 204275970Scy#include <time.h> 205275970Scy 206275970Scy#ifdef HAVE_CONFIG_H 207275970Scy#include "config.h" 208275970Scy#undef VERSION /* avoid conflict below */ 209275970Scy#endif 210275970Scy 211275970Scy#ifdef HAVE_SYS_SOUNDCARD_H 212275970Scy#include <sys/soundcard.h> 213275970Scy#else 214275970Scy# ifdef HAVE_SYS_AUDIOIO_H 215275970Scy# include <sys/audioio.h> 216275970Scy# else 217275970Scy# include <sys/audio.h> 218275970Scy# endif 219275970Scy#endif 220275970Scy 221275970Scy#include "ntp_stdlib.h" /* for strlcat(), strlcpy() */ 222275970Scy 223275970Scy#include <math.h> 224275970Scy#include <errno.h> 225275970Scy#include <sys/types.h> 226275970Scy#include <sys/stat.h> 227275970Scy#include <fcntl.h> 228275970Scy#include <string.h> 229275970Scy#include <unistd.h> 230275970Scy#include <ctype.h> 231275970Scy#include <sys/ioctl.h> 232275970Scy#include <sys/time.h> 233275970Scy 234275970Scy#define VERSION (0) 235275970Scy#define ISSUE (23) 236275970Scy#define ISSUE_DATE "2007-02-12" 237275970Scy 238275970Scy#define SECOND (8000) /* one second of 125-us samples */ 239275970Scy#define BUFLNG (400) /* buffer size */ 240275970Scy#define DEVICE "/dev/audio" /* default audio device */ 241275970Scy#define WWV (0) /* WWV encoder */ 242275970Scy#define IRIG (1) /* IRIG-B encoder */ 243275970Scy#define OFF (0) /* zero amplitude */ 244275970Scy#define LOW (1) /* low amplitude */ 245275970Scy#define HIGH (2) /* high amplitude */ 246275970Scy#define DATA0 (200) /* WWV/H 0 pulse */ 247275970Scy#define DATA1 (500) /* WWV/H 1 pulse */ 248275970Scy#define PI (800) /* WWV/H PI pulse */ 249275970Scy#define M2 (2) /* IRIG 0 pulse */ 250275970Scy#define M5 (5) /* IRIG 1 pulse */ 251275970Scy#define M8 (8) /* IRIG PI pulse */ 252275970Scy 253275970Scy#define NUL (0) 254275970Scy 255275970Scy#define SECONDS_PER_MINUTE (60) 256275970Scy#define SECONDS_PER_HOUR (3600) 257275970Scy 258275970Scy#define OUTPUT_DATA_STRING_LENGTH (200) 259275970Scy 260275970Scy/* Attempt at unmodulated - "high" */ 261275970Scyint u6000[] = { 262275970Scy 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, /* 0- 9 */ 263275970Scy 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, /* 10-19 */ 264275970Scy 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, /* 20-29 */ 265275970Scy 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, /* 30-39 */ 266275970Scy 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, /* 40-49 */ 267275970Scy 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, /* 50-59 */ 268275970Scy 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, /* 60-69 */ 269275970Scy 247, 247, 247, 247, 247, 247, 247, 247, 247, 247}; /* 70-79 */ 270275970Scy 271275970Scy/* Attempt at unmodulated - "low" */ 272275970Scyint u3000[] = { 273275970Scy 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, /* 0- 9 */ 274275970Scy 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, /* 10-19 */ 275275970Scy 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, /* 20-29 */ 276275970Scy 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, /* 30-39 */ 277275970Scy 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, /* 40-49 */ 278275970Scy 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, /* 50-59 */ 279275970Scy 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, /* 60-69 */ 280275970Scy 119, 119, 119, 119, 119, 119, 119, 119, 119, 119}; /* 70-79 */ 281275970Scy 282275970Scy/* 283275970Scy * Companded sine table amplitude 3000 units 284275970Scy */ 285275970Scyint c3000[] = {1, 48, 63, 70, 78, 82, 85, 89, 92, 94, /* 0-9 */ 286275970Scy 96, 98, 99, 100, 101, 101, 102, 103, 103, 103, /* 10-19 */ 287275970Scy 103, 103, 103, 103, 102, 101, 101, 100, 99, 98, /* 20-29 */ 288275970Scy 96, 94, 92, 89, 85, 82, 78, 70, 63, 48, /* 30-39 */ 289275970Scy 129, 176, 191, 198, 206, 210, 213, 217, 220, 222, /* 40-49 */ 290275970Scy 224, 226, 227, 228, 229, 229, 230, 231, 231, 231, /* 50-59 */ 291275970Scy 231, 231, 231, 231, 230, 229, 229, 228, 227, 226, /* 60-69 */ 292275970Scy 224, 222, 220, 217, 213, 210, 206, 198, 191, 176}; /* 70-79 */ 293275970Scy/* 294275970Scy * Companded sine table amplitude 6000 units 295275970Scy */ 296275970Scyint c6000[] = {1, 63, 78, 86, 93, 98, 101, 104, 107, 110, /* 0-9 */ 297275970Scy 112, 113, 115, 116, 117, 117, 118, 118, 119, 119, /* 10-19 */ 298275970Scy 119, 119, 119, 118, 118, 117, 117, 116, 115, 113, /* 20-29 */ 299275970Scy 112, 110, 107, 104, 101, 98, 93, 86, 78, 63, /* 30-39 */ 300275970Scy 129, 191, 206, 214, 221, 226, 229, 232, 235, 238, /* 40-49 */ 301275970Scy 240, 241, 243, 244, 245, 245, 246, 246, 247, 247, /* 50-59 */ 302275970Scy 247, 247, 247, 246, 246, 245, 245, 244, 243, 241, /* 60-69 */ 303275970Scy 240, 238, 235, 232, 229, 226, 221, 214, 206, 191}; /* 70-79 */ 304275970Scy 305275970Scy/* 306275970Scy * Decoder operations at the end of each second are driven by a state 307275970Scy * machine. The transition matrix consists of a dispatch table indexed 308275970Scy * by second number. Each entry in the table contains a case switch 309275970Scy * number and argument. 310275970Scy */ 311275970Scystruct progx { 312275970Scy int sw; /* case switch number */ 313275970Scy int arg; /* argument */ 314275970Scy}; 315275970Scy 316275970Scy/* 317275970Scy * Case switch numbers 318275970Scy */ 319275970Scy#define DATA (0) /* send data (0, 1, PI) */ 320275970Scy#define COEF (1) /* send BCD bit */ 321275970Scy#define DEC (2) /* decrement to next digit and send PI */ 322275970Scy#define MIN (3) /* minute pulse */ 323275970Scy#define LEAP (4) /* leap warning */ 324275970Scy#define DUT1 (5) /* DUT1 bits */ 325275970Scy#define DST1 (6) /* DST1 bit */ 326275970Scy#define DST2 (7) /* DST2 bit */ 327275970Scy#define DECZ (8) /* decrement to next digit and send zero */ 328275970Scy#define DECC (9) /* decrement to next digit and send bit */ 329275970Scy#define NODEC (10) /* no decerement to next digit, send PI */ 330275970Scy#define DECX (11) /* decrement to next digit, send PI, but no tick */ 331275970Scy#define DATAX (12) /* send data (0, 1, PI), but no tick */ 332275970Scy 333275970Scy/* 334275970Scy * WWV/H format (100-Hz, 9 digits, 1 m frame) 335275970Scy */ 336275970Scystruct progx progx[] = { 337275970Scy {MIN, 800}, /* 0 minute sync pulse */ 338275970Scy {DATA, DATA0}, /* 1 */ 339275970Scy {DST2, 0}, /* 2 DST2 */ 340275970Scy {LEAP, 0}, /* 3 leap warning */ 341275970Scy {COEF, 1}, /* 4 1 year units */ 342275970Scy {COEF, 2}, /* 5 2 */ 343275970Scy {COEF, 4}, /* 6 4 */ 344275970Scy {COEF, 8}, /* 7 8 */ 345275970Scy {DEC, DATA0}, /* 8 */ 346275970Scy {DATA, PI}, /* 9 p1 */ 347275970Scy {COEF, 1}, /* 10 1 minute units */ 348275970Scy {COEF, 2}, /* 11 2 */ 349275970Scy {COEF, 4}, /* 12 4 */ 350275970Scy {COEF, 8}, /* 13 8 */ 351275970Scy {DEC, DATA0}, /* 14 */ 352275970Scy {COEF, 1}, /* 15 10 minute tens */ 353275970Scy {COEF, 2}, /* 16 20 */ 354275970Scy {COEF, 4}, /* 17 40 */ 355275970Scy {COEF, 8}, /* 18 80 (not used) */ 356275970Scy {DEC, PI}, /* 19 p2 */ 357275970Scy {COEF, 1}, /* 20 1 hour units */ 358275970Scy {COEF, 2}, /* 21 2 */ 359275970Scy {COEF, 4}, /* 22 4 */ 360275970Scy {COEF, 8}, /* 23 8 */ 361275970Scy {DEC, DATA0}, /* 24 */ 362275970Scy {COEF, 1}, /* 25 10 hour tens */ 363275970Scy {COEF, 2}, /* 26 20 */ 364275970Scy {COEF, 4}, /* 27 40 (not used) */ 365275970Scy {COEF, 8}, /* 28 80 (not used) */ 366275970Scy {DECX, PI}, /* 29 p3 */ 367275970Scy {COEF, 1}, /* 30 1 day units */ 368275970Scy {COEF, 2}, /* 31 2 */ 369275970Scy {COEF, 4}, /* 32 4 */ 370275970Scy {COEF, 8}, /* 33 8 */ 371275970Scy {DEC, DATA0}, /* 34 not used */ 372275970Scy {COEF, 1}, /* 35 10 day tens */ 373275970Scy {COEF, 2}, /* 36 20 */ 374275970Scy {COEF, 4}, /* 37 40 */ 375275970Scy {COEF, 8}, /* 38 80 */ 376275970Scy {DEC, PI}, /* 39 p4 */ 377275970Scy {COEF, 1}, /* 40 100 day hundreds */ 378275970Scy {COEF, 2}, /* 41 200 */ 379275970Scy {COEF, 4}, /* 42 400 (not used) */ 380275970Scy {COEF, 8}, /* 43 800 (not used) */ 381275970Scy {DEC, DATA0}, /* 44 */ 382275970Scy {DATA, DATA0}, /* 45 */ 383275970Scy {DATA, DATA0}, /* 46 */ 384275970Scy {DATA, DATA0}, /* 47 */ 385275970Scy {DATA, DATA0}, /* 48 */ 386275970Scy {DATA, PI}, /* 49 p5 */ 387275970Scy {DUT1, 8}, /* 50 DUT1 sign */ 388275970Scy {COEF, 1}, /* 51 10 year tens */ 389275970Scy {COEF, 2}, /* 52 20 */ 390275970Scy {COEF, 4}, /* 53 40 */ 391275970Scy {COEF, 8}, /* 54 80 */ 392275970Scy {DST1, 0}, /* 55 DST1 */ 393275970Scy {DUT1, 1}, /* 56 0.1 DUT1 fraction */ 394275970Scy {DUT1, 2}, /* 57 0.2 */ 395275970Scy {DUT1, 4}, /* 58 0.4 */ 396275970Scy {DATAX, PI}, /* 59 p6 */ 397275970Scy {DATA, DATA0}, /* 60 leap */ 398275970Scy}; 399275970Scy 400275970Scy/* 401275970Scy * IRIG format frames (1000 Hz, 1 second for 10 frames of data) 402275970Scy */ 403275970Scy 404275970Scy/* 405275970Scy * IRIG format frame 10 - MS straight binary seconds 406275970Scy */ 407275970Scystruct progx progu[] = { 408275970Scy {COEF, 2}, /* 0 0x0 0200 seconds */ 409275970Scy {COEF, 4}, /* 1 0x0 0400 */ 410275970Scy {COEF, 8}, /* 2 0x0 0800 */ 411275970Scy {DECC, 1}, /* 3 0x0 1000 */ 412275970Scy {COEF, 2}, /* 4 0x0 2000 */ 413275970Scy {COEF, 4}, /* 6 0x0 4000 */ 414275970Scy {COEF, 8}, /* 7 0x0 8000 */ 415275970Scy {DECC, 1}, /* 8 0x1 0000 */ 416275970Scy {COEF, 2}, /* 9 0x2 0000 - but only 86,401 / 0x1 5181 seconds in a day, so always zero */ 417275970Scy {NODEC, M8}, /* 9 PI */ 418275970Scy}; 419275970Scy 420275970Scy/* 421275970Scy * IRIG format frame 8 - MS control functions 422275970Scy */ 423275970Scystruct progx progv[] = { 424275970Scy {COEF, 2}, /* 0 CF # 19 */ 425275970Scy {COEF, 4}, /* 1 CF # 20 */ 426275970Scy {COEF, 8}, /* 2 CF # 21 */ 427275970Scy {DECC, 1}, /* 3 CF # 22 */ 428275970Scy {COEF, 2}, /* 4 CF # 23 */ 429275970Scy {COEF, 4}, /* 6 CF # 24 */ 430275970Scy {COEF, 8}, /* 7 CF # 25 */ 431275970Scy {DECC, 1}, /* 8 CF # 26 */ 432275970Scy {COEF, 2}, /* 9 CF # 27 */ 433275970Scy {DEC, M8}, /* 10 PI */ 434275970Scy}; 435275970Scy 436275970Scy/* 437275970Scy * IRIG format frames 7 & 9 - LS control functions & LS straight binary seconds 438275970Scy */ 439275970Scystruct progx progw[] = { 440275970Scy {COEF, 1}, /* 0 CF # 10, 0x0 0001 seconds */ 441275970Scy {COEF, 2}, /* 1 CF # 11, 0x0 0002 */ 442275970Scy {COEF, 4}, /* 2 CF # 12, 0x0 0004 */ 443275970Scy {COEF, 8}, /* 3 CF # 13, 0x0 0008 */ 444275970Scy {DECC, 1}, /* 4 CF # 14, 0x0 0010 */ 445275970Scy {COEF, 2}, /* 6 CF # 15, 0x0 0020 */ 446275970Scy {COEF, 4}, /* 7 CF # 16, 0x0 0040 */ 447275970Scy {COEF, 8}, /* 8 CF # 17, 0x0 0080 */ 448275970Scy {DECC, 1}, /* 9 CF # 18, 0x0 0100 */ 449275970Scy {NODEC, M8}, /* 10 PI */ 450275970Scy}; 451275970Scy 452275970Scy/* 453275970Scy * IRIG format frames 2 to 6 - minutes, hours, days, hundreds days, 2 digit years (also called control functions bits 1-9) 454275970Scy */ 455275970Scystruct progx progy[] = { 456275970Scy {COEF, 1}, /* 0 1 units, CF # 1 */ 457275970Scy {COEF, 2}, /* 1 2 units, CF # 2 */ 458275970Scy {COEF, 4}, /* 2 4 units, CF # 3 */ 459275970Scy {COEF, 8}, /* 3 8 units, CF # 4 */ 460275970Scy {DECZ, M2}, /* 4 zero bit, CF # 5 / unused, default zero in years */ 461275970Scy {COEF, 1}, /* 5 10 tens, CF # 6 */ 462275970Scy {COEF, 2}, /* 6 20 tens, CF # 7*/ 463275970Scy {COEF, 4}, /* 7 40 tens, CF # 8*/ 464275970Scy {COEF, 8}, /* 8 80 tens, CF # 9*/ 465275970Scy {DEC, M8}, /* 9 PI */ 466275970Scy}; 467275970Scy 468275970Scy/* 469275970Scy * IRIG format first frame, frame 1 - seconds 470275970Scy */ 471275970Scystruct progx progz[] = { 472275970Scy {MIN, M8}, /* 0 PI (on-time marker for the second at zero cross of 1st cycle) */ 473275970Scy {COEF, 1}, /* 1 1 units */ 474275970Scy {COEF, 2}, /* 2 2 */ 475275970Scy {COEF, 4}, /* 3 4 */ 476275970Scy {COEF, 8}, /* 4 8 */ 477275970Scy {DECZ, M2}, /* 5 zero bit */ 478275970Scy {COEF, 1}, /* 6 10 tens */ 479275970Scy {COEF, 2}, /* 7 20 */ 480275970Scy {COEF, 4}, /* 8 40 */ 481275970Scy {DEC, M8}, /* 9 PI */ 482275970Scy}; 483275970Scy 484275970Scy/* LeapState values. */ 485275970Scy#define LEAPSTATE_NORMAL (0) 486275970Scy#define LEAPSTATE_DELETING (1) 487275970Scy#define LEAPSTATE_INSERTING (2) 488275970Scy#define LEAPSTATE_ZERO_AFTER_INSERT (3) 489275970Scy 490275970Scy 491275970Scy/* 492275970Scy * Forward declarations 493275970Scy */ 494275970Scyvoid WWV_Second(int, int); /* send second */ 495275970Scyvoid WWV_SecondNoTick(int, int); /* send second with no tick */ 496275970Scyvoid digit(int); /* encode digit */ 497275970Scyvoid peep(int, int, int); /* send cycles */ 498275970Scyvoid poop(int, int, int, int); /* Generate unmodulated from similar tables */ 499275970Scyvoid delay(int); /* delay samples */ 500275970Scyint ConvertMonthDayToDayOfYear (int, int, int); /* Calc day of year from year month & day */ 501275970Scyvoid Help (void); /* Usage message */ 502275970Scyvoid ReverseString(char *); 503275970Scy 504275970Scy/* 505275970Scy * Extern declarations, don't know why not in headers 506275970Scy */ 507275970Scy//float round ( float ); 508275970Scy 509275970Scy/* 510275970Scy * Global variables 511275970Scy */ 512275970Scychar buffer[BUFLNG]; /* output buffer */ 513275970Scyint bufcnt = 0; /* buffer counter */ 514275970Scyint fd; /* audio codec file descriptor */ 515275970Scyint tone = 1000; /* WWV sync frequency */ 516275970Scyint HourTone = 1500; /* WWV hour on-time frequency */ 517275970Scyint encode = IRIG; /* encoder select */ 518275970Scyint leap = 0; /* leap indicator */ 519275970Scyint DstFlag = 0; /* winter/summer time */ 520275970Scyint dut1 = 0; /* DUT1 correction (sign, magnitude) */ 521275970Scyint utc = 0; /* option epoch */ 522275970Scyint IrigIncludeYear = FALSE; /* Whether to send year in first control functions area, between P5 and P6. */ 523275970Scyint IrigIncludeIeee = FALSE; /* Whether to send IEEE 1344 control functions extensions between P6 and P8. */ 524275970Scyint StraightBinarySeconds = 0; 525275970Scyint ControlFunctions = 0; 526275970Scyint Debug = FALSE; 527275970Scyint Verbose = TRUE; 528275970Scychar *CommandName; 529275970Scy 530275970Scy#ifndef HAVE_SYS_SOUNDCARD_H 531275970Scyint level = AUDIO_MAX_GAIN / 8; /* output level */ 532275970Scyint port = AUDIO_LINE_OUT; /* output port */ 533275970Scy#endif 534275970Scy 535275970Scyint TotalSecondsCorrected = 0; 536275970Scyint TotalCyclesAdded = 0; 537275970Scyint TotalCyclesRemoved = 0; 538275970Scy 539275970Scy 540275970Scy/* 541275970Scy * Main program 542275970Scy */ 543275970Scyint 544275970Scymain( 545275970Scy int argc, /* command line options */ 546275970Scy char **argv /* poiniter to list of tokens */ 547275970Scy ) 548275970Scy{ 549275970Scy#ifndef HAVE_SYS_SOUNDCARD_H 550275970Scy audio_info_t info; /* Sun audio structure */ 551275970Scy int rval; /* For IOCTL calls */ 552275970Scy#endif 553275970Scy 554275970Scy struct timeval TimeValue; /* System clock at startup */ 555275970Scy time_t SecondsPartOfTime; /* Sent to gmtime() for calculation of TimeStructure (can apply offset). */ 556275970Scy time_t BaseRealTime; /* Base realtime so can determine seconds since starting. */ 557275970Scy time_t NowRealTime; /* New realtime to can determine seconds as of now. */ 558275970Scy unsigned SecondsRunningRealTime; /* Difference between NowRealTime and BaseRealTime. */ 559275970Scy unsigned SecondsRunningSimulationTime; /* Time that the simulator has been running. */ 560275970Scy int SecondsRunningDifference; /* Difference between what real time says we have been running */ 561275970Scy /* and what simulator says we have been running - will slowly */ 562275970Scy /* change because of clock drift. */ 563275970Scy int ExpectedRunningDifference = 0; /* Stable value that we've obtained from check at initial start-up. */ 564275970Scy unsigned StabilityCount; /* Used to check stability of difference while starting */ 565275970Scy#define RUN_BEFORE_STABILITY_CHECK (30) // Must run this many seconds before even checking stability. 566275970Scy#define MINIMUM_STABILITY_COUNT (10) // Number of consecutive differences that need to be within initial stability band to say we are stable. 567275970Scy#define INITIAL_STABILITY_BAND ( 2) // Determining initial stability for consecutive differences within +/- this value. 568275970Scy#define RUNNING_STABILITY_BAND ( 5) // When running, stability is defined as difference within +/- this value. 569275970Scy 570275970Scy struct tm *TimeStructure = NULL; /* Structure returned by gmtime */ 571275970Scy char device[200]; /* audio device */ 572275970Scy char code[200]; /* timecode */ 573275970Scy int temp; 574275970Scy int arg = 0; 575275970Scy int sw = 0; 576275970Scy int ptr = 0; 577275970Scy 578275970Scy int Year; 579275970Scy int Month; 580275970Scy int DayOfMonth; 581275970Scy int Hour; 582275970Scy int Minute; 583275970Scy int Second = 0; 584275970Scy int DayOfYear; 585275970Scy 586275970Scy int BitNumber; 587275970Scy#ifdef HAVE_SYS_SOUNDCARD_H 588275970Scy int AudioFormat; 589275970Scy int MonoStereo; /* 0=mono, 1=stereo */ 590275970Scy#define MONO (0) 591275970Scy#define STEREO (1) 592275970Scy int SampleRate; 593275970Scy int SampleRateDifference; 594275970Scy#endif 595275970Scy int SetSampleRate; 596275970Scy char FormatCharacter = '3'; /* Default is IRIG-B with IEEE 1344 extensions */ 597275970Scy char AsciiValue; 598275970Scy int HexValue; 599275970Scy int OldPtr = 0; 600275970Scy int FrameNumber = 0; 601275970Scy 602275970Scy /* Time offset for IEEE 1344 indication. */ 603275970Scy float TimeOffset = 0.0; 604275970Scy int OffsetSignBit = 0; 605275970Scy int OffsetOnes = 0; 606275970Scy int OffsetHalf = 0; 607275970Scy 608275970Scy int TimeQuality = 0; /* Time quality for IEEE 1344 indication. */ 609275970Scy char ParityString[200]; /* Partial output string, to calculate parity on. */ 610275970Scy int ParitySum = 0; 611275970Scy int ParityValue; 612275970Scy char *StringPointer; 613275970Scy 614275970Scy /* Flags to indicate requested leap second addition or deletion by command line option. */ 615275970Scy /* Should be mutually exclusive - generally ensured by code which interprets command line option. */ 616275970Scy int InsertLeapSecond = FALSE; 617275970Scy int DeleteLeapSecond = FALSE; 618275970Scy 619275970Scy /* Date and time of requested leap second addition or deletion. */ 620275970Scy int LeapYear = 0; 621275970Scy int LeapMonth = 0; 622275970Scy int LeapDayOfMonth = 0; 623275970Scy int LeapHour = 0; 624275970Scy int LeapMinute = 0; 625275970Scy int LeapDayOfYear = 0; 626275970Scy 627275970Scy /* State flag for the insertion and deletion of leap seconds, esp. deletion, */ 628275970Scy /* where the logic gets a bit tricky. */ 629275970Scy int LeapState = LEAPSTATE_NORMAL; 630275970Scy 631275970Scy /* Flags for indication of leap second pending and leap secod polarity in IEEE 1344 */ 632275970Scy int LeapSecondPending = FALSE; 633275970Scy int LeapSecondPolarity = FALSE; 634275970Scy 635275970Scy /* Date and time of requested switch into or out of DST by command line option. */ 636275970Scy int DstSwitchYear = 0; 637275970Scy int DstSwitchMonth = 0; 638275970Scy int DstSwitchDayOfMonth = 0; 639275970Scy int DstSwitchHour = 0; 640275970Scy int DstSwitchMinute = 0; 641275970Scy int DstSwitchDayOfYear = 0; 642275970Scy 643275970Scy /* Indicate when we have been asked to switch into or out of DST by command line option. */ 644275970Scy int DstSwitchFlag = FALSE; 645275970Scy 646275970Scy /* To allow predict for DstPendingFlag in IEEE 1344 */ 647275970Scy int DstSwitchPendingYear = 0; /* Default value isn't valid, but I don't care. */ 648275970Scy int DstSwitchPendingDayOfYear = 0; 649275970Scy int DstSwitchPendingHour = 0; 650275970Scy int DstSwitchPendingMinute = 0; 651275970Scy 652275970Scy /* /Flag for indication of a DST switch pending in IEEE 1344 */ 653275970Scy int DstPendingFlag = FALSE; 654275970Scy 655275970Scy /* Attempt at unmodulated */ 656275970Scy int Unmodulated = FALSE; 657275970Scy int UnmodulatedInverted = FALSE; 658275970Scy 659275970Scy /* Offset to actual time value sent. */ 660275970Scy float UseOffsetHoursFloat; 661275970Scy int UseOffsetSecondsInt = 0; 662275970Scy float UseOffsetSecondsFloat; 663275970Scy 664275970Scy /* String to allow us to put out reversed data - so can read the binary numbers. */ 665275970Scy char OutputDataString[OUTPUT_DATA_STRING_LENGTH]; 666275970Scy 667275970Scy /* Number of seconds to send before exiting. Default = 0 = forever. */ 668275970Scy int SecondsToSend = 0; 669275970Scy int CountOfSecondsSent = 0; /* Counter of seconds */ 670275970Scy 671275970Scy /* Flags to indicate whether to add or remove a cycle for time adjustment. */ 672275970Scy int AddCycle = FALSE; // We are ahead, add cycle to slow down and get back in sync. 673275970Scy int RemoveCycle = FALSE; // We are behind, remove cycle to slow down and get back in sync. 674275970Scy int RateCorrection; // Aggregate flag for passing to subroutines. 675275970Scy int EnableRateCorrection = TRUE; 676275970Scy 677275970Scy float RatioError; 678275970Scy 679275970Scy 680275970Scy CommandName = argv[0]; 681275970Scy 682275970Scy if (argc < 1) 683275970Scy { 684275970Scy Help (); 685275970Scy exit (-1); 686275970Scy } 687275970Scy 688275970Scy /* 689275970Scy * Parse options 690275970Scy */ 691275970Scy strlcpy(device, DEVICE, sizeof(device)); 692275970Scy Year = 0; 693275970Scy SetSampleRate = SECOND; 694275970Scy 695275970Scy#if HAVE_SYS_SOUNDCARD_H 696275970Scy while ((temp = getopt(argc, argv, "a:b:c:df:g:hHi:jk:l:o:q:r:stu:xy:z?")) != -1) { 697275970Scy#else 698275970Scy while ((temp = getopt(argc, argv, "a:b:c:df:g:hHi:jk:l:o:q:r:stu:v:xy:z?")) != -1) { 699275970Scy#endif 700275970Scy switch (temp) { 701275970Scy 702275970Scy case 'a': /* specify audio device (/dev/audio) */ 703275970Scy strlcpy(device, optarg, sizeof(device)); 704275970Scy break; 705275970Scy 706275970Scy case 'b': /* Remove (delete) a leap second at the end of the specified minute. */ 707275970Scy sscanf(optarg, "%2d%2d%2d%2d%2d", &LeapYear, &LeapMonth, &LeapDayOfMonth, 708275970Scy &LeapHour, &LeapMinute); 709275970Scy InsertLeapSecond = FALSE; 710275970Scy DeleteLeapSecond = TRUE; 711275970Scy break; 712275970Scy 713275970Scy case 'c': /* specify number of seconds to send output for before exiting, 0 = forever */ 714275970Scy sscanf(optarg, "%d", &SecondsToSend); 715275970Scy break; 716275970Scy 717275970Scy case 'd': /* set DST for summer (WWV/H only) / start with DST active (IRIG) */ 718275970Scy DstFlag++; 719275970Scy break; 720275970Scy 721275970Scy case 'f': /* select format: i=IRIG-98 (default) 2=IRIG-2004 3-IRIG+IEEE-1344 w=WWV(H) */ 722275970Scy sscanf(optarg, "%c", &FormatCharacter); 723275970Scy break; 724275970Scy 725275970Scy case 'g': /* Date and time to switch back into / out of DST active. */ 726275970Scy sscanf(optarg, "%2d%2d%2d%2d%2d", &DstSwitchYear, &DstSwitchMonth, &DstSwitchDayOfMonth, 727275970Scy &DstSwitchHour, &DstSwitchMinute); 728275970Scy DstSwitchFlag = TRUE; 729275970Scy break; 730275970Scy 731275970Scy case 'h': 732275970Scy case 'H': 733275970Scy case '?': 734275970Scy Help (); 735275970Scy exit(-1); 736275970Scy break; 737275970Scy 738275970Scy case 'i': /* Insert (add) a leap second at the end of the specified minute. */ 739275970Scy sscanf(optarg, "%2d%2d%2d%2d%2d", &LeapYear, &LeapMonth, &LeapDayOfMonth, 740275970Scy &LeapHour, &LeapMinute); 741275970Scy InsertLeapSecond = TRUE; 742275970Scy DeleteLeapSecond = FALSE; 743275970Scy break; 744275970Scy 745275970Scy case 'j': 746275970Scy EnableRateCorrection = FALSE; 747275970Scy break; 748275970Scy 749275970Scy case 'k': 750275970Scy sscanf (optarg, "%d", &RateCorrection); 751275970Scy EnableRateCorrection = FALSE; 752275970Scy if (RateCorrection < 0) 753275970Scy { 754275970Scy RemoveCycle = TRUE; 755275970Scy AddCycle = FALSE; 756275970Scy 757275970Scy if (Verbose) 758275970Scy printf ("\n> Forcing rate correction removal of cycle...\n"); 759275970Scy } 760275970Scy else 761275970Scy { 762275970Scy if (RateCorrection > 0) 763275970Scy { 764275970Scy RemoveCycle = FALSE; 765275970Scy AddCycle = TRUE; 766275970Scy 767275970Scy if (Verbose) 768275970Scy printf ("\n> Forcing rate correction addition of cycle...\n"); 769275970Scy } 770275970Scy } 771275970Scy break; 772275970Scy 773275970Scy case 'l': /* use time offset from UTC */ 774275970Scy sscanf(optarg, "%f", &UseOffsetHoursFloat); 775275970Scy UseOffsetSecondsFloat = UseOffsetHoursFloat * (float) SECONDS_PER_HOUR; 776275970Scy UseOffsetSecondsInt = (int) (UseOffsetSecondsFloat + 0.5); 777275970Scy break; 778275970Scy 779275970Scy case 'o': /* Set IEEE 1344 time offset in hours - positive or negative, to the half hour */ 780275970Scy sscanf(optarg, "%f", &TimeOffset); 781275970Scy if (TimeOffset >= -0.2) 782275970Scy { 783275970Scy OffsetSignBit = 0; 784275970Scy 785275970Scy if (TimeOffset > 0) 786275970Scy { 787275970Scy OffsetOnes = TimeOffset; 788275970Scy 789275970Scy if ( (TimeOffset - floor(TimeOffset)) >= 0.4) 790275970Scy OffsetHalf = 1; 791275970Scy else 792275970Scy OffsetHalf = 0; 793275970Scy } 794275970Scy else 795275970Scy { 796275970Scy OffsetOnes = 0; 797275970Scy OffsetHalf = 0; 798275970Scy } 799275970Scy } 800275970Scy else 801275970Scy { 802275970Scy OffsetSignBit = 1; 803275970Scy OffsetOnes = -TimeOffset; 804275970Scy 805275970Scy if ( (ceil(TimeOffset) - TimeOffset) >= 0.4) 806275970Scy OffsetHalf = 1; 807275970Scy else 808275970Scy OffsetHalf = 0; 809275970Scy } 810275970Scy 811275970Scy /*printf ("\nGot TimeOffset = %3.1f, OffsetSignBit = %d, OffsetOnes = %d, OffsetHalf = %d...\n", 812275970Scy TimeOffset, OffsetSignBit, OffsetOnes, OffsetHalf); 813275970Scy */ 814275970Scy break; 815275970Scy 816275970Scy case 'q': /* Hex quality code 0 to 0x0F - 0 = maximum, 0x0F = no lock */ 817275970Scy sscanf(optarg, "%x", &TimeQuality); 818275970Scy TimeQuality &= 0x0F; 819275970Scy /*printf ("\nGot TimeQuality = 0x%1X...\n", TimeQuality); 820275970Scy */ 821275970Scy break; 822275970Scy 823275970Scy case 'r': /* sample rate (nominally 8000, integer close to 8000 I hope) */ 824275970Scy sscanf(optarg, "%d", &SetSampleRate); 825275970Scy break; 826275970Scy 827275970Scy case 's': /* set leap warning bit (WWV/H only) */ 828275970Scy leap++; 829275970Scy break; 830275970Scy 831275970Scy case 't': /* select WWVH sync frequency */ 832275970Scy tone = 1200; 833275970Scy break; 834275970Scy 835275970Scy case 'u': /* set DUT1 offset (-7 to +7) */ 836275970Scy sscanf(optarg, "%d", &dut1); 837275970Scy if (dut1 < 0) 838275970Scy dut1 = abs(dut1); 839275970Scy else 840275970Scy dut1 |= 0x8; 841275970Scy break; 842275970Scy 843275970Scy#ifndef HAVE_SYS_SOUNDCARD_H 844275970Scy case 'v': /* set output level (0-255) */ 845275970Scy sscanf(optarg, "%d", &level); 846275970Scy break; 847275970Scy#endif 848275970Scy 849275970Scy case 'x': /* Turn off verbose output. */ 850275970Scy Verbose = FALSE; 851275970Scy break; 852275970Scy 853275970Scy case 'y': /* Set initial date and time */ 854275970Scy sscanf(optarg, "%2d%2d%2d%2d%2d%2d", &Year, &Month, &DayOfMonth, 855275970Scy &Hour, &Minute, &Second); 856275970Scy utc++; 857275970Scy break; 858275970Scy 859275970Scy case 'z': /* Turn on Debug output (also turns on Verbose below) */ 860275970Scy Debug = TRUE; 861275970Scy break; 862275970Scy 863275970Scy default: 864275970Scy printf("Invalid option \"%c\", aborting...\n", temp); 865275970Scy exit (-1); 866275970Scy break; 867275970Scy } 868275970Scy } 869275970Scy 870275970Scy if (Debug) 871275970Scy Verbose = TRUE; 872275970Scy 873275970Scy if (InsertLeapSecond || DeleteLeapSecond) 874275970Scy { 875275970Scy LeapDayOfYear = ConvertMonthDayToDayOfYear (LeapYear, LeapMonth, LeapDayOfMonth); 876275970Scy 877275970Scy if (Debug) 878275970Scy { 879275970Scy printf ("\nHave request for leap second %s at year %4d day %3d at %2.2dh%2.2d....\n",\ 880275970Scy DeleteLeapSecond ? "DELETION" : (InsertLeapSecond ? "ADDITION" : "( error ! )" ), 881275970Scy LeapYear, LeapDayOfYear, LeapHour, LeapMinute); 882275970Scy } 883275970Scy } 884275970Scy 885275970Scy if (DstSwitchFlag) 886275970Scy { 887275970Scy DstSwitchDayOfYear = ConvertMonthDayToDayOfYear (DstSwitchYear, DstSwitchMonth, DstSwitchDayOfMonth); 888275970Scy 889275970Scy /* Figure out time of minute previous to DST switch, so can put up warning flag in IEEE 1344 */ 890275970Scy DstSwitchPendingYear = DstSwitchYear; 891275970Scy DstSwitchPendingDayOfYear = DstSwitchDayOfYear; 892275970Scy DstSwitchPendingHour = DstSwitchHour; 893275970Scy DstSwitchPendingMinute = DstSwitchMinute - 1; 894275970Scy if (DstSwitchPendingMinute < 0) 895275970Scy { 896275970Scy DstSwitchPendingMinute = 59; 897275970Scy DstSwitchPendingHour--; 898275970Scy if (DstSwitchPendingHour < 0) 899275970Scy { 900275970Scy DstSwitchPendingHour = 23; 901275970Scy DstSwitchPendingDayOfYear--; 902275970Scy if (DstSwitchPendingDayOfYear < 1) 903275970Scy { 904275970Scy DstSwitchPendingYear--; 905275970Scy } 906275970Scy } 907275970Scy } 908275970Scy 909275970Scy if (Debug) 910275970Scy { 911275970Scy printf ("\nHave DST switch request for year %4d day %3d at %2.2dh%2.2d,", 912275970Scy DstSwitchYear, DstSwitchDayOfYear, DstSwitchHour, DstSwitchMinute); 913275970Scy printf ("\n so will have warning at year %4d day %3d at %2.2dh%2.2d.\n", 914275970Scy DstSwitchPendingYear, DstSwitchPendingDayOfYear, DstSwitchPendingHour, DstSwitchPendingMinute); 915275970Scy } 916275970Scy } 917275970Scy 918275970Scy switch (tolower(FormatCharacter)) { 919275970Scy case 'i': 920275970Scy printf ("\nFormat is IRIG-1998 (no year coded)...\n\n"); 921275970Scy encode = IRIG; 922275970Scy IrigIncludeYear = FALSE; 923275970Scy IrigIncludeIeee = FALSE; 924275970Scy break; 925275970Scy 926275970Scy case '2': 927275970Scy printf ("\nFormat is IRIG-2004 (BCD year coded)...\n\n"); 928275970Scy encode = IRIG; 929275970Scy IrigIncludeYear = TRUE; 930275970Scy IrigIncludeIeee = FALSE; 931275970Scy break; 932275970Scy 933275970Scy case '3': 934275970Scy printf ("\nFormat is IRIG with IEEE-1344 (BCD year coded, and more control functions)...\n\n"); 935275970Scy encode = IRIG; 936275970Scy IrigIncludeYear = TRUE; 937275970Scy IrigIncludeIeee = TRUE; 938275970Scy break; 939275970Scy 940275970Scy case '4': 941275970Scy printf ("\nFormat is unmodulated IRIG with IEEE-1344 (BCD year coded, and more control functions)...\n\n"); 942275970Scy encode = IRIG; 943275970Scy IrigIncludeYear = TRUE; 944275970Scy IrigIncludeIeee = TRUE; 945275970Scy 946275970Scy Unmodulated = TRUE; 947275970Scy UnmodulatedInverted = FALSE; 948275970Scy break; 949275970Scy 950275970Scy case '5': 951275970Scy printf ("\nFormat is inverted unmodulated IRIG with IEEE-1344 (BCD year coded, and more control functions)...\n\n"); 952275970Scy encode = IRIG; 953275970Scy IrigIncludeYear = TRUE; 954275970Scy IrigIncludeIeee = TRUE; 955275970Scy 956275970Scy Unmodulated = TRUE; 957275970Scy UnmodulatedInverted = TRUE; 958275970Scy break; 959275970Scy 960275970Scy case 'w': 961275970Scy printf ("\nFormat is WWV(H)...\n\n"); 962275970Scy encode = WWV; 963275970Scy break; 964275970Scy 965275970Scy default: 966275970Scy printf ("\n\nUnexpected format value of \'%c\', cannot parse, aborting...\n\n", FormatCharacter); 967275970Scy exit (-1); 968275970Scy break; 969275970Scy } 970275970Scy 971275970Scy /* 972275970Scy * Open audio device and set options 973275970Scy */ 974275970Scy fd = open(device, O_WRONLY); 975275970Scy if (fd <= 0) { 976275970Scy printf("Unable to open audio device \"%s\", aborting: %s\n", device, strerror(errno)); 977275970Scy exit(1); 978275970Scy } 979275970Scy 980275970Scy#ifdef HAVE_SYS_SOUNDCARD_H 981275970Scy /* First set coding type */ 982275970Scy AudioFormat = AFMT_MU_LAW; 983275970Scy if (ioctl(fd, SNDCTL_DSP_SETFMT, &AudioFormat)==-1) 984275970Scy { /* Fatal error */ 985275970Scy printf ("\nUnable to set output format, aborting...\n\n"); 986275970Scy exit(-1); 987275970Scy } 988275970Scy 989275970Scy if (AudioFormat != AFMT_MU_LAW) 990275970Scy { 991275970Scy printf ("\nUnable to set output format for mu law, aborting...\n\n"); 992275970Scy exit(-1); 993275970Scy } 994275970Scy 995275970Scy /* Next set number of channels */ 996275970Scy MonoStereo = MONO; /* Mono */ 997275970Scy if (ioctl(fd, SNDCTL_DSP_STEREO, &MonoStereo)==-1) 998275970Scy { /* Fatal error */ 999275970Scy printf ("\nUnable to set mono/stereo, aborting...\n\n"); 1000275970Scy exit(-1); 1001275970Scy } 1002275970Scy 1003275970Scy if (MonoStereo != MONO) 1004275970Scy { 1005275970Scy printf ("\nUnable to set mono/stereo for mono, aborting...\n\n"); 1006275970Scy exit(-1); 1007275970Scy } 1008275970Scy 1009275970Scy /* Now set sample rate */ 1010275970Scy SampleRate = SetSampleRate; 1011275970Scy if (ioctl(fd, SNDCTL_DSP_SPEED, &SampleRate)==-1) 1012275970Scy { /* Fatal error */ 1013275970Scy printf ("\nUnable to set sample rate to %d, returned %d, aborting...\n\n", SetSampleRate, SampleRate); 1014275970Scy exit(-1); 1015275970Scy } 1016275970Scy 1017275970Scy SampleRateDifference = SampleRate - SetSampleRate; 1018275970Scy 1019275970Scy if (SampleRateDifference < 0) 1020275970Scy SampleRateDifference = - SampleRateDifference; 1021275970Scy 1022275970Scy /* Fixed allowable sample rate error 0.1% */ 1023275970Scy if (SampleRateDifference > (SetSampleRate/1000)) 1024275970Scy { 1025275970Scy printf ("\nUnable to set sample rate to %d, result was %d, more than 0.1 percent, aborting...\n\n", SetSampleRate, SampleRate); 1026275970Scy exit(-1); 1027275970Scy } 1028275970Scy else 1029275970Scy { 1030275970Scy /* printf ("\nAttempt to set sample rate to %d, actual %d...\n\n", SetSampleRate, SampleRate); */ 1031275970Scy } 1032275970Scy#else 1033275970Scy rval = ioctl(fd, AUDIO_GETINFO, &info); 1034275970Scy if (rval < 0) { 1035275970Scy printf("\naudio control %s", strerror(errno)); 1036275970Scy exit(0); 1037275970Scy } 1038275970Scy info.play.port = port; 1039275970Scy info.play.gain = level; 1040275970Scy info.play.sample_rate = SetSampleRate; 1041275970Scy info.play.channels = 1; 1042275970Scy info.play.precision = 8; 1043275970Scy info.play.encoding = AUDIO_ENCODING_ULAW; 1044275970Scy printf("\nport %d gain %d rate %d chan %d prec %d encode %d\n", 1045275970Scy info.play.port, info.play.gain, info.play.sample_rate, 1046275970Scy info.play.channels, info.play.precision, 1047275970Scy info.play.encoding); 1048275970Scy ioctl(fd, AUDIO_SETINFO, &info); 1049275970Scy#endif 1050275970Scy 1051275970Scy /* 1052275970Scy * Unless specified otherwise, read the system clock and 1053275970Scy * initialize the time. 1054275970Scy */ 1055275970Scy gettimeofday(&TimeValue, NULL); // Now always read the system time to keep "real time" of operation. 1056275970Scy NowRealTime = BaseRealTime = SecondsPartOfTime = TimeValue.tv_sec; 1057275970Scy SecondsRunningSimulationTime = 0; // Just starting simulation, running zero seconds as of now. 1058275970Scy StabilityCount = 0; // No stability yet. 1059275970Scy 1060275970Scy if (utc) 1061275970Scy { 1062275970Scy DayOfYear = ConvertMonthDayToDayOfYear (Year, Month, DayOfMonth); 1063275970Scy } 1064275970Scy else 1065275970Scy { 1066275970Scy /* Apply offset to time. */ 1067275970Scy if (UseOffsetSecondsInt >= 0) 1068275970Scy SecondsPartOfTime += (time_t) UseOffsetSecondsInt; 1069275970Scy else 1070275970Scy SecondsPartOfTime -= (time_t) (-UseOffsetSecondsInt); 1071275970Scy 1072275970Scy TimeStructure = gmtime(&SecondsPartOfTime); 1073275970Scy Minute = TimeStructure->tm_min; 1074275970Scy Hour = TimeStructure->tm_hour; 1075275970Scy DayOfYear = TimeStructure->tm_yday + 1; 1076275970Scy Year = TimeStructure->tm_year % 100; 1077275970Scy Second = TimeStructure->tm_sec; 1078275970Scy 1079275970Scy /* 1080275970Scy * Delay the first second so the generator is accurately 1081275970Scy * aligned with the system clock within one sample (125 1082275970Scy * microseconds ). 1083275970Scy */ 1084275970Scy delay(SECOND - TimeValue.tv_usec * 8 / 1000); 1085275970Scy } 1086275970Scy 1087275970Scy StraightBinarySeconds = Second + (Minute * SECONDS_PER_MINUTE) + (Hour * SECONDS_PER_HOUR); 1088275970Scy 1089275970Scy memset(code, 0, sizeof(code)); 1090275970Scy switch (encode) { 1091275970Scy 1092275970Scy /* 1093275970Scy * For WWV/H and default time, carefully set the signal 1094275970Scy * generator seconds number to agree with the current time. 1095275970Scy */ 1096275970Scy case WWV: 1097275970Scy printf("WWV time signal, starting point:\n"); 1098275970Scy printf(" Year = %02d, Day of year = %03d, Time = %02d:%02d:%02d, Minute tone = %d Hz, Hour tone = %d Hz.\n", 1099275970Scy Year, DayOfYear, Hour, Minute, Second, tone, HourTone); 1100275970Scy snprintf(code, sizeof(code), "%01d%03d%02d%02d%01d", 1101275970Scy Year / 10, DayOfYear, Hour, Minute, Year % 10); 1102275970Scy if (Verbose) 1103275970Scy { 1104275970Scy printf("\n Year = %2.2d, Day of year = %3d, Time = %2.2d:%2.2d:%2.2d, Code = %s", 1105275970Scy Year, DayOfYear, Hour, Minute, Second, code); 1106275970Scy 1107275970Scy if ((EnableRateCorrection) || (RemoveCycle) || (AddCycle)) 1108275970Scy printf (", CountOfSecondsSent = %d, TotalCyclesAdded = %d, TotalCyclesRemoved = %d\n", CountOfSecondsSent, TotalCyclesAdded, TotalCyclesRemoved); 1109275970Scy else 1110275970Scy printf ("\n"); 1111275970Scy } 1112275970Scy 1113275970Scy ptr = 8; 1114275970Scy for (BitNumber = 0; BitNumber <= Second; BitNumber++) { 1115275970Scy if (progx[BitNumber].sw == DEC) 1116275970Scy ptr--; 1117275970Scy } 1118275970Scy break; 1119275970Scy 1120275970Scy /* 1121275970Scy * For IRIG the signal generator runs every second, so requires 1122275970Scy * no additional alignment. 1123275970Scy */ 1124275970Scy case IRIG: 1125275970Scy printf ("IRIG-B time signal, starting point:\n"); 1126275970Scy printf (" Year = %02d, Day of year = %03d, Time = %02d:%02d:%02d, Straight binary seconds (SBS) = %05d / 0x%04X.\n", 1127275970Scy Year, DayOfYear, Hour, Minute, Second, StraightBinarySeconds, StraightBinarySeconds); 1128275970Scy printf ("\n"); 1129275970Scy if (Verbose) 1130275970Scy { 1131275970Scy printf ("Codes: \".\" = marker/position indicator, \"-\" = zero dummy bit, \"0\" = zero bit, \"1\" = one bit.\n"); 1132275970Scy if ((EnableRateCorrection) || (AddCycle) || (RemoveCycle)) 1133275970Scy { 1134275970Scy printf (" \"o\" = short zero, \"*\" = long zero, \"x\" = short one, \"+\" = long one.\n"); 1135275970Scy } 1136275970Scy printf ("Numerical values are time order reversed in output to make it easier to read.\n"); 1137275970Scy /* 111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999 */ 1138275970Scy /* 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 */ 1139275970Scy printf ("\n"); 1140275970Scy printf ("Legend of output codes:\n"); 1141275970Scy //printf ("\n"); 1142275970Scy //printf ("| StraightBinSecs | IEEE_1344_Control | Year | Day_of_Year | Hours | Minutes |Seconds |\n"); 1143275970Scy //printf ("| --------------- | ----------------- | ---- | ----------- | ----- | ------- |------- |\n"); 1144275970Scy //printf ("| | | | | | | |\n"); 1145275970Scy } 1146275970Scy break; 1147275970Scy } 1148275970Scy 1149275970Scy /* 1150275970Scy * Run the signal generator to generate new timecode strings 1151275970Scy * once per minute for WWV/H and once per second for IRIG. 1152275970Scy */ 1153275970Scy for (CountOfSecondsSent=0; ((SecondsToSend==0) || (CountOfSecondsSent<SecondsToSend)); CountOfSecondsSent++) 1154275970Scy { 1155275970Scy if ((encode == IRIG) && (((Second % 20) == 0) || (CountOfSecondsSent == 0))) 1156275970Scy { 1157275970Scy printf ("\n"); 1158275970Scy 1159275970Scy printf (" Year = %02d, Day of year = %03d, Time = %02d:%02d:%02d, Straight binary seconds (SBS) = %05d / 0x%04X.\n", 1160275970Scy Year, DayOfYear, Hour, Minute, Second, StraightBinarySeconds, StraightBinarySeconds); 1161275970Scy if ((EnableRateCorrection) || (RemoveCycle) || (AddCycle)) 1162275970Scy { 1163275970Scy printf (" CountOfSecondsSent = %d, TotalCyclesAdded = %d, TotalCyclesRemoved = %d\n", CountOfSecondsSent, TotalCyclesAdded, TotalCyclesRemoved); 1164275970Scy if ((CountOfSecondsSent != 0) && ((TotalCyclesAdded != 0) || (TotalCyclesRemoved != 0))) 1165275970Scy { 1166275970Scy RatioError = ((float) (TotalCyclesAdded - TotalCyclesRemoved)) / (1000.0 * (float) CountOfSecondsSent); 1167275970Scy printf (" Adjusted by %2.1f%%, apparent send frequency is %4.2f Hz not %d Hz.\n\n", 1168275970Scy RatioError*100.0, (1.0+RatioError)*((float) SetSampleRate), SetSampleRate); 1169275970Scy } 1170275970Scy } 1171275970Scy else 1172275970Scy printf ("\n"); 1173275970Scy 1174275970Scy /* printf ("|Seconds | Minutes | Hours | Day_of_Year | Year | IEEE_1344_Control | StraightBinSecs |\n"); 1175275970Scy printf ("|------- | ------- | ----- | ----------- | ---- | ----------------- |-------------------|\n"); 1176275970Scy printf ("| | | | | | | |\n");*/ 1177275970Scy printf ("| StraightBinSecs | IEEE_1344_Control | Year | Day_of_Year | Hours | Minutes |Seconds |\n"); 1178275970Scy printf ("| --------------- | ----------------- | ---- | ----------- | ----- | ------- |------- |\n"); 1179275970Scy printf ("| | | | | | | |\n"); 1180275970Scy } 1181275970Scy 1182275970Scy if (RemoveCycle) 1183275970Scy { 1184275970Scy RateCorrection = -1; 1185275970Scy TotalSecondsCorrected ++; 1186275970Scy } 1187275970Scy else 1188275970Scy { 1189275970Scy if (AddCycle) 1190275970Scy { 1191275970Scy TotalSecondsCorrected ++; 1192275970Scy RateCorrection = +1; 1193275970Scy } 1194275970Scy else 1195275970Scy RateCorrection = 0; 1196275970Scy } 1197275970Scy 1198275970Scy /* 1199275970Scy * Crank the state machine to propagate carries to the 1200275970Scy * year of century. Note that we delayed up to one 1201275970Scy * second for alignment after reading the time, so this 1202275970Scy * is the next second. 1203275970Scy */ 1204275970Scy 1205275970Scy if (LeapState == LEAPSTATE_NORMAL) 1206275970Scy { 1207275970Scy /* If on the second of a leap (second 59 in the specified minute), then add or delete a second */ 1208275970Scy if ((Year == LeapYear) && (DayOfYear == LeapDayOfYear) && (Hour == LeapHour) && (Minute == LeapMinute)) 1209275970Scy { 1210275970Scy /* To delete a second, which means we go from 58->60 instead of 58->59->00. */ 1211275970Scy if ((DeleteLeapSecond) && (Second == 58)) 1212275970Scy { 1213275970Scy LeapState = LEAPSTATE_DELETING; 1214275970Scy 1215275970Scy if (Debug) 1216275970Scy printf ("\n<--- Ready to delete a leap second...\n"); 1217275970Scy } 1218275970Scy else 1219275970Scy { /* Delete takes precedence over insert. */ 1220275970Scy /* To add a second, which means we go from 59->60->00 instead of 59->00. */ 1221275970Scy if ((InsertLeapSecond) && (Second == 59)) 1222275970Scy { 1223275970Scy LeapState = LEAPSTATE_INSERTING; 1224275970Scy 1225275970Scy if (Debug) 1226275970Scy printf ("\n<--- Ready to insert a leap second...\n"); 1227275970Scy } 1228275970Scy } 1229275970Scy } 1230275970Scy } 1231275970Scy 1232275970Scy switch (LeapState) 1233275970Scy { 1234275970Scy case LEAPSTATE_NORMAL: 1235275970Scy Second = (Second + 1) % 60; 1236275970Scy break; 1237275970Scy 1238275970Scy case LEAPSTATE_DELETING: 1239275970Scy Second = 0; 1240275970Scy LeapState = LEAPSTATE_NORMAL; 1241275970Scy 1242275970Scy if (Debug) 1243275970Scy printf ("\n<--- Deleting a leap second...\n"); 1244275970Scy break; 1245275970Scy 1246275970Scy case LEAPSTATE_INSERTING: 1247275970Scy Second = 60; 1248275970Scy LeapState = LEAPSTATE_ZERO_AFTER_INSERT; 1249275970Scy 1250275970Scy if (Debug) 1251275970Scy printf ("\n<--- Inserting a leap second...\n"); 1252275970Scy break; 1253275970Scy 1254275970Scy case LEAPSTATE_ZERO_AFTER_INSERT: 1255275970Scy Second = 0; 1256275970Scy LeapState = LEAPSTATE_NORMAL; 1257275970Scy 1258275970Scy if (Debug) 1259275970Scy printf ("\n<--- Inserted a leap second, now back to zero...\n"); 1260275970Scy break; 1261275970Scy 1262275970Scy default: 1263275970Scy printf ("\n\nLeap second state invalid value of %d, aborting...", LeapState); 1264275970Scy exit (-1); 1265275970Scy break; 1266275970Scy } 1267275970Scy 1268275970Scy /* Check for second rollover, increment minutes and ripple upward if required. */ 1269275970Scy if (Second == 0) { 1270275970Scy Minute++; 1271275970Scy if (Minute >= 60) { 1272275970Scy Minute = 0; 1273275970Scy Hour++; 1274275970Scy } 1275275970Scy 1276275970Scy /* Check for activation of DST switch. */ 1277275970Scy /* If DST is active, this would mean that at the appointed time, we de-activate DST, */ 1278275970Scy /* which translates to going backward an hour (repeating the last hour). */ 1279275970Scy /* If DST is not active, this would mean that at the appointed time, we activate DST, */ 1280275970Scy /* which translates to going forward an hour (skipping the next hour). */ 1281275970Scy if (DstSwitchFlag) 1282275970Scy { 1283275970Scy /* The actual switch happens on the zero'th second of the actual minute specified. */ 1284275970Scy if ((Year == DstSwitchYear) && (DayOfYear == DstSwitchDayOfYear) && (Hour == DstSwitchHour) && (Minute == DstSwitchMinute)) 1285275970Scy { 1286275970Scy if (DstFlag == 0) 1287275970Scy { /* DST flag is zero, not in DST, going to DST, "spring ahead", so increment hour by two instead of one. */ 1288275970Scy Hour++; 1289275970Scy DstFlag = 1; 1290275970Scy 1291275970Scy /* Must adjust offset to keep consistent with UTC. */ 1292275970Scy /* Here we have to increase offset by one hour. If it goes from negative to positive, then we fix that. */ 1293275970Scy if (OffsetSignBit == 0) 1294275970Scy { /* Offset is positive */ 1295275970Scy if (OffsetOnes == 0x0F) 1296275970Scy { 1297275970Scy OffsetSignBit = 1; 1298275970Scy OffsetOnes = (OffsetHalf == 0) ? 8 : 7; 1299275970Scy } 1300275970Scy else 1301275970Scy OffsetOnes++; 1302275970Scy } 1303275970Scy else 1304275970Scy { /* Offset is negative */ 1305275970Scy if (OffsetOnes == 0) 1306275970Scy { 1307275970Scy OffsetSignBit = 0; 1308275970Scy OffsetOnes = (OffsetHalf == 0) ? 1 : 0; 1309275970Scy } 1310275970Scy else 1311275970Scy OffsetOnes--; 1312275970Scy } 1313275970Scy 1314275970Scy if (Debug) 1315275970Scy printf ("\n<--- DST activated, spring ahead an hour, new offset !...\n"); 1316275970Scy } 1317275970Scy else 1318275970Scy { /* DST flag is non zero, in DST, going out of DST, "fall back", so no increment of hour. */ 1319275970Scy Hour--; 1320275970Scy DstFlag = 0; 1321275970Scy 1322275970Scy /* Must adjust offset to keep consistent with UTC. */ 1323275970Scy /* Here we have to reduce offset by one hour. If it goes negative, then we fix that. */ 1324275970Scy if (OffsetSignBit == 0) 1325275970Scy { /* Offset is positive */ 1326275970Scy if (OffsetOnes == 0) 1327275970Scy { 1328275970Scy OffsetSignBit = 1; 1329275970Scy OffsetOnes = (OffsetHalf == 0) ? 1 : 0; 1330275970Scy } 1331275970Scy else 1332275970Scy OffsetOnes--; 1333275970Scy } 1334275970Scy else 1335275970Scy { /* Offset is negative */ 1336275970Scy if (OffsetOnes == 0x0F) 1337275970Scy { 1338275970Scy OffsetSignBit = 0; 1339275970Scy OffsetOnes = (OffsetHalf == 0) ? 8 : 7; 1340275970Scy } 1341275970Scy else 1342275970Scy OffsetOnes++; 1343275970Scy } 1344275970Scy 1345275970Scy if (Debug) 1346275970Scy printf ("\n<--- DST de-activated, fall back an hour!...\n"); 1347275970Scy } 1348275970Scy 1349275970Scy DstSwitchFlag = FALSE; /* One time deal, not intended to run this program past two switches... */ 1350275970Scy } 1351275970Scy } 1352275970Scy 1353275970Scy if (Hour >= 24) { 1354275970Scy /* Modified, just in case dumb case where activating DST advances 23h59:59 -> 01h00:00 */ 1355275970Scy Hour = Hour % 24; 1356275970Scy DayOfYear++; 1357275970Scy } 1358275970Scy 1359275970Scy /* 1360275970Scy * At year rollover check for leap second. 1361275970Scy */ 1362275970Scy if (DayOfYear >= (Year & 0x3 ? 366 : 367)) { 1363275970Scy if (leap) { 1364275970Scy WWV_Second(DATA0, RateCorrection); 1365275970Scy if (Verbose) 1366275970Scy printf("\nLeap!"); 1367275970Scy leap = 0; 1368275970Scy } 1369275970Scy DayOfYear = 1; 1370275970Scy Year++; 1371275970Scy } 1372275970Scy if (encode == WWV) { 1373275970Scy snprintf(code, sizeof(code), 1374275970Scy "%01d%03d%02d%02d%01d", Year / 10, 1375275970Scy DayOfYear, Hour, Minute, Year % 10); 1376275970Scy if (Verbose) 1377275970Scy printf("\n Year = %2.2d, Day of year = %3d, Time = %2.2d:%2.2d:%2.2d, Code = %s", 1378275970Scy Year, DayOfYear, Hour, Minute, Second, code); 1379275970Scy 1380275970Scy if ((EnableRateCorrection) || (RemoveCycle) || (AddCycle)) 1381275970Scy { 1382275970Scy printf (", CountOfSecondsSent = %d, TotalCyclesAdded = %d, TotalCyclesRemoved = %d\n", CountOfSecondsSent, TotalCyclesAdded, TotalCyclesRemoved); 1383275970Scy if ((CountOfSecondsSent != 0) && ((TotalCyclesAdded != 0) || (TotalCyclesRemoved != 0))) 1384275970Scy { 1385275970Scy RatioError = ((float) (TotalCyclesAdded - TotalCyclesRemoved)) / (1000.0 * (float) CountOfSecondsSent); 1386275970Scy printf (" Adjusted by %2.1f%%, apparent send frequency is %4.2f Hz not %d Hz.\n\n", 1387275970Scy RatioError*100.0, (1.0+RatioError)*((float) SetSampleRate), SetSampleRate); 1388275970Scy } 1389275970Scy } 1390275970Scy else 1391275970Scy printf ("\n"); 1392275970Scy 1393275970Scy ptr = 8; 1394275970Scy } 1395275970Scy } /* End of "if (Second == 0)" */ 1396275970Scy 1397275970Scy /* After all that, if we are in the minute just prior to a leap second, warn of leap second pending */ 1398275970Scy /* and of the polarity */ 1399275970Scy if ((Year == LeapYear) && (DayOfYear == LeapDayOfYear) && (Hour == LeapHour) && (Minute == LeapMinute)) 1400275970Scy { 1401275970Scy LeapSecondPending = TRUE; 1402275970Scy LeapSecondPolarity = DeleteLeapSecond; 1403275970Scy } 1404275970Scy else 1405275970Scy { 1406275970Scy LeapSecondPending = FALSE; 1407275970Scy LeapSecondPolarity = FALSE; 1408275970Scy } 1409275970Scy 1410275970Scy /* Notification through IEEE 1344 happens during the whole minute previous to the minute specified. */ 1411275970Scy /* The time of that minute has been previously calculated. */ 1412275970Scy if ((Year == DstSwitchPendingYear) && (DayOfYear == DstSwitchPendingDayOfYear) && 1413275970Scy (Hour == DstSwitchPendingHour) && (Minute == DstSwitchPendingMinute)) 1414275970Scy { 1415275970Scy DstPendingFlag = TRUE; 1416275970Scy } 1417275970Scy else 1418275970Scy { 1419275970Scy DstPendingFlag = FALSE; 1420275970Scy } 1421275970Scy 1422275970Scy 1423275970Scy StraightBinarySeconds = Second + (Minute * SECONDS_PER_MINUTE) + (Hour * SECONDS_PER_HOUR); 1424275970Scy 1425275970Scy if (encode == IRIG) { 1426275970Scy if (IrigIncludeIeee) 1427275970Scy { 1428275970Scy if ((OffsetOnes == 0) && (OffsetHalf == 0)) 1429275970Scy OffsetSignBit = 0; 1430275970Scy 1431275970Scy ControlFunctions = (LeapSecondPending == 0 ? 0x00000 : 0x00001) | (LeapSecondPolarity == 0 ? 0x00000 : 0x00002) 1432275970Scy | (DstPendingFlag == 0 ? 0x00000 : 0x00004) | (DstFlag == 0 ? 0x00000 : 0x00008) 1433275970Scy | (OffsetSignBit == 0 ? 0x00000 : 0x00010) | ((OffsetOnes & 0x0F) << 5) | (OffsetHalf == 0 ? 0x00000 : 0x00200) 1434275970Scy | ((TimeQuality & 0x0F) << 10); 1435275970Scy /* if (Verbose) 1436275970Scy printf ("\nDstFlag = %d, OffsetSignBit = %d, OffsetOnes = %d, OffsetHalf = %d, TimeQuality = 0x%1.1X ==> ControlFunctions = 0x%5.5X...", 1437275970Scy DstFlag, OffsetSignBit, OffsetOnes, OffsetHalf, TimeQuality, ControlFunctions); 1438275970Scy */ 1439275970Scy } 1440275970Scy else 1441275970Scy ControlFunctions = 0; 1442275970Scy 1443275970Scy /* 1444275970Scy YearDay HourMin Sec 1445275970Scy snprintf(code, sizeof(code), "%04x%04d%06d%02d%02d%02d", 1446275970Scy 0, Year, DayOfYear, Hour, Minute, Second); 1447275970Scy */ 1448275970Scy if (IrigIncludeYear) { 1449275970Scy snprintf(ParityString, sizeof(ParityString), 1450275970Scy "%04X%02d%04d%02d%02d%02d", 1451275970Scy ControlFunctions & 0x7FFF, Year, 1452275970Scy DayOfYear, Hour, Minute, Second); 1453275970Scy } else { 1454275970Scy snprintf(ParityString, sizeof(ParityString), 1455275970Scy "%04X%02d%04d%02d%02d%02d", 1456275970Scy ControlFunctions & 0x7FFF, 1457275970Scy 0, DayOfYear, Hour, Minute, Second); 1458275970Scy } 1459275970Scy 1460275970Scy if (IrigIncludeIeee) 1461275970Scy { 1462275970Scy ParitySum = 0; 1463275970Scy for (StringPointer=ParityString; *StringPointer!=NUL; StringPointer++) 1464275970Scy { 1465275970Scy switch (toupper(*StringPointer)) 1466275970Scy { 1467275970Scy case '1': 1468275970Scy case '2': 1469275970Scy case '4': 1470275970Scy case '8': 1471275970Scy ParitySum += 1; 1472275970Scy break; 1473275970Scy 1474275970Scy case '3': 1475275970Scy case '5': 1476275970Scy case '6': 1477275970Scy case '9': 1478275970Scy case 'A': 1479275970Scy case 'C': 1480275970Scy ParitySum += 2; 1481275970Scy break; 1482275970Scy 1483275970Scy case '7': 1484275970Scy case 'B': 1485275970Scy case 'D': 1486275970Scy case 'E': 1487275970Scy ParitySum += 3; 1488275970Scy break; 1489275970Scy 1490275970Scy case 'F': 1491275970Scy ParitySum += 4; 1492275970Scy break; 1493275970Scy } 1494275970Scy } 1495275970Scy 1496275970Scy if ((ParitySum & 0x01) == 0x01) 1497275970Scy ParityValue = 0x01; 1498275970Scy else 1499275970Scy ParityValue = 0; 1500275970Scy } 1501275970Scy else 1502275970Scy ParityValue = 0; 1503275970Scy 1504275970Scy ControlFunctions |= ((ParityValue & 0x01) << 14); 1505275970Scy 1506275970Scy if (IrigIncludeYear) { 1507275970Scy snprintf(code, sizeof(code), 1508275970Scy /* YearDay HourMin Sec */ 1509275970Scy "%05X%05X%02d%04d%02d%02d%02d", 1510275970Scy StraightBinarySeconds, 1511275970Scy ControlFunctions, Year, DayOfYear, 1512275970Scy Hour, Minute, Second); 1513275970Scy } else { 1514275970Scy snprintf(code, sizeof(code), 1515275970Scy /* YearDay HourMin Sec */ 1516275970Scy "%05X%05X%02d%04d%02d%02d%02d", 1517275970Scy StraightBinarySeconds, 1518275970Scy ControlFunctions, 0, DayOfYear, 1519275970Scy Hour, Minute, Second); 1520275970Scy } 1521275970Scy 1522275970Scy if (Debug) 1523275970Scy printf("\nCode string: %s, ParityString = %s, ParitySum = 0x%2.2X, ParityValue = %d, DstFlag = %d...\n", code, ParityString, ParitySum, ParityValue, DstFlag); 1524275970Scy 1525275970Scy ptr = strlen(code)-1; 1526275970Scy OldPtr = 0; 1527275970Scy } 1528275970Scy 1529275970Scy /* 1530275970Scy * Generate data for the second 1531275970Scy */ 1532275970Scy switch (encode) { 1533275970Scy 1534275970Scy /* 1535275970Scy * The IRIG second consists of 20 BCD digits of width- 1536275970Scy * modulateod pulses at 2, 5 and 8 ms and modulated 50 1537275970Scy * percent on the 1000-Hz carrier. 1538275970Scy */ 1539275970Scy case IRIG: 1540275970Scy /* Initialize the output string */ 1541275970Scy OutputDataString[0] = '\0'; 1542275970Scy 1543275970Scy for (BitNumber = 0; BitNumber < 100; BitNumber++) { 1544275970Scy FrameNumber = (BitNumber/10) + 1; 1545275970Scy switch (FrameNumber) 1546275970Scy { 1547275970Scy case 1: 1548275970Scy /* bits 0 to 9, first frame */ 1549275970Scy sw = progz[BitNumber % 10].sw; 1550275970Scy arg = progz[BitNumber % 10].arg; 1551275970Scy break; 1552275970Scy 1553275970Scy case 2: 1554275970Scy case 3: 1555275970Scy case 4: 1556275970Scy case 5: 1557275970Scy case 6: 1558275970Scy /* bits 10 to 59, second to sixth frame */ 1559275970Scy sw = progy[BitNumber % 10].sw; 1560275970Scy arg = progy[BitNumber % 10].arg; 1561275970Scy break; 1562275970Scy 1563275970Scy case 7: 1564275970Scy /* bits 60 to 69, seventh frame */ 1565275970Scy sw = progw[BitNumber % 10].sw; 1566275970Scy arg = progw[BitNumber % 10].arg; 1567275970Scy break; 1568275970Scy 1569275970Scy case 8: 1570275970Scy /* bits 70 to 79, eighth frame */ 1571275970Scy sw = progv[BitNumber % 10].sw; 1572275970Scy arg = progv[BitNumber % 10].arg; 1573275970Scy break; 1574275970Scy 1575275970Scy case 9: 1576275970Scy /* bits 80 to 89, ninth frame */ 1577275970Scy sw = progw[BitNumber % 10].sw; 1578275970Scy arg = progw[BitNumber % 10].arg; 1579275970Scy break; 1580275970Scy 1581275970Scy case 10: 1582275970Scy /* bits 90 to 99, tenth frame */ 1583275970Scy sw = progu[BitNumber % 10].sw; 1584275970Scy arg = progu[BitNumber % 10].arg; 1585275970Scy break; 1586275970Scy 1587275970Scy default: 1588275970Scy /* , Unexpected values of FrameNumber */ 1589275970Scy printf ("\n\nUnexpected value of FrameNumber = %d, cannot parse, aborting...\n\n", FrameNumber); 1590275970Scy exit (-1); 1591275970Scy break; 1592275970Scy } 1593275970Scy 1594275970Scy switch(sw) { 1595275970Scy 1596275970Scy case DECC: /* decrement pointer and send bit. */ 1597275970Scy ptr--; 1598275970Scy case COEF: /* send BCD bit */ 1599275970Scy AsciiValue = toupper(code[ptr]); 1600275970Scy HexValue = isdigit(AsciiValue) ? AsciiValue - '0' : (AsciiValue - 'A')+10; 1601275970Scy /* if (Debug) { 1602275970Scy if (ptr != OldPtr) { 1603275970Scy if (Verbose) 1604275970Scy printf("\n(%c->%X)", AsciiValue, HexValue); 1605275970Scy OldPtr = ptr; 1606275970Scy } 1607275970Scy } 1608275970Scy */ 1609275970Scy // OK, adjust all unused bits in hundreds of days. 1610275970Scy if ((FrameNumber == 5) && ((BitNumber % 10) > 1)) 1611275970Scy { 1612275970Scy if (RateCorrection < 0) 1613275970Scy { // Need to remove cycles to catch up. 1614275970Scy if ((HexValue & arg) != 0) 1615275970Scy { 1616275970Scy if (Unmodulated) 1617275970Scy { 1618275970Scy poop(M5, 1000, HIGH, UnmodulatedInverted); 1619275970Scy poop(M5-1, 1000, LOW, UnmodulatedInverted); 1620275970Scy 1621275970Scy TotalCyclesRemoved += 1; 1622275970Scy } 1623275970Scy else 1624275970Scy { 1625275970Scy peep(M5, 1000, HIGH); 1626275970Scy peep(M5-1, 1000, LOW); 1627275970Scy 1628275970Scy TotalCyclesRemoved += 1; 1629275970Scy } 1630275970Scy strlcat(OutputDataString, "x", OUTPUT_DATA_STRING_LENGTH); 1631275970Scy } 1632275970Scy else 1633275970Scy { 1634275970Scy if (Unmodulated) 1635275970Scy { 1636275970Scy poop(M2, 1000, HIGH, UnmodulatedInverted); 1637275970Scy poop(M8-1, 1000, LOW, UnmodulatedInverted); 1638275970Scy 1639275970Scy TotalCyclesRemoved += 1; 1640275970Scy } 1641275970Scy else 1642275970Scy { 1643275970Scy peep(M2, 1000, HIGH); 1644275970Scy peep(M8-1, 1000, LOW); 1645275970Scy 1646275970Scy TotalCyclesRemoved += 1; 1647275970Scy } 1648275970Scy strlcat(OutputDataString, "o", OUTPUT_DATA_STRING_LENGTH); 1649275970Scy } 1650275970Scy } // End of true clause for "if (RateCorrection < 0)" 1651275970Scy else 1652275970Scy { // Else clause for "if (RateCorrection < 0)" 1653275970Scy if (RateCorrection > 0) 1654275970Scy { // Need to add cycles to slow back down. 1655275970Scy if ((HexValue & arg) != 0) 1656275970Scy { 1657275970Scy if (Unmodulated) 1658275970Scy { 1659275970Scy poop(M5, 1000, HIGH, UnmodulatedInverted); 1660275970Scy poop(M5+1, 1000, LOW, UnmodulatedInverted); 1661275970Scy 1662275970Scy TotalCyclesAdded += 1; 1663275970Scy } 1664275970Scy else 1665275970Scy { 1666275970Scy peep(M5, 1000, HIGH); 1667275970Scy peep(M5+1, 1000, LOW); 1668275970Scy 1669275970Scy TotalCyclesAdded += 1; 1670275970Scy } 1671275970Scy strlcat(OutputDataString, "+", OUTPUT_DATA_STRING_LENGTH); 1672275970Scy } 1673275970Scy else 1674275970Scy { 1675275970Scy if (Unmodulated) 1676275970Scy { 1677275970Scy poop(M2, 1000, HIGH, UnmodulatedInverted); 1678275970Scy poop(M8+1, 1000, LOW, UnmodulatedInverted); 1679275970Scy 1680275970Scy TotalCyclesAdded += 1; 1681275970Scy } 1682275970Scy else 1683275970Scy { 1684275970Scy peep(M2, 1000, HIGH); 1685275970Scy peep(M8+1, 1000, LOW); 1686275970Scy 1687275970Scy TotalCyclesAdded += 1; 1688275970Scy } 1689275970Scy strlcat(OutputDataString, "*", OUTPUT_DATA_STRING_LENGTH); 1690275970Scy } 1691275970Scy } // End of true clause for "if (RateCorrection > 0)" 1692275970Scy else 1693275970Scy { // Else clause for "if (RateCorrection > 0)" 1694275970Scy // Rate is OK, just do what you feel! 1695275970Scy if ((HexValue & arg) != 0) 1696275970Scy { 1697275970Scy if (Unmodulated) 1698275970Scy { 1699275970Scy poop(M5, 1000, HIGH, UnmodulatedInverted); 1700275970Scy poop(M5, 1000, LOW, UnmodulatedInverted); 1701275970Scy } 1702275970Scy else 1703275970Scy { 1704275970Scy peep(M5, 1000, HIGH); 1705275970Scy peep(M5, 1000, LOW); 1706275970Scy } 1707275970Scy strlcat(OutputDataString, "1", OUTPUT_DATA_STRING_LENGTH); 1708275970Scy } 1709275970Scy else 1710275970Scy { 1711275970Scy if (Unmodulated) 1712275970Scy { 1713275970Scy poop(M2, 1000, HIGH, UnmodulatedInverted); 1714275970Scy poop(M8, 1000, LOW, UnmodulatedInverted); 1715275970Scy } 1716275970Scy else 1717275970Scy { 1718275970Scy peep(M2, 1000, HIGH); 1719275970Scy peep(M8, 1000, LOW); 1720275970Scy } 1721275970Scy strlcat(OutputDataString, "0", OUTPUT_DATA_STRING_LENGTH); 1722275970Scy } 1723275970Scy } // End of else clause for "if (RateCorrection > 0)" 1724275970Scy } // End of else claues for "if (RateCorrection < 0)" 1725275970Scy } // End of true clause for "if ((FrameNumber == 5) && (BitNumber == 8))" 1726275970Scy else 1727275970Scy { // Else clause for "if ((FrameNumber == 5) && (BitNumber == 8))" 1728275970Scy if ((HexValue & arg) != 0) 1729275970Scy { 1730275970Scy if (Unmodulated) 1731275970Scy { 1732275970Scy poop(M5, 1000, HIGH, UnmodulatedInverted); 1733275970Scy poop(M5, 1000, LOW, UnmodulatedInverted); 1734275970Scy } 1735275970Scy else 1736275970Scy { 1737275970Scy peep(M5, 1000, HIGH); 1738275970Scy peep(M5, 1000, LOW); 1739275970Scy } 1740275970Scy strlcat(OutputDataString, "1", OUTPUT_DATA_STRING_LENGTH); 1741275970Scy } 1742275970Scy else 1743275970Scy { 1744275970Scy if (Unmodulated) 1745275970Scy { 1746275970Scy poop(M2, 1000, HIGH, UnmodulatedInverted); 1747275970Scy poop(M8, 1000, LOW, UnmodulatedInverted); 1748275970Scy } 1749275970Scy else 1750275970Scy { 1751275970Scy peep(M2, 1000, HIGH); 1752275970Scy peep(M8, 1000, LOW); 1753275970Scy } 1754275970Scy strlcat(OutputDataString, "0", OUTPUT_DATA_STRING_LENGTH); 1755275970Scy } 1756275970Scy } // end of else clause for "if ((FrameNumber == 5) && (BitNumber == 8))" 1757275970Scy break; 1758275970Scy 1759275970Scy case DECZ: /* decrement pointer and send zero bit */ 1760275970Scy ptr--; 1761275970Scy if (Unmodulated) 1762275970Scy { 1763275970Scy poop(M2, 1000, HIGH, UnmodulatedInverted); 1764275970Scy poop(M8, 1000, LOW, UnmodulatedInverted); 1765275970Scy } 1766275970Scy else 1767275970Scy { 1768275970Scy peep(M2, 1000, HIGH); 1769275970Scy peep(M8, 1000, LOW); 1770275970Scy } 1771275970Scy strlcat(OutputDataString, "-", OUTPUT_DATA_STRING_LENGTH); 1772275970Scy break; 1773275970Scy 1774275970Scy case DEC: /* send marker/position indicator IM/PI bit */ 1775275970Scy ptr--; 1776275970Scy case NODEC: /* send marker/position indicator IM/PI bit but no decrement pointer */ 1777275970Scy case MIN: /* send "second start" marker/position indicator IM/PI bit */ 1778275970Scy if (Unmodulated) 1779275970Scy { 1780275970Scy poop(arg, 1000, HIGH, UnmodulatedInverted); 1781275970Scy poop(10 - arg, 1000, LOW, UnmodulatedInverted); 1782275970Scy } 1783275970Scy else 1784275970Scy { 1785275970Scy peep(arg, 1000, HIGH); 1786275970Scy peep(10 - arg, 1000, LOW); 1787275970Scy } 1788275970Scy strlcat(OutputDataString, ".", OUTPUT_DATA_STRING_LENGTH); 1789275970Scy break; 1790275970Scy 1791275970Scy default: 1792275970Scy printf ("\n\nUnknown state machine value \"%d\", unable to continue, aborting...\n\n", sw); 1793275970Scy exit (-1); 1794275970Scy break; 1795275970Scy } 1796275970Scy if (ptr < 0) 1797275970Scy break; 1798275970Scy } 1799275970Scy ReverseString ( OutputDataString ); 1800275970Scy if (Verbose) 1801275970Scy { 1802275970Scy printf("%s", OutputDataString); 1803275970Scy if (RateCorrection > 0) 1804275970Scy printf(" fast\n"); 1805275970Scy else 1806275970Scy { 1807275970Scy if (RateCorrection < 0) 1808275970Scy printf (" slow\n"); 1809275970Scy else 1810275970Scy printf ("\n"); 1811275970Scy } 1812275970Scy } 1813275970Scy break; 1814275970Scy 1815275970Scy /* 1816275970Scy * The WWV/H second consists of 9 BCD digits of width- 1817275970Scy * modulateod pulses 200, 500 and 800 ms at 100-Hz. 1818275970Scy */ 1819275970Scy case WWV: 1820275970Scy sw = progx[Second].sw; 1821275970Scy arg = progx[Second].arg; 1822275970Scy switch(sw) { 1823275970Scy 1824275970Scy case DATA: /* send data bit */ 1825275970Scy WWV_Second(arg, RateCorrection); 1826275970Scy if (Verbose) 1827275970Scy { 1828275970Scy if (arg == DATA0) 1829275970Scy printf ("0"); 1830275970Scy else 1831275970Scy { 1832275970Scy if (arg == DATA1) 1833275970Scy printf ("1"); 1834275970Scy else 1835275970Scy { 1836275970Scy if (arg == PI) 1837275970Scy printf ("P"); 1838275970Scy else 1839275970Scy printf ("?"); 1840275970Scy } 1841275970Scy } 1842275970Scy } 1843275970Scy break; 1844275970Scy 1845275970Scy case DATAX: /* send data bit */ 1846275970Scy WWV_SecondNoTick(arg, RateCorrection); 1847275970Scy if (Verbose) 1848275970Scy { 1849275970Scy if (arg == DATA0) 1850275970Scy printf ("0"); 1851275970Scy else 1852275970Scy { 1853275970Scy if (arg == DATA1) 1854275970Scy printf ("1"); 1855275970Scy else 1856275970Scy { 1857275970Scy if (arg == PI) 1858275970Scy printf ("P"); 1859275970Scy else 1860275970Scy printf ("?"); 1861275970Scy } 1862275970Scy } 1863275970Scy } 1864275970Scy break; 1865275970Scy 1866275970Scy case COEF: /* send BCD bit */ 1867275970Scy if (code[ptr] & arg) { 1868275970Scy WWV_Second(DATA1, RateCorrection); 1869275970Scy if (Verbose) 1870275970Scy printf("1"); 1871275970Scy } else { 1872275970Scy WWV_Second(DATA0, RateCorrection); 1873275970Scy if (Verbose) 1874275970Scy printf("0"); 1875275970Scy } 1876275970Scy break; 1877275970Scy 1878275970Scy case LEAP: /* send leap bit */ 1879275970Scy if (leap) { 1880275970Scy WWV_Second(DATA1, RateCorrection); 1881275970Scy if (Verbose) 1882275970Scy printf("L"); 1883275970Scy } else { 1884275970Scy WWV_Second(DATA0, RateCorrection); 1885275970Scy if (Verbose) 1886275970Scy printf("0"); 1887275970Scy } 1888275970Scy break; 1889275970Scy 1890275970Scy case DEC: /* send data bit */ 1891275970Scy ptr--; 1892275970Scy WWV_Second(arg, RateCorrection); 1893275970Scy if (Verbose) 1894275970Scy { 1895275970Scy if (arg == DATA0) 1896275970Scy printf ("0"); 1897275970Scy else 1898275970Scy { 1899275970Scy if (arg == DATA1) 1900275970Scy printf ("1"); 1901275970Scy else 1902275970Scy { 1903275970Scy if (arg == PI) 1904275970Scy printf ("P"); 1905275970Scy else 1906275970Scy printf ("?"); 1907275970Scy } 1908275970Scy } 1909275970Scy } 1910275970Scy break; 1911275970Scy 1912275970Scy case DECX: /* send data bit with no tick */ 1913275970Scy ptr--; 1914275970Scy WWV_SecondNoTick(arg, RateCorrection); 1915275970Scy if (Verbose) 1916275970Scy { 1917275970Scy if (arg == DATA0) 1918275970Scy printf ("0"); 1919275970Scy else 1920275970Scy { 1921275970Scy if (arg == DATA1) 1922275970Scy printf ("1"); 1923275970Scy else 1924275970Scy { 1925275970Scy if (arg == PI) 1926275970Scy printf ("P"); 1927275970Scy else 1928275970Scy printf ("?"); 1929275970Scy } 1930275970Scy } 1931275970Scy } 1932275970Scy break; 1933275970Scy 1934275970Scy case MIN: /* send minute sync */ 1935275970Scy if (Minute == 0) 1936275970Scy { 1937275970Scy peep(arg, HourTone, HIGH); 1938275970Scy 1939275970Scy if (RateCorrection < 0) 1940275970Scy { 1941275970Scy peep( 990 - arg, HourTone, OFF); 1942275970Scy TotalCyclesRemoved += 10; 1943275970Scy 1944275970Scy if (Debug) 1945275970Scy printf ("\n* Shorter Second: "); 1946275970Scy } 1947275970Scy else 1948275970Scy { 1949275970Scy if (RateCorrection > 0) 1950275970Scy { 1951275970Scy peep(1010 - arg, HourTone, OFF); 1952275970Scy 1953275970Scy TotalCyclesAdded += 10; 1954275970Scy 1955275970Scy if (Debug) 1956275970Scy printf ("\n* Longer Second: "); 1957275970Scy } 1958275970Scy else 1959275970Scy { 1960275970Scy peep(1000 - arg, HourTone, OFF); 1961275970Scy } 1962275970Scy } 1963275970Scy 1964275970Scy if (Verbose) 1965275970Scy printf("H"); 1966275970Scy } 1967275970Scy else 1968275970Scy { 1969275970Scy peep(arg, tone, HIGH); 1970275970Scy 1971275970Scy if (RateCorrection < 0) 1972275970Scy { 1973275970Scy peep( 990 - arg, tone, OFF); 1974275970Scy TotalCyclesRemoved += 10; 1975275970Scy 1976275970Scy if (Debug) 1977275970Scy printf ("\n* Shorter Second: "); 1978275970Scy } 1979275970Scy else 1980275970Scy { 1981275970Scy if (RateCorrection > 0) 1982275970Scy { 1983275970Scy peep(1010 - arg, tone, OFF); 1984275970Scy 1985275970Scy TotalCyclesAdded += 10; 1986275970Scy 1987275970Scy if (Debug) 1988275970Scy printf ("\n* Longer Second: "); 1989275970Scy } 1990275970Scy else 1991275970Scy { 1992275970Scy peep(1000 - arg, tone, OFF); 1993275970Scy } 1994275970Scy } 1995275970Scy 1996275970Scy if (Verbose) 1997275970Scy printf("M"); 1998275970Scy } 1999275970Scy break; 2000275970Scy 2001275970Scy case DUT1: /* send DUT1 bits */ 2002275970Scy if (dut1 & arg) 2003275970Scy { 2004275970Scy WWV_Second(DATA1, RateCorrection); 2005275970Scy if (Verbose) 2006275970Scy printf("1"); 2007275970Scy } 2008275970Scy else 2009275970Scy { 2010275970Scy WWV_Second(DATA0, RateCorrection); 2011275970Scy if (Verbose) 2012275970Scy printf("0"); 2013275970Scy } 2014275970Scy break; 2015275970Scy 2016275970Scy case DST1: /* send DST1 bit */ 2017275970Scy ptr--; 2018275970Scy if (DstFlag) 2019275970Scy { 2020275970Scy WWV_Second(DATA1, RateCorrection); 2021275970Scy if (Verbose) 2022275970Scy printf("1"); 2023275970Scy } 2024275970Scy else 2025275970Scy { 2026275970Scy WWV_Second(DATA0, RateCorrection); 2027275970Scy if (Verbose) 2028275970Scy printf("0"); 2029275970Scy } 2030275970Scy break; 2031275970Scy 2032275970Scy case DST2: /* send DST2 bit */ 2033275970Scy if (DstFlag) 2034275970Scy { 2035275970Scy WWV_Second(DATA1, RateCorrection); 2036275970Scy if (Verbose) 2037275970Scy printf("1"); 2038275970Scy } 2039275970Scy else 2040275970Scy { 2041275970Scy WWV_Second(DATA0, RateCorrection); 2042275970Scy if (Verbose) 2043275970Scy printf("0"); 2044275970Scy } 2045275970Scy break; 2046275970Scy } 2047275970Scy } 2048275970Scy 2049275970Scy if (EnableRateCorrection) 2050275970Scy { 2051275970Scy SecondsRunningSimulationTime++; 2052275970Scy 2053275970Scy gettimeofday(&TimeValue, NULL); 2054275970Scy NowRealTime = TimeValue.tv_sec; 2055275970Scy 2056275970Scy if (NowRealTime >= BaseRealTime) // Just in case system time corrects backwards, do not blow up. 2057275970Scy { 2058275970Scy SecondsRunningRealTime = (unsigned) (NowRealTime - BaseRealTime); 2059275970Scy SecondsRunningDifference = SecondsRunningSimulationTime - SecondsRunningRealTime; 2060275970Scy 2061275970Scy if (Debug) 2062275970Scy { 2063275970Scy printf ("> NowRealTime = 0x%8.8X, BaseRealtime = 0x%8.8X, SecondsRunningRealTime = 0x%8.8X, SecondsRunningSimulationTime = 0x%8.8X.\n", 2064275970Scy (unsigned) NowRealTime, (unsigned) BaseRealTime, SecondsRunningRealTime, SecondsRunningSimulationTime); 2065275970Scy printf ("> SecondsRunningDifference = 0x%8.8X, ExpectedRunningDifference = 0x%8.8X.\n", 2066275970Scy SecondsRunningDifference, ExpectedRunningDifference); 2067275970Scy } 2068275970Scy 2069275970Scy if (SecondsRunningSimulationTime > RUN_BEFORE_STABILITY_CHECK) 2070275970Scy { 2071275970Scy if (StabilityCount < MINIMUM_STABILITY_COUNT) 2072275970Scy { 2073275970Scy if (StabilityCount == 0) 2074275970Scy { 2075275970Scy ExpectedRunningDifference = SecondsRunningDifference; 2076275970Scy StabilityCount++; 2077275970Scy if (Debug) 2078275970Scy printf ("> Starting stability check.\n"); 2079275970Scy } 2080275970Scy else 2081275970Scy { // Else for "if (StabilityCount == 0)" 2082275970Scy if ((ExpectedRunningDifference+INITIAL_STABILITY_BAND > SecondsRunningDifference) 2083275970Scy && (ExpectedRunningDifference-INITIAL_STABILITY_BAND < SecondsRunningDifference)) 2084275970Scy { // So far, still within stability band, increment count. 2085275970Scy StabilityCount++; 2086275970Scy if (Debug) 2087275970Scy printf ("> StabilityCount = %d.\n", StabilityCount); 2088275970Scy } 2089275970Scy else 2090275970Scy { // Outside of stability band, start over. 2091275970Scy StabilityCount = 0; 2092275970Scy if (Debug) 2093275970Scy printf ("> Out of stability band, start over.\n"); 2094275970Scy } 2095275970Scy } // End of else for "if (StabilityCount == 0)" 2096275970Scy } // End of true clause for "if (StabilityCount < MINIMUM_STABILITY_COUNT))" 2097275970Scy else 2098275970Scy { // Else clause for "if (StabilityCount < MINIMUM_STABILITY_COUNT))" - OK, so we are supposed to be stable. 2099275970Scy if (AddCycle) 2100275970Scy { 2101275970Scy if (ExpectedRunningDifference >= SecondsRunningDifference) 2102275970Scy { 2103275970Scy if (Debug) 2104275970Scy printf ("> Was adding cycles, ExpectedRunningDifference >= SecondsRunningDifference, can stop it now.\n"); 2105275970Scy 2106275970Scy AddCycle = FALSE; 2107275970Scy RemoveCycle = FALSE; 2108275970Scy } 2109275970Scy else 2110275970Scy { 2111275970Scy if (Debug) 2112275970Scy printf ("> Was adding cycles, not done yet.\n"); 2113275970Scy } 2114275970Scy } 2115275970Scy else 2116275970Scy { 2117275970Scy if (RemoveCycle) 2118275970Scy { 2119275970Scy if (ExpectedRunningDifference <= SecondsRunningDifference) 2120275970Scy { 2121275970Scy if (Debug) 2122275970Scy printf ("> Was removing cycles, ExpectedRunningDifference <= SecondsRunningDifference, can stop it now.\n"); 2123275970Scy 2124275970Scy AddCycle = FALSE; 2125275970Scy RemoveCycle = FALSE; 2126275970Scy } 2127275970Scy else 2128275970Scy { 2129275970Scy if (Debug) 2130275970Scy printf ("> Was removing cycles, not done yet.\n"); 2131275970Scy } 2132275970Scy } 2133275970Scy else 2134275970Scy { 2135275970Scy if ((ExpectedRunningDifference+RUNNING_STABILITY_BAND > SecondsRunningDifference) 2136275970Scy && (ExpectedRunningDifference-RUNNING_STABILITY_BAND < SecondsRunningDifference)) 2137275970Scy { // All is well, within tolerances. 2138275970Scy if (Debug) 2139275970Scy printf ("> All is well, within tolerances.\n"); 2140275970Scy } 2141275970Scy else 2142275970Scy { // Oops, outside tolerances. Else clause of "if ((ExpectedRunningDifference...SecondsRunningDifference)" 2143275970Scy if (ExpectedRunningDifference > SecondsRunningDifference) 2144275970Scy { 2145275970Scy if (Debug) 2146275970Scy printf ("> ExpectedRunningDifference > SecondsRunningDifference, running behind real time.\n"); 2147275970Scy 2148275970Scy // Behind real time, have to add a cycle to slow down and get back in sync. 2149275970Scy AddCycle = FALSE; 2150275970Scy RemoveCycle = TRUE; 2151275970Scy } 2152275970Scy else 2153275970Scy { // Else clause of "if (ExpectedRunningDifference < SecondsRunningDifference)" 2154275970Scy if (ExpectedRunningDifference < SecondsRunningDifference) 2155275970Scy { 2156275970Scy if (Debug) 2157275970Scy printf ("> ExpectedRunningDifference < SecondsRunningDifference, running ahead of real time.\n"); 2158275970Scy 2159275970Scy // Ahead of real time, have to remove a cycle to speed up and get back in sync. 2160275970Scy AddCycle = TRUE; 2161275970Scy RemoveCycle = FALSE; 2162275970Scy } 2163275970Scy else 2164275970Scy { 2165275970Scy if (Debug) 2166275970Scy printf ("> Oops, outside tolerances, but doesn't fit the profiles, how can this be?\n"); 2167275970Scy } 2168275970Scy } // End of else clause of "if (ExpectedRunningDifference > SecondsRunningDifference)" 2169275970Scy } // End of else clause of "if ((ExpectedRunningDifference...SecondsRunningDifference)" 2170275970Scy } // End of else clause of "if (RemoveCycle)". 2171275970Scy } // End of else clause of "if (AddCycle)". 2172275970Scy } // End of else clause for "if (StabilityCount < MINIMUM_STABILITY_COUNT))" 2173275970Scy } // End of true clause for "if ((SecondsRunningSimulationTime > RUN_BEFORE_STABILITY_CHECK)" 2174275970Scy } // End of true clause for "if (NowRealTime >= BaseRealTime)" 2175275970Scy else 2176275970Scy { 2177275970Scy if (Debug) 2178275970Scy printf ("> Hmm, time going backwards?\n"); 2179275970Scy } 2180275970Scy } // End of true clause for "if (EnableRateCorrection)" 2181275970Scy 2182275970Scy fflush (stdout); 2183275970Scy } 2184275970Scy 2185275970Scy 2186275970Scyprintf ("\n\n>> Completed %d seconds, exiting...\n\n", SecondsToSend); 2187275970Scyreturn (0); 2188275970Scy} 2189275970Scy 2190275970Scy 2191275970Scy/* 2192275970Scy * Generate WWV/H 0 or 1 data pulse. 2193275970Scy */ 2194275970Scyvoid WWV_Second( 2195275970Scy int code, /* DATA0, DATA1, PI */ 2196275970Scy int Rate /* <0 -> do a short second, 0 -> normal second, >0 -> long second */ 2197275970Scy ) 2198275970Scy{ 2199275970Scy /* 2200275970Scy * The WWV data pulse begins with 5 ms of 1000 Hz follwed by a 2201275970Scy * guard time of 25 ms. The data pulse is 170, 570 or 770 ms at 2202275970Scy * 100 Hz corresponding to 0, 1 or position indicator (PI), 2203275970Scy * respectively. Note the 100-Hz data pulses are transmitted 6 2204275970Scy * dB below the 1000-Hz sync pulses. Originally the data pulses 2205275970Scy * were transmited 10 dB below the sync pulses, but the station 2206275970Scy * engineers increased that to 6 dB because the Heath GC-1000 2207275970Scy * WWV/H radio clock worked much better. 2208275970Scy */ 2209275970Scy peep(5, tone, HIGH); /* send seconds tick */ 2210275970Scy peep(25, tone, OFF); 2211275970Scy peep(code - 30, 100, LOW); /* send data */ 2212275970Scy 2213275970Scy /* The quiet time is shortened or lengthened to get us back on time */ 2214275970Scy if (Rate < 0) 2215275970Scy { 2216275970Scy peep( 990 - code, 100, OFF); 2217275970Scy 2218275970Scy TotalCyclesRemoved += 10; 2219275970Scy 2220275970Scy if (Debug) 2221275970Scy printf ("\n* Shorter Second: "); 2222275970Scy } 2223275970Scy else 2224275970Scy { 2225275970Scy if (Rate > 0) 2226275970Scy { 2227275970Scy peep(1010 - code, 100, OFF); 2228275970Scy 2229275970Scy TotalCyclesAdded += 10; 2230275970Scy 2231275970Scy if (Debug) 2232275970Scy printf ("\n* Longer Second: "); 2233275970Scy } 2234275970Scy else 2235275970Scy peep(1000 - code, 100, OFF); 2236275970Scy } 2237275970Scy} 2238275970Scy 2239275970Scy/* 2240275970Scy * Generate WWV/H 0 or 1 data pulse, with no tick, for 29th and 59th seconds 2241275970Scy */ 2242275970Scyvoid WWV_SecondNoTick( 2243275970Scy int code, /* DATA0, DATA1, PI */ 2244275970Scy int Rate /* <0 -> do a short second, 0 -> normal second, >0 -> long second */ 2245275970Scy ) 2246275970Scy{ 2247275970Scy /* 2248275970Scy * The WWV data pulse begins with 5 ms of 1000 Hz follwed by a 2249275970Scy * guard time of 25 ms. The data pulse is 170, 570 or 770 ms at 2250275970Scy * 100 Hz corresponding to 0, 1 or position indicator (PI), 2251275970Scy * respectively. Note the 100-Hz data pulses are transmitted 6 2252275970Scy * dB below the 1000-Hz sync pulses. Originally the data pulses 2253275970Scy * were transmited 10 dB below the sync pulses, but the station 2254275970Scy * engineers increased that to 6 dB because the Heath GC-1000 2255275970Scy * WWV/H radio clock worked much better. 2256275970Scy */ 2257275970Scy peep(30, tone, OFF); /* send seconds non-tick */ 2258275970Scy peep(code - 30, 100, LOW); /* send data */ 2259275970Scy 2260275970Scy /* The quiet time is shortened or lengthened to get us back on time */ 2261275970Scy if (Rate < 0) 2262275970Scy { 2263275970Scy peep( 990 - code, 100, OFF); 2264275970Scy 2265275970Scy TotalCyclesRemoved += 10; 2266275970Scy 2267275970Scy if (Debug) 2268275970Scy printf ("\n* Shorter Second: "); 2269275970Scy } 2270275970Scy else 2271275970Scy { 2272275970Scy if (Rate > 0) 2273275970Scy { 2274275970Scy peep(1010 - code, 100, OFF); 2275275970Scy 2276275970Scy TotalCyclesAdded += 10; 2277275970Scy 2278275970Scy if (Debug) 2279275970Scy printf ("\n* Longer Second: "); 2280275970Scy } 2281275970Scy else 2282275970Scy peep(1000 - code, 100, OFF); 2283275970Scy } 2284275970Scy} 2285275970Scy 2286275970Scy/* 2287275970Scy * Generate cycles of 100 Hz or any multiple of 100 Hz. 2288275970Scy */ 2289275970Scyvoid peep( 2290275970Scy int pulse, /* pulse length (ms) */ 2291275970Scy int freq, /* frequency (Hz) */ 2292275970Scy int amp /* amplitude */ 2293275970Scy ) 2294275970Scy{ 2295275970Scy int increm; /* phase increment */ 2296275970Scy int i, j; 2297275970Scy 2298275970Scy if (amp == OFF || freq == 0) 2299275970Scy increm = 10; 2300275970Scy else 2301275970Scy increm = freq / 100; 2302275970Scy j = 0; 2303275970Scy for (i = 0 ; i < pulse * 8; i++) { 2304275970Scy switch (amp) { 2305275970Scy 2306275970Scy case HIGH: 2307275970Scy buffer[bufcnt++] = ~c6000[j]; 2308275970Scy break; 2309275970Scy 2310275970Scy case LOW: 2311275970Scy buffer[bufcnt++] = ~c3000[j]; 2312275970Scy break; 2313275970Scy 2314275970Scy default: 2315275970Scy buffer[bufcnt++] = ~0; 2316275970Scy } 2317275970Scy if (bufcnt >= BUFLNG) { 2318275970Scy write(fd, buffer, BUFLNG); 2319275970Scy bufcnt = 0; 2320275970Scy } 2321275970Scy j = (j + increm) % 80; 2322275970Scy } 2323275970Scy} 2324275970Scy 2325275970Scy 2326275970Scy/* 2327275970Scy * Generate unmodulated from similar tables. 2328275970Scy */ 2329275970Scyvoid poop( 2330275970Scy int pulse, /* pulse length (ms) */ 2331275970Scy int freq, /* frequency (Hz) */ 2332275970Scy int amp, /* amplitude */ 2333275970Scy int inverted /* is upside down */ 2334275970Scy ) 2335275970Scy{ 2336275970Scy int increm; /* phase increment */ 2337275970Scy int i, j; 2338275970Scy 2339275970Scy if (amp == OFF || freq == 0) 2340275970Scy increm = 10; 2341275970Scy else 2342275970Scy increm = freq / 100; 2343275970Scy j = 0; 2344275970Scy for (i = 0 ; i < pulse * 8; i++) { 2345275970Scy switch (amp) { 2346275970Scy 2347275970Scy case HIGH: 2348275970Scy if (inverted) 2349275970Scy buffer[bufcnt++] = ~u3000[j]; 2350275970Scy else 2351275970Scy buffer[bufcnt++] = ~u6000[j]; 2352275970Scy break; 2353275970Scy 2354275970Scy case LOW: 2355275970Scy if (inverted) 2356275970Scy buffer[bufcnt++] = ~u6000[j]; 2357275970Scy else 2358275970Scy buffer[bufcnt++] = ~u3000[j]; 2359275970Scy break; 2360275970Scy 2361275970Scy default: 2362275970Scy buffer[bufcnt++] = ~0; 2363275970Scy } 2364275970Scy if (bufcnt >= BUFLNG) { 2365275970Scy write(fd, buffer, BUFLNG); 2366275970Scy bufcnt = 0; 2367275970Scy } 2368275970Scy j = (j + increm) % 80; 2369275970Scy } 2370275970Scy} 2371275970Scy 2372275970Scy/* 2373275970Scy * Delay for initial phasing 2374275970Scy */ 2375275970Scyvoid delay ( 2376275970Scy int Delay /* delay in samples */ 2377275970Scy ) 2378275970Scy{ 2379275970Scy int samples; /* samples remaining */ 2380275970Scy 2381275970Scy samples = Delay; 2382275970Scy memset(buffer, 0, BUFLNG); 2383275970Scy while (samples >= BUFLNG) { 2384275970Scy write(fd, buffer, BUFLNG); 2385275970Scy samples -= BUFLNG; 2386275970Scy } 2387275970Scy write(fd, buffer, samples); 2388275970Scy} 2389275970Scy 2390275970Scy 2391275970Scy/* Calc day of year from year month & day */ 2392275970Scy/* Year - 0 means 2000, 100 means 2100. */ 2393275970Scy/* Month - 1 means January, 12 means December. */ 2394275970Scy/* DayOfMonth - 1 is first day of month */ 2395275970Scyint 2396275970ScyConvertMonthDayToDayOfYear (int YearValue, int MonthValue, int DayOfMonthValue) 2397275970Scy { 2398275970Scy int ReturnValue; 2399275970Scy int LeapYear; 2400275970Scy int MonthCounter; 2401275970Scy 2402275970Scy /* Array of days in a month. Note that here January is zero. */ 2403275970Scy /* NB: have to add 1 to days in February in a leap year! */ 2404275970Scy int DaysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 2405275970Scy 2406275970Scy 2407275970Scy LeapYear = FALSE; 2408275970Scy if ((YearValue % 4) == 0) 2409275970Scy { 2410275970Scy if ((YearValue % 100) == 0) 2411275970Scy { 2412275970Scy if ((YearValue % 400) == 0) 2413275970Scy { 2414275970Scy LeapYear = TRUE; 2415275970Scy } 2416275970Scy } 2417275970Scy else 2418275970Scy { 2419275970Scy LeapYear = TRUE; 2420275970Scy } 2421275970Scy } 2422275970Scy 2423275970Scy if (Debug) 2424275970Scy printf ("\nConvertMonthDayToDayOfYear(): Year %d %s a leap year.\n", YearValue+2000, LeapYear ? "is" : "is not"); 2425275970Scy 2426275970Scy /* Day of month given us starts in this algorithm. */ 2427275970Scy ReturnValue = DayOfMonthValue; 2428275970Scy 2429275970Scy /* Add in days in month for each month past January. */ 2430275970Scy for (MonthCounter=1; MonthCounter<MonthValue; MonthCounter++) 2431275970Scy { 2432275970Scy ReturnValue += DaysInMonth [ MonthCounter - 1 ]; 2433275970Scy } 2434275970Scy 2435275970Scy /* Add a day for leap years where we are past February. */ 2436275970Scy if ((LeapYear) && (MonthValue > 2)) 2437275970Scy { 2438275970Scy ReturnValue++; 2439275970Scy } 2440275970Scy 2441275970Scy if (Debug) 2442275970Scy printf ("\nConvertMonthDayToDayOfYear(): %4.4d-%2.2d-%2.2d represents day %3d of year.\n", 2443275970Scy YearValue+2000, MonthValue, DayOfMonthValue, ReturnValue); 2444275970Scy 2445275970Scy return (ReturnValue); 2446275970Scy } 2447275970Scy 2448275970Scy 2449275970Scyvoid 2450275970ScyHelp ( void ) 2451275970Scy { 2452275970Scy printf ("\n\nTime Code Generation - IRIG-B or WWV, v%d.%d, %s dmw", VERSION, ISSUE, ISSUE_DATE); 2453275970Scy printf ("\n\nRCS Info:"); 2454275970Scy printf ( "\n $Header: /home/dmw/src/IRIG_generation/ntp-4.2.2p3/util/RCS/tg.c,v 1.28 2007/02/12 23:57:45 dmw Exp $"); 2455275970Scy printf ("\n\nUsage: %s [option]*", CommandName); 2456275970Scy printf ("\n\nOptions: -a device_name Output audio device name (default /dev/audio)"); 2457275970Scy printf ( "\n -b yymmddhhmm Remove leap second at end of minute specified"); 2458275970Scy printf ( "\n -c seconds_to_send Number of seconds to send (default 0 = forever)"); 2459275970Scy printf ( "\n -d Start with IEEE 1344 DST active"); 2460275970Scy printf ( "\n -f format_type i = Modulated IRIG-B 1998 (no year coded)"); 2461275970Scy printf ( "\n 2 = Modulated IRIG-B 2002 (year coded)"); 2462275970Scy printf ( "\n 3 = Modulated IRIG-B w/IEEE 1344 (year & control funcs) (default)"); 2463275970Scy printf ( "\n 4 = Unmodulated IRIG-B w/IEEE 1344 (year & control funcs)"); 2464275970Scy printf ( "\n 5 = Inverted unmodulated IRIG-B w/IEEE 1344 (year & control funcs)"); 2465275970Scy printf ( "\n w = WWV(H)"); 2466275970Scy printf ( "\n -g yymmddhhmm Switch into/out of DST at beginning of minute specified"); 2467275970Scy printf ( "\n -i yymmddhhmm Insert leap second at end of minute specified"); 2468275970Scy printf ( "\n -j Disable time rate correction against system clock (default enabled)"); 2469275970Scy printf ( "\n -k nn Force rate correction for testing (+1 = add cycle, -1 = remove cycle)"); 2470275970Scy printf ( "\n -l time_offset Set offset of time sent to UTC as per computer, +/- float hours"); 2471275970Scy printf ( "\n -o time_offset Set IEEE 1344 time offset, +/-, to 0.5 hour (default 0)"); 2472275970Scy printf ( "\n -q quality_code_hex Set IEEE 1344 quality code (default 0)"); 2473275970Scy printf ( "\n -r sample_rate Audio sample rate (default 8000)"); 2474275970Scy printf ( "\n -s Set leap warning bit (WWV[H] only)"); 2475275970Scy printf ( "\n -t sync_frequency WWV(H) on-time pulse tone frequency (default 1200)"); 2476275970Scy printf ( "\n -u DUT1_offset Set WWV(H) DUT1 offset -7 to +7 (default 0)"); 2477275970Scy#ifndef HAVE_SYS_SOUNDCARD_H 2478275970Scy printf ( "\n -v initial_output_level Set initial output level (default %d, must be 0 to 255)", AUDIO_MAX_GAIN/8); 2479275970Scy#endif 2480275970Scy printf ( "\n -x Turn off verbose output (default on)"); 2481275970Scy printf ( "\n -y yymmddhhmmss Set initial date and time as specified (default system time)"); 2482275970Scy printf ("\n\nThis software licenced under the GPL, modifications performed 2006 & 2007 by Dean Weiten"); 2483275970Scy printf ( "\nContact: Dean Weiten, Norscan Instruments Ltd., Winnipeg, MB, Canada, ph (204)-233-9138, E-mail dmw@norscan.com"); 2484275970Scy printf ("\n\n"); 2485275970Scy } 2486275970Scy 2487275970Scy/* Reverse string order for nicer print. */ 2488275970Scyvoid 2489275970ScyReverseString(char *str) 2490275970Scy { 2491275970Scy int StringLength; 2492275970Scy int IndexCounter; 2493275970Scy int CentreOfString; 2494275970Scy char TemporaryCharacter; 2495275970Scy 2496275970Scy 2497275970Scy StringLength = strlen(str); 2498275970Scy CentreOfString = (StringLength/2)+1; 2499275970Scy for (IndexCounter = StringLength; IndexCounter >= CentreOfString; IndexCounter--) 2500275970Scy { 2501275970Scy TemporaryCharacter = str[IndexCounter-1]; 2502275970Scy str[IndexCounter-1] = str[StringLength-IndexCounter]; 2503275970Scy str[StringLength-IndexCounter] = TemporaryCharacter; 2504275970Scy } 2505275970Scy } 2506275970Scy 2507