// // pctsc -- PC Time Stamping Counter using serial port. // // - Input is serial port pin 1 (DCD) // - Note serial port pin 5 is Gnd. // - Displays precise time stamp for each event. // - Run this program with the COM port number as argument. // // 28-Sep-2011 Tom Van Baak (tvb) www.LeapSecond.com/tools // #include #include void com_open (int port, int baud); void com_close (void); void com_wait_DCD_rising (void); double microclock (void); char *get_date_time_str (int utc); void main (int argc, char *argv[]) { int com_port; char *date_time; long interval, event_count, max_time; double elapsed_time, elapsed_prev, time_interval; printf("PCTSC -- PC-based Time Stamping Counter\n"); if (argc < 2) { fprintf(stderr, "Usage: %s [COM port] [nth] [max time]\n", argv[0]); fprintf(stderr, "Example: %s 3\n", argv[0]); fprintf(stderr, "Example: %s 1 100\n", argv[0]); exit(1); } com_port = (argc > 1) ? atoi(argv[1]) : 1; interval = (argc > 2) ? atol(argv[2]) : 1; max_time = (argc > 3) ? atol(argv[3]) : 0; if (interval < 1) interval = 1; if (max_time < 0) max_time = 0; com_open(com_port, 9600); printf("Waiting for rising edge of DCD (pin1)\n"); com_wait_DCD_rising(); (void) microclock(); // first call zeros clock event_count = 0; elapsed_prev = 0; while (1) { com_wait_DCD_rising(); elapsed_time = microclock(); event_count += 1; if ((event_count % interval) == 0) { date_time = get_date_time_str(0); time_interval = elapsed_time - elapsed_prev; elapsed_prev = elapsed_time; printf("%ld %s %.6lf %.6lf\n", event_count, date_time, elapsed_time, time_interval); fflush(stdout); } if (max_time != 0 && elapsed_time >= max_time) { break; } } com_close(); } // ------------------------------------------------------------------------ // // // Win32 specific code for modem signals. // #define WIN32_LEAN_AND_MEAN #include // Return microsecond resolution elapsed time (units are seconds). // - based on ISA clock, which is precise but not necessarily accurate. double microclock (void) { static LARGE_INTEGER base, freq = { 0, 0 }; // STATIC LARGE_INTEGER now; QueryPerformanceCounter(&now); if (freq.LowPart == 0) { QueryPerformanceFrequency(&freq); base = now; } return (double)(now.QuadPart - base.QuadPart) / (double)freq.LowPart; } // Return date and time with millisecond resolution as formatted string. char *get_date_time_str (int utc) { static char buf[100]; // STATIC SYSTEMTIME st; utc ? GetSystemTime(&st) : GetLocalTime(&st); sprintf(buf, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d.%.3d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); return buf; // STATIC } #define ASSERT_SUCCESS(Function) \ if (!Success) { \ fprintf(stderr, "File %s Line %d Function %s failed (%.8x)\n", \ __FILE__, __LINE__, #Function, GetLastError()); \ exit(1); \ } HANDLE hCom; void com_open (int port, int baud) { char path[100]; DCB Dcb; BOOL Success; sprintf(path, "\\\\.\\COM%d", port); hCom = CreateFile(path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == hCom) { fprintf(stderr, "CreateFile failed: %s (%.8x)\n", path, GetLastError()); exit(1); } memset(&Dcb, 0x0, sizeof(DCB)); Dcb.DCBlength = sizeof (DCB); Success = GetCommState(hCom, &Dcb); ASSERT_SUCCESS(GetCommState); Dcb.Parity = NOPARITY; Dcb.BaudRate = baud; Dcb.ByteSize = 8; Dcb.StopBits = ONESTOPBIT; Success = SetCommState(hCom, &Dcb); ASSERT_SUCCESS(SetCommState); printf("Opened %s\n", path); } void com_close (void) { CloseHandle(hCom); } // Wait for rising edge of pin1/DCD. void com_wait_DCD_rising (void) { DWORD Event, Modem; BOOL Success; do { Event = EV_RLSD; Success = SetCommMask(hCom, Event); ASSERT_SUCCESS(SetCommMask); Success = WaitCommEvent(hCom, &Event, NULL); ASSERT_SUCCESS(WaitCommEvent); Success = GetCommModemStatus(hCom, &Modem); ASSERT_SUCCESS(GetCommModemStatus); } while ( !(Modem & MS_RLSD_ON) ) ; } // // RS-232 DE9P (9-pin male plug). // COM1 port on rear of PC (or USB/serial adapter) looks like: // // 1 2 3 4 5 // +-----------------------+ // \ . . . . . / // \ / // \ . . . . / // +---------------+ // 6 7 8 9 // // Signal assignments (PC is DTE): // // pin 1 - DCD (PC input) // pin 2 - Rx (PC input) // pin 3 - Tx (PC output) // pin 4 - DTR (PC output) // pin 5 - ground // pin 6 - DSR (PC input) // pin 7 - RTS (PC output) // pin 8 - CTS (PC input) // pin 9 - RI (PC input) // // RS-232 DE9S (9-pin female socket). // as seen on a serial device or cable connected to a PC: // // 5 4 3 2 1 // +-----------------------+ // \ o o o o o / // \ / // \ o o o o / // +---------------+ // 9 8 7 6 //