📄 refclock_neoclock4x.c
字号:
/* * * Refclock_neoclock4x.c * - NeoClock4X driver for DCF77 or FIA Timecode * * Date: 2004-04-07 v1.14 * * see http://www.linum.com/redir/jump/id=neoclock4x&action=redir * for details about the NeoClock4X device * * Copyright (C) 2002-2004 by Linum Software GmbH <neoclock4x@linum.com> * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * */#ifdef HAVE_CONFIG_H# include "config.h"#endif#if defined(REFCLOCK) && (defined(CLOCK_NEOCLOCK4X))#include <unistd.h>#include <sys/time.h>#include <sys/types.h>#include <termios.h>#include <sys/ioctl.h>#include <ctype.h>#include "ntpd.h"#include "ntp_io.h"#include "ntp_control.h"#include "ntp_refclock.h"#include "ntp_unixtime.h"#include "ntp_stdlib.h"#if defined HAVE_SYS_MODEM_H# include <sys/modem.h># ifndef __QNXNTO__# define TIOCMSET MCSETA# define TIOCMGET MCGETA# define TIOCM_RTS MRTS# endif#endif#ifdef HAVE_TERMIOS_H# ifdef TERMIOS_NEEDS__SVID3# define _SVID3# endif# include <termios.h># ifdef TERMIOS_NEEDS__SVID3# undef _SVID3# endif#endif#ifdef HAVE_SYS_IOCTL_H# include <sys/ioctl.h>#endif/* * NTP version 4.20 change the pp->msec field to pp->nsec. * To allow to support older ntp versions with this sourcefile * you can define NTP_REP_420 to allow this driver to compile * with ntp version back to 4.1.2. * */#if 0#define NTP_PRE_420#endif/* * If you want the driver for whatever reason to not use * the TX line to send anything to your NeoClock4X * device you must tell the NTP refclock driver which * firmware you NeoClock4X device uses. * * If you want to enable this feature change the "#if 0" * line to "#if 1" and make sure that the defined firmware * matches the firmware off your NeoClock4X receiver! * */#if 0#define NEOCLOCK4X_FIRMWARE NEOCLOCK4X_FIRMWARE_VERSION_A#endif/* at this time only firmware version A is known */#define NEOCLOCK4X_FIRMWARE_VERSION_A 'A'#define NEOCLOCK4X_TIMECODELEN 37#define NEOCLOCK4X_OFFSET_SERIAL 3#define NEOCLOCK4X_OFFSET_RADIOSIGNAL 9#define NEOCLOCK4X_OFFSET_DAY 12#define NEOCLOCK4X_OFFSET_MONTH 14#define NEOCLOCK4X_OFFSET_YEAR 16#define NEOCLOCK4X_OFFSET_HOUR 18#define NEOCLOCK4X_OFFSET_MINUTE 20#define NEOCLOCK4X_OFFSET_SECOND 22#define NEOCLOCK4X_OFFSET_HSEC 24#define NEOCLOCK4X_OFFSET_DOW 26#define NEOCLOCK4X_OFFSET_TIMESOURCE 28#define NEOCLOCK4X_OFFSET_DSTSTATUS 29#define NEOCLOCK4X_OFFSET_QUARZSTATUS 30#define NEOCLOCK4X_OFFSET_ANTENNA1 31#define NEOCLOCK4X_OFFSET_ANTENNA2 33#define NEOCLOCK4X_OFFSET_CRC 35#define NEOCLOCK4X_DRIVER_VERSION "1.14 (2004-04-07)"struct neoclock4x_unit { l_fp laststamp; /* last receive timestamp */ short unit; /* NTP refclock unit number */ u_long polled; /* flag to detect noreplies */ char leap_status; /* leap second flag */ int recvnow; char firmware[80]; char firmwaretag; char serial[7]; char radiosignal[4]; char timesource; char dststatus; char quarzstatus; int antenna1; int antenna2; int utc_year; int utc_month; int utc_day; int utc_hour; int utc_minute; int utc_second; int utc_msec;};static int neoclock4x_start P((int, struct peer *));static void neoclock4x_shutdown P((int, struct peer *));static void neoclock4x_receive P((struct recvbuf *));static void neoclock4x_poll P((int, struct peer *));static void neoclock4x_control P((int, struct refclockstat *, struct refclockstat *, struct peer *));static int neol_atoi_len P((const char str[], int *, int));static int neol_hexatoi_len P((const char str[], int *, int));static void neol_jdn_to_ymd P((unsigned long, int *, int *, int *));static void neol_localtime P((unsigned long, int* , int*, int*, int*, int*, int*));static unsigned long neol_mktime P((int, int, int, int, int, int));#if 0static void neol_mdelay P((int));#endif#if !defined(NEOCLOCK4X_FIRMWARE)static int neol_query_firmware P((int, int, char *, int));static int neol_check_firmware P((int, const char*, char *));#endifstruct refclock refclock_neoclock4x = { neoclock4x_start, /* start up driver */ neoclock4x_shutdown, /* shut down driver */ neoclock4x_poll, /* transmit poll message */ neoclock4x_control, noentry, /* initialize driver (not used) */ noentry, /* not used */ NOFLAGS /* not used */};static intneoclock4x_start(int unit, struct peer *peer){ struct neoclock4x_unit *up; struct refclockproc *pp; int fd; char dev[20]; int sl232;#if defined(HAVE_TERMIOS) struct termios termsettings;#endif#if !defined(NEOCLOCK4X_FIRMWARE) int tries;#endif (void) snprintf(dev, sizeof(dev)-1, "/dev/neoclock4x-%d", unit); /* LDISC_STD, LDISC_RAW * Open serial port. Use CLK line discipline, if available. */ fd = refclock_open(dev, B2400, LDISC_STD); if(fd <= 0) { return (0); }#if defined(HAVE_TERMIOS)#if 1 if(tcgetattr(fd, &termsettings) < 0) { msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit); (void) close(fd); return (0); } /* 2400 Baud 8N2 */ termsettings.c_iflag = IGNBRK | IGNPAR | ICRNL; termsettings.c_oflag = 0; termsettings.c_cflag = CS8 | CSTOPB | CLOCAL | CREAD; (void)cfsetispeed(&termsettings, (u_int)B2400); (void)cfsetospeed(&termsettings, (u_int)B2400); if(tcsetattr(fd, TCSANOW, &termsettings) < 0) { msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit); (void) close(fd); return (0); }#else if(tcgetattr(fd, &termsettings) < 0) { msyslog(LOG_CRIT, "NeoClock4X(%d): (tcgetattr) can't query serial port settings: %m", unit); (void) close(fd); return (0); } /* 2400 Baud 8N2 */ termsettings.c_cflag &= ~PARENB; termsettings.c_cflag |= CSTOPB; termsettings.c_cflag &= ~CSIZE; termsettings.c_cflag |= CS8; if(tcsetattr(fd, TCSANOW, &termsettings) < 0) { msyslog(LOG_CRIT, "NeoClock4X(%d): (tcsetattr) can't set serial port 2400 8N2: %m", unit); (void) close(fd); return (0); }#endif#elif defined(HAVE_SYSV_TTYS) if(ioctl(fd, TCGETA, &termsettings) < 0) { msyslog(LOG_CRIT, "NeoClock4X(%d): (TCGETA) can't query serial port settings: %m", unit); (void) close(fd); return (0); } /* 2400 Baud 8N2 */ termsettings.c_cflag &= ~PARENB; termsettings.c_cflag |= CSTOPB; termsettings.c_cflag &= ~CSIZE; termsettings.c_cflag |= CS8; if(ioctl(fd, TCSETA, &termsettings) < 0) { msyslog(LOG_CRIT, "NeoClock4X(%d): (TSGETA) can't set serial port 2400 8N2: %m", unit); (void) close(fd); return (0); }#else msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set port to 2400 8N2 with this OS!", unit); (void) close(fd); return (0);#endif#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS)) /* turn on RTS, and DTR for power supply */ /* NeoClock4x is powered from serial line */ if(ioctl(fd, TIOCMGET, (caddr_t)&sl232) == -1) { msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit); (void) close(fd); return (0); }#ifdef TIOCM_RTS sl232 = sl232 | TIOCM_DTR | TIOCM_RTS; /* turn on RTS, and DTR for power supply */#else sl232 = sl232 | CIOCM_DTR | CIOCM_RTS; /* turn on RTS, and DTR for power supply */#endif if(ioctl(fd, TIOCMSET, (caddr_t)&sl232) == -1) { msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit); (void) close(fd); return (0); }#else msyslog(LOG_EMERG, "NeoClock4X(%d): don't know how to set DTR/RTS to power NeoClock4X with this OS!", unit); (void) close(fd); return (0);#endif up = (struct neoclock4x_unit *) emalloc(sizeof(struct neoclock4x_unit)); if(!(up)) { msyslog(LOG_ERR, "NeoClock4X(%d): can't allocate memory for: %m",unit); (void) close(fd); return (0); } memset((char *)up, 0, sizeof(struct neoclock4x_unit)); pp = peer->procptr; pp->clockdesc = "NeoClock4X"; pp->unitptr = (caddr_t)up; pp->io.clock_recv = neoclock4x_receive; pp->io.srcclock = (caddr_t)peer; pp->io.datalen = 0; pp->io.fd = fd; /* * no fudge time is given by user! * use 169.583333 ms to compensate the serial line delay * formula is: * 2400 Baud / 11 bit = 218.18 charaters per second * (NeoClock4X timecode len) */ pp->fudgetime1 = (NEOCLOCK4X_TIMECODELEN * 11) / 2400.0; /* * Initialize miscellaneous variables */ peer->precision = -10; peer->burst = NSTAGE; memcpy((char *)&pp->refid, "neol", 4); up->leap_status = 0; up->unit = unit; strcpy(up->firmware, "?"); up->firmwaretag = '?'; strcpy(up->serial, "?"); strcpy(up->radiosignal, "?"); up->timesource = '?'; up->dststatus = '?'; up->quarzstatus = '?'; up->antenna1 = -1; up->antenna2 = -1; up->utc_year = 0; up->utc_month = 0; up->utc_day = 0; up->utc_hour = 0; up->utc_minute = 0; up->utc_second = 0; up->utc_msec = 0;#if defined(NEOCLOCK4X_FIRMWARE)#if NEOCLOCK4X_FIRMWARE == NEOCLOCK4X_FIRMWARE_VERSION_A strcpy(up->firmware, "(c) 2002 NEOL S.A. FRANCE / L0.01 NDF:A:* (compile time)"); up->firmwaretag = 'A';#else msyslog(LOG_EMERG, "NeoClock4X(%d): Unkown firmware defined at compile time for NeoClock4X", unit); (void) close(fd); pp->io.fd = -1; free(pp->unitptr); pp->unitptr = NULL; return (0);#endif#else for(tries=0; tries < 5; tries++) { NLOG(NLOG_CLOCKINFO) msyslog(LOG_INFO, "NeoClock4X(%d): checking NeoClock4X firmware version (%d/5)", unit, tries); /* wait 3 seconds for receiver to power up */ sleep(3); if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware))) { break; } } /* can I handle this firmware version? */ if(!neol_check_firmware(up->unit, up->firmware, &up->firmwaretag)) { (void) close(fd); pp->io.fd = -1; free(pp->unitptr); pp->unitptr = NULL; return (0); }#endif if(!io_addclock(&pp->io)) { msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m", unit); (void) close(fd); pp->io.fd = -1; free(pp->unitptr); pp->unitptr = NULL; return (0); } NLOG(NLOG_CLOCKINFO) msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit); return (1);}static voidneoclock4x_shutdown(int unit, struct peer *peer){ struct neoclock4x_unit *up; struct refclockproc *pp; int sl232; if(NULL != peer) { pp = peer->procptr; if(pp != NULL) { up = (struct neoclock4x_unit *)pp->unitptr; if(up != NULL) { if(-1 != pp->io.fd) {#if defined(TIOCMSET) && (defined(TIOCM_RTS) || defined(CIOCM_RTS)) /* turn on RTS, and DTR for power supply */ /* NeoClock4x is powered from serial line */ if(ioctl(pp->io.fd, TIOCMGET, (caddr_t)&sl232) == -1) { msyslog(LOG_CRIT, "NeoClock4X(%d): can't query RTS/DTR state: %m", unit); }#ifdef TIOCM_RTS /* turn on RTS, and DTR for power supply */ sl232 &= ~(TIOCM_DTR | TIOCM_RTS);#else /* turn on RTS, and DTR for power supply */ sl232 &= ~(CIOCM_DTR | CIOCM_RTS);#endif if(ioctl(pp->io.fd, TIOCMSET, (caddr_t)&sl232) == -1) { msyslog(LOG_CRIT, "NeoClock4X(%d): can't set RTS/DTR to power neoclock4x: %m", unit); }#endif io_closeclock(&pp->io); } free(up); pp->unitptr = NULL; } } } msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit); NLOG(NLOG_CLOCKINFO) msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit);}static voidneoclock4x_receive(struct recvbuf *rbufp){ struct neoclock4x_unit *up; struct refclockproc *pp; struct peer *peer; unsigned long calc_utc; int day; int month; /* ddd conversion */ int c; int dsec; unsigned char calc_chksum; int recv_chksum; peer = (struct peer *)rbufp->recv_srcclock; pp = peer->procptr; up = (struct neoclock4x_unit *)pp->unitptr; /* wait till poll interval is reached */ if(0 == up->recvnow) return; /* reset poll interval flag */ up->recvnow = 0; /* read last received timecode */ pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &pp->lastrec); pp->leap = LEAP_NOWARNING; if(NEOCLOCK4X_TIMECODELEN != pp->lencode) { NLOG(NLOG_CLOCKEVENT) msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid length, expected %d bytes, received %d bytes: %s", up->unit, NEOCLOCK4X_TIMECODELEN, pp->lencode, pp->a_lastcode); refclock_report(peer, CEVNT_BADREPLY); return; } neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_CRC], &recv_chksum, 2); /* calculate checksum */ calc_chksum = 0; for(c=0; c < NEOCLOCK4X_OFFSET_CRC; c++) { calc_chksum += pp->a_lastcode[c]; } if(recv_chksum != calc_chksum) { NLOG(NLOG_CLOCKEVENT) msyslog(LOG_WARNING, "NeoClock4X(%d): received data has invalid chksum: %s", up->unit, pp->a_lastcode); refclock_report(peer, CEVNT_BADREPLY); return; } /* Allow synchronization even is quartz clock is * never initialized. * WARNING: This is dangerous! */ up->quarzstatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_QUARZSTATUS]; if(0==(pp->sloppyclockflag & CLK_FLAG2)) { if('I' != up->quarzstatus) { NLOG(NLOG_CLOCKEVENT) msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is not initialized: %s", up->unit, pp->a_lastcode); pp->leap = LEAP_NOTINSYNC; refclock_report(peer, CEVNT_BADDATE); return; } } if('I' != up->quarzstatus) { NLOG(NLOG_CLOCKEVENT) msyslog(LOG_NOTICE, "NeoClock4X(%d): using uninitialized quartz clock for time synchronization: %s", up->unit, pp->a_lastcode); } /* * If NeoClock4X is not synchronized to a radio clock * check if we're allowed to synchronize with the quartz * clock. */ up->timesource = pp->a_lastcode[NEOCLOCK4X_OFFSET_TIMESOURCE]; if(0==(pp->sloppyclockflag & CLK_FLAG2)) { if('A' != up->timesource) { /* not allowed to sync with quartz clock */ if(0==(pp->sloppyclockflag & CLK_FLAG1)) { refclock_report(peer, CEVNT_BADTIME); pp->leap = LEAP_NOTINSYNC; return; } } } /* this should only used when first install is done */ if(pp->sloppyclockflag & CLK_FLAG4) { msyslog(LOG_DEBUG, "NeoClock4X(%d): received data: %s", up->unit, pp->a_lastcode); } /* 123456789012345678901234567890123456789012345 */ /* S/N123456DCF1004021010001202ASX1213CR\r\n */ neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_YEAR], &pp->year, 2); neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MONTH], &month, 2); neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_DAY], &day, 2); neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HOUR], &pp->hour, 2); neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_MINUTE], &pp->minute, 2);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -