// // comdcd2 -- Count 60 Hz cycles using serial port DCD pin. // // Proof of concept program to measure 60 Hz frequency. // - Get a low voltage (5 to 12 Vrms) AC (not DC) transformer. // - Connect one wire to serial port pin 5 (Gnd). // - Connect the other wire to serial port pin 1 (DCD). // - Run this program with the COM port number as argument. // // 04-Jul-2011 tvb // #include #include #include #define V 12 void com_open (int port, int baud); void com_close (void); long get_pin1_DCD (void); void set_pin2_TX (char data); long get_pin3_RX (void); void set_pin4_DTR (int v); long get_pin6_DSR (void); void set_pin7_RTS (int v); long get_pin8_CTS (void); long get_pin9_RI (void); void wait_pin1_DCD (void); double microclock (void); double get_fmjd (void); // Wait for rising edge of pin1/DCD void wait_pin1_DCD_rising (void) { do { wait_pin1_DCD(); } while (get_pin1_DCD() < 0); } main (int argc, char *argv[]) { int port; double mjd_start, mjd_now, mjd_elapsed, us_start, us_now, us_elapsed; long cycles; port = (argc > 1) ? atoi(argv[1]) : 1; com_open(port, 9600); printf("port%d: waiting for rising edge of DCD(pin1)\n", port); wait_pin1_DCD_rising(); us_start = microclock(); mjd_start = get_fmjd(); cycles = 0; while (1) { wait_pin1_DCD_rising(); us_now = microclock(); mjd_now = get_fmjd(); // This is millisecond precise and accurate PC time. mjd_elapsed = mjd_now - mjd_start; // This is microsecond precise but less accurate PC time. us_elapsed = us_now - us_start; cycles += 1; // Display counts every 60 cycles. if ((cycles % 60) == 0) { double power_line_time = cycles / 60.0; double elapsed_time = mjd_elapsed * 86400.0; double time_error = power_line_time - elapsed_time; printf("%.9lf mjd, %ld cycles, %.3lf sec, %.3lf\n", mjd_now, cycles, mjd_elapsed * 86400.0, time_error); } } } // ------------------------------------------------------------------------ // // // Win32 specific code for modem signals. // #define WIN32_LEAN_AND_MEAN #include // Convert year, month (1-12), and day (1-31) to Modified Julian Day (MJD). // Adapted from sci.astro FAQ (valid for Gregorian dates from 17-Nov-1858). long date_to_mjd (long yyyy, long mm, long dd) { return 367 * yyyy - 7 * (yyyy + (mm + 9) / 12) / 4 - 3 * ((yyyy + (mm - 9) / 7) / 100 + 1) / 4 + 275 * mm / 9 + dd + 1721028 - 2400000; } // Convert hours, minutes, and seconds to elapsed seconds since midnight. #define STOD(hh,mm,ss) ( ((( (hh) * 60) + (mm) ) * 60) + (ss) ) // Convert Win32 system time to floating point fractional MJD. // - based on system time clock, which follows UTC (if NTP is used). double st_to_fmjd (SYSTEMTIME *st) { long mjd = date_to_mjd(st->wYear, st->wMonth, st->wDay); long sec = STOD(st->wHour, st->wMinute, st->wSecond); long ms = sec * 1000 + st->wMilliseconds; return (double)mjd + ms / (1000.0 * 86400.0); } // Return floating point MJD with millisecond resolution. double get_fmjd (void) { SYSTEMTIME st; GetSystemTime(&st); return st_to_fmjd(&st); } // 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; } #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 CommPath[100]; DCB Dcb; BOOL Success; sprintf(CommPath, "\\\\.\\COM%d", port); hCom = CreateFile(CommPath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hCom == INVALID_HANDLE_VALUE) { fprintf(stderr, "CreateFile failed: %s (%.8x)\n", CommPath, 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; Dcb.fDtrControl = DTR_CONTROL_ENABLE; Dcb.fRtsControl = RTS_CONTROL_ENABLE; Success = SetCommState(hCom, &Dcb); ASSERT_SUCCESS(SetCommState); printf("Opened: %s\n", CommPath); } void com_close (void) { CloseHandle(hCom); } // Note: TX = Transmit Data void set_pin2_TX (char data) { DWORD count; BOOL Success = WriteFile(hCom, &data, 1, &count, 0); ASSERT_SUCCESS(WriteFile); } // Note: DTR = Data Terminal Ready void set_pin4_DTR (int v) { BOOL Success = EscapeCommFunction(hCom, v > 0 ? SETDTR : CLRDTR); ASSERT_SUCCESS(EscapeCommFunction); } // Note: RTS = Request to Send void set_pin7_RTS (int v) { BOOL Success = EscapeCommFunction(hCom, v > 0 ? SETRTS : CLRRTS); ASSERT_SUCCESS(EscapeCommFunction); } // Note: RLSD = Receive Line Signal Detect // a/k/a CD = Carrier Detect // a/k/a DCD = Data Carrier Detect long get_pin1_DCD (void) { DWORD Modem; BOOL Success = GetCommModemStatus(hCom, &Modem); ASSERT_SUCCESS(GetCommModemStatus); return (Modem & MS_RLSD_ON) ? V : -V; } // Note: DSR = Data Set Ready long get_pin6_DSR (void) { DWORD Modem; BOOL Success = GetCommModemStatus(hCom, &Modem); ASSERT_SUCCESS(GetCommModemStatus); return (Modem & MS_DSR_ON) ? V : -V; } // Note: CTS = Clear to Send long get_pin8_CTS (void) { DWORD Modem; BOOL Success = GetCommModemStatus(hCom, &Modem); ASSERT_SUCCESS(GetCommModemStatus); return (Modem & MS_CTS_ON) ? V : -V; } // Note: RI = Ring Indicator long get_pin9_RI (void) { DWORD Modem; BOOL Success = GetCommModemStatus(hCom, &Modem); ASSERT_SUCCESS(GetCommModemStatus); return (Modem & MS_RING_ON) ? V : -V; } // Wait for pin1 to change. void wait_pin1_DCD (void) { DWORD Event = EV_RLSD; BOOL Success = SetCommMask(hCom, Event); ASSERT_SUCCESS(SetCommMask); Success = WaitCommEvent(hCom, &Event, NULL); ASSERT_SUCCESS(WaitCommEvent); } // // RS-232 DE9P (9-pin male plug). // COM1 on backside of a PC 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) // // // Quiescent Voltages: // // 1 2 3 4 5 6 7 8 9 // // - - -12 -12 0 - +12 - - // // Asserting RTS/DTR makes the pin +12 V (nominal) // // // RS-232 DE9S (9-pin female socket). // as seen on a device connected to a PC. // // 5 4 3 2 1 // +-----------------------+ // \ o o o o o / // \ / // \ o o o o / // +---------------+ // 9 8 7 6 // // // RS232 idle: -V // start bit: +V //