From: Claas Hilbrecht Date: Thu, 18 Jul 2002 15:44:35 +0000 (+0200) Subject: Many files: X-Git-Tag: NTP_4_1_1C_RC1~23 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e49510ccd3cd0637520246221c6da76c19bd9a4c;p=thirdparty%2Fntp.git Many files: support for new refclock driver NeoClock4X neoclock4x.gif, driver44.htm, refclock_neoclock4x.c: support for new refclock driver NeoClock4X bk: 3d36e263qTcPxtPlcslZINzm5H40zQ --- diff --git a/configure.in b/configure.in index 8503d6cded..ae9d6de3bd 100644 --- a/configure.in +++ b/configure.in @@ -2152,6 +2152,15 @@ if test "$ntp_ok" = "yes"; then fi AC_MSG_RESULT($ntp_ok) +AC_MSG_CHECKING(for NeoClock4X receiver) +AC_ARG_ENABLE(NEOCLOCK4X, + AC_HELP_STRING([--enable-NEOCLOCK4X], [+ NeoClock4X DCF77 / TDF receiver]), + [ntp_ok=$enableval], [ntp_ok=$ntp_eac]) +if test "$ntp_ok" = "yes"; then + ntp_refclock=yes + AC_DEFINE(CLOCK_NEOCLOCK4X, 1, [NeoClock4X]) +fi +AC_MSG_RESULT($ntp_ok) AC_MSG_CHECKING(for default inclusion of all suitable PARSE clocks) AC_ARG_ENABLE(parse-clocks, [ --enable-parse-clocks - include all suitable PARSE clocks:], diff --git a/html/driver44.htm b/html/driver44.htm new file mode 100755 index 0000000000..0d2938426b --- /dev/null +++ b/html/driver44.htm @@ -0,0 +1,131 @@ + + + + NeoClock4X + + + + + +

NeoClock4X - DCF77 / TDF serial line receiver
+

+ +
+

Synopsis

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
Adress
+
127.127.44.u
+
Reference ID
+
neol
+
Driver ID
+
NEOCLK4X
+
Serial Port
+
/dev/neoclock4x-u
+
+
+
NeoClock4X - DCF77 receiver +
+
+ +
+

Description

+ The refclock_neoclock4x driver supports the NeoClock4X receiver available + from Linum Software GmbH. The receiver + is available as a DCF77 or TDF receiver. + Both receivers have the same output string. For more information about the + NeoClock4X receiver please visit http://www.linum.com/redir/jump/id=neoclock4x&action=redir. +   +
+

Fudge Factors

+ +
+
time1 time
+
Specifies the time offset calibration factor with the default value + off 0.16958333 seconds. This offset is used  to correct serial line and +operating system delays incurred in capturing time stamps. If you want to +fudge the time1 offset ALWAYS add a value off 0.16958333. This is +neccessary to compensate to delay that is caused by transmit the timestamp +at 2400 Baud. If you want to compensate the delay that the DCF77 or TDF radio +signal takes to travel to your site simply add the needed millisecond delay +to the given value. Note that the time here is given in seconds.
+
Default setting is 0.16958333 seconds.
+
+
+ +
+
time2 time
+
Not used by this driver.
+
+ +
+
flag1 0 | 1
+
When set to 1 the driver will feed ntp with timestampe even if the +radio signal is lost. In this case an internal backup clock generates the +timestamps. This is ok as long as the receiver is synced once since the receiver +is able to keep time for a long period.
+
Default setting is 0 = don't synchronize to CMOS clock.
+
+

+
+
flag2 0 | 1
+
You can allow the NeoClock4X driver to use the quartz clock even if + it is never synchronized to a radio clock. This is usally not a good idea + if you want preceise timestamps since the CMOS clock is maybe not adjusted + to a dst status change. So PLEASE switch this only on if you now +what you're doing.
+
Default setting is 0 = don't synchronize to unsynchronized CMOS clock.
+
+

+
+
flag3 0 | 1
+
Not used by this driver.
+

+
+
flag4 0 | 1
+
It is recommended to allow extensive logging while you setup the NeoClock4X + receiver. If you activate flag4 every received data is logged. You should + turn off flag4 as soon as the clock works as expected to reduce logfile +cluttering.
+
Default setting is 0 = don't log received data and converted utc time.
+
+
+ +
Please send any comments or question to neoclock4x@linum.com.
+
+
+ + diff --git a/html/pic/neoclock4x.gif b/html/pic/neoclock4x.gif new file mode 100755 index 0000000000..4df95af54f Binary files /dev/null and b/html/pic/neoclock4x.gif differ diff --git a/html/refclock.htm b/html/refclock.htm index 079babac20..df4af3a9df 100644 --- a/html/refclock.htm +++ b/html/refclock.htm @@ -1,254 +1,202 @@ - -Reference Clock Drivers + + + Reference Clock Drivers - + +

Reference Clock Drivers

- -gifMaster Time -Facility at the -UDel Internet Research Laboratory:
-
-

Support for most of the commonly available radio and modem -reference clocks is included in the default configuration of the -NTP daemon for Unix ntpd. Individual clocks can be -activated by configuration file commands, specifically the -server and fudge commands described in the ntpd program manual page. The following -discussion presents Information on how to select and configure the -device drivers in a running Unix system.

- -

Many radio reference clocks can be set to display local time as -adjusted for timezone and daylight saving mode. For use with NTP -the clock must be set for Coordinated Universal Time (UTC) only. -Ordinarily, these adjustments are performed by the kernel, so the -fact that the clock runs on UTC will be transparent to the -user.

- -

Radio and modem clocks by convention have addresses in the form -127.127.t.u, where t is the clock type and u -is a unit number in the range 0-3 used to distinguish multiple -instances of clocks of the same type. Most of these clocks require -support in the form of a serial port or special bus peripheral, but -some can work directly from the audio codec found in some -workstations. The particular device is normally specified by adding -a soft link /dev/deviceu to the particular hardware -device involved, where u correspond to the unit -number above.

- -

Most clock drivers communicate with the reference clock using a -serial port, usually at 9600 bps. There are several application -program interfaces (API) used in the various Unix and NT systems, -most of which can be detected at configuration time. Thus, it is -important that the NTP daemon and utilities be compiled on the -target system or clone. In some cases special features are -available, such as timestamping in the kernel or pulse-per-second -(PPS) interface. In most cases these features can be detected at -configuration time as well; however, the kernel may have to be -recompiled in order for them to work.

- -

The audio drivers are a special case. These include support for -the NIST time/frequency stations WWV and WWVH, the Canadian -time/frequency station CHU and generic IRIG signals. Currently, -support for the Solaris and SunOS audio API is included in the -distribution. It is left to the volunteer corps to extend this -support to other systems. Further information on hookup, debugging -and monitoring is given in the Audio -Drivers page.

- -

The local clock driver is also a special case. A server -configured with this driver can operate as a primary server to -synchronize other clients when no other external synchronization -sources are available. If the server is connected directly or -indirectly to the public Internet, there is some danger that it can -adversely affect the operation of unrelated clients. Carefully read -the Undisciplined Local Clock page and -respect the stratum limit.

- -

The local clock driver also supports an external synchronization -source such as a high resolution counter disciplined by a GPS -receiver, for example. Further information is on the External Clock Discipline and the Local Clock -Driver page.

- + gif +Master Time Facility at the UDel Internet Research +Laboratory:
+ +
+

Support for most of the commonly available radio and modem reference clocks +is included in the default configuration of the NTP daemon for Unix ntpd. +Individual clocks can be activated by configuration file commands, specifically +the server and fudge commands described in the ntpd program manual page. The following discussion +presents Information on how to select and configure the device drivers in +a running Unix system.

+ +

Many radio reference clocks can be set to display local time as adjusted +for timezone and daylight saving mode. For use with NTP the clock must be +set for Coordinated Universal Time (UTC) only. Ordinarily, these adjustments +are performed by the kernel, so the fact that the clock runs on UTC will +be transparent to the user.

+ +

Radio and modem clocks by convention have addresses in the form 127.127.t.u, +where t is the clock type and u is a unit number in the range +0-3 used to distinguish multiple instances of clocks of the same type. Most +of these clocks require support in the form of a serial port or special bus +peripheral, but some can work directly from the audio codec found in some +workstations. The particular device is normally specified by adding a soft +link /dev/deviceu to the particular hardware device involved, +where u correspond to the unit number above.

+ +

Most clock drivers communicate with the reference clock using a serial +port, usually at 9600 bps. There are several application program interfaces +(API) used in the various Unix and NT systems, most of which can be detected +at configuration time. Thus, it is important that the NTP daemon and utilities +be compiled on the target system or clone. In some cases special features +are available, such as timestamping in the kernel or pulse-per-second (PPS) +interface. In most cases these features can be detected at configuration +time as well; however, the kernel may have to be recompiled in order for +them to work.

+ +

The audio drivers are a special case. These include support for the NIST +time/frequency stations WWV and WWVH, the Canadian time/frequency station +CHU and generic IRIG signals. Currently, support for the Solaris and SunOS +audio API is included in the distribution. It is left to the volunteer corps +to extend this support to other systems. Further information on hookup, debugging +and monitoring is given in the Audio Drivers page.

+ +

The local clock driver is also a special case. A server configured with +this driver can operate as a primary server to synchronize other clients +when no other external synchronization sources are available. If the server +is connected directly or indirectly to the public Internet, there is some +danger that it can adversely affect the operation of unrelated clients. Carefully +read the Undisciplined Local Clock page and respect +the stratum limit.

+ +

The local clock driver also supports an external synchronization source +such as a high resolution counter disciplined by a GPS receiver, for example. +Further information is on the External Clock Discipline +and the Local Clock Driver page.

+

Driver Calibration

- -

Some drivers depending on longwave and shortwave radio services -need to know the radio propagation time from the transmitter to the -receiver, which can amount to some tens of milliseconds. This must -be calculated for each specific receiver location and requires the -geographic coordinates of both the transmitter and receiver. The -transmitter coordinates for various radio services are given in the -Stations, Frequencies and Geographic -Coordinates page. Receiver coordinates can be obtained or -estimated from various sources. The actual calculations are beyond -the scope of this document.

- -

When more than one clock driver is supported, it is often the -case that each shows small systematic offset differences relative -to the rest. To reduce the effects of jitter when switching from -one driver to the another, it is useful to calibrate the drivers to -a common ensemble offset. The enable calibrate -configuration command in the Miscellaneous -Options page is useful for this purpose. The calibration -function can also be enabled and disabled using the ntpdc -program utility.

- -

Most clock drivers use the time1 value specified in the -fudge configuration command to provide the calibration -correction when this cannot be provided by the clock or interface. -When the calibration function is enabled, the time1 value -is automatically adjusted to match the offset of the remote server -or local clock driver selected for synchronization. Ordinarily, the -NTP selection algorithm chooses the best from among all sources, -usually the best radio clock determined on the basis of stratum, -synchronization distance and jitter. The calibration function -adjusts the time1 values for all clock drivers except this -source so that their indicated offsets tend to zero. If the -selected source is the kernel PPS discipline, the fudge + +

Some drivers depending on longwave and shortwave radio services need to +know the radio propagation time from the transmitter to the receiver, which +can amount to some tens of milliseconds. This must be calculated for each +specific receiver location and requires the geographic coordinates of both +the transmitter and receiver. The transmitter coordinates for various radio +services are given in the Stations, Frequencies and Geographic +Coordinates page. Receiver coordinates can be obtained or estimated from +various sources. The actual calculations are beyond the scope of this document.

+ +

When more than one clock driver is supported, it is often the case that +each shows small systematic offset differences relative to the rest. To reduce +the effects of jitter when switching from one driver to the another, it is +useful to calibrate the drivers to a common ensemble offset. The enable +calibrate configuration command in the Miscellaneous +Options page is useful for this purpose. The calibration function can +also be enabled and disabled using the ntpdc program utility.

+ +

Most clock drivers use the time1 value specified in the fudge +configuration command to provide the calibration correction when this cannot +be provided by the clock or interface. When the calibration function is enabled, +the time1 value is automatically adjusted to match the offset of +the remote server or local clock driver selected for synchronization. Ordinarily, +the NTP selection algorithm chooses the best from among all sources, usually +the best radio clock determined on the basis of stratum, synchronization +distance and jitter. The calibration function adjusts the time1 +values for all clock drivers except this source so that their indicated offsets +tend to zero. If the selected source is the kernel PPS discipline, the fudge time1 values for all clock drivers are adjusted.

- -

The adjustment function is an exponential average designed to -improve accuracy, so the function takes some time to converge. The -recommended procedure is to enable the function, let it run for an -hour or so, then edit the configuration file using the -time1 values displayed by the ntpq utility and -clockvar command. Finally, disable the calibration function to -avoid possible future disruptions due to misbehaving clocks or -drivers.

- + +

The adjustment function is an exponential average designed to improve +accuracy, so the function takes some time to converge. The recommended procedure +is to enable the function, let it run for an hour or so, then edit the configuration +file using the time1 values displayed by the ntpq utility +and clockvar command. Finally, disable the calibration function +to avoid possible future disruptions due to misbehaving clocks or drivers.

+

Performance Enhancements

- -

In general, performance can be improved, especially when more -than one clock driver is supported, to use the prefer peer function -described in the Mitigation Rules and the -prefer Keyword page. The prefer peer is ordinarily -designated the remote peer or local clock driver which provides the -best quality time. All other things equal, only the prefer peer -source is used to discipline the system clock and jitter-producing -"clockhopping" between sources is avoided. This is valuable when -more than one clock driver is present and especially valuable when -the PPS clock driver (type 22) is used. Support for PPS signals is -summarized in the Pulse-per-second (PPS) Signal -Interfacing page.

- -

Where the highest performance is required, generally better than -one millisecond, additional hardware and/or software functions may -be required. Kernel modifications for precision time are described -in the A Kernel Model for Precision -Timekeeping page. Special line discipline and streams modules + +

In general, performance can be improved, especially when more than one +clock driver is supported, to use the prefer peer function described in the +Mitigation Rules and the prefer Keyword +page. The prefer peer is ordinarily designated the remote peer or local clock +driver which provides the best quality time. All other things equal, only +the prefer peer source is used to discipline the system clock and jitter-producing +"clockhopping" between sources is avoided. This is valuable when more than +one clock driver is present and especially valuable when the PPS clock driver +(type 22) is used. Support for PPS signals is summarized in the Pulse-per-second (PPS) Signal Interfacing page.

+ +

Where the highest performance is required, generally better than one millisecond, +additional hardware and/or software functions may be required. Kernel modifications +for precision time are described in the A Kernel Model +for Precision Timekeeping page. Special line discipline and streams modules for use in capturing precision timestamps are described in the Line Disciplines and Streams Drivers page.

- + href="ldisc.htm">Line Disciplines and Streams Drivers page.

+

Comprehensive List of Clock Drivers

- -

Following is a list showing the type and title of each driver -currently implemented. The compile-time identifier for each is -shown in parentheses. Click on a selected type for specific -description and configuration documentation, including the clock -address, reference ID, driver ID, device name and serial line -speed, and features (line disciplines, etc.). For those drivers -without specific documentation, please contact the author listed in -the Copyright Notice page.

- -

Type 1 Undisciplined Local Clock -(LOCAL)
-Type 2 Trak 8820 GPS Receiver -(GPS_TRAK)
-Type 3 PSTI/Traconex 1020 WWV/WWVH -Receiver (WWV_PST)
-Type 4 Spectracom WWVB and GPS Receivers -(WWVB_SPEC)
-Type 5 TrueTime GPS/GOES/OMEGA Receivers -(TRUETIME)
-Type 6 IRIG Audio Decoder -(IRIG_AUDIO)
-Type 7 Radio CHU Audio -Demodulator/Decoder (CHU)
-Type 8 Generic Reference Driver -(PARSE)
-Type 9 Magnavox MX4200 GPS Receiver -(GPS_MX4200)
-Type 10 Austron 2200A/2201A GPS -Receivers (GPS_AS2201)
-Type 11 Arbiter 1088A/B GPS Receiver -(GPS_ARBITER)
-Type 12 KSI/Odetics TPRO/S IRIG -Interface (IRIG_TPRO)
-Type 13 Leitch CSD 5300 Master Clock Controller -(ATOM_LEITCH)
-Type 14 EES M201 MSF Receiver (MSF_EES)
-Type 15 * TrueTime generic receivers
-Type 16 Bancomm GPS/IRIG Receiver -(GPS_BANCOMM)
-Type 17 Datum Precision Time System (GPS_DATUM)
-Type 18 NIST Modem Time Service -(ACTS_NIST)
-Type 19 Heath WWV/WWVH Receiver -(WWV_HEATH)
-Type 20 Generic NMEA GPS Receiver -(NMEA)
-Type 21 TrueTime GPS-VME Interface (GPS_VME)
-Type 22 PPS Clock Discipline -(PPS)
-Type 23 PTB Modem Time Service -(ACTS_PTB)
-Type 24 USNO Modem Time Service -(ACTS_USNO)
-Type 25 * TrueTime generic receivers
-Type 26 Hewlett Packard 58503A GPS -Receiver (GPS_HP)
-Type 27 Arcron MSF Receiver -(MSF_ARCRON)
-Type 28 Shared Memory Driver -(SHM)
-Type 29 Trimble Navigation Palisade GPS -(GPS_PALISADE)
-Type 30 Motorola UT Oncore GPS -(GPS_ONCORE)
-Type 31 Rockwell Jupiter GPS (GPS_JUPITER)
-Type 32 Chrono-log K-series WWVB -receiver (CHRONOLOG)
-Type 33 Dumb Clock (DUMBCLOCK)
-Type 34 Ultralink WWVB Receivers (ULINK)
-Type 35 Conrad Parallel Port Radio Clock -(PCF)
-Type 36 Radio WWV/H Audio -Demodulator/Decoder (WWV)
-Type 37 Forum Graphic GPS Dating station -(FG)
-Type 38 hopf GPS/DCF77 6021/komp for -Serial Line (HOPF_S)
-Type 39 hopf GPS/DCF77 6039 for PCI-Bus -(HOPF_P)
-Type 40 JJY Receivers (JJY)
-

- - -

* All TrueTime receivers are now supported by one driver, type -5. Types 15 and 25 will be retained only for a limited time and may -be reassigned in future.

- + +

Following is a list showing the type and title of each driver currently +implemented. The compile-time identifier for each is shown in parentheses. +Click on a selected type for specific description and configuration documentation, +including the clock address, reference ID, driver ID, device name and serial +line speed, and features (line disciplines, etc.). For those drivers without +specific documentation, please contact the author listed in the Copyright Notice page.

+ +

Type 1 Undisciplined Local Clock (LOCAL)
+ Type 2 Trak 8820 GPS Receiver (GPS_TRAK)
+ Type 3 PSTI/Traconex 1020 WWV/WWVH Receiver (WWV_PST)
+ Type 4 Spectracom WWVB and GPS Receivers (WWVB_SPEC)
+ Type 5 TrueTime GPS/GOES/OMEGA Receivers (TRUETIME)
+ Type 6 IRIG Audio Decoder (IRIG_AUDIO)
+ Type 7 Radio CHU Audio Demodulator/Decoder (CHU)
+ Type 8 Generic Reference Driver (PARSE)
+ Type 9 Magnavox MX4200 GPS Receiver (GPS_MX4200)
+ Type 10 Austron 2200A/2201A GPS Receivers (GPS_AS2201)
+ Type 11 Arbiter 1088A/B GPS Receiver (GPS_ARBITER)
+ Type 12 KSI/Odetics TPRO/S IRIG Interface (IRIG_TPRO)
+ Type 13 Leitch CSD 5300 Master Clock Controller (ATOM_LEITCH)
+ Type 14 EES M201 MSF Receiver (MSF_EES)
+ Type 15 * TrueTime generic receivers
+ Type 16 Bancomm GPS/IRIG Receiver (GPS_BANCOMM)
+ Type 17 Datum Precision Time System (GPS_DATUM)
+ Type 18 NIST Modem Time Service (ACTS_NIST)
+ Type 19 Heath WWV/WWVH Receiver (WWV_HEATH)
+ Type 20 Generic NMEA GPS Receiver (NMEA)
+ Type 21 TrueTime GPS-VME Interface (GPS_VME)
+ Type 22 PPS Clock Discipline (PPS)
+ Type 23 PTB Modem Time Service (ACTS_PTB)
+ Type 24 USNO Modem Time Service (ACTS_USNO)
+ Type 25 * TrueTime generic receivers
+ Type 26 Hewlett Packard 58503A GPS Receiver (GPS_HP)
+ Type 27 Arcron MSF Receiver (MSF_ARCRON)
+ Type 28 Shared Memory Driver (SHM)
+ Type 29 Trimble Navigation Palisade GPS (GPS_PALISADE)
+ Type 30 Motorola UT Oncore GPS (GPS_ONCORE)
+ Type 31 Rockwell Jupiter GPS (GPS_JUPITER)
+ Type 32 Chrono-log K-series WWVB receiver (CHRONOLOG)
+ Type 33 Dumb Clock (DUMBCLOCK)
+ Type 34 Ultralink WWVB Receivers (ULINK)
+ Type 35 Conrad Parallel Port Radio Clock (PCF)
+ Type 36 Radio WWV/H Audio Demodulator/Decoder +(WWV)
+ Type 37 Forum Graphic GPS Dating station (FG)
+ Type 38 hopf GPS/DCF77 6021/komp for Serial Line +(HOPF_S)
+ Type 39 hopf GPS/DCF77 6039 for PCI-Bus (HOPF_P)
+ Type 40 JJY Receivers (JJY)
+Type 44 NeoClock4X DCF77 / TDF receiver
+

+ +

* All TrueTime receivers are now supported by one driver, type 5. Types +15 and 25 will be retained only for a limited time and may be reassigned +in future.

+

Additional Information

- -

Mitigation Rules and the prefer -Keyword
-Debugging Hints for Reference Clock -Drivers
-A Kernel Model for Precision Timekeeping
-Line Disciplines and Streams Drivers
-Reference Clock Audio Drivers
-Pulse-per-second (PPS) Signal Interfacing
-How To Write a Reference Clock Driver

- -
--"gif" - -
David L. Mills -<mills@udel.edu>
+ +

Mitigation Rules and the prefer Keyword
+ Debugging Hints for Reference Clock Drivers
+ A Kernel Model for Precision Timekeeping
+ Line Disciplines and Streams Drivers
+ Reference Clock Audio Drivers
+ Pulse-per-second (PPS) Signal Interfacing
+ How To Write a Reference Clock Driver

+ +
gif + +
David L. Mills <mills@udel.edu>
+
- diff --git a/include/ntp.h b/include/ntp.h index 353c1f3941..5708b7c315 100644 --- a/include/ntp.h +++ b/include/ntp.h @@ -461,7 +461,8 @@ struct peer { #define REFCLK_TT560 41 /* TrueTime 560 IRIG-B decoder */ #define REFCLK_ZYFER 42 /* Zyfer GPStarplus receiver */ #define REFCLK_RIPENCC 43 /* RIPE NCC Trimble driver */ -#define REFCLK_MAX 43 /* Grow as needed... */ +#define REFCLK_NEOCLOCK4X 44 /* NeoClock4X DCF77 or TDF receiver */ +#define REFCLK_MAX 44 /* Grow as needed... */ /* * We tell reference clocks from real peers by giving the reference diff --git a/libntp/clocktypes.c b/libntp/clocktypes.c index 4e425c544b..ec99ba8814 100644 --- a/libntp/clocktypes.c +++ b/libntp/clocktypes.c @@ -98,6 +98,8 @@ struct clktype clktypes[] = { "GPS_ZYFER" }, { REFCLK_RIPENCC, "RIPE NCC Trimble driver (43)", "GPS_RIPENCC" }, + { REFCLK_NEOCLOCK4X, "NeoClock4X DCF77 / TDF receiver (44)", + "NEOCLK4X"}, { -1, "", "" } }; diff --git a/ntpd/Makefile.am b/ntpd/Makefile.am index b057cae893..94dfd092a6 100644 --- a/ntpd/Makefile.am +++ b/ntpd/Makefile.am @@ -37,7 +37,7 @@ ntpd_SOURCES = cmd_args.c jupiter.h map_vme.c ntp_config.c ntp_control.c \ refclock_pcf.c refclock_pst.c refclock_ptbacts.c refclock_shm.c \ refclock_tpro.c refclock_trak.c refclock_true.c refclock_tt560.c \ refclock_ulink.c refclock_usno.c refclock_wwv.c refclock_wwvb.c \ - refclock_zyfer.c refclock_ripencc.c + refclock_zyfer.c refclock_ripencc.c refclock_neoclock4x.c $(PROGRAMS): $(LDADD) diff --git a/ntpd/ntp_control.c b/ntpd/ntp_control.c index f1e943c30d..d7109cabef 100644 --- a/ntpd/ntp_control.c +++ b/ntpd/ntp_control.c @@ -399,6 +399,7 @@ static u_char clocktypes[] = { CTL_SST_TS_UHF, /* REFCLK_TT560 (41) */ CTL_SST_TS_UHF, /* REFCLK_ZYFER (42) */ CTL_SST_TS_UHF, /* REFCLK_RIPENCC (43) */ + CTL_SST_TS_UHF, /* REFCLK_NEOCLOCK4X (44) */ }; diff --git a/ntpd/refclock_conf.c b/ntpd/refclock_conf.c index 62949b0a48..04166d9f67 100644 --- a/ntpd/refclock_conf.c +++ b/ntpd/refclock_conf.c @@ -264,6 +264,12 @@ extern struct refclock refclock_ripencc; #define refclock_ripencc refclock_none #endif +#ifdef CLOCK_NEOCLOCK4X +extern struct refclock refclock_neoclock4x; +#else +#define refclock_neoclock4x refclock_none +#endif + /* * Order is clock_start(), clock_shutdown(), clock_poll(), * clock_control(), clock_init(), clock_buginfo, clock_flags; @@ -314,7 +320,8 @@ struct refclock *refclock_conf[] = { &refclock_jjy, /* 40 REFCLK_JJY */ &refclock_tt560, /* 41 REFCLK_TT560 */ &refclock_zyfer, /* 42 REFCLK_ZYFER */ - &refclock_ripencc /* 43 REFCLK_RIPENCC */ + &refclock_ripencc, /* 43 REFCLK_RIPENCC */ + &refclock_neoclock4x /* 44 REFCLK_NEOCLOCK4X */ }; u_char num_refclock_conf = sizeof(refclock_conf)/sizeof(struct refclock *); diff --git a/ntpd/refclock_neoclock4x.c b/ntpd/refclock_neoclock4x.c new file mode 100644 index 0000000000..3b6401752b --- /dev/null +++ b/ntpd/refclock_neoclock4x.c @@ -0,0 +1,864 @@ +/* + * + * refclock_neoclock4x.c + * - NeoClock4X driver for DCF77 or FIA Timecode + * + * Date: 2002-04-27 1.0 + * + * see http://www.linum.com/redir/jump/id=neoclock4x&action=redir + * for details about the NeoClock4X device + * + * Copyright (C) 2002 by Linum Software GmbH + * + * 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 +#include +#include +#include +#include +#include + +#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 +# define TIOCMSET MCSETA +# define TIOCMGET MCGETA +# define TIOCM_RTS MRTS +#endif + +#ifdef HAVE_TERMIOS_H +# ifdef TERMIOS_NEEDS__SVID3 +# define _SVID3 +# endif +# include +# ifdef TERMIOS_NEEDS__SVID3 +# undef _SVID3 +# endif +#endif + +#ifdef HAVE_SYS_IOCTL_H +# include +#endif + +#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 + +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 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)); +static void neol_mdelay P((int)); +static int neol_query_firmware P((int, int, char *, int)); + +struct 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 int +neoclock4x_start(int unit, + struct peer *peer) +{ + struct neoclock4x_unit *up; + struct refclockproc *pp; + int fd; + char dev[20]; + int sl232; + struct termios termsettings; + int tries; + + (void) sprintf(dev, "/dev/neoclock4x-%d", unit); + + /* LDISC_STD, LDISC_RAW + * Open serial port. Use CLK line discipline, if available. + */ + fd = refclock_open(dev, B2400, LDISC_CLK); + if(fd <= 0) + { + return (0); + } + +#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); + } +#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); + } + + if(ioctl(fd, TCGETS, (caddr_t)&termsettings) == -1) + { + msyslog(LOG_CRIT, "NeoClock4X(%d): can't query serial port settings: %m", unit); + } + + /* 2400 Baud mit 8N2 */ + termsettings.c_cflag &= ~PARENB; + termsettings.c_cflag |= CSTOPB; + termsettings.c_cflag &= ~CSIZE; + termsettings.c_cflag |= CS8; + + if(ioctl(fd, TCSETS, &termsettings) == -1) + { + msyslog(LOG_CRIT, "NeoClock4X(%d): can't set serial port to 2400 8N2: %m", unit); + } +#else + msyslog(LOG_EMERG, "NeoClock4X(%d): OS interface is incapable of setting DTR/RTS to power NeoClock4X", + unit); +#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 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; + + if (!io_addclock(&pp->io)) + { + msyslog(LOG_ERR, "NeoClock4X(%d): error add peer to ntpd: %m",unit); + (void) close(fd); + free(up); + return (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, "?"); + 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; + + for(tries=0; tries < 5; tries++) + { + /* + * Wait 3 second for receiver to power up + */ + NLOG(NLOG_CLOCKINFO) + msyslog(LOG_INFO, "NeoClock4X(%d): try query NeoClock4X firmware version (%d/5)", unit, tries); + sleep(3); + if(neol_query_firmware(pp->io.fd, up->unit, up->firmware, sizeof(up->firmware))) + { + break; + } + } + + NLOG(NLOG_CLOCKINFO) + msyslog(LOG_INFO, "NeoClock4X(%d): receiver setup successful done", unit); + + return (1); +} + +static void +neoclock4x_shutdown(int unit, + struct peer *peer) +{ + struct neoclock4x_unit *up; + struct refclockproc *pp; + int sl232; + + pp = peer->procptr; + up = (struct neoclock4x_unit *)pp->unitptr; + +#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 + sl232 &= ~(TIOCM_DTR | TIOCM_RTS); /* turn on RTS, and DTR for power supply */ +#else + sl232 &= ~(CIOCM_DTR | CIOCM_RTS); /* turn on RTS, and DTR for power supply */ +#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 + msyslog(LOG_ERR, "NeoClock4X(%d): shutdown", unit); + + io_closeclock(&pp->io); + free(up); + NLOG(NLOG_CLOCKINFO) + msyslog(LOG_INFO, "NeoClock4X(%d): receiver shutdown done", unit); +} + +static void +neoclock4x_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; + 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); + + 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); + neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_SECOND], &pp->second, 2); + neol_atoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_HSEC], &pp->msec, 2); + pp->msec *= 10; /* convert 1/100s from neoclock to real miliseconds */ + + memcpy(up->radiosignal, &pp->a_lastcode[NEOCLOCK4X_OFFSET_RADIOSIGNAL], 3); + up->radiosignal[3] = 0; + memcpy(up->serial, &pp->a_lastcode[NEOCLOCK4X_OFFSET_SERIAL], 6); + up->serial[6] = 0; + up->dststatus = pp->a_lastcode[NEOCLOCK4X_OFFSET_DSTSTATUS]; + neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA1], &up->antenna1, 2); + neol_hexatoi_len(&pp->a_lastcode[NEOCLOCK4X_OFFSET_ANTENNA2], &up->antenna2, 2); + + /* + Validate received values at least enough to prevent internal + array-bounds problems, etc. + */ + if((pp->hour < 0) || (pp->hour > 23) || + (pp->minute < 0) || (pp->minute > 59) || + (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ || + (day < 1) || (day > 31) || + (month < 1) || (month > 12) || + (pp->year < 0) || (pp->year > 99)) { + /* Data out of range. */ + NLOG(NLOG_CLOCKEVENT) + msyslog(LOG_WARNING, "NeoClock4X(%d): date/time out of range: %s", + up->unit, pp->a_lastcode); + refclock_report(peer, CEVNT_BADDATE); + return; + } + + /* Year-2000 check! */ + /* wrap 2-digit date into 4-digit */ + + if(pp->year < YEAR_PIVOT) /* < 98 */ + { + pp->year += 100; + } + pp->year += 1900; + + calc_utc = neol_mktime(pp->year, month, day, pp->hour, pp->minute, pp->second); + calc_utc -= 3600; + if('S' == up->dststatus) + calc_utc -= 3600; + neol_localtime(calc_utc, &pp->year, &month, &day, &pp->hour, &pp->minute, &pp->second); + + /* + some preparations + */ + pp->day = ymd2yd(pp->year,month,day); + pp->leap = 0; + + + if(pp->sloppyclockflag & CLK_FLAG4) + { + msyslog(LOG_DEBUG, "NeoClock4X(%d): calculated UTC date/time: %04d-%02d-%02d %02d:%02d:%02d.%03d", + up->unit, + pp->year, month, day, + pp->hour, pp->minute, pp->second, pp->msec); + } + + up->utc_year = pp->year; + up->utc_month = month; + up->utc_day = day; + up->utc_hour = pp->hour; + up->utc_minute = pp->minute; + up->utc_second = pp->second; + up->utc_msec = pp->msec; + + if(!refclock_process(pp)) + { + NLOG(NLOG_CLOCKEVENT) + msyslog(LOG_WARNING, "NeoClock4X(%d): refclock_process failed!", up->unit); + refclock_report(peer, CEVNT_FAULT); + return; + } + refclock_receive(peer); + + record_clock_stats(&peer->srcadr, pp->a_lastcode); +} + +static void +neoclock4x_poll(int unit, + struct peer *peer) +{ + struct neoclock4x_unit *up; + struct refclockproc *pp; + + pp = peer->procptr; + up = (struct neoclock4x_unit *)pp->unitptr; + + pp->polls++; + up->recvnow = 1; +} + +static void +neoclock4x_control(int unit, + struct refclockstat *in, + struct refclockstat *out, + struct peer *peer) +{ + struct neoclock4x_unit *up; + struct refclockproc *pp; + + if(NULL == peer) + { + msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit); + return; + } + + pp = peer->procptr; + if(NULL == pp) + { + msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit); + return; + } + + up = (struct neoclock4x_unit *)pp->unitptr; + if(NULL == up) + { + msyslog(LOG_ERR, "NeoClock4X(%d): control: unit invalid/inactive", unit); + return; + } + + if(NULL != in) + { + /* check to see if a user supplied time offset is given */ + if(in->haveflags & CLK_HAVETIME1) + { + pp->fudgetime1 = in->fudgetime1; + NLOG(NLOG_CLOCKINFO) + msyslog(LOG_NOTICE, "NeoClock4X(%d): using fudgetime1 with %0.5fs from ntp.conf.", + unit, pp->fudgetime1); + } + + /* notify */ + if(pp->sloppyclockflag & CLK_FLAG1) + { + NLOG(NLOG_CLOCKINFO) + msyslog(LOG_NOTICE, "NeoClock4X(%d): quartz clock is used to synchronize time if radio clock has no reception.", unit); + } + else + { + NLOG(NLOG_CLOCKINFO) + msyslog(LOG_NOTICE, "NeoClock4X(%d): time is only adjusted with radio signal reception.", unit); + } + } + + if(NULL != out) + { + static char outstatus[800]; /* status output buffer */ + char *tt; + char tmpbuf[80]; + + outstatus[0] = '\0'; + out->kv_list = (struct ctl_var *)0; + out->type = REFCLK_NEOCLOCK4X; + + sprintf(tmpbuf, "%04d-%02d-%02d %02d:%02d:%02d.%03d", + up->utc_year, up->utc_month, up->utc_day, + up->utc_hour, up->utc_minute, up->utc_second, + up->utc_msec); + + tt = add_var(&out->kv_list, 512, RO|DEF); + tt += sprintf(tt, "calc_utc=\"%s\"", tmpbuf); + tt = add_var(&out->kv_list, 512, RO|DEF); + tt += sprintf(tt, "radiosignal=\"%s\"", up->radiosignal); + tt = add_var(&out->kv_list, 512, RO|DEF); + tt += sprintf(tt, "antenna1=\"%d\"", up->antenna1); + tt = add_var(&out->kv_list, 512, RO|DEF); + tt += sprintf(tt, "antenna2=\"%d\"", up->antenna2); + tt = add_var(&out->kv_list, 512, RO|DEF); + if('A' == up->timesource) + tt += sprintf(tt, "timesource=\"radio\""); + else if('C' == up->timesource) + tt += sprintf(tt, "timesource=\"quartz\""); + else + tt += sprintf(tt, "timesource=\"unknown\""); + tt = add_var(&out->kv_list, 512, RO|DEF); + if('I' == up->quarzstatus) + tt += sprintf(tt, "quartzstatus=\"synchronized\""); + else if('X' == up->quarzstatus) + tt += sprintf(tt, "quartzstatus=\"not synchronized\""); + else + tt += sprintf(tt, "quartzstatus=\"unknown\""); + tt = add_var(&out->kv_list, 512, RO|DEF); + if('S' == up->dststatus) + tt += sprintf(tt, "dststatus=\"summer\""); + else if('W' == up->dststatus) + tt += sprintf(tt, "dststatus=\"winter\""); + else + tt += sprintf(tt, "dststatus=\"unknown\""); + tt = add_var(&out->kv_list, 512, RO|DEF); + tt += sprintf(tt, "firmware=\"%s\"", up->firmware); + tt = add_var(&out->kv_list, 512, RO|DEF); + tt += sprintf(tt, "serialnumber=\"%s\"", up->serial); + tt = add_var(&out->kv_list, 512, RO|DEF); + } +} + +static int neol_hexatoi_len(const char str[], + int *result, + int maxlen) +{ + int hexdigit; + int i; + int n = 0; + + for(i=0; isxdigit(str[i]) && i < maxlen; i++) + { + hexdigit = isdigit(str[i]) ? toupper(str[i]) - '0' : toupper(str[i]) - 'A' + 10; + n = 16 * n + hexdigit; + } + *result = n; + return (n); +} + +int neol_atoi_len(const char str[], + int *result, + int maxlen) +{ + int digit; + int i; + int n = 0; + + for(i=0; isdigit(str[i]) && i < maxlen; i++) + { + digit = str[i] - '0'; + n = 10 * n + digit; + } + *result = n; + return (n); +} + +/* Converts Gregorian date to seconds since 1970-01-01 00:00:00. + * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 + * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. + * + * [For the Julian calendar (which was used in Russia before 1917, + * Britain & colonies before 1752, anywhere else before 1582, + * and is still in use by some communities) leave out the + * -year/100+year/400 terms, and add 10.] + * + * This algorithm was first published by Gauss (I think). + * + * WARNING: this function will overflow on 2106-02-07 06:28:16 on + * machines were long is 32-bit! (However, as time_t is signed, we + * will already get problems at other places on 2038-01-19 03:14:08) + */ +static unsigned long neol_mktime(int year, + int mon, + int day, + int hour, + int min, + int sec) +{ + if (0 >= (int) (mon -= 2)) { /* 1..12 . 11,12,1..10 */ + mon += 12; /* Puts Feb last since it has leap day */ + year -= 1; + } + return ((( + (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) + + year*365 - 719499 + )*24 + hour /* now have hours */ + )*60 + min /* now have minutes */ + )*60 + sec; /* finally seconds */ +} + +static void neol_localtime(unsigned long utc, + int* year, + int* month, + int* day, + int* hour, + int* minute, + int* second) +{ + ldiv_t d; + + /* Sekunden */ + d = ldiv(utc, 60); + *second = d.rem; + + /* Minute */ + d = ldiv(d.quot, 60); + *minute = d.rem; + + /* Stunden */ + d = ldiv(d.quot, 24); + *hour = d.rem; + + /* JDN Date 1/1/1970 */ + neol_jdn_to_ymd(d.quot + 2440588L, year, month, day); +} + +static void neol_jdn_to_ymd(unsigned long jdn, + int *yy, + int *mm, + int *dd) +{ + unsigned long x, z, m, d, y; + unsigned long daysPer400Years = 146097UL; + unsigned long fudgedDaysPer4000Years = 1460970UL + 31UL; + + x = jdn + 68569UL; + z = 4UL * x / daysPer400Years; + x = x - (daysPer400Years * z + 3UL) / 4UL; + y = 4000UL * (x + 1) / fudgedDaysPer4000Years; + x = x - 1461UL * y / 4UL + 31UL; + m = 80UL * x / 2447UL; + d = x - 2447UL * m / 80UL; + x = m / 11UL; + m = m + 2UL - 12UL * x; + y = 100UL * (z - 49UL) + y + x; + + *yy = (int)y; + *mm = (int)m; + *dd = (int)d; +} + +/* + * delay in milliseconds + */ +static void +neol_mdelay(int milliseconds) +{ + struct timeval tv; + + if (milliseconds) + { + tv.tv_sec = 0; + tv.tv_usec = milliseconds * 1000; + select(1, NULL, NULL, NULL, &tv); + } +} + +static int +neol_query_firmware(int fd, + int unit, + char *firmware, + int maxlen) +{ + unsigned char tmpbuf[256]; + int len; + int lastsearch; + unsigned char c; + int last_c_was_crlf; + int last_crlf_conv_len; + int init; + int read_tries; + int flag = 0; + + /* wait a little bit */ + neol_mdelay(250); + if(-1 != write(fd, "V", 1)) + { + /* wait a little bit */ + neol_mdelay(250); + memset(tmpbuf, 0x00, sizeof(tmpbuf)); + + len = 0; + lastsearch = 0; + last_c_was_crlf = 0; + last_crlf_conv_len = 0; + init = 1; + read_tries = 0; + for(;;) + { + if(read_tries++ > 500) + { + msyslog(LOG_ERR, "NeoClock4X(%d): can't read firmware version (timeout)", unit); + strcpy(tmpbuf, "unknown due to timeout"); + break; + } + if(-1 == read(fd, &c, 1)) + { + neol_mdelay(25); + continue; + } + if(init) + { + if(0xA9 != c) /* wait for (c) char in input stream */ + continue; + + strcpy(tmpbuf, "(c)"); + len = 3; + init = 0; + continue; + } + + //msyslog(LOG_NOTICE, "NeoClock4X(%d): firmware %c = %02Xh", unit, c, c); + if(0x0A == c || 0x0D == c) + { + if(last_c_was_crlf) + { + char *ptr; + ptr = strstr(&tmpbuf[lastsearch], "S/N"); + if(NULL != ptr) + { + tmpbuf[last_crlf_conv_len] = 0; + flag = 1; + break; + } + /* convert \n to / */ + last_crlf_conv_len = len; + tmpbuf[len++] = ' '; + tmpbuf[len++] = '/'; + tmpbuf[len++] = ' '; + lastsearch = len; + } + last_c_was_crlf = 1; + } + else + { + last_c_was_crlf = 0; + if(0x00 != c) + tmpbuf[len++] = c; + } + tmpbuf[len] = '\0'; + if(len > sizeof(tmpbuf)-5) + break; + } + } + else + { + msyslog(LOG_ERR, "NeoClock4X(%d): can't query firmware version", unit); + strcpy(tmpbuf, "unknown error"); + } + strncpy(firmware, tmpbuf, maxlen); + firmware[maxlen] = '\0'; + + if(flag) + { + NLOG(NLOG_CLOCKINFO) + msyslog(LOG_INFO, "NeoClock4X(%d): firmware version: %s", unit, firmware); + } + + return (flag); +} + +#else +int refclock_neoclock4x_bs; +#endif /* REFCLOCK */ + +/* + * History: + * refclock_neoclock4x.c + * + * 2002/04/27 cjh + * Revision 1.0 first release + * + * 2002/0715 cjh + * preparing for bitkeeper reposity + * + */