1/* $NetBSD: tg2.c,v 1.7 2020/05/25 20:47:37 christos Exp $ */ 2 3/* 4 * tg.c generate WWV or IRIG signals for test 5 */ 6/* 7 * This program can generate audio signals that simulate the WWV/H 8 * broadcast timecode. Alternatively, it can generate the IRIG-B 9 * timecode commonly used to synchronize laboratory equipment. It is 10 * intended to test the WWV/H driver (refclock_wwv.c) and the IRIG 11 * driver (refclock_irig.c) in the NTP driver collection. 12 * 13 * Besides testing the drivers themselves, this program can be used to 14 * synchronize remote machines over audio transmission lines or program 15 * feeds. The program reads the time on the local machine and sets the 16 * initial epoch of the signal generator within one millisecond. 17 * Alernatively, the initial epoch can be set to an arbitrary time. This 18 * is useful when searching for bugs and testing for correct response to 19 * a leap second in UTC. Note however, the ultimate accuracy is limited 20 * by the intrinsic frequency error of the codec sample clock, which can 21 # reach well over 100 PPM. 22 * 23 * The default is to route generated signals to the line output 24 * jack; the s option on the command line routes these signals to the 25 * internal speaker as well. The v option controls the speaker volume 26 * over the range 0-255. The signal generator by default uses WWV 27 * format; the h option switches to WWVH format and the i option 28 * switches to IRIG-B format. 29 * 30 * Once started the program runs continuously. The default initial epoch 31 * for the signal generator is read from the computer system clock when 32 * the program starts. The y option specifies an alternate epoch using a 33 * string yydddhhmmss, where yy is the year of century, ddd the day of 34 * year, hh the hour of day and mm the minute of hour. For instance, 35 * 1946Z on 1 January 2006 is 060011946. The l option lights the leap 36 * warning bit in the WWV/H timecode, so is handy to check for correct 37 * behavior at the next leap second epoch. The remaining options are 38 * specified below under the Parse Options heading. Most of these are 39 * for testing. 40 * 41 * During operation the program displays the WWV/H timecode (9 digits) 42 * or IRIG timecode (20 digits) as each new string is constructed. The 43 * display is followed by the BCD binary bits as transmitted. Note that 44 * the transmissionorder is low-order first as the frame is processed 45 * left to right. For WWV/H The leap warning L preceeds the first bit. 46 * For IRIG the on-time marker M preceeds the first (units) bit, so its 47 * code is delayed one bit and the next digit (tens) needs only three 48 * bits. 49 * 50 * The program has been tested with the Sun Blade 1500 running Solaris 51 * 10, but not yet with other machines. It uses no special features and 52 * should be readily portable to other hardware and operating systems. 53 * 54 * Log: tg.c,v 55 * Revision 1.28 2007/02/12 23:57:45 dmw 56 * v0.23 2007-02-12 dmw: 57 * - Changed statistics to include calculated error 58 * of frequency, based on number of added or removed 59 * cycles over time. 60 * 61 * Revision 1.27 2007/02/09 02:28:59 dmw 62 * v0.22 2007-02-08 dmw: 63 * - Changed default for rate correction to "enabled", "-j" switch now disables. 64 * - Adjusted help message accordingly. 65 * - Added "2007" to modifications note at end of help message. 66 * 67 * Revision 1.26 2007/02/08 03:36:17 dmw 68 * v0.21 2007-02-07 dmw: 69 * - adjusted strings for shorten and lengthen to make 70 * fit on smaller screen. 71 * 72 * Revision 1.25 2007/02/01 06:08:09 dmw 73 * v0.20 2007-02-01 dmw: 74 * - Added periodic display of running time along with legend on IRIG-B, allows tracking how 75 * close IRIG output is to actual clock time. 76 * 77 * Revision 1.24 2007/01/31 19:24:11 dmw 78 * v0.19 2007-01-31 dmw: 79 * - Added tracking of how many seconds have been adjusted, 80 * how many cycles added (actually in milliseconds), how 81 * many cycles removed, print periodically if verbose is 82 * active. 83 * - Corrected lack of lengthen or shorten of minute & hour 84 * pulses for WWV format. 85 * 86 * Revision 1.23 2007/01/13 07:09:12 dmw 87 * v0.18 2007-01-13 dmw: 88 * - added -k option, which allows force of long or short 89 * cycles, to test against IRIG-B decoder. 90 * 91 * Revision 1.22 2007/01/08 16:27:23 dmw 92 * v0.17 2007-01-08 dmw: 93 * - Changed -j option to **enable** rate correction, not disable. 94 * 95 * Revision 1.21 2007/01/08 06:22:36 dmw 96 * v0.17 2007-01-08 dmw: 97 * - Run stability check versus ongoing system clock (assume NTP correction) 98 * and adjust time code rate to try to correct, if gets too far out of sync. 99 * Disable this algorithm with -j option. 100 * 101 * Revision 1.20 2006/12/19 04:59:04 dmw 102 * v0.16 2006-12-18 dmw 103 * - Corrected print of setting of output frequency, always 104 * showed 8000 samples/sec, now as specified on command line. 105 * - Modified to reflect new employer Norscan. 106 * 107 * Revision 1.19 2006/12/19 03:45:38 dmw 108 * v0.15 2006-12-18 dmw: 109 * - Added count of number of seconds to output then exit, 110 * default zero for forever. 111 * 112 * Revision 1.18 2006/12/18 05:43:36 dmw 113 * v0.14 2006-12-17 dmw: 114 * - Corrected WWV(H) signal to leave "tick" sound off of 29th and 59th second of minute. 115 * - Adjusted verbose output format for WWV(H). 116 * 117 * Revision 1.17 2006/12/18 02:31:33 dmw 118 * v0.13 2006-12-17 dmw: 119 * - Put SPARC code back in, hopefully will work, but I don't have 120 * a SPARC to try it on... 121 * - Reworked Verbose mode, different flag to initiate (x not v) 122 * and actually implement turn off of verbosity when this flag used. 123 * - Re-claimed v flag for output level. 124 * - Note that you must define OSS_MODS to get OSS to compile, 125 * otherwise will expect to compile using old SPARC options, as 126 * it used to be. 127 * 128 * Revision 1.16 2006/10/26 19:08:43 dmw 129 * v0.12 2006-10-26 dmw: 130 * - Reversed output binary dump for IRIG, makes it easier to read the numbers. 131 * 132 * Revision 1.15 2006/10/24 15:57:09 dmw 133 * v0.11 2006-10-24 dmw: 134 * - another tweak. 135 * 136 * Revision 1.14 2006/10/24 15:55:53 dmw 137 * v0.11 2006-10-24 dmw: 138 * - Curses a fix to the fix to the fix of the usaeg. 139 * 140 * Revision 1.13 2006/10/24 15:53:25 dmw 141 * v0.11 (still) 2006-10-24 dmw: 142 * - Messed with usage message that's all. 143 * 144 * Revision 1.12 2006/10/24 15:50:05 dmw 145 * v0.11 2006-10-24 dmw: 146 * - oops, needed to note "hours" in usage of that offset. 147 * 148 * Revision 1.11 2006/10/24 15:49:09 dmw 149 * v0.11 2006-10-24 dmw: 150 * - Added ability to offset actual time sent, from the UTC time 151 * as per the computer. 152 * 153 * Revision 1.10 2006/10/24 03:25:55 dmw 154 * v0.10 2006-10-23 dmw: 155 * - Corrected polarity of correction of offset when going into or out of DST. 156 * - Ensure that zero offset is always positive (pet peeve). 157 * 158 * Revision 1.9 2006/10/24 00:00:35 dmw 159 * v0.9 2006-10-23 dmw: 160 * - Shift time offset when DST in or out. 161 * 162 * Revision 1.8 2006/10/23 23:49:28 dmw 163 * v0.8 2006-10-23 dmw: 164 * - made offset of zero default positive. 165 * 166 * Revision 1.7 2006/10/23 23:44:13 dmw 167 * v0.7 2006-10-23 dmw: 168 * - Added unmodulated and inverted unmodulated output. 169 * 170 * Revision 1.6 2006/10/23 18:10:37 dmw 171 * v0.6 2006-10-23 dmw: 172 * - Cleaned up usage message. 173 * - Require at least one option, or prints usage message and exits. 174 * 175 * Revision 1.5 2006/10/23 16:58:10 dmw 176 * v0.5 2006-10-23 dmw: 177 * - Finally added a usage message. 178 * - Added leap second pending and DST change pending into IEEE 1344. 179 * - Default code type is now IRIG-B with IEEE 1344. 180 * 181 * Revision 1.4 2006/10/23 03:27:25 dmw 182 * v0.4 2006-10-22 dmw: 183 * - Added leap second addition and deletion. 184 * - Added DST changing forward and backward. 185 * - Changed date specification to more conventional year, month, and day of month 186 * (rather than day of year). 187 * 188 * Revision 1.3 2006/10/22 21:04:12 dmw 189 * v0.2 2006-10-22 dmw: 190 * - Corrected format of legend line. 191 * 192 * Revision 1.2 2006/10/22 21:01:07 dmw 193 * v0.1 2006-10-22 dmw: 194 * - Added some more verbose output (as is my style) 195 * - Corrected frame format - there were markers in the 196 * middle of frames, now correctly as "zero" bits. 197 * - Added header line to show fields of output. 198 * - Added straight binary seconds, were not implemented 199 * before. 200 * - Added IEEE 1344 with parity. 201 * 202 * 203 */ 204#include <stdio.h> 205#include <stdlib.h> 206#include <time.h> 207 208#ifdef HAVE_CONFIG_H 209#include "config.h" 210#undef VERSION /* avoid conflict below */ 211#endif 212 213#ifdef HAVE_SYS_SOUNDCARD_H 214#include <sys/soundcard.h> 215#else 216# ifdef HAVE_SYS_AUDIOIO_H 217# include <sys/audioio.h> 218# else 219# include <sys/audio.h> 220# endif 221#endif 222 223#include "ntp_stdlib.h" /* for strlcat(), strlcpy() */ 224 225#include <math.h> 226#include <errno.h> 227#include <sys/types.h> 228#include <sys/stat.h> 229#include <fcntl.h> 230#include <string.h> 231#include <unistd.h> 232#include <ctype.h> 233#include <sys/ioctl.h> 234#include <sys/time.h> 235 236#define VERSION (0) 237#define ISSUE (23) 238#define ISSUE_DATE "2007-02-12" 239 240#define SECOND (8000) /* one second of 125-us samples */ 241#define BUFLNG (400) /* buffer size */ 242#define DEVICE "/dev/audio" /* default audio device */ 243#define WWV (0) /* WWV encoder */ 244#define IRIG (1) /* IRIG-B encoder */ 245#define OFF (0) /* zero amplitude */ 246#define LOW (1) /* low amplitude */ 247#define HIGH (2) /* high amplitude */ 248#define DATA0 (200) /* WWV/H 0 pulse */ 249#define DATA1 (500) /* WWV/H 1 pulse */ 250#define PI (800) /* WWV/H PI pulse */ 251#define M2 (2) /* IRIG 0 pulse */ 252#define M5 (5) /* IRIG 1 pulse */ 253#define M8 (8) /* IRIG PI pulse */ 254 255#define NUL (0) 256 257#define SECONDS_PER_MINUTE (60) 258#define SECONDS_PER_HOUR (3600) 259 260#define OUTPUT_DATA_STRING_LENGTH (200) 261 262/* Attempt at unmodulated - "high" */ 263int u6000[] = { 264 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, /* 0- 9 */ 265 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, /* 10-19 */ 266 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, /* 20-29 */ 267 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, /* 30-39 */ 268 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, /* 40-49 */ 269 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, /* 50-59 */ 270 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, /* 60-69 */ 271 247, 247, 247, 247, 247, 247, 247, 247, 247, 247}; /* 70-79 */ 272 273/* Attempt at unmodulated - "low" */ 274int u3000[] = { 275 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, /* 0- 9 */ 276 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, /* 10-19 */ 277 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, /* 20-29 */ 278 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, /* 30-39 */ 279 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, /* 40-49 */ 280 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, /* 50-59 */ 281 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, /* 60-69 */ 282 119, 119, 119, 119, 119, 119, 119, 119, 119, 119}; /* 70-79 */ 283 284/* 285 * Companded sine table amplitude 3000 units 286 */ 287int c3000[] = {1, 48, 63, 70, 78, 82, 85, 89, 92, 94, /* 0-9 */ 288 96, 98, 99, 100, 101, 101, 102, 103, 103, 103, /* 10-19 */ 289 103, 103, 103, 103, 102, 101, 101, 100, 99, 98, /* 20-29 */ 290 96, 94, 92, 89, 85, 82, 78, 70, 63, 48, /* 30-39 */ 291 129, 176, 191, 198, 206, 210, 213, 217, 220, 222, /* 40-49 */ 292 224, 226, 227, 228, 229, 229, 230, 231, 231, 231, /* 50-59 */ 293 231, 231, 231, 231, 230, 229, 229, 228, 227, 226, /* 60-69 */ 294 224, 222, 220, 217, 213, 210, 206, 198, 191, 176}; /* 70-79 */ 295/* 296 * Companded sine table amplitude 6000 units 297 */ 298int c6000[] = {1, 63, 78, 86, 93, 98, 101, 104, 107, 110, /* 0-9 */ 299 112, 113, 115, 116, 117, 117, 118, 118, 119, 119, /* 10-19 */ 300 119, 119, 119, 118, 118, 117, 117, 116, 115, 113, /* 20-29 */ 301 112, 110, 107, 104, 101, 98, 93, 86, 78, 63, /* 30-39 */ 302 129, 191, 206, 214, 221, 226, 229, 232, 235, 238, /* 40-49 */ 303 240, 241, 243, 244, 245, 245, 246, 246, 247, 247, /* 50-59 */ 304 247, 247, 247, 246, 246, 245, 245, 244, 243, 241, /* 60-69 */ 305 240, 238, 235, 232, 229, 226, 221, 214, 206, 191}; /* 70-79 */ 306 307/* 308 * Decoder operations at the end of each second are driven by a state 309 * machine. The transition matrix consists of a dispatch table indexed 310 * by second number. Each entry in the table contains a case switch 311 * number and argument. 312 */ 313struct progx { 314 int sw; /* case switch number */ 315 int arg; /* argument */ 316}; 317 318/* 319 * Case switch numbers 320 */ 321#define DATA (0) /* send data (0, 1, PI) */ 322#define COEF (1) /* send BCD bit */ 323#define DEC (2) /* decrement to next digit and send PI */ 324#define MIN (3) /* minute pulse */ 325#define LEAP (4) /* leap warning */ 326#define DUT1 (5) /* DUT1 bits */ 327#define DST1 (6) /* DST1 bit */ 328#define DST2 (7) /* DST2 bit */ 329#define DECZ (8) /* decrement to next digit and send zero */ 330#define DECC (9) /* decrement to next digit and send bit */ 331#define NODEC (10) /* no decerement to next digit, send PI */ 332#define DECX (11) /* decrement to next digit, send PI, but no tick */ 333#define DATAX (12) /* send data (0, 1, PI), but no tick */ 334 335/* 336 * WWV/H format (100-Hz, 9 digits, 1 m frame) 337 */ 338struct progx progx[] = { 339 {MIN, 800}, /* 0 minute sync pulse */ 340 {DATA, DATA0}, /* 1 */ 341 {DST2, 0}, /* 2 DST2 */ 342 {LEAP, 0}, /* 3 leap warning */ 343 {COEF, 1}, /* 4 1 year units */ 344 {COEF, 2}, /* 5 2 */ 345 {COEF, 4}, /* 6 4 */ 346 {COEF, 8}, /* 7 8 */ 347 {DEC, DATA0}, /* 8 */ 348 {DATA, PI}, /* 9 p1 */ 349 {COEF, 1}, /* 10 1 minute units */ 350 {COEF, 2}, /* 11 2 */ 351 {COEF, 4}, /* 12 4 */ 352 {COEF, 8}, /* 13 8 */ 353 {DEC, DATA0}, /* 14 */ 354 {COEF, 1}, /* 15 10 minute tens */ 355 {COEF, 2}, /* 16 20 */ 356 {COEF, 4}, /* 17 40 */ 357 {COEF, 8}, /* 18 80 (not used) */ 358 {DEC, PI}, /* 19 p2 */ 359 {COEF, 1}, /* 20 1 hour units */ 360 {COEF, 2}, /* 21 2 */ 361 {COEF, 4}, /* 22 4 */ 362 {COEF, 8}, /* 23 8 */ 363 {DEC, DATA0}, /* 24 */ 364 {COEF, 1}, /* 25 10 hour tens */ 365 {COEF, 2}, /* 26 20 */ 366 {COEF, 4}, /* 27 40 (not used) */ 367 {COEF, 8}, /* 28 80 (not used) */ 368 {DECX, PI}, /* 29 p3 */ 369 {COEF, 1}, /* 30 1 day units */ 370 {COEF, 2}, /* 31 2 */ 371 {COEF, 4}, /* 32 4 */ 372 {COEF, 8}, /* 33 8 */ 373 {DEC, DATA0}, /* 34 not used */ 374 {COEF, 1}, /* 35 10 day tens */ 375 {COEF, 2}, /* 36 20 */ 376 {COEF, 4}, /* 37 40 */ 377 {COEF, 8}, /* 38 80 */ 378 {DEC, PI}, /* 39 p4 */ 379 {COEF, 1}, /* 40 100 day hundreds */ 380 {COEF, 2}, /* 41 200 */ 381 {COEF, 4}, /* 42 400 (not used) */ 382 {COEF, 8}, /* 43 800 (not used) */ 383 {DEC, DATA0}, /* 44 */ 384 {DATA, DATA0}, /* 45 */ 385 {DATA, DATA0}, /* 46 */ 386 {DATA, DATA0}, /* 47 */ 387 {DATA, DATA0}, /* 48 */ 388 {DATA, PI}, /* 49 p5 */ 389 {DUT1, 8}, /* 50 DUT1 sign */ 390 {COEF, 1}, /* 51 10 year tens */ 391 {COEF, 2}, /* 52 20 */ 392 {COEF, 4}, /* 53 40 */ 393 {COEF, 8}, /* 54 80 */ 394 {DST1, 0}, /* 55 DST1 */ 395 {DUT1, 1}, /* 56 0.1 DUT1 fraction */ 396 {DUT1, 2}, /* 57 0.2 */ 397 {DUT1, 4}, /* 58 0.4 */ 398 {DATAX, PI}, /* 59 p6 */ 399 {DATA, DATA0}, /* 60 leap */ 400}; 401 402/* 403 * IRIG format frames (1000 Hz, 1 second for 10 frames of data) 404 */ 405 406/* 407 * IRIG format frame 10 - MS straight binary seconds 408 */ 409struct progx progu[] = { 410 {COEF, 2}, /* 0 0x0 0200 seconds */ 411 {COEF, 4}, /* 1 0x0 0400 */ 412 {COEF, 8}, /* 2 0x0 0800 */ 413 {DECC, 1}, /* 3 0x0 1000 */ 414 {COEF, 2}, /* 4 0x0 2000 */ 415 {COEF, 4}, /* 6 0x0 4000 */ 416 {COEF, 8}, /* 7 0x0 8000 */ 417 {DECC, 1}, /* 8 0x1 0000 */ 418 {COEF, 2}, /* 9 0x2 0000 - but only 86,401 / 0x1 5181 seconds in a day, so always zero */ 419 {NODEC, M8}, /* 9 PI */ 420}; 421 422/* 423 * IRIG format frame 8 - MS control functions 424 */ 425struct progx progv[] = { 426 {COEF, 2}, /* 0 CF # 19 */ 427 {COEF, 4}, /* 1 CF # 20 */ 428 {COEF, 8}, /* 2 CF # 21 */ 429 {DECC, 1}, /* 3 CF # 22 */ 430 {COEF, 2}, /* 4 CF # 23 */ 431 {COEF, 4}, /* 6 CF # 24 */ 432 {COEF, 8}, /* 7 CF # 25 */ 433 {DECC, 1}, /* 8 CF # 26 */ 434 {COEF, 2}, /* 9 CF # 27 */ 435 {DEC, M8}, /* 10 PI */ 436}; 437 438/* 439 * IRIG format frames 7 & 9 - LS control functions & LS straight binary seconds 440 */ 441struct progx progw[] = { 442 {COEF, 1}, /* 0 CF # 10, 0x0 0001 seconds */ 443 {COEF, 2}, /* 1 CF # 11, 0x0 0002 */ 444 {COEF, 4}, /* 2 CF # 12, 0x0 0004 */ 445 {COEF, 8}, /* 3 CF # 13, 0x0 0008 */ 446 {DECC, 1}, /* 4 CF # 14, 0x0 0010 */ 447 {COEF, 2}, /* 6 CF # 15, 0x0 0020 */ 448 {COEF, 4}, /* 7 CF # 16, 0x0 0040 */ 449 {COEF, 8}, /* 8 CF # 17, 0x0 0080 */ 450 {DECC, 1}, /* 9 CF # 18, 0x0 0100 */ 451 {NODEC, M8}, /* 10 PI */ 452}; 453 454/* 455 * IRIG format frames 2 to 6 - minutes, hours, days, hundreds days, 2 digit years (also called control functions bits 1-9) 456 */ 457struct progx progy[] = { 458 {COEF, 1}, /* 0 1 units, CF # 1 */ 459 {COEF, 2}, /* 1 2 units, CF # 2 */ 460 {COEF, 4}, /* 2 4 units, CF # 3 */ 461 {COEF, 8}, /* 3 8 units, CF # 4 */ 462 {DECZ, M2}, /* 4 zero bit, CF # 5 / unused, default zero in years */ 463 {COEF, 1}, /* 5 10 tens, CF # 6 */ 464 {COEF, 2}, /* 6 20 tens, CF # 7*/ 465 {COEF, 4}, /* 7 40 tens, CF # 8*/ 466 {COEF, 8}, /* 8 80 tens, CF # 9*/ 467 {DEC, M8}, /* 9 PI */ 468}; 469 470/* 471 * IRIG format first frame, frame 1 - seconds 472 */ 473struct progx progz[] = { 474 {MIN, M8}, /* 0 PI (on-time marker for the second at zero cross of 1st cycle) */ 475 {COEF, 1}, /* 1 1 units */ 476 {COEF, 2}, /* 2 2 */ 477 {COEF, 4}, /* 3 4 */ 478 {COEF, 8}, /* 4 8 */ 479 {DECZ, M2}, /* 5 zero bit */ 480 {COEF, 1}, /* 6 10 tens */ 481 {COEF, 2}, /* 7 20 */ 482 {COEF, 4}, /* 8 40 */ 483 {DEC, M8}, /* 9 PI */ 484}; 485 486/* LeapState values. */ 487#define LEAPSTATE_NORMAL (0) 488#define LEAPSTATE_DELETING (1) 489#define LEAPSTATE_INSERTING (2) 490#define LEAPSTATE_ZERO_AFTER_INSERT (3) 491 492 493/* 494 * Forward declarations 495 */ 496void WWV_Second(int, int); /* send second */ 497void WWV_SecondNoTick(int, int); /* send second with no tick */ 498void digit(int); /* encode digit */ 499void peep(int, int, int); /* send cycles */ 500void poop(int, int, int, int); /* Generate unmodulated from similar tables */ 501void delay(int); /* delay samples */ 502int ConvertMonthDayToDayOfYear (int, int, int); /* Calc day of year from year month & day */ 503void Help (void); /* Usage message */ 504void ReverseString(char *); 505 506/* 507 * Extern declarations, don't know why not in headers 508 */ 509//float round ( float ); 510 511/* 512 * Global variables 513 */ 514char buffer[BUFLNG]; /* output buffer */ 515int bufcnt = 0; /* buffer counter */ 516int fd; /* audio codec file descriptor */ 517int tone = 1000; /* WWV sync frequency */ 518int HourTone = 1500; /* WWV hour on-time frequency */ 519int encode = IRIG; /* encoder select */ 520int leap = 0; /* leap indicator */ 521int DstFlag = 0; /* winter/summer time */ 522int dut1 = 0; /* DUT1 correction (sign, magnitude) */ 523int utc = 0; /* option epoch */ 524int IrigIncludeYear = FALSE; /* Whether to send year in first control functions area, between P5 and P6. */ 525int IrigIncludeIeee = FALSE; /* Whether to send IEEE 1344 control functions extensions between P6 and P8. */ 526int StraightBinarySeconds = 0; 527int ControlFunctions = 0; 528int Debug = FALSE; 529int Verbose = TRUE; 530char *CommandName; 531 532#ifndef HAVE_SYS_SOUNDCARD_H 533int level = AUDIO_MAX_GAIN / 8; /* output level */ 534int port = AUDIO_LINE_OUT; /* output port */ 535#endif 536 537int TotalSecondsCorrected = 0; 538int TotalCyclesAdded = 0; 539int TotalCyclesRemoved = 0; 540 541 542/* 543 * Main program 544 */ 545int 546main( 547 int argc, /* command line options */ 548 char **argv /* poiniter to list of tokens */ 549 ) 550{ 551#ifndef HAVE_SYS_SOUNDCARD_H 552 audio_info_t info; /* Sun audio structure */ 553 int rval; /* For IOCTL calls */ 554#endif 555 556 struct timeval TimeValue; /* System clock at startup */ 557 time_t SecondsPartOfTime; /* Sent to gmtime() for calculation of TimeStructure (can apply offset). */ 558 time_t BaseRealTime; /* Base realtime so can determine seconds since starting. */ 559 time_t NowRealTime; /* New realtime to can determine seconds as of now. */ 560 unsigned SecondsRunningRealTime; /* Difference between NowRealTime and BaseRealTime. */ 561 unsigned SecondsRunningSimulationTime; /* Time that the simulator has been running. */ 562 int SecondsRunningDifference; /* Difference between what real time says we have been running */ 563 /* and what simulator says we have been running - will slowly */ 564 /* change because of clock drift. */ 565 int ExpectedRunningDifference = 0; /* Stable value that we've obtained from check at initial start-up. */ 566 unsigned StabilityCount; /* Used to check stability of difference while starting */ 567#define RUN_BEFORE_STABILITY_CHECK (30) // Must run this many seconds before even checking stability. 568#define MINIMUM_STABILITY_COUNT (10) // Number of consecutive differences that need to be within initial stability band to say we are stable. 569#define INITIAL_STABILITY_BAND ( 2) // Determining initial stability for consecutive differences within +/- this value. 570#define RUNNING_STABILITY_BAND ( 5) // When running, stability is defined as difference within +/- this value. 571 572 struct tm *TimeStructure = NULL; /* Structure returned by gmtime */ 573 char device[200]; /* audio device */ 574 char code[200]; /* timecode */ 575 int temp; 576 int arg = 0; 577 int sw = 0; 578 int ptr = 0; 579 580 int Year; 581 int Month; 582 int DayOfMonth; 583 int Hour; 584 int Minute; 585 int Second = 0; 586 int DayOfYear; 587 588 int BitNumber; 589#ifdef HAVE_SYS_SOUNDCARD_H 590 int AudioFormat; 591 int MonoStereo; /* 0=mono, 1=stereo */ 592#define MONO (0) 593#define STEREO (1) 594 int SampleRate; 595 int SampleRateDifference; 596#endif 597 int SetSampleRate; 598 char FormatCharacter = '3'; /* Default is IRIG-B with IEEE 1344 extensions */ 599 char AsciiValue; 600 int HexValue; 601 int OldPtr = 0; 602 int FrameNumber = 0; 603 604 /* Time offset for IEEE 1344 indication. */ 605 float TimeOffset = 0.0; 606 int OffsetSignBit = 0; 607 int OffsetOnes = 0; 608 int OffsetHalf = 0; 609 610 int TimeQuality = 0; /* Time quality for IEEE 1344 indication. */ 611 char ParityString[200]; /* Partial output string, to calculate parity on. */ 612 int ParitySum = 0; 613 int ParityValue; 614 char *StringPointer; 615 616 /* Flags to indicate requested leap second addition or deletion by command line option. */ 617 /* Should be mutually exclusive - generally ensured by code which interprets command line option. */ 618 int InsertLeapSecond = FALSE; 619 int DeleteLeapSecond = FALSE; 620 621 /* Date and time of requested leap second addition or deletion. */ 622 int LeapYear = 0; 623 int LeapMonth = 0; 624 int LeapDayOfMonth = 0; 625 int LeapHour = 0; 626 int LeapMinute = 0; 627 int LeapDayOfYear = 0; 628 629 /* State flag for the insertion and deletion of leap seconds, esp. deletion, */ 630 /* where the logic gets a bit tricky. */ 631 int LeapState = LEAPSTATE_NORMAL; 632 633 /* Flags for indication of leap second pending and leap secod polarity in IEEE 1344 */ 634 int LeapSecondPending = FALSE; 635 int LeapSecondPolarity = FALSE; 636 637 /* Date and time of requested switch into or out of DST by command line option. */ 638 int DstSwitchYear = 0; 639 int DstSwitchMonth = 0; 640 int DstSwitchDayOfMonth = 0; 641 int DstSwitchHour = 0; 642 int DstSwitchMinute = 0; 643 int DstSwitchDayOfYear = 0; 644 645 /* Indicate when we have been asked to switch into or out of DST by command line option. */ 646 int DstSwitchFlag = FALSE; 647 648 /* To allow predict for DstPendingFlag in IEEE 1344 */ 649 int DstSwitchPendingYear = 0; /* Default value isn't valid, but I don't care. */ 650 int DstSwitchPendingDayOfYear = 0; 651 int DstSwitchPendingHour = 0; 652 int DstSwitchPendingMinute = 0; 653 654 /* /Flag for indication of a DST switch pending in IEEE 1344 */ 655 int DstPendingFlag = FALSE; 656 657 /* Attempt at unmodulated */ 658 int Unmodulated = FALSE; 659 int UnmodulatedInverted = FALSE; 660 661 /* Offset to actual time value sent. */ 662 float UseOffsetHoursFloat; 663 int UseOffsetSecondsInt = 0; 664 float UseOffsetSecondsFloat; 665 666 /* String to allow us to put out reversed data - so can read the binary numbers. */ 667 char OutputDataString[OUTPUT_DATA_STRING_LENGTH]; 668 669 /* Number of seconds to send before exiting. Default = 0 = forever. */ 670 int SecondsToSend = 0; 671 int CountOfSecondsSent = 0; /* Counter of seconds */ 672 673 /* Flags to indicate whether to add or remove a cycle for time adjustment. */ 674 int AddCycle = FALSE; // We are ahead, add cycle to slow down and get back in sync. 675 int RemoveCycle = FALSE; // We are behind, remove cycle to slow down and get back in sync. 676 int RateCorrection; // Aggregate flag for passing to subroutines. 677 int EnableRateCorrection = TRUE; 678 679 float RatioError; 680 681 682 CommandName = argv[0]; 683 684 if (argc < 1) 685 { 686 Help (); 687 exit (-1); 688 } 689 690 /* 691 * Parse options 692 */ 693 strlcpy(device, DEVICE, sizeof(device)); 694 Year = 0; 695 SetSampleRate = SECOND; 696 697#if HAVE_SYS_SOUNDCARD_H 698 while ((temp = getopt(argc, argv, "a:b:c:df:g:hHi:jk:l:o:q:r:stu:xy:z?")) != -1) { 699#else 700 while ((temp = getopt(argc, argv, "a:b:c:df:g:hHi:jk:l:o:q:r:stu:v:xy:z?")) != -1) { 701#endif 702 switch (temp) { 703 704 case 'a': /* specify audio device (/dev/audio) */ 705 strlcpy(device, optarg, sizeof(device)); 706 break; 707 708 case 'b': /* Remove (delete) a leap second at the end of the specified minute. */ 709 sscanf(optarg, "%2d%2d%2d%2d%2d", &LeapYear, &LeapMonth, &LeapDayOfMonth, 710 &LeapHour, &LeapMinute); 711 InsertLeapSecond = FALSE; 712 DeleteLeapSecond = TRUE; 713 break; 714 715 case 'c': /* specify number of seconds to send output for before exiting, 0 = forever */ 716 sscanf(optarg, "%d", &SecondsToSend); 717 break; 718 719 case 'd': /* set DST for summer (WWV/H only) / start with DST active (IRIG) */ 720 DstFlag++; 721 break; 722 723 case 'f': /* select format: i=IRIG-98 (default) 2=IRIG-2004 3-IRIG+IEEE-1344 w=WWV(H) */ 724 sscanf(optarg, "%c", &FormatCharacter); 725 break; 726 727 case 'g': /* Date and time to switch back into / out of DST active. */ 728 sscanf(optarg, "%2d%2d%2d%2d%2d", &DstSwitchYear, &DstSwitchMonth, &DstSwitchDayOfMonth, 729 &DstSwitchHour, &DstSwitchMinute); 730 DstSwitchFlag = TRUE; 731 break; 732 733 case 'h': 734 case 'H': 735 case '?': 736 Help (); 737 exit(-1); 738 break; 739 740 case 'i': /* Insert (add) a leap second at the end of the specified minute. */ 741 sscanf(optarg, "%2d%2d%2d%2d%2d", &LeapYear, &LeapMonth, &LeapDayOfMonth, 742 &LeapHour, &LeapMinute); 743 InsertLeapSecond = TRUE; 744 DeleteLeapSecond = FALSE; 745 break; 746 747 case 'j': 748 EnableRateCorrection = FALSE; 749 break; 750 751 case 'k': 752 sscanf (optarg, "%d", &RateCorrection); 753 EnableRateCorrection = FALSE; 754 if (RateCorrection < 0) 755 { 756 RemoveCycle = TRUE; 757 AddCycle = FALSE; 758 759 if (Verbose) 760 printf ("\n> Forcing rate correction removal of cycle...\n"); 761 } 762 else 763 { 764 if (RateCorrection > 0) 765 { 766 RemoveCycle = FALSE; 767 AddCycle = TRUE; 768 769 if (Verbose) 770 printf ("\n> Forcing rate correction addition of cycle...\n"); 771 } 772 } 773 break; 774 775 case 'l': /* use time offset from UTC */ 776 sscanf(optarg, "%f", &UseOffsetHoursFloat); 777 UseOffsetSecondsFloat = UseOffsetHoursFloat * (float) SECONDS_PER_HOUR; 778 UseOffsetSecondsInt = (int) (UseOffsetSecondsFloat + 0.5); 779 break; 780 781 case 'o': /* Set IEEE 1344 time offset in hours - positive or negative, to the half hour */ 782 sscanf(optarg, "%f", &TimeOffset); 783 if (TimeOffset >= -0.2) 784 { 785 OffsetSignBit = 0; 786 787 if (TimeOffset > 0) 788 { 789 OffsetOnes = TimeOffset; 790 791 if ( (TimeOffset - floor(TimeOffset)) >= 0.4) 792 OffsetHalf = 1; 793 else 794 OffsetHalf = 0; 795 } 796 else 797 { 798 OffsetOnes = 0; 799 OffsetHalf = 0; 800 } 801 } 802 else 803 { 804 OffsetSignBit = 1; 805 OffsetOnes = -TimeOffset; 806 807 if ( (ceil(TimeOffset) - TimeOffset) >= 0.4) 808 OffsetHalf = 1; 809 else 810 OffsetHalf = 0; 811 } 812 813 /*printf ("\nGot TimeOffset = %3.1f, OffsetSignBit = %d, OffsetOnes = %d, OffsetHalf = %d...\n", 814 TimeOffset, OffsetSignBit, OffsetOnes, OffsetHalf); 815 */ 816 break; 817 818 case 'q': /* Hex quality code 0 to 0x0F - 0 = maximum, 0x0F = no lock */ 819 sscanf(optarg, "%x", &TimeQuality); 820 TimeQuality &= 0x0F; 821 /*printf ("\nGot TimeQuality = 0x%1X...\n", TimeQuality); 822 */ 823 break; 824 825 case 'r': /* sample rate (nominally 8000, integer close to 8000 I hope) */ 826 sscanf(optarg, "%d", &SetSampleRate); 827 break; 828 829 case 's': /* set leap warning bit (WWV/H only) */ 830 leap++; 831 break; 832 833 case 't': /* select WWVH sync frequency */ 834 tone = 1200; 835 break; 836 837 case 'u': /* set DUT1 offset (-7 to +7) */ 838 sscanf(optarg, "%d", &dut1); 839 if (dut1 < 0) 840 dut1 = abs(dut1); 841 else 842 dut1 |= 0x8; 843 break; 844 845#ifndef HAVE_SYS_SOUNDCARD_H 846 case 'v': /* set output level (0-255) */ 847 sscanf(optarg, "%d", &level); 848 break; 849#endif 850 851 case 'x': /* Turn off verbose output. */ 852 Verbose = FALSE; 853 break; 854 855 case 'y': /* Set initial date and time */ 856 sscanf(optarg, "%2d%2d%2d%2d%2d%2d", &Year, &Month, &DayOfMonth, 857 &Hour, &Minute, &Second); 858 utc++; 859 break; 860 861 case 'z': /* Turn on Debug output (also turns on Verbose below) */ 862 Debug = TRUE; 863 break; 864 865 default: 866 printf("Invalid option \"%c\", aborting...\n", temp); 867 exit (-1); 868 break; 869 } 870 } 871 872 if (Debug) 873 Verbose = TRUE; 874 875 if (InsertLeapSecond || DeleteLeapSecond) 876 { 877 LeapDayOfYear = ConvertMonthDayToDayOfYear (LeapYear, LeapMonth, LeapDayOfMonth); 878 879 if (Debug) 880 { 881 printf ("\nHave request for leap second %s at year %4d day %3d at %2.2dh%2.2d....\n",\ 882 DeleteLeapSecond ? "DELETION" : (InsertLeapSecond ? "ADDITION" : "( error ! )" ), 883 LeapYear, LeapDayOfYear, LeapHour, LeapMinute); 884 } 885 } 886 887 if (DstSwitchFlag) 888 { 889 DstSwitchDayOfYear = ConvertMonthDayToDayOfYear (DstSwitchYear, DstSwitchMonth, DstSwitchDayOfMonth); 890 891 /* Figure out time of minute previous to DST switch, so can put up warning flag in IEEE 1344 */ 892 DstSwitchPendingYear = DstSwitchYear; 893 DstSwitchPendingDayOfYear = DstSwitchDayOfYear; 894 DstSwitchPendingHour = DstSwitchHour; 895 DstSwitchPendingMinute = DstSwitchMinute - 1; 896 if (DstSwitchPendingMinute < 0) 897 { 898 DstSwitchPendingMinute = 59; 899 DstSwitchPendingHour--; 900 if (DstSwitchPendingHour < 0) 901 { 902 DstSwitchPendingHour = 23; 903 DstSwitchPendingDayOfYear--; 904 if (DstSwitchPendingDayOfYear < 1) 905 { 906 DstSwitchPendingYear--; 907 } 908 } 909 } 910 911 if (Debug) 912 { 913 printf ("\nHave DST switch request for year %4d day %3d at %2.2dh%2.2d,", 914 DstSwitchYear, DstSwitchDayOfYear, DstSwitchHour, DstSwitchMinute); 915 printf ("\n so will have warning at year %4d day %3d at %2.2dh%2.2d.\n", 916 DstSwitchPendingYear, DstSwitchPendingDayOfYear, DstSwitchPendingHour, DstSwitchPendingMinute); 917 } 918 } 919 920 switch (tolower(FormatCharacter)) { 921 case 'i': 922 printf ("\nFormat is IRIG-1998 (no year coded)...\n\n"); 923 encode = IRIG; 924 IrigIncludeYear = FALSE; 925 IrigIncludeIeee = FALSE; 926 break; 927 928 case '2': 929 printf ("\nFormat is IRIG-2004 (BCD year coded)...\n\n"); 930 encode = IRIG; 931 IrigIncludeYear = TRUE; 932 IrigIncludeIeee = FALSE; 933 break; 934 935 case '3': 936 printf ("\nFormat is IRIG with IEEE-1344 (BCD year coded, and more control functions)...\n\n"); 937 encode = IRIG; 938 IrigIncludeYear = TRUE; 939 IrigIncludeIeee = TRUE; 940 break; 941 942 case '4': 943 printf ("\nFormat is unmodulated IRIG with IEEE-1344 (BCD year coded, and more control functions)...\n\n"); 944 encode = IRIG; 945 IrigIncludeYear = TRUE; 946 IrigIncludeIeee = TRUE; 947 948 Unmodulated = TRUE; 949 UnmodulatedInverted = FALSE; 950 break; 951 952 case '5': 953 printf ("\nFormat is inverted unmodulated IRIG with IEEE-1344 (BCD year coded, and more control functions)...\n\n"); 954 encode = IRIG; 955 IrigIncludeYear = TRUE; 956 IrigIncludeIeee = TRUE; 957 958 Unmodulated = TRUE; 959 UnmodulatedInverted = TRUE; 960 break; 961 962 case 'w': 963 printf ("\nFormat is WWV(H)...\n\n"); 964 encode = WWV; 965 break; 966 967 default: 968 printf ("\n\nUnexpected format value of \'%c\', cannot parse, aborting...\n\n", FormatCharacter); 969 exit (-1); 970 break; 971 } 972 973 /* 974 * Open audio device and set options 975 */ 976 fd = open(device, O_WRONLY); 977 if (fd <= 0) { 978 printf("Unable to open audio device \"%s\", aborting: %s\n", device, strerror(errno)); 979 exit(1); 980 } 981 982#ifdef HAVE_SYS_SOUNDCARD_H 983 /* First set coding type */ 984 AudioFormat = AFMT_MU_LAW; 985 if (ioctl(fd, SNDCTL_DSP_SETFMT, &AudioFormat)==-1) 986 { /* Fatal error */ 987 printf ("\nUnable to set output format, aborting...\n\n"); 988 exit(-1); 989 } 990 991 if (AudioFormat != AFMT_MU_LAW) 992 { 993 printf ("\nUnable to set output format for mu law, aborting...\n\n"); 994 exit(-1); 995 } 996 997 /* Next set number of channels */ 998 MonoStereo = MONO; /* Mono */ 999 if (ioctl(fd, SNDCTL_DSP_STEREO, &MonoStereo)==-1) 1000 { /* Fatal error */ 1001 printf ("\nUnable to set mono/stereo, aborting...\n\n"); 1002 exit(-1); 1003 } 1004 1005 if (MonoStereo != MONO) 1006 { 1007 printf ("\nUnable to set mono/stereo for mono, aborting...\n\n"); 1008 exit(-1); 1009 } 1010 1011 /* Now set sample rate */ 1012 SampleRate = SetSampleRate; 1013 if (ioctl(fd, SNDCTL_DSP_SPEED, &SampleRate)==-1) 1014 { /* Fatal error */ 1015 printf ("\nUnable to set sample rate to %d, returned %d, aborting...\n\n", SetSampleRate, SampleRate); 1016 exit(-1); 1017 } 1018 1019 SampleRateDifference = SampleRate - SetSampleRate; 1020 1021 if (SampleRateDifference < 0) 1022 SampleRateDifference = - SampleRateDifference; 1023 1024 /* Fixed allowable sample rate error 0.1% */ 1025 if (SampleRateDifference > (SetSampleRate/1000)) 1026 { 1027 printf ("\nUnable to set sample rate to %d, result was %d, more than 0.1 percent, aborting...\n\n", SetSampleRate, SampleRate); 1028 exit(-1); 1029 } 1030 else 1031 { 1032 /* printf ("\nAttempt to set sample rate to %d, actual %d...\n\n", SetSampleRate, SampleRate); */ 1033 } 1034#else 1035 rval = ioctl(fd, AUDIO_GETINFO, &info); 1036 if (rval < 0) { 1037 printf("\naudio control %s", strerror(errno)); 1038 exit(0); 1039 } 1040 info.play.port = port; 1041 info.play.gain = level; 1042 info.play.sample_rate = SetSampleRate; 1043 info.play.channels = 1; 1044 info.play.precision = 8; 1045 info.play.encoding = AUDIO_ENCODING_ULAW; 1046 printf("\nport %d gain %d rate %d chan %d prec %d encode %d\n", 1047 info.play.port, info.play.gain, info.play.sample_rate, 1048 info.play.channels, info.play.precision, 1049 info.play.encoding); 1050 ioctl(fd, AUDIO_SETINFO, &info); 1051#endif 1052 1053 /* 1054 * Unless specified otherwise, read the system clock and 1055 * initialize the time. 1056 */ 1057 gettimeofday(&TimeValue, NULL); // Now always read the system time to keep "real time" of operation. 1058 NowRealTime = BaseRealTime = SecondsPartOfTime = TimeValue.tv_sec; 1059 SecondsRunningSimulationTime = 0; // Just starting simulation, running zero seconds as of now. 1060 StabilityCount = 0; // No stability yet. 1061 1062 if (utc) 1063 { 1064 DayOfYear = ConvertMonthDayToDayOfYear (Year, Month, DayOfMonth); 1065 } 1066 else 1067 { 1068 /* Apply offset to time. */ 1069 if (UseOffsetSecondsInt >= 0) 1070 SecondsPartOfTime += (time_t) UseOffsetSecondsInt; 1071 else 1072 SecondsPartOfTime -= (time_t) (-UseOffsetSecondsInt); 1073 1074 TimeStructure = gmtime(&SecondsPartOfTime); 1075 Minute = TimeStructure->tm_min; 1076 Hour = TimeStructure->tm_hour; 1077 DayOfYear = TimeStructure->tm_yday + 1; 1078 Year = TimeStructure->tm_year % 100; 1079 Second = TimeStructure->tm_sec; 1080 1081 /* 1082 * Delay the first second so the generator is accurately 1083 * aligned with the system clock within one sample (125 1084 * microseconds ). 1085 */ 1086 delay(SECOND - TimeValue.tv_usec * 8 / 1000); 1087 } 1088 1089 StraightBinarySeconds = Second + (Minute * SECONDS_PER_MINUTE) + (Hour * SECONDS_PER_HOUR); 1090 1091 memset(code, 0, sizeof(code)); 1092 switch (encode) { 1093 1094 /* 1095 * For WWV/H and default time, carefully set the signal 1096 * generator seconds number to agree with the current time. 1097 */ 1098 case WWV: 1099 printf("WWV time signal, starting point:\n"); 1100 printf(" Year = %02d, Day of year = %03d, Time = %02d:%02d:%02d, Minute tone = %d Hz, Hour tone = %d Hz.\n", 1101 Year, DayOfYear, Hour, Minute, Second, tone, HourTone); 1102 snprintf(code, sizeof(code), "%01d%03d%02d%02d%01d", 1103 Year / 10, DayOfYear, Hour, Minute, Year % 10); 1104 if (Verbose) 1105 { 1106 printf("\n Year = %2.2d, Day of year = %3d, Time = %2.2d:%2.2d:%2.2d, Code = %s", 1107 Year, DayOfYear, Hour, Minute, Second, code); 1108 1109 if ((EnableRateCorrection) || (RemoveCycle) || (AddCycle)) 1110 printf (", CountOfSecondsSent = %d, TotalCyclesAdded = %d, TotalCyclesRemoved = %d\n", CountOfSecondsSent, TotalCyclesAdded, TotalCyclesRemoved); 1111 else 1112 printf ("\n"); 1113 } 1114 1115 ptr = 8; 1116 for (BitNumber = 0; BitNumber <= Second; BitNumber++) { 1117 if (progx[BitNumber].sw == DEC) 1118 ptr--; 1119 } 1120 break; 1121 1122 /* 1123 * For IRIG the signal generator runs every second, so requires 1124 * no additional alignment. 1125 */ 1126 case IRIG: 1127 printf ("IRIG-B time signal, starting point:\n"); 1128 printf (" Year = %02d, Day of year = %03d, Time = %02d:%02d:%02d, Straight binary seconds (SBS) = %05d / 0x%04X.\n", 1129 Year, DayOfYear, Hour, Minute, Second, StraightBinarySeconds, StraightBinarySeconds); 1130 printf ("\n"); 1131 if (Verbose) 1132 { 1133 printf ("Codes: \".\" = marker/position indicator, \"-\" = zero dummy bit, \"0\" = zero bit, \"1\" = one bit.\n"); 1134 if ((EnableRateCorrection) || (AddCycle) || (RemoveCycle)) 1135 { 1136 printf (" \"o\" = short zero, \"*\" = long zero, \"x\" = short one, \"+\" = long one.\n"); 1137 } 1138 printf ("Numerical values are time order reversed in output to make it easier to read.\n"); 1139 /* 111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999 */ 1140 /* 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 */ 1141 printf ("\n"); 1142 printf ("Legend of output codes:\n"); 1143 //printf ("\n"); 1144 //printf ("| StraightBinSecs | IEEE_1344_Control | Year | Day_of_Year | Hours | Minutes |Seconds |\n"); 1145 //printf ("| --------------- | ----------------- | ---- | ----------- | ----- | ------- |------- |\n"); 1146 //printf ("| | | | | | | |\n"); 1147 } 1148 break; 1149 } 1150 1151 /* 1152 * Run the signal generator to generate new timecode strings 1153 * once per minute for WWV/H and once per second for IRIG. 1154 */ 1155 for (CountOfSecondsSent=0; ((SecondsToSend==0) || (CountOfSecondsSent<SecondsToSend)); CountOfSecondsSent++) 1156 { 1157 if ((encode == IRIG) && (((Second % 20) == 0) || (CountOfSecondsSent == 0))) 1158 { 1159 printf ("\n"); 1160 1161 printf (" Year = %02d, Day of year = %03d, Time = %02d:%02d:%02d, Straight binary seconds (SBS) = %05d / 0x%04X.\n", 1162 Year, DayOfYear, Hour, Minute, Second, StraightBinarySeconds, StraightBinarySeconds); 1163 if ((EnableRateCorrection) || (RemoveCycle) || (AddCycle)) 1164 { 1165 printf (" CountOfSecondsSent = %d, TotalCyclesAdded = %d, TotalCyclesRemoved = %d\n", CountOfSecondsSent, TotalCyclesAdded, TotalCyclesRemoved); 1166 if ((CountOfSecondsSent != 0) && ((TotalCyclesAdded != 0) || (TotalCyclesRemoved != 0))) 1167 { 1168 RatioError = ((float) (TotalCyclesAdded - TotalCyclesRemoved)) / (1000.0 * (float) CountOfSecondsSent); 1169 printf (" Adjusted by %2.1f%%, apparent send frequency is %4.2f Hz not %d Hz.\n\n", 1170 RatioError*100.0, (1.0+RatioError)*((float) SetSampleRate), SetSampleRate); 1171 } 1172 } 1173 else 1174 printf ("\n"); 1175 1176 /* printf ("|Seconds | Minutes | Hours | Day_of_Year | Year | IEEE_1344_Control | StraightBinSecs |\n"); 1177 printf ("|------- | ------- | ----- | ----------- | ---- | ----------------- |-------------------|\n"); 1178 printf ("| | | | | | | |\n");*/ 1179 printf ("| StraightBinSecs | IEEE_1344_Control | Year | Day_of_Year | Hours | Minutes |Seconds |\n"); 1180 printf ("| --------------- | ----------------- | ---- | ----------- | ----- | ------- |------- |\n"); 1181 printf ("| | | | | | | |\n"); 1182 } 1183 1184 if (RemoveCycle) 1185 { 1186 RateCorrection = -1; 1187 TotalSecondsCorrected ++; 1188 } 1189 else 1190 { 1191 if (AddCycle) 1192 { 1193 TotalSecondsCorrected ++; 1194 RateCorrection = +1; 1195 } 1196 else 1197 RateCorrection = 0; 1198 } 1199 1200 /* 1201 * Crank the state machine to propagate carries to the 1202 * year of century. Note that we delayed up to one 1203 * second for alignment after reading the time, so this 1204 * is the next second. 1205 */ 1206 1207 if (LeapState == LEAPSTATE_NORMAL) 1208 { 1209 /* If on the second of a leap (second 59 in the specified minute), then add or delete a second */ 1210 if ((Year == LeapYear) && (DayOfYear == LeapDayOfYear) && (Hour == LeapHour) && (Minute == LeapMinute)) 1211 { 1212 /* To delete a second, which means we go from 58->60 instead of 58->59->00. */ 1213 if ((DeleteLeapSecond) && (Second == 58)) 1214 { 1215 LeapState = LEAPSTATE_DELETING; 1216 1217 if (Debug) 1218 printf ("\n<--- Ready to delete a leap second...\n"); 1219 } 1220 else 1221 { /* Delete takes precedence over insert. */ 1222 /* To add a second, which means we go from 59->60->00 instead of 59->00. */ 1223 if ((InsertLeapSecond) && (Second == 59)) 1224 { 1225 LeapState = LEAPSTATE_INSERTING; 1226 1227 if (Debug) 1228 printf ("\n<--- Ready to insert a leap second...\n"); 1229 } 1230 } 1231 } 1232 } 1233 1234 switch (LeapState) 1235 { 1236 case LEAPSTATE_NORMAL: 1237 Second = (Second + 1) % 60; 1238 break; 1239 1240 case LEAPSTATE_DELETING: 1241 Second = 0; 1242 LeapState = LEAPSTATE_NORMAL; 1243 1244 if (Debug) 1245 printf ("\n<--- Deleting a leap second...\n"); 1246 break; 1247 1248 case LEAPSTATE_INSERTING: 1249 Second = 60; 1250 LeapState = LEAPSTATE_ZERO_AFTER_INSERT; 1251 1252 if (Debug) 1253 printf ("\n<--- Inserting a leap second...\n"); 1254 break; 1255 1256 case LEAPSTATE_ZERO_AFTER_INSERT: 1257 Second = 0; 1258 LeapState = LEAPSTATE_NORMAL; 1259 1260 if (Debug) 1261 printf ("\n<--- Inserted a leap second, now back to zero...\n"); 1262 break; 1263 1264 default: 1265 printf ("\n\nLeap second state invalid value of %d, aborting...", LeapState); 1266 exit (-1); 1267 break; 1268 } 1269 1270 /* Check for second rollover, increment minutes and ripple upward if required. */ 1271 if (Second == 0) { 1272 Minute++; 1273 if (Minute >= 60) { 1274 Minute = 0; 1275 Hour++; 1276 } 1277 1278 /* Check for activation of DST switch. */ 1279 /* If DST is active, this would mean that at the appointed time, we de-activate DST, */ 1280 /* which translates to going backward an hour (repeating the last hour). */ 1281 /* If DST is not active, this would mean that at the appointed time, we activate DST, */ 1282 /* which translates to going forward an hour (skipping the next hour). */ 1283 if (DstSwitchFlag) 1284 { 1285 /* The actual switch happens on the zero'th second of the actual minute specified. */ 1286 if ((Year == DstSwitchYear) && (DayOfYear == DstSwitchDayOfYear) && (Hour == DstSwitchHour) && (Minute == DstSwitchMinute)) 1287 { 1288 if (DstFlag == 0) 1289 { /* DST flag is zero, not in DST, going to DST, "spring ahead", so increment hour by two instead of one. */ 1290 Hour++; 1291 DstFlag = 1; 1292 1293 /* Must adjust offset to keep consistent with UTC. */ 1294 /* Here we have to increase offset by one hour. If it goes from negative to positive, then we fix that. */ 1295 if (OffsetSignBit == 0) 1296 { /* Offset is positive */ 1297 if (OffsetOnes == 0x0F) 1298 { 1299 OffsetSignBit = 1; 1300 OffsetOnes = (OffsetHalf == 0) ? 8 : 7; 1301 } 1302 else 1303 OffsetOnes++; 1304 } 1305 else 1306 { /* Offset is negative */ 1307 if (OffsetOnes == 0) 1308 { 1309 OffsetSignBit = 0; 1310 OffsetOnes = (OffsetHalf == 0) ? 1 : 0; 1311 } 1312 else 1313 OffsetOnes--; 1314 } 1315 1316 if (Debug) 1317 printf ("\n<--- DST activated, spring ahead an hour, new offset !...\n"); 1318 } 1319 else 1320 { /* DST flag is non zero, in DST, going out of DST, "fall back", so no increment of hour. */ 1321 Hour--; 1322 DstFlag = 0; 1323 1324 /* Must adjust offset to keep consistent with UTC. */ 1325 /* Here we have to reduce offset by one hour. If it goes negative, then we fix that. */ 1326 if (OffsetSignBit == 0) 1327 { /* Offset is positive */ 1328 if (OffsetOnes == 0) 1329 { 1330 OffsetSignBit = 1; 1331 OffsetOnes = (OffsetHalf == 0) ? 1 : 0; 1332 } 1333 else 1334 OffsetOnes--; 1335 } 1336 else 1337 { /* Offset is negative */ 1338 if (OffsetOnes == 0x0F) 1339 { 1340 OffsetSignBit = 0; 1341 OffsetOnes = (OffsetHalf == 0) ? 8 : 7; 1342 } 1343 else 1344 OffsetOnes++; 1345 } 1346 1347 if (Debug) 1348 printf ("\n<--- DST de-activated, fall back an hour!...\n"); 1349 } 1350 1351 DstSwitchFlag = FALSE; /* One time deal, not intended to run this program past two switches... */ 1352 } 1353 } 1354 1355 if (Hour >= 24) { 1356 /* Modified, just in case dumb case where activating DST advances 23h59:59 -> 01h00:00 */ 1357 Hour = Hour % 24; 1358 DayOfYear++; 1359 } 1360 1361 /* 1362 * At year rollover check for leap second. 1363 */ 1364 if (DayOfYear >= (Year & 0x3 ? 366 : 367)) { 1365 if (leap) { 1366 WWV_Second(DATA0, RateCorrection); 1367 if (Verbose) 1368 printf("\nLeap!"); 1369 leap = 0; 1370 } 1371 DayOfYear = 1; 1372 Year++; 1373 } 1374 if (encode == WWV) { 1375 snprintf(code, sizeof(code), 1376 "%01d%03d%02d%02d%01d", Year / 10, 1377 DayOfYear, Hour, Minute, Year % 10); 1378 if (Verbose) 1379 printf("\n Year = %2.2d, Day of year = %3d, Time = %2.2d:%2.2d:%2.2d, Code = %s", 1380 Year, DayOfYear, Hour, Minute, Second, code); 1381 1382 if ((EnableRateCorrection) || (RemoveCycle) || (AddCycle)) 1383 { 1384 printf (", CountOfSecondsSent = %d, TotalCyclesAdded = %d, TotalCyclesRemoved = %d\n", CountOfSecondsSent, TotalCyclesAdded, TotalCyclesRemoved); 1385 if ((CountOfSecondsSent != 0) && ((TotalCyclesAdded != 0) || (TotalCyclesRemoved != 0))) 1386 { 1387 RatioError = ((float) (TotalCyclesAdded - TotalCyclesRemoved)) / (1000.0 * (float) CountOfSecondsSent); 1388 printf (" Adjusted by %2.1f%%, apparent send frequency is %4.2f Hz not %d Hz.\n\n", 1389 RatioError*100.0, (1.0+RatioError)*((float) SetSampleRate), SetSampleRate); 1390 } 1391 } 1392 else 1393 printf ("\n"); 1394 1395 ptr = 8; 1396 } 1397 } /* End of "if (Second == 0)" */ 1398 1399 /* After all that, if we are in the minute just prior to a leap second, warn of leap second pending */ 1400 /* and of the polarity */ 1401 if ((Year == LeapYear) && (DayOfYear == LeapDayOfYear) && (Hour == LeapHour) && (Minute == LeapMinute)) 1402 { 1403 LeapSecondPending = TRUE; 1404 LeapSecondPolarity = DeleteLeapSecond; 1405 } 1406 else 1407 { 1408 LeapSecondPending = FALSE; 1409 LeapSecondPolarity = FALSE; 1410 } 1411 1412 /* Notification through IEEE 1344 happens during the whole minute previous to the minute specified. */ 1413 /* The time of that minute has been previously calculated. */ 1414 if ((Year == DstSwitchPendingYear) && (DayOfYear == DstSwitchPendingDayOfYear) && 1415 (Hour == DstSwitchPendingHour) && (Minute == DstSwitchPendingMinute)) 1416 { 1417 DstPendingFlag = TRUE; 1418 } 1419 else 1420 { 1421 DstPendingFlag = FALSE; 1422 } 1423 1424 1425 StraightBinarySeconds = Second + (Minute * SECONDS_PER_MINUTE) + (Hour * SECONDS_PER_HOUR); 1426 1427 if (encode == IRIG) { 1428 if (IrigIncludeIeee) 1429 { 1430 if ((OffsetOnes == 0) && (OffsetHalf == 0)) 1431 OffsetSignBit = 0; 1432 1433 ControlFunctions = (LeapSecondPending == 0 ? 0x00000 : 0x00001) | (LeapSecondPolarity == 0 ? 0x00000 : 0x00002) 1434 | (DstPendingFlag == 0 ? 0x00000 : 0x00004) | (DstFlag == 0 ? 0x00000 : 0x00008) 1435 | (OffsetSignBit == 0 ? 0x00000 : 0x00010) | ((OffsetOnes & 0x0F) << 5) | (OffsetHalf == 0 ? 0x00000 : 0x00200) 1436 | ((TimeQuality & 0x0F) << 10); 1437 /* if (Verbose) 1438 printf ("\nDstFlag = %d, OffsetSignBit = %d, OffsetOnes = %d, OffsetHalf = %d, TimeQuality = 0x%1.1X ==> ControlFunctions = 0x%5.5X...", 1439 DstFlag, OffsetSignBit, OffsetOnes, OffsetHalf, TimeQuality, ControlFunctions); 1440 */ 1441 } 1442 else 1443 ControlFunctions = 0; 1444 1445 /* 1446 YearDay HourMin Sec 1447 snprintf(code, sizeof(code), "%04x%04d%06d%02d%02d%02d", 1448 0, Year, DayOfYear, Hour, Minute, Second); 1449 */ 1450 if (IrigIncludeYear) { 1451 snprintf(ParityString, sizeof(ParityString), 1452 "%04X%02d%04d%02d%02d%02d", 1453 ControlFunctions & 0x7FFF, Year, 1454 DayOfYear, Hour, Minute, Second); 1455 } else { 1456 snprintf(ParityString, sizeof(ParityString), 1457 "%04X%02d%04d%02d%02d%02d", 1458 ControlFunctions & 0x7FFF, 1459 0, DayOfYear, Hour, Minute, Second); 1460 } 1461 1462 if (IrigIncludeIeee) 1463 { 1464 ParitySum = 0; 1465 for (StringPointer=ParityString; *StringPointer!=NUL; StringPointer++) 1466 { 1467 switch (toupper(*StringPointer)) 1468 { 1469 case '1': 1470 case '2': 1471 case '4': 1472 case '8': 1473 ParitySum += 1; 1474 break; 1475 1476 case '3': 1477 case '5': 1478 case '6': 1479 case '9': 1480 case 'A': 1481 case 'C': 1482 ParitySum += 2; 1483 break; 1484 1485 case '7': 1486 case 'B': 1487 case 'D': 1488 case 'E': 1489 ParitySum += 3; 1490 break; 1491 1492 case 'F': 1493 ParitySum += 4; 1494 break; 1495 } 1496 } 1497 1498 if ((ParitySum & 0x01) == 0x01) 1499 ParityValue = 0x01; 1500 else 1501 ParityValue = 0; 1502 } 1503 else 1504 ParityValue = 0; 1505 1506 ControlFunctions |= ((ParityValue & 0x01) << 14); 1507 1508 if (IrigIncludeYear) { 1509 snprintf(code, sizeof(code), 1510 /* YearDay HourMin Sec */ 1511 "%05X%05X%02d%04d%02d%02d%02d", 1512 StraightBinarySeconds, 1513 ControlFunctions, Year, DayOfYear, 1514 Hour, Minute, Second); 1515 } else { 1516 snprintf(code, sizeof(code), 1517 /* YearDay HourMin Sec */ 1518 "%05X%05X%02d%04d%02d%02d%02d", 1519 StraightBinarySeconds, 1520 ControlFunctions, 0, DayOfYear, 1521 Hour, Minute, Second); 1522 } 1523 1524 if (Debug) 1525 printf("\nCode string: %s, ParityString = %s, ParitySum = 0x%2.2X, ParityValue = %d, DstFlag = %d...\n", code, ParityString, ParitySum, ParityValue, DstFlag); 1526 1527 ptr = strlen(code)-1; 1528 OldPtr = 0; 1529 } 1530 1531 /* 1532 * Generate data for the second 1533 */ 1534 switch (encode) { 1535 1536 /* 1537 * The IRIG second consists of 20 BCD digits of width- 1538 * modulateod pulses at 2, 5 and 8 ms and modulated 50 1539 * percent on the 1000-Hz carrier. 1540 */ 1541 case IRIG: 1542 /* Initialize the output string */ 1543 OutputDataString[0] = '\0'; 1544 1545 for (BitNumber = 0; BitNumber < 100; BitNumber++) { 1546 FrameNumber = (BitNumber/10) + 1; 1547 switch (FrameNumber) 1548 { 1549 case 1: 1550 /* bits 0 to 9, first frame */ 1551 sw = progz[BitNumber % 10].sw; 1552 arg = progz[BitNumber % 10].arg; 1553 break; 1554 1555 case 2: 1556 case 3: 1557 case 4: 1558 case 5: 1559 case 6: 1560 /* bits 10 to 59, second to sixth frame */ 1561 sw = progy[BitNumber % 10].sw; 1562 arg = progy[BitNumber % 10].arg; 1563 break; 1564 1565 case 7: 1566 /* bits 60 to 69, seventh frame */ 1567 sw = progw[BitNumber % 10].sw; 1568 arg = progw[BitNumber % 10].arg; 1569 break; 1570 1571 case 8: 1572 /* bits 70 to 79, eighth frame */ 1573 sw = progv[BitNumber % 10].sw; 1574 arg = progv[BitNumber % 10].arg; 1575 break; 1576 1577 case 9: 1578 /* bits 80 to 89, ninth frame */ 1579 sw = progw[BitNumber % 10].sw; 1580 arg = progw[BitNumber % 10].arg; 1581 break; 1582 1583 case 10: 1584 /* bits 90 to 99, tenth frame */ 1585 sw = progu[BitNumber % 10].sw; 1586 arg = progu[BitNumber % 10].arg; 1587 break; 1588 1589 default: 1590 /* , Unexpected values of FrameNumber */ 1591 printf ("\n\nUnexpected value of FrameNumber = %d, cannot parse, aborting...\n\n", FrameNumber); 1592 exit (-1); 1593 break; 1594 } 1595 1596 switch(sw) { 1597 1598 case DECC: /* decrement pointer and send bit. */ 1599 ptr--; 1600 case COEF: /* send BCD bit */ 1601 AsciiValue = toupper(code[ptr]); 1602 HexValue = isdigit(AsciiValue) ? AsciiValue - '0' : (AsciiValue - 'A')+10; 1603 /* if (Debug) { 1604 if (ptr != OldPtr) { 1605 if (Verbose) 1606 printf("\n(%c->%X)", AsciiValue, HexValue); 1607 OldPtr = ptr; 1608 } 1609 } 1610 */ 1611 // OK, adjust all unused bits in hundreds of days. 1612 if ((FrameNumber == 5) && ((BitNumber % 10) > 1)) 1613 { 1614 if (RateCorrection < 0) 1615 { // Need to remove cycles to catch up. 1616 if ((HexValue & arg) != 0) 1617 { 1618 if (Unmodulated) 1619 { 1620 poop(M5, 1000, HIGH, UnmodulatedInverted); 1621 poop(M5-1, 1000, LOW, UnmodulatedInverted); 1622 1623 TotalCyclesRemoved += 1; 1624 } 1625 else 1626 { 1627 peep(M5, 1000, HIGH); 1628 peep(M5-1, 1000, LOW); 1629 1630 TotalCyclesRemoved += 1; 1631 } 1632 strlcat(OutputDataString, "x", OUTPUT_DATA_STRING_LENGTH); 1633 } 1634 else 1635 { 1636 if (Unmodulated) 1637 { 1638 poop(M2, 1000, HIGH, UnmodulatedInverted); 1639 poop(M8-1, 1000, LOW, UnmodulatedInverted); 1640 1641 TotalCyclesRemoved += 1; 1642 } 1643 else 1644 { 1645 peep(M2, 1000, HIGH); 1646 peep(M8-1, 1000, LOW); 1647 1648 TotalCyclesRemoved += 1; 1649 } 1650 strlcat(OutputDataString, "o", OUTPUT_DATA_STRING_LENGTH); 1651 } 1652 } // End of true clause for "if (RateCorrection < 0)" 1653 else 1654 { // Else clause for "if (RateCorrection < 0)" 1655 if (RateCorrection > 0) 1656 { // Need to add cycles to slow back down. 1657 if ((HexValue & arg) != 0) 1658 { 1659 if (Unmodulated) 1660 { 1661 poop(M5, 1000, HIGH, UnmodulatedInverted); 1662 poop(M5+1, 1000, LOW, UnmodulatedInverted); 1663 1664 TotalCyclesAdded += 1; 1665 } 1666 else 1667 { 1668 peep(M5, 1000, HIGH); 1669 peep(M5+1, 1000, LOW); 1670 1671 TotalCyclesAdded += 1; 1672 } 1673 strlcat(OutputDataString, "+", OUTPUT_DATA_STRING_LENGTH); 1674 } 1675 else 1676 { 1677 if (Unmodulated) 1678 { 1679 poop(M2, 1000, HIGH, UnmodulatedInverted); 1680 poop(M8+1, 1000, LOW, UnmodulatedInverted); 1681 1682 TotalCyclesAdded += 1; 1683 } 1684 else 1685 { 1686 peep(M2, 1000, HIGH); 1687 peep(M8+1, 1000, LOW); 1688 1689 TotalCyclesAdded += 1; 1690 } 1691 strlcat(OutputDataString, "*", OUTPUT_DATA_STRING_LENGTH); 1692 } 1693 } // End of true clause for "if (RateCorrection > 0)" 1694 else 1695 { // Else clause for "if (RateCorrection > 0)" 1696 // Rate is OK, just do what you feel! 1697 if ((HexValue & arg) != 0) 1698 { 1699 if (Unmodulated) 1700 { 1701 poop(M5, 1000, HIGH, UnmodulatedInverted); 1702 poop(M5, 1000, LOW, UnmodulatedInverted); 1703 } 1704 else 1705 { 1706 peep(M5, 1000, HIGH); 1707 peep(M5, 1000, LOW); 1708 } 1709 strlcat(OutputDataString, "1", OUTPUT_DATA_STRING_LENGTH); 1710 } 1711 else 1712 { 1713 if (Unmodulated) 1714 { 1715 poop(M2, 1000, HIGH, UnmodulatedInverted); 1716 poop(M8, 1000, LOW, UnmodulatedInverted); 1717 } 1718 else 1719 { 1720 peep(M2, 1000, HIGH); 1721 peep(M8, 1000, LOW); 1722 } 1723 strlcat(OutputDataString, "0", OUTPUT_DATA_STRING_LENGTH); 1724 } 1725 } // End of else clause for "if (RateCorrection > 0)" 1726 } // End of else claues for "if (RateCorrection < 0)" 1727 } // End of true clause for "if ((FrameNumber == 5) && (BitNumber == 8))" 1728 else 1729 { // Else clause for "if ((FrameNumber == 5) && (BitNumber == 8))" 1730 if ((HexValue & arg) != 0) 1731 { 1732 if (Unmodulated) 1733 { 1734 poop(M5, 1000, HIGH, UnmodulatedInverted); 1735 poop(M5, 1000, LOW, UnmodulatedInverted); 1736 } 1737 else 1738 { 1739 peep(M5, 1000, HIGH); 1740 peep(M5, 1000, LOW); 1741 } 1742 strlcat(OutputDataString, "1", OUTPUT_DATA_STRING_LENGTH); 1743 } 1744 else 1745 { 1746 if (Unmodulated) 1747 { 1748 poop(M2, 1000, HIGH, UnmodulatedInverted); 1749 poop(M8, 1000, LOW, UnmodulatedInverted); 1750 } 1751 else 1752 { 1753 peep(M2, 1000, HIGH); 1754 peep(M8, 1000, LOW); 1755 } 1756 strlcat(OutputDataString, "0", OUTPUT_DATA_STRING_LENGTH); 1757 } 1758 } // end of else clause for "if ((FrameNumber == 5) && (BitNumber == 8))" 1759 break; 1760 1761 case DECZ: /* decrement pointer and send zero bit */ 1762 ptr--; 1763 if (Unmodulated) 1764 { 1765 poop(M2, 1000, HIGH, UnmodulatedInverted); 1766 poop(M8, 1000, LOW, UnmodulatedInverted); 1767 } 1768 else 1769 { 1770 peep(M2, 1000, HIGH); 1771 peep(M8, 1000, LOW); 1772 } 1773 strlcat(OutputDataString, "-", OUTPUT_DATA_STRING_LENGTH); 1774 break; 1775 1776 case DEC: /* send marker/position indicator IM/PI bit */ 1777 ptr--; 1778 case NODEC: /* send marker/position indicator IM/PI bit but no decrement pointer */ 1779 case MIN: /* send "second start" marker/position indicator IM/PI bit */ 1780 if (Unmodulated) 1781 { 1782 poop(arg, 1000, HIGH, UnmodulatedInverted); 1783 poop(10 - arg, 1000, LOW, UnmodulatedInverted); 1784 } 1785 else 1786 { 1787 peep(arg, 1000, HIGH); 1788 peep(10 - arg, 1000, LOW); 1789 } 1790 strlcat(OutputDataString, ".", OUTPUT_DATA_STRING_LENGTH); 1791 break; 1792 1793 default: 1794 printf ("\n\nUnknown state machine value \"%d\", unable to continue, aborting...\n\n", sw); 1795 exit (-1); 1796 break; 1797 } 1798 if (ptr < 0) 1799 break; 1800 } 1801 ReverseString ( OutputDataString ); 1802 if (Verbose) 1803 { 1804 printf("%s", OutputDataString); 1805 if (RateCorrection > 0) 1806 printf(" fast\n"); 1807 else 1808 { 1809 if (RateCorrection < 0) 1810 printf (" slow\n"); 1811 else 1812 printf ("\n"); 1813 } 1814 } 1815 break; 1816 1817 /* 1818 * The WWV/H second consists of 9 BCD digits of width- 1819 * modulateod pulses 200, 500 and 800 ms at 100-Hz. 1820 */ 1821 case WWV: 1822 sw = progx[Second].sw; 1823 arg = progx[Second].arg; 1824 switch(sw) { 1825 1826 case DATA: /* send data bit */ 1827 WWV_Second(arg, RateCorrection); 1828 if (Verbose) 1829 { 1830 if (arg == DATA0) 1831 printf ("0"); 1832 else 1833 { 1834 if (arg == DATA1) 1835 printf ("1"); 1836 else 1837 { 1838 if (arg == PI) 1839 printf ("P"); 1840 else 1841 printf ("?"); 1842 } 1843 } 1844 } 1845 break; 1846 1847 case DATAX: /* send data bit */ 1848 WWV_SecondNoTick(arg, RateCorrection); 1849 if (Verbose) 1850 { 1851 if (arg == DATA0) 1852 printf ("0"); 1853 else 1854 { 1855 if (arg == DATA1) 1856 printf ("1"); 1857 else 1858 { 1859 if (arg == PI) 1860 printf ("P"); 1861 else 1862 printf ("?"); 1863 } 1864 } 1865 } 1866 break; 1867 1868 case COEF: /* send BCD bit */ 1869 if (code[ptr] & arg) { 1870 WWV_Second(DATA1, RateCorrection); 1871 if (Verbose) 1872 printf("1"); 1873 } else { 1874 WWV_Second(DATA0, RateCorrection); 1875 if (Verbose) 1876 printf("0"); 1877 } 1878 break; 1879 1880 case LEAP: /* send leap bit */ 1881 if (leap) { 1882 WWV_Second(DATA1, RateCorrection); 1883 if (Verbose) 1884 printf("L"); 1885 } else { 1886 WWV_Second(DATA0, RateCorrection); 1887 if (Verbose) 1888 printf("0"); 1889 } 1890 break; 1891 1892 case DEC: /* send data bit */ 1893 ptr--; 1894 WWV_Second(arg, RateCorrection); 1895 if (Verbose) 1896 { 1897 if (arg == DATA0) 1898 printf ("0"); 1899 else 1900 { 1901 if (arg == DATA1) 1902 printf ("1"); 1903 else 1904 { 1905 if (arg == PI) 1906 printf ("P"); 1907 else 1908 printf ("?"); 1909 } 1910 } 1911 } 1912 break; 1913 1914 case DECX: /* send data bit with no tick */ 1915 ptr--; 1916 WWV_SecondNoTick(arg, RateCorrection); 1917 if (Verbose) 1918 { 1919 if (arg == DATA0) 1920 printf ("0"); 1921 else 1922 { 1923 if (arg == DATA1) 1924 printf ("1"); 1925 else 1926 { 1927 if (arg == PI) 1928 printf ("P"); 1929 else 1930 printf ("?"); 1931 } 1932 } 1933 } 1934 break; 1935 1936 case MIN: /* send minute sync */ 1937 if (Minute == 0) 1938 { 1939 peep(arg, HourTone, HIGH); 1940 1941 if (RateCorrection < 0) 1942 { 1943 peep( 990 - arg, HourTone, OFF); 1944 TotalCyclesRemoved += 10; 1945 1946 if (Debug) 1947 printf ("\n* Shorter Second: "); 1948 } 1949 else 1950 { 1951 if (RateCorrection > 0) 1952 { 1953 peep(1010 - arg, HourTone, OFF); 1954 1955 TotalCyclesAdded += 10; 1956 1957 if (Debug) 1958 printf ("\n* Longer Second: "); 1959 } 1960 else 1961 { 1962 peep(1000 - arg, HourTone, OFF); 1963 } 1964 } 1965 1966 if (Verbose) 1967 printf("H"); 1968 } 1969 else 1970 { 1971 peep(arg, tone, HIGH); 1972 1973 if (RateCorrection < 0) 1974 { 1975 peep( 990 - arg, tone, OFF); 1976 TotalCyclesRemoved += 10; 1977 1978 if (Debug) 1979 printf ("\n* Shorter Second: "); 1980 } 1981 else 1982 { 1983 if (RateCorrection > 0) 1984 { 1985 peep(1010 - arg, tone, OFF); 1986 1987 TotalCyclesAdded += 10; 1988 1989 if (Debug) 1990 printf ("\n* Longer Second: "); 1991 } 1992 else 1993 { 1994 peep(1000 - arg, tone, OFF); 1995 } 1996 } 1997 1998 if (Verbose) 1999 printf("M"); 2000 } 2001 break; 2002 2003 case DUT1: /* send DUT1 bits */ 2004 if (dut1 & arg) 2005 { 2006 WWV_Second(DATA1, RateCorrection); 2007 if (Verbose) 2008 printf("1"); 2009 } 2010 else 2011 { 2012 WWV_Second(DATA0, RateCorrection); 2013 if (Verbose) 2014 printf("0"); 2015 } 2016 break; 2017 2018 case DST1: /* send DST1 bit */ 2019 ptr--; 2020 if (DstFlag) 2021 { 2022 WWV_Second(DATA1, RateCorrection); 2023 if (Verbose) 2024 printf("1"); 2025 } 2026 else 2027 { 2028 WWV_Second(DATA0, RateCorrection); 2029 if (Verbose) 2030 printf("0"); 2031 } 2032 break; 2033 2034 case DST2: /* send DST2 bit */ 2035 if (DstFlag) 2036 { 2037 WWV_Second(DATA1, RateCorrection); 2038 if (Verbose) 2039 printf("1"); 2040 } 2041 else 2042 { 2043 WWV_Second(DATA0, RateCorrection); 2044 if (Verbose) 2045 printf("0"); 2046 } 2047 break; 2048 } 2049 } 2050 2051 if (EnableRateCorrection) 2052 { 2053 SecondsRunningSimulationTime++; 2054 2055 gettimeofday(&TimeValue, NULL); 2056 NowRealTime = TimeValue.tv_sec; 2057 2058 if (NowRealTime >= BaseRealTime) // Just in case system time corrects backwards, do not blow up. 2059 { 2060 SecondsRunningRealTime = (unsigned) (NowRealTime - BaseRealTime); 2061 SecondsRunningDifference = SecondsRunningSimulationTime - SecondsRunningRealTime; 2062 2063 if (Debug) 2064 { 2065 printf ("> NowRealTime = 0x%8.8X, BaseRealtime = 0x%8.8X, SecondsRunningRealTime = 0x%8.8X, SecondsRunningSimulationTime = 0x%8.8X.\n", 2066 (unsigned) NowRealTime, (unsigned) BaseRealTime, SecondsRunningRealTime, SecondsRunningSimulationTime); 2067 printf ("> SecondsRunningDifference = 0x%8.8X, ExpectedRunningDifference = 0x%8.8X.\n", 2068 SecondsRunningDifference, ExpectedRunningDifference); 2069 } 2070 2071 if (SecondsRunningSimulationTime > RUN_BEFORE_STABILITY_CHECK) 2072 { 2073 if (StabilityCount < MINIMUM_STABILITY_COUNT) 2074 { 2075 if (StabilityCount == 0) 2076 { 2077 ExpectedRunningDifference = SecondsRunningDifference; 2078 StabilityCount++; 2079 if (Debug) 2080 printf ("> Starting stability check.\n"); 2081 } 2082 else 2083 { // Else for "if (StabilityCount == 0)" 2084 if ((ExpectedRunningDifference+INITIAL_STABILITY_BAND > SecondsRunningDifference) 2085 && (ExpectedRunningDifference-INITIAL_STABILITY_BAND < SecondsRunningDifference)) 2086 { // So far, still within stability band, increment count. 2087 StabilityCount++; 2088 if (Debug) 2089 printf ("> StabilityCount = %d.\n", StabilityCount); 2090 } 2091 else 2092 { // Outside of stability band, start over. 2093 StabilityCount = 0; 2094 if (Debug) 2095 printf ("> Out of stability band, start over.\n"); 2096 } 2097 } // End of else for "if (StabilityCount == 0)" 2098 } // End of true clause for "if (StabilityCount < MINIMUM_STABILITY_COUNT))" 2099 else 2100 { // Else clause for "if (StabilityCount < MINIMUM_STABILITY_COUNT))" - OK, so we are supposed to be stable. 2101 if (AddCycle) 2102 { 2103 if (ExpectedRunningDifference >= SecondsRunningDifference) 2104 { 2105 if (Debug) 2106 printf ("> Was adding cycles, ExpectedRunningDifference >= SecondsRunningDifference, can stop it now.\n"); 2107 2108 AddCycle = FALSE; 2109 RemoveCycle = FALSE; 2110 } 2111 else 2112 { 2113 if (Debug) 2114 printf ("> Was adding cycles, not done yet.\n"); 2115 } 2116 } 2117 else 2118 { 2119 if (RemoveCycle) 2120 { 2121 if (ExpectedRunningDifference <= SecondsRunningDifference) 2122 { 2123 if (Debug) 2124 printf ("> Was removing cycles, ExpectedRunningDifference <= SecondsRunningDifference, can stop it now.\n"); 2125 2126 AddCycle = FALSE; 2127 RemoveCycle = FALSE; 2128 } 2129 else 2130 { 2131 if (Debug) 2132 printf ("> Was removing cycles, not done yet.\n"); 2133 } 2134 } 2135 else 2136 { 2137 if ((ExpectedRunningDifference+RUNNING_STABILITY_BAND > SecondsRunningDifference) 2138 && (ExpectedRunningDifference-RUNNING_STABILITY_BAND < SecondsRunningDifference)) 2139 { // All is well, within tolerances. 2140 if (Debug) 2141 printf ("> All is well, within tolerances.\n"); 2142 } 2143 else 2144 { // Oops, outside tolerances. Else clause of "if ((ExpectedRunningDifference...SecondsRunningDifference)" 2145 if (ExpectedRunningDifference > SecondsRunningDifference) 2146 { 2147 if (Debug) 2148 printf ("> ExpectedRunningDifference > SecondsRunningDifference, running behind real time.\n"); 2149 2150 // Behind real time, have to add a cycle to slow down and get back in sync. 2151 AddCycle = FALSE; 2152 RemoveCycle = TRUE; 2153 } 2154 else 2155 { // Else clause of "if (ExpectedRunningDifference < SecondsRunningDifference)" 2156 if (ExpectedRunningDifference < SecondsRunningDifference) 2157 { 2158 if (Debug) 2159 printf ("> ExpectedRunningDifference < SecondsRunningDifference, running ahead of real time.\n"); 2160 2161 // Ahead of real time, have to remove a cycle to speed up and get back in sync. 2162 AddCycle = TRUE; 2163 RemoveCycle = FALSE; 2164 } 2165 else 2166 { 2167 if (Debug) 2168 printf ("> Oops, outside tolerances, but doesn't fit the profiles, how can this be?\n"); 2169 } 2170 } // End of else clause of "if (ExpectedRunningDifference > SecondsRunningDifference)" 2171 } // End of else clause of "if ((ExpectedRunningDifference...SecondsRunningDifference)" 2172 } // End of else clause of "if (RemoveCycle)". 2173 } // End of else clause of "if (AddCycle)". 2174 } // End of else clause for "if (StabilityCount < MINIMUM_STABILITY_COUNT))" 2175 } // End of true clause for "if ((SecondsRunningSimulationTime > RUN_BEFORE_STABILITY_CHECK)" 2176 } // End of true clause for "if (NowRealTime >= BaseRealTime)" 2177 else 2178 { 2179 if (Debug) 2180 printf ("> Hmm, time going backwards?\n"); 2181 } 2182 } // End of true clause for "if (EnableRateCorrection)" 2183 2184 fflush (stdout); 2185 } 2186 2187 2188printf ("\n\n>> Completed %d seconds, exiting...\n\n", SecondsToSend); 2189return (0); 2190} 2191 2192 2193/* 2194 * Generate WWV/H 0 or 1 data pulse. 2195 */ 2196void WWV_Second( 2197 int code, /* DATA0, DATA1, PI */ 2198 int Rate /* <0 -> do a short second, 0 -> normal second, >0 -> long second */ 2199 ) 2200{ 2201 /* 2202 * The WWV data pulse begins with 5 ms of 1000 Hz follwed by a 2203 * guard time of 25 ms. The data pulse is 170, 570 or 770 ms at 2204 * 100 Hz corresponding to 0, 1 or position indicator (PI), 2205 * respectively. Note the 100-Hz data pulses are transmitted 6 2206 * dB below the 1000-Hz sync pulses. Originally the data pulses 2207 * were transmited 10 dB below the sync pulses, but the station 2208 * engineers increased that to 6 dB because the Heath GC-1000 2209 * WWV/H radio clock worked much better. 2210 */ 2211 peep(5, tone, HIGH); /* send seconds tick */ 2212 peep(25, tone, OFF); 2213 peep(code - 30, 100, LOW); /* send data */ 2214 2215 /* The quiet time is shortened or lengthened to get us back on time */ 2216 if (Rate < 0) 2217 { 2218 peep( 990 - code, 100, OFF); 2219 2220 TotalCyclesRemoved += 10; 2221 2222 if (Debug) 2223 printf ("\n* Shorter Second: "); 2224 } 2225 else 2226 { 2227 if (Rate > 0) 2228 { 2229 peep(1010 - code, 100, OFF); 2230 2231 TotalCyclesAdded += 10; 2232 2233 if (Debug) 2234 printf ("\n* Longer Second: "); 2235 } 2236 else 2237 peep(1000 - code, 100, OFF); 2238 } 2239} 2240 2241/* 2242 * Generate WWV/H 0 or 1 data pulse, with no tick, for 29th and 59th seconds 2243 */ 2244void WWV_SecondNoTick( 2245 int code, /* DATA0, DATA1, PI */ 2246 int Rate /* <0 -> do a short second, 0 -> normal second, >0 -> long second */ 2247 ) 2248{ 2249 /* 2250 * The WWV data pulse begins with 5 ms of 1000 Hz follwed by a 2251 * guard time of 25 ms. The data pulse is 170, 570 or 770 ms at 2252 * 100 Hz corresponding to 0, 1 or position indicator (PI), 2253 * respectively. Note the 100-Hz data pulses are transmitted 6 2254 * dB below the 1000-Hz sync pulses. Originally the data pulses 2255 * were transmited 10 dB below the sync pulses, but the station 2256 * engineers increased that to 6 dB because the Heath GC-1000 2257 * WWV/H radio clock worked much better. 2258 */ 2259 peep(30, tone, OFF); /* send seconds non-tick */ 2260 peep(code - 30, 100, LOW); /* send data */ 2261 2262 /* The quiet time is shortened or lengthened to get us back on time */ 2263 if (Rate < 0) 2264 { 2265 peep( 990 - code, 100, OFF); 2266 2267 TotalCyclesRemoved += 10; 2268 2269 if (Debug) 2270 printf ("\n* Shorter Second: "); 2271 } 2272 else 2273 { 2274 if (Rate > 0) 2275 { 2276 peep(1010 - code, 100, OFF); 2277 2278 TotalCyclesAdded += 10; 2279 2280 if (Debug) 2281 printf ("\n* Longer Second: "); 2282 } 2283 else 2284 peep(1000 - code, 100, OFF); 2285 } 2286} 2287 2288/* 2289 * Generate cycles of 100 Hz or any multiple of 100 Hz. 2290 */ 2291void peep( 2292 int pulse, /* pulse length (ms) */ 2293 int freq, /* frequency (Hz) */ 2294 int amp /* amplitude */ 2295 ) 2296{ 2297 int increm; /* phase increment */ 2298 int i, j; 2299 2300 if (amp == OFF || freq == 0) 2301 increm = 10; 2302 else 2303 increm = freq / 100; 2304 j = 0; 2305 for (i = 0 ; i < pulse * 8; i++) { 2306 switch (amp) { 2307 2308 case HIGH: 2309 buffer[bufcnt++] = ~c6000[j]; 2310 break; 2311 2312 case LOW: 2313 buffer[bufcnt++] = ~c3000[j]; 2314 break; 2315 2316 default: 2317 buffer[bufcnt++] = ~0; 2318 } 2319 if (bufcnt >= BUFLNG) { 2320 write(fd, buffer, BUFLNG); 2321 bufcnt = 0; 2322 } 2323 j = (j + increm) % 80; 2324 } 2325} 2326 2327 2328/* 2329 * Generate unmodulated from similar tables. 2330 */ 2331void poop( 2332 int pulse, /* pulse length (ms) */ 2333 int freq, /* frequency (Hz) */ 2334 int amp, /* amplitude */ 2335 int inverted /* is upside down */ 2336 ) 2337{ 2338 int increm; /* phase increment */ 2339 int i, j; 2340 2341 if (amp == OFF || freq == 0) 2342 increm = 10; 2343 else 2344 increm = freq / 100; 2345 j = 0; 2346 for (i = 0 ; i < pulse * 8; i++) { 2347 switch (amp) { 2348 2349 case HIGH: 2350 if (inverted) 2351 buffer[bufcnt++] = ~u3000[j]; 2352 else 2353 buffer[bufcnt++] = ~u6000[j]; 2354 break; 2355 2356 case LOW: 2357 if (inverted) 2358 buffer[bufcnt++] = ~u6000[j]; 2359 else 2360 buffer[bufcnt++] = ~u3000[j]; 2361 break; 2362 2363 default: 2364 buffer[bufcnt++] = ~0; 2365 } 2366 if (bufcnt >= BUFLNG) { 2367 write(fd, buffer, BUFLNG); 2368 bufcnt = 0; 2369 } 2370 j = (j + increm) % 80; 2371 } 2372} 2373 2374/* 2375 * Delay for initial phasing 2376 */ 2377void delay ( 2378 int Delay /* delay in samples */ 2379 ) 2380{ 2381 int samples; /* samples remaining */ 2382 2383 samples = Delay; 2384 memset(buffer, 0, BUFLNG); 2385 while (samples >= BUFLNG) { 2386 write(fd, buffer, BUFLNG); 2387 samples -= BUFLNG; 2388 } 2389 write(fd, buffer, samples); 2390} 2391 2392 2393/* Calc day of year from year month & day */ 2394/* Year - 0 means 2000, 100 means 2100. */ 2395/* Month - 1 means January, 12 means December. */ 2396/* DayOfMonth - 1 is first day of month */ 2397int 2398ConvertMonthDayToDayOfYear (int YearValue, int MonthValue, int DayOfMonthValue) 2399 { 2400 int ReturnValue; 2401 int LeapYear; 2402 int MonthCounter; 2403 2404 /* Array of days in a month. Note that here January is zero. */ 2405 /* NB: have to add 1 to days in February in a leap year! */ 2406 int DaysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 2407 2408 2409 LeapYear = FALSE; 2410 if ((YearValue % 4) == 0) 2411 { 2412 if ((YearValue % 100) == 0) 2413 { 2414 if ((YearValue % 400) == 0) 2415 { 2416 LeapYear = TRUE; 2417 } 2418 } 2419 else 2420 { 2421 LeapYear = TRUE; 2422 } 2423 } 2424 2425 if (Debug) 2426 printf ("\nConvertMonthDayToDayOfYear(): Year %d %s a leap year.\n", YearValue+2000, LeapYear ? "is" : "is not"); 2427 2428 /* Day of month given us starts in this algorithm. */ 2429 ReturnValue = DayOfMonthValue; 2430 2431 /* Add in days in month for each month past January. */ 2432 for (MonthCounter=1; MonthCounter<MonthValue; MonthCounter++) 2433 { 2434 ReturnValue += DaysInMonth [ MonthCounter - 1 ]; 2435 } 2436 2437 /* Add a day for leap years where we are past February. */ 2438 if ((LeapYear) && (MonthValue > 2)) 2439 { 2440 ReturnValue++; 2441 } 2442 2443 if (Debug) 2444 printf ("\nConvertMonthDayToDayOfYear(): %4.4d-%2.2d-%2.2d represents day %3d of year.\n", 2445 YearValue+2000, MonthValue, DayOfMonthValue, ReturnValue); 2446 2447 return (ReturnValue); 2448 } 2449 2450 2451void 2452Help ( void ) 2453 { 2454 printf ("\n\nTime Code Generation - IRIG-B or WWV, v%d.%d, %s dmw", VERSION, ISSUE, ISSUE_DATE); 2455 printf ("\n\nRCS Info:"); 2456 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 "); 2457 printf ("\n\nUsage: %s [option]*", CommandName); 2458 printf ("\n\nOptions: -a device_name Output audio device name (default /dev/audio)"); 2459 printf ( "\n -b yymmddhhmm Remove leap second at end of minute specified"); 2460 printf ( "\n -c seconds_to_send Number of seconds to send (default 0 = forever)"); 2461 printf ( "\n -d Start with IEEE 1344 DST active"); 2462 printf ( "\n -f format_type i = Modulated IRIG-B 1998 (no year coded)"); 2463 printf ( "\n 2 = Modulated IRIG-B 2002 (year coded)"); 2464 printf ( "\n 3 = Modulated IRIG-B w/IEEE 1344 (year & control funcs) (default)"); 2465 printf ( "\n 4 = Unmodulated IRIG-B w/IEEE 1344 (year & control funcs)"); 2466 printf ( "\n 5 = Inverted unmodulated IRIG-B w/IEEE 1344 (year & control funcs)"); 2467 printf ( "\n w = WWV(H)"); 2468 printf ( "\n -g yymmddhhmm Switch into/out of DST at beginning of minute specified"); 2469 printf ( "\n -i yymmddhhmm Insert leap second at end of minute specified"); 2470 printf ( "\n -j Disable time rate correction against system clock (default enabled)"); 2471 printf ( "\n -k nn Force rate correction for testing (+1 = add cycle, -1 = remove cycle)"); 2472 printf ( "\n -l time_offset Set offset of time sent to UTC as per computer, +/- float hours"); 2473 printf ( "\n -o time_offset Set IEEE 1344 time offset, +/-, to 0.5 hour (default 0)"); 2474 printf ( "\n -q quality_code_hex Set IEEE 1344 quality code (default 0)"); 2475 printf ( "\n -r sample_rate Audio sample rate (default 8000)"); 2476 printf ( "\n -s Set leap warning bit (WWV[H] only)"); 2477 printf ( "\n -t sync_frequency WWV(H) on-time pulse tone frequency (default 1200)"); 2478 printf ( "\n -u DUT1_offset Set WWV(H) DUT1 offset -7 to +7 (default 0)"); 2479#ifndef HAVE_SYS_SOUNDCARD_H 2480 printf ( "\n -v initial_output_level Set initial output level (default %d, must be 0 to 255)", AUDIO_MAX_GAIN/8); 2481#endif 2482 printf ( "\n -x Turn off verbose output (default on)"); 2483 printf ( "\n -y yymmddhhmmss Set initial date and time as specified (default system time)"); 2484 printf ("\n\nThis software licenced under the GPL, modifications performed 2006 & 2007 by Dean Weiten"); 2485 printf ( "\nContact: Dean Weiten, Norscan Instruments Ltd., Winnipeg, MB, Canada, ph (204)-233-9138, E-mail dmw@norscan.com"); 2486 printf ("\n\n"); 2487 } 2488 2489/* Reverse string order for nicer print. */ 2490void 2491ReverseString(char *str) 2492 { 2493 int StringLength; 2494 int IndexCounter; 2495 int CentreOfString; 2496 char TemporaryCharacter; 2497 2498 2499 StringLength = strlen(str); 2500 CentreOfString = (StringLength/2)+1; 2501 for (IndexCounter = StringLength; IndexCounter >= CentreOfString; IndexCounter--) 2502 { 2503 TemporaryCharacter = str[IndexCounter-1]; 2504 str[IndexCounter-1] = str[StringLength-IndexCounter]; 2505 str[StringLength-IndexCounter] = TemporaryCharacter; 2506 } 2507 } 2508 2509