// // tco1.exe - Convert WWVB time code text into a usuable TCO signal. // // - The timecode is read from standard input. The format is the // typical 60 character string of WWVB 0's, 1's, and 2's. Comment // lines or non-subcode fields are ignored. // // - Output is Tx pin, DB9P pin 3, and ground is DB9P pin 5. // // - Use a series diode to keep TCO signal levels positive. // - Use a resistor divider to achieve proper voltage levels. // - Double check signal with a 'scope before applying to TCO input. // // - This tool works as long as your COM port operates down to 10 // baud and as long as the baud rate is precise. The good news is // that all onboard COM ports I tested this week work at 10 baud. // // - The bad news is that most COM ports that I've tested this week // seem to run fast by about 0.16 percent, regardless of the rate. // - Don't know the reason yet. 9600 baud is more like 9615 baud and // 10 baud is closer to 10.016 baud. // - It doesn't sound like much but it will cause the bits to skew // by a few milliseconds per second. WWVB clocks can't sync when // the TCO has that much drift. Note 0.16 percent is 1600 ppm. // // - Use baudrate.exe to check COM port accuracy and stability. // // - Sample usage for COM1: wwvb1 | tco1 // // 08-Feb-2005 /tvb // #include #include #include #define WIN32_LEAN_AND_MEAN #include #include "comlib.h" void subcode (char *bits, int size); void trim (char *s); int main (int argc, char *argv[]) { int length; int port, baud; char line[1000]; char *p, *field; // Look for about 60 zeros, ones, and twos somewhere in the line port = (argc > 1) ? atoi(argv[1]) : 1; baud = 10; CommOpen(port, baud, 0, 0); while (fgets(line, sizeof line, stdin) != NULL) { trim(line); p = line; while (*p) { while (*p && isspace(*p)) { p++; // Skip leading white space } if (*p) { field = p; length = 0; } while (*p && !isspace(*p)) { p++; // Skip until next white space length++; } if (length >= 59 && length <= 61) { subcode(field, length); break; } } } CommClose(); return 0; } // // The WWVB 1 Hz subcode consists of 0.2 0.5 and 0.8 second wide pulses. // #define WWVB_CHAR(c) ( (c) == '0' || (c) == '1' || (c) == '2' ) void subcode (char *bits, int size) { int i; unsigned char c, b; for (i = 0; i < size; i += 1) { if ( ! WWVB_CHAR(bits[i]) ) { printf("skip: %s\n", bits); return; } } for (i = 0; i < size; i += 1) { // // RS-232 serial Tx levels are inverted in the sense that a zero // bit becomes a +V voltage and a one bit becomes a -V voltage. // The start bit looks like a zero bit and the stop a one bit. // Also bit order is reversed: the LSB is transmitted first. // // So when the UART is configured for 10 baud (yes, 10 baud) the // code below translates 0, 1, or 2 into a special bit pattern // that gives 200, 500, or 800 millisecond wide pulses at a 1 Hz // rate. Exactly what we need for WWVB. Magic. // c = bits[i]; switch (c) { case '0' : // FE is 1111 1110 and becomes a HHLLLLLLLL pulse b = 0xFE; break; case '1' : // F0 is 1111 0000 and becomes a HHHHHLLLLL pulse b = 0xF0; break; case '2' : // 80 is 1000 0000 and becomes a HHHHHHHHLL pulse b = 0x80; break; default : return; } putchar(c); CommWrite(&b, 1); } printf("\n"); } int iswhite (char c) { return (c == '\r') || (c == '\n') || (c == ' ') || (c == '\t'); } void trim (char *s) { while (*s && iswhite(s[strlen(s) - 1])) { s[strlen(s) - 1] = '\0'; } } #include "comlib.c"