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
main(int argc,char ** argv)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