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