]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
Arcron clock - enchanged driver and documentation, additional
authorCPrice <cprice@cs-home.com>
Sat, 22 Feb 2003 10:47:19 +0000 (04:47 -0600)
committerCPrice <cprice@cs-home.com>
Sat, 22 Feb 2003 10:47:19 +0000 (04:47 -0600)
features included

bk: 3e575537mllNZPiT0skLLqcXf8Na8w

html/driver27.htm
ntpd/refclock_arc.c

index 686e985b1b77b746175954dc187cf7f884e52401..93581ddec62e354a26a83a683f5ec4c1f0d3be47 100644 (file)
@@ -2,37 +2,49 @@
 <HEAD>
    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
    <META NAME="GENERATOR" CONTENT="Mozilla/4.01 [en] (Win95; I) [Netscape]">
-   <TITLE>Arcron MSF Receiver
+   <TITLE>Arcron MSF/DCF/WWVB Receiver
 </TITLE>
 </HEAD>
 <BODY>
 
 <H3>
-Arcron MSF Receiver</H3>
+Arcron MSF/DCF/WWVB Receiver</H3>
 
 <HR>
 <H4>
 Synopsis</H4>
 Address: 127.127.27.<I>u</I>
-<BR>Reference ID: <TT>MSFa</TT>
+<BR>Reference ID: <TT>MSFa</TT> / <TT>MSF</TT> / <TT>DCF</TT> / <TT>WWVB</TT>
 <BR>Driver ID: <TT>MSF_ARCRON</TT>
 <BR>Serial Port: <TT>/dev/arc<I>u</I></TT>; 300 baud, 8-bits, 2-stop, no
 parity
 <BR>Features: <TT>tty_clk</TT>
 <H4>
 Description</H4>
-This driver supports the Arcron MSF receiver, and would probably also support
-the DCF77 variant of the same clock. The clock reports its ID as ``<TT>MSFa</TT>''
-to indicate MSF as a source and the use of the ARCRON driver.
-
-<P>This documentation describes version V1.1 (1997/06/23) of the source
-and has been tested (amongst others) against ntpd3-5.90 on Solaris-1 (SunOS
+This driver supports the Arcron MSF, DCF and WWVB receivers. The clock 
+reports its ID as ``<TT>MSFa</TT>'', ``<TT>MSF</TT>'', ``<TT>DCF</TT>'' 
+or ``<TT>WWVB</TT>'' to indicate the time source.
+
+<P>This documentation describes version V1.3 (2003/2/21) of the source
+and has been tested against ntp 4.1.0 on linux x86. Changes from v1.1 include
+patches to work with the new ntp-4 code, clock support for DCF and WWVB
+configurable via mode flag, an option to ingore resync requests (for those of
+us at the fringes of the WWVB signal, for instance), averaging of the signal
+quality poll and several bug fixes, code cleanup and standardizations.  In
+all other respects, the driver works as per v1.1 if a mode is not
+specified.
+
+<P>To use the alternate modes, the mode flag must be specified. If the mode
+flag is 0, or unspecified, the orginal MSF version is assumed. This should
+assure backwards compatibility and should not break existing setups.
+
+<P>The previous documenation described version V1.1 (1997/06/23) of the source
+and had been tested (amoungst others) against ntpd3-5.90 on Solaris-1 (SunOS
 4.1.3_U1 on an SS1 serving as a router and firewall) and against ntpd3-5.90
-on Solaris-2.5 (on a SS1+ and TurboSPARC 170MHz). This code will probably
-work, and show increased stability, reduced jitter and more efficiency
-(fewer context switches) with the <TT>tty_clk</TT> discipline/STREAMS module
-installed, but this has not been tested. For a to-do list see the comments
-at the start of the code.
+on Solaris-2.5 (on a SS1+ and TurboSPARC 170MHz). This code claimed to 
+increase stability, reduce jitter and more efficiency (fewer context switches)
+with the <TT>tty_clk</TT> discipline/STREAMS module installed, but this has
+not been tested. For a to-do list see the comments at the start of the code.
 
 <P>This code has been significantly slimmed down since the V1.0 version,
 roughly halving the memory footprint of its code and data.
@@ -46,25 +58,31 @@ with thanks. The code was originally made to work with the clock by <A HREF="mai
 Mulcahy</A>, with modifications by <A HREF="mailto:d@hd.org">Damon Hart-Davis</A>.
 Thanks also to <A HREF="mailto:lyndond@sentinet.co.uk">Lyndon David</A>
 for some of the specifications of the clock.
+<A HREF="mailto:cprice@cs-home.com">Christopher Price</A> added enhanced
+support for the MSF, DCF and WWVB clocks.
 
 <P>There is support for a Tcl/Tk monitor written by Derek Mulcahy that
-examines the output stats; see the <A HREF="http://www2.exnet.com/NTP/ARC/ARC.htm">ARC
-Rugby MSF Receiver</A> page for more details and the code.
+examines the output stats; see the <A HREF="http://www2.exnet.com/NTP/ARC/ARC.html">ARC
+Rugby MSF Receiver</A> page for more details and the code. Information on the
+WWVB version is available from <A HREF="http://www.arctime.com">Atomic Time</A>
+as their <A HREF="http://www.atomictime.com/Product17.html">Atomic Time PC</A>.
 
 <P>Look at the notes at the start of the code for further information;
 some of the more important details follow.
 
 <P>The driver interrogates the clock at each poll (ie every 64s by default)
 for a timestamp. The clock responds at the start of the next second (with
-the start bit of the first byte being on-time). The time is in `local'
-format, including the daylight savings adjustment when it is in effect.
-The driver code converts the time back to UTC.
+the start bit of the first byte being on-time). In the default or orignal MSF
+mode, the time is in `local' format, including the daylight savings adjustment
+when it is in effect. The driver code converts the time back to UTC. In modes
+1-3 the driver can be configured for UTC or local time depending on the setting
+of flag1.
 
-<P>The clock claims to be accurate to within about 20ms of the MSF-broadcast
+<P>The clock claims to be accurate to within about 20ms of the broadcast
 time, and given the low data transmission speed from clock to host, and
-the fact that the clock is not in continuous sync with MSF, it seems sensible
-to set the `precision' of this clock to -5 or -4, -4 being used in this
-code, which builds in a reported dispersion of over 63ms (ie says ``This
+the fact that the clock is not in continuous sync with the signal, it seems
+sensible to set the `precision' of this clock to -5 or -4, -4 being used in 
+this code, which builds in a reported dispersion of over 63ms (ie says ``This
 clock is not very good.''). You can improve the reported precision to -4
 (and thus reduce the base dispersion to about 31ms) by setting the fudge
 <TT>flag3</TT> to <TT>1</TT>.
@@ -78,32 +96,33 @@ such as GPS.
 <P>By default this clock reports itself to be at stratum 2 rather than
 the usual stratum 0 for a refclock, because it is not really suited to
 be used as other than a backup source. The stratum reported can be changed
-with the <TT>fudge</TT> directive to be whatever you like. After careful
+with the <TT>stratum</TT> directive to be whatever you like. After careful
 monitoring of your clock, and appropriate choice of the <TT>time1</TT>
 fudge factor to remove systematic errors in the clock's reported time,
 you might fudge the clock to stratum 1 to allow a stratum-2 secondary server
 to sync to it.
 
-<P>The driver code arranges to resync the clock to MSF at intervals of
-a little less than an hour (deliberately avoiding the same time each hour
-to avoid any systematic problems with the signal or host). Whilst resyncing,
-the driver supplements the normal polls for time from the clock with polls
-for the reception signal quality reported by the clock. If the signal quality
-is too low (0--2 out of a range of 0--5), we chose not to trust the clock
-until the next resync (which we bring forward by about half an hour). If
-we don't catch the resync, and so don't know the signal quality, we do
+<P>In default mode, the driver code arranges to resync the clock to MSF at 
+intervals of a little less than an hour (deliberately avoiding the same time 
+each hour to avoid any systematic problems with the signal or host). Whilst 
+resyncing, the driver supplements the normal polls for time from the clock 
+with polls for the reception signal quality reported by the clock. If the 
+signal quality is too low (0--2 out of a range of 0--5), we chose not to trust 
+the clock until the next resync (which we bring forward by about half an hour). 
+If we don't catch the resync, and so don't know the signal quality, we do
 trust the clock (because this would generally be when the signal is very
 good and a resync happens quickly), but we still bring the next resync
-forward and reduce the reported precision (and thus increase reported dispersion).
+forward and reduce the reported precision (and thus increase reported 
+dispersion).
 
-<P>If we force resyncs to MSF too often we will needlessly exhaust the
+<P>If we force resyncs to the signal too often we will needlessly exhaust the
 batteries the unit runs from. During clock resync this driver tries to
 take enough time samples to avoid <TT>ntpd</TT> losing sync in case this
-clock is the current peer. By default the clock would only resync to MSF
+clock is the current peer. By default the clock would only resync to the signal 
 about once per day, which would almost certainly not be acceptable for
 NTP purposes.
 
-<P>The driver does not force an immediate resync of the clock to MSF when
+<P>The driver does not force an immediate resync of the clock to the signal when
 it starts up to avoid excessive battery drain in case <TT>ntpd</TT> is
 going to be repeatedly restarted for any reason, and also to allow enough
 samples of the clock to be taken for <TT>ntpd</TT> to sync immediately
@@ -114,7 +133,12 @@ will not accept the timestamps from the clock if the status flag delivered
 with the time code indicates that the last resync attempt was unsuccessful,
 so the initial timestamps will be close to reality, even if with up to
 a day's clock drift in the worst case (the clock by default resyncs to
-MSF once per day).
+the signal once per day).
+
+<P>When alternate modes 1-3 are selected, the driver can be configured to
+ignore the resync requests by setting <TT>flag2</TT> to <TT>1</TT>.  This
+allows clocks at the fringe of the signal to resync at night when signals
+are stronger.
 
 <P>The clock has a peculiar RS232 arrangement where the transmit lines
 are powered from the receive lines, presumably to minimise battery drain.
@@ -126,7 +150,7 @@ Your RS232 interface must drive both +ve and -ve</LI>
 <LI>
 You must (in theory) wait for an echo and a further 10ms between characters</LI>
 </UL>
-This driver, running on standard Sun hardware, seems to work fine; note
+This driver, running on standard Sun and x86 hardware, seems to work fine; note
 the use of the <TT>send_slow()</TT> routine to queue up command characters
 to be sent once every two seconds.
 
@@ -145,7 +169,7 @@ The commands and their responses are:
 
 <DD>
 Request for signal quality. Answer only valid during (late part of) resync
-to MSF signal. The response consists of two characters as follows:</DD>
+to the signal. The response consists of two characters as follows:</DD>
 
 <OL>
 <DL compact>
@@ -245,13 +269,13 @@ not `3'.</DD>
 <TT>h</TT> CR</DT>
 
 <DD>
-Request to resync to MSF. Can take up from about 30s to 360s. Drains batteries
-so should not be used excessively. After this the clock time and date should
-be correct and the phase within 20ms of time as transmitted from Rugby
-MSF (remember to allow for propagation time). By default the clock resyncs
-once per day shortly after 2am (presumably to catch transitions to/from
-daylight saving time quickly). With this driver code we resync at least
-once per hour to minimise clock wander.</DD>
+Request to resync to signal. Can take up from about 30s to 360s. Drains 
+batteries so should not be used excessively. After this the clock time and 
+date should be correct and the phase within 20ms of time as transmitted from 
+the source signal (remember to allow for propagation time). By default the
+clock resyncs once per day in the late evening/early morning (presumably to 
+catch transitions to/from daylight saving time quickly). With this driver
+code, by default, we resync at least once per hour to minimise clock wander.</DD>
 
 <DT>
 <TT>o</TT> CR</DT>
@@ -335,25 +359,34 @@ always 1</DD>
 bit 3</DT>
 
 <DD>
-always 0</DD>
+(MSF) always 0<BR>
+(WWVB) Leap year indicator bit<BR>
+  0 = non-leap year<BR>
+  1 = leap year</DD>
 
 <DT>
 bit 2</DT>
 
 <DD>
-= 1 if UTC is in effect (reverse of bit 1)</DD>
+= (MSF) 1 if UTC is in effect (reverse of bit 1)<BR>
+= (WWVB) Leap second warning bit
+  </DD>
 
 <DT>
 bit 1</DT>
 
 <DD>
-= 1 if BST is in effect (reverse of bit 2)</DD>
+= (MSF) 1 if BST is in effect (reverse of bit 2)<BR>
+= (WWVB) 0 if ST is in effect, 1 if DST is in effect, 1 if transition from ST
+with bit 0 set to 0</DD>
 
 <DT>
 bit 0</DT>
 
 <DD>
-= 1 if BST/UTC change pending</DD>
+= (MSF) 1 if BST/UTC change pending<BR>
+= (WWVB) 0 if ST is in effect, 1 if DST is in effect, 0 if transition from 
+DST with bit 1 set to 0</DD>
 </DL>
 
 <LI>
@@ -394,14 +427,17 @@ bit 3</DT>
 bit 2</DT>
 
 <DD>
-= 1 if last resync failed (though officially undefined for the MSF clock)</DD>
+= 1 if last resync failed (though officially undefined for the MSF clock,
+officially defined for WWVB)</DD>
 
 <DT>
 bit 1</DT>
 
 <DD>
-= 1 if at least one reception attempt since 0230 for the MSF clock was
-successful (0300 for the DCF77 clock)</DD>
+= 1 if at least one reception attempt was successful<BR>
+(MSF) since 0230<BR>
+(DCF) since 0300<BR>
+(WWVB) resets if not successful between 0300-0400</DD>
 
 <DT>
 bit 0</DT>
@@ -412,9 +448,8 @@ at power-up), and set to 1 after first successful resync attempt.</DD>
 </DL>
 </OL>
 The driver only accepts time from the clock if the bottom three bits of
-the status byte are <TT>011</TT>. The leap-year logic for computing day-in-year
-is only valid until 2099, and the clock will ignore stamps from the clock
-that claim BST is in effect in the first hour of each year. If the UK parliament
+the status byte are <TT>011</TT> or <TT>flag2</TT> is set to <TT>1</TT> to
+ignore resync requests. For the MSF clock, if the UK parliament
 decides to move us to +0100/+0200 time as opposed to the current +0000/+0100
 time, it is not clear what effect that will have on the time broadcast
 by MSF, and therefore on this driver's usefulness.</DL>
@@ -430,6 +465,9 @@ server 127.127.27.0 # ARCRON MSF radio clock(1).
 # Fudge stratum and other features as required.
 # ADJUST time1 VALUE FOR YOUR HOST, CLOCK AND LOCATION!
 fudge 127.127.27.0 stratum 1 time1 0.016 flag3 1 flag4 1
+# WWVB users should change that line to:
+server 127.127.27.0 mode 3 # ARCRON WWVB radio clock
+fudge 127.127.27.0 stratum 1 time1 0.30 flag1 1 flag3 1
 
 peer 11.22.33.9 # tick(1--2).
 peer 11.22.33.4 # tock(3), boot/NFS server.
@@ -469,26 +507,6 @@ to undefine this, especially if you have better sources of time or your
 reception is ropey. However, there are many checks built in even with this
 flag defined.</DD>
 
-<DT>
-<TT>ARCRON_OWN_FILTER</TT></DT>
-
-<DD>
-When defined, the code uses its own median-filter code rather than that
-available in <TT>ntp_refclock.c</TT> since the latter seems to have a minor
-bug, at least in version 3-5.90. If this bug goes away this flag should
-be turned off to avoid duplication of code. (The bug, if that's what it
-is, causes the last raw offset to be used rather than the median offset.)</DD>
-
-
-<P>Without this defined (and without <TT>ARCRON_MULTIPLE_SAMPLES</TT> below)
-a typical set of offsets reported and used to drive the clock-filter algorithm
-is (oldest last):
-<PRE>filtoffset=&nbsp; -4.32&nbsp; -34.82&nbsp;&nbsp; -0.78&nbsp;&nbsp;&nbsp; 0.89&nbsp;&nbsp;&nbsp; 2.76&nbsp;&nbsp;&nbsp; 4.58&nbsp;&nbsp; -3.92&nbsp;&nbsp; -2.17</PRE>
-Look at that spike!
-
-<P>With this defined a typical set of offsets is:
-<PRE>filtoffset=&nbsp; -7.06&nbsp;&nbsp; -7.06&nbsp;&nbsp; -2.91&nbsp;&nbsp; -2.91&nbsp;&nbsp; -2.91&nbsp;&nbsp; -1.27&nbsp;&nbsp; -9.54&nbsp;&nbsp; -6.70</PRE>
-with the repeated values being some evidence of outlyers being discarded.
 <DT>
 <TT>ARCRON_MULTIPLE_SAMPLES</TT></DT>
 
@@ -521,28 +539,12 @@ good experience with the clock and you live on the edge. Note that the
 if clock signal quality is known good. So maybe just leave this alone.
 B^)</DD>
 
-<DT>
-<TT>NSAMPLES</TT></DT>
-
-<DD>
-Should be at least 3 to help smooth out sampling jitters. Can be more,
-but if made too long can make <TT>ntpd</TT> overshoot on clock corrections
-and can hold onto bad samples longer than you would like. With this set
-to 4 and <TT>NKEEP</TT> set to 3 this allows the occasional bad sample
-(in my experience less than 1 value in 10) to be dropped. (Note that there
-seems to be some sort of `beat' effect in the offset with a periodicity
-of about 7 samples as of this writing (1997/05/11) still under investigation;
-a filter of approximately this length should be able to almost completely
-suppress this effect.) Note that if the fudge-factor <TT>flag3</TT> is
-set to 1, a larger <TT>NSAMPLES</TT> is used.</DD>
-</DL>
-
 <H4>
 Monitor Data</H4>
 Each timecode is written to the <TT>clockstats</TT> file with a signal
 quality value appended (`0'--`5' as reported by the clock, or `6' for unknown).
 
-<P>Each resync and result (plus gaining or losing MSF sync) is logged to
+<P>Each resync and result (plus gaining or losing signal sync) is logged to
 the system log at level <TT>LOG_NOTICE</TT>; note that each resync drains
 the unit's batteries, so the syslog entry seems justified.
 
@@ -560,6 +562,18 @@ May 10 12:41:34 oolong ntpd[615]: ARCRON: sync finished, signal quality 3: OK, w
 Fudge Factors</H4>
 
 <DL>
+<DT>
+<TT>mode 0 | 1 | 2 | 3</TT><DT>
+
+<DD>
+Specified the clock hardware model.  This parameter is optional, it defaults
+to the original mode of operation.
+<DD>Supported modes of operation:
+<DD>0 - Default, Original MSF
+<DD>1 - Updated MSF
+<DD>2 - New DCF77
+<DD>3 - New WWVB
+
 <DT>
 <TT>time1 <I>time</I></TT></DT>
 
@@ -578,7 +592,7 @@ Not currently used by this driver.</DD>
 <TT>stratum <I>number</I></TT></DT>
 
 <DD>
-Specifies the driver stratum, in decimal from 0 to 15, with default 0.
+Specifies the driver stratum, in decimal from 0 to 15, with default 2.
 It is suggested that the clock be fudged to stratum 1 so this it is used
 a backup time source rather than a primary when more accurate sources are
 available.</DD>
@@ -588,19 +602,24 @@ available.</DD>
 
 <DD>
 Specifies the driver reference identifier, an ASCII string from one to
-four characters, with default <TT>MSFa</TT>.</DD>
+four characters, with default <TT>MSFa</TT>. When used in modes 1-3, the
+driver will report either <TT>MSF</TT>, <TT>DCF</TT>, or <TT>WWVB,</TT>
+respectively</DD>
 
 <DT>
 <TT>flag1 0 | 1</TT></DT>
 
 <DD>
-Not used by this driver.</DD>
+(Modes 1-3) If set to 0 (the default), the clock is set to UTC time.  If set
+to 1, the clock is set to localtime.</DD>
 
 <DT>
 <TT>flag2 0 | 1</TT></DT>
 
 <DD>
-Not used by this driver.</DD>
+(Modes 1-3) If set to 0 (the default), the clock will be forced to resync
+approximately every hour. If set to 1, the clock will resync per normal
+operations (approximately midnight).</DD>
 
 <DT>
 <TT>flag3 0 | 1</TT></DT>
@@ -613,18 +632,14 @@ clock's received signal quality is known to be good.</DD>
 <TT>flag4 0 | 1</TT></DT>
 
 <DD>
-If set to 1, a longer-than-normal (8-stage rather than 4-stage) median
-filter is used, to provide some extra smoothing of clock output and reduction
-in jitter, at the cost of extra clock overshoot. Probably not advisable
-unless the server using this clock has other sources it can use to help
-mitigate the overshoot.</DD>
+Note used by this driver.</DD>
 </DL>
 
 <H4>
 Additional Information</H4>
 <A HREF="refclock.htm">Reference Clock Drivers</A>
 
-<P><A HREF="http://www2.exnet.com/NTP/ARC/ARC.htm">ARC Rugby MSF Receiver</A>
+<P><A HREF="http://www2.exnet.com/NTP/ARC/ARC.html">ARC Rugby MSF Receiver</A>
 page&nbsp;
 <HR>
 <ADDRESS>
index c771f478f67774621c6eba24eac622a62bad99a7..61352fd3bdfc61b88436d89b3216a6f42264ffc9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * refclock_arc - clock driver for ARCRON MSF receivers
+ * refclock_arc - clock driver for ARCRON MSF/DCF/WWVB receivers
  */
 
 #ifdef HAVE_CONFIG_H
@@ -7,9 +7,11 @@
 #endif
 
 #if defined(REFCLOCK) && defined(CLOCK_ARCRON_MSF)
-static const char arc_version[] = { "V1.1 1997/06/23" };
+static const char arc_version[] = { "V1.3 2003/02/21" };
 
-#undef ARCRON_DEBUG /* Define only while in development... */
+/* define PRE_NTP420 for compatibility to previous versions of NTP (at least
+   to 4.1.0 */
+#define PRE_NTP420
 
 #ifndef ARCRON_NOT_KEEN
 #define ARCRON_KEEN 1 /* Be keen, and trusting of the clock, if defined. */
@@ -28,6 +30,7 @@ static const char arc_version[] = { "V1.1 1997/06/23" };
 /*
 Code by Derek Mulcahy, <derek@toybox.demon.co.uk>, 1997.
 Modifications by Damon Hart-Davis, <d@hd.org>, 1997.
+Modifications by Christopher M. Price, <cprice@cs-home.com>, 2003.
 
 THIS CODE IS SUPPLIED AS IS, WITH NO WARRANTY OF ANY KIND.  USE AT
 YOUR OWN RISK.
@@ -42,6 +45,33 @@ reproduced.
 
 -------------------------------------------------------------------------------
 
+Christopher's notes:
+
+MAJOR CHANGES SINCE V1.2 
+========================
+ 1) Applied patch by Andrey Bray <abuse@madhouse.demon.co.uk>
+    2001-02-17 comp.protocols.time.ntp
+
+ 2) Added WWVB support via clock mode command, localtime/UTC time configured
+    via flag1=(0=UTC, 1=localtime)
+
+ 3) Added ignore resync request via flag2=(0=resync, 1=ignore resync)
+
+ 4) Added simplified conversion from localtime to UTC with dst/bst translation
+
+ 5) Added average signal quality poll
+
+ 6) Fixed a badformat error when no code is available due to stripping 
+    \n & \r's 
+
+ 7) Fixed a badformat error when clearing lencode & memset a_lastcode in poll
+    routine
+
+ 8) Lots of code cleanup, including standardized DEBUG macros and removal 
+    of unused code 
+
+-------------------------------------------------------------------------------
+
 Author's original note:
 
 I enclose my ntp driver for the Galleon Systems Arc MSF receiver.
@@ -306,6 +336,7 @@ Also note h<cr> command which starts a resync to MSF signal.
 #include "ntpd.h"
 #include "ntp_io.h"
 #include "ntp_refclock.h"
+#include "ntp_calendar.h"
 #include "ntp_stdlib.h"
 
 #include <stdio.h>
@@ -324,7 +355,7 @@ Also note h<cr> command which starts a resync to MSF signal.
 #endif
 
 /*
- * This driver supports the ARCRON MSF Radio Controlled Clock
+ * This driver supports the ARCRON MSF/DCF/WWVB Radio Controlled Clock
  */
 
 /*
@@ -335,9 +366,16 @@ Also note h<cr> command which starts a resync to MSF signal.
 #define PRECISION       (-4)            /* Precision  (~63 ms). */
 #define HIGHPRECISION   (-5)            /* If things are going well... */
 #define REFID           "MSFa"          /* Reference ID. */
-#define DESCRIPTION     "ARCRON MSF Receiver"
+#define REFID_MSF       "MSF"           /* Reference ID. */
+#define REFID_DCF77     "DCF"           /* Reference ID. */
+#define REFID_WWVB      "WWVB"          /* Reference ID. */
+#define DESCRIPTION     "ARCRON MSF/DCF/WWVB Receiver"
 
-#define NSAMPLESLONG    8               /* Stages of long filter. */
+#ifdef PRE_NTP420
+#define MODE ttlmax
+#else
+#define MODE ttl
+#endif
 
 #define LENARC          16              /* Format `o' timecode length. */
 
@@ -405,22 +443,6 @@ Also note h<cr> command which starts a resync to MSF signal.
 #endif
      };
 
-/* Chose filter length dependent on fudge flag 4. */
-#define CHOSENSAMPLES(pp) \
-(((pp)->sloppyclockflag & CLK_FLAG4) ? NSAMPLESLONG : NSAMPLES)
-     /*
-Chose how many filter samples to keep.  Several factors are in play.
-
- 1) Discard at least one sample to allow a spike value to be
-    discarded.
-
- 2) Discard about 1-in-8 to 1-in-30 samples to handle spikes.
-
- 3) Keep an odd number of samples to avoid median value being biased
-    high or low.
-*/
-#define NKEEP(pp) ((CHOSENSAMPLES(pp) - 1 - (CHOSENSAMPLES(pp)>>3)) | 1)
-
 #define DEFAULT_RESYNC_TIME  (57*60)    /* Gap between resync attempts (s). */
 #define RETRY_RESYNC_TIME    (27*60)    /* Gap to emergency resync attempt. */
 #ifdef ARCRON_KEEN
@@ -454,6 +476,7 @@ struct arcunit {
 
        int quality;        /* Quality of reception 0--5 for unit. */
        /* We may also use the values -1 or 6 internally. */
+       u_long quality_stamp; /* Next time to reset quality average. */
 
        u_long next_resync; /* Next resync time (s) compared to current_time. */
        int resyncing;      /* Resync in progress if true. */
@@ -463,6 +486,7 @@ struct arcunit {
 
        u_long saved_flags; /* Saved fudge flags. */
 };
+
 #ifdef ARCRON_LEAPSECOND_KEEN
 /* The flag `possible_leap' is set non-zero when any MSF unit
        thinks a leap-second may have happened.
@@ -522,18 +546,16 @@ struct  refclock refclock_arc = {
 /* Queue us up for the next tick. */
 #define ENQUEUE(up) \
        do { \
-            if((up)->ev.next != 0) { break; } /* WHOOPS! */ \
-            peer->nextdate = current_time + QUEUETICK; \
+            peer->nextaction = current_time + QUEUETICK; \
        } while(0)
 
-#if 0
-/* Placeholder event handler---does nothing safely---soaks up lose tick. */
+/* Placeholder event handler---does nothing safely---soaks up loose tick. */
 static void
 dummy_event_handler(
        struct peer *peer
        )
 {
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
        if(debug) { printf("arc: dummy_event_handler() called.\n"); }
 #endif
 }
@@ -558,7 +580,7 @@ arc_event_handler(
        register struct arcunit *up = (struct arcunit *)pp->unitptr;
        int i;
        char c;
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
        if(debug > 2) { printf("arc: arc_event_handler() called.\n"); }
 #endif
 
@@ -572,12 +594,13 @@ arc_event_handler(
                if(write(pp->io.fd, &c, 1) != 1) {
                        msyslog(LOG_NOTICE, "ARCRON: write to fd %d failed", pp->io.fd);
                }
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
                else if(debug) { printf("arc: sent `%2.2x', fd %d.\n", c, pp->io.fd); }
 #endif
        }
+
+       ENQUEUE(up);
 }
-#endif /* 0 */
 
 /*
  * arc_start - open the devices and initialize data for processing
@@ -597,7 +620,7 @@ arc_start(
 #endif
 
        msyslog(LOG_NOTICE, "ARCRON: %s: opening unit %d", arc_version, unit);
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
        if(debug) {
                printf("arc: %s: attempt to open unit %d.\n", arc_version, unit);
        }
@@ -612,7 +635,7 @@ arc_start(
        (void)sprintf(device, DEVICE, unit);
        if (!(fd = refclock_open(device, SPEED, LDISC_CLK)))
                return(0);
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
        if(debug) { printf("arc: unit %d using open().\n", unit); }
 #endif
        fd = open(device, OPEN_FLAGS);
@@ -624,9 +647,9 @@ arc_start(
        }
 
        fcntl(fd, F_SETFL, 0); /* clear the descriptor flags */
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
        if(debug)
-       { printf("Opened RS232 port with file descriptor %d.\n", fd); }
+       { printf("arc: opened RS232 port with file descriptor %d.\n", fd); }
 #endif
 
 #ifdef HAVE_TERMIOS
@@ -667,7 +690,27 @@ arc_start(
        peer->precision = PRECISION;
        peer->stratum = 2;              /* Default to stratum 2 not 0. */
        pp->clockdesc = DESCRIPTION;
-       memcpy((char *)&pp->refid, REFID, 4);
+       if (peer->MODE > 3) {
+               msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d", peer->MODE);
+               return 0;
+       }
+#ifdef DEBUG
+       if(debug) { printf("arc: mode = %d.\n", peer->MODE); }
+#endif
+       switch (peer->MODE) {
+           case 1:
+               memcpy((char *)&pp->refid, REFID_MSF, 4);
+               break;
+           case 2:
+               memcpy((char *)&pp->refid, REFID_DCF77, 4);
+               break;
+           case 3:
+               memcpy((char *)&pp->refid, REFID_WWVB, 4);
+               break;
+           default:
+               memcpy((char *)&pp->refid, REFID, 4);
+               break;
+       }
        /* Spread out resyncs so that they should remain separated. */
        up->next_resync = current_time + INITIAL_RESYNC_DELAY + (67*unit)%1009;
 
@@ -686,6 +729,11 @@ arc_start(
 #else
        up->quality = MIN_CLOCK_QUALITY;/* Don't trust the clock yet. */
 #endif
+
+       peer->action = arc_event_handler;
+
+       ENQUEUE(up);
+
        return(1);
 }
 
@@ -702,6 +750,8 @@ arc_shutdown(
        register struct arcunit *up;
        struct refclockproc *pp;
 
+       peer->action = dummy_event_handler;
+
        pp = peer->procptr;
        up = (struct arcunit *)pp->unitptr;
        io_closeclock(&pp->io);
@@ -740,11 +790,11 @@ send_slow(
        int sl = strlen(s);
        int spaceleft = space_left(up);
 
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
        if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); }
 #endif
        if(spaceleft < sl) { /* Should not normally happen... */
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
                msyslog(LOG_NOTICE, "ARCRON: send-buffer overrun (%d/%d)",
                       sl, spaceleft);
 #endif
@@ -776,8 +826,11 @@ arc_receive(
        struct refclockproc *pp;
        struct peer *peer;
        char c;
-       int i, n, wday, month, bst, status;
+       int i, n, wday, month, flags, status;
        int arc_last_offset;
+       static int quality_average = 0;
+       static int quality_sum = 0;
+       static int quality_polls = 0;
 
        /*
         * Initialize pointers and read the timecode and timestamp
@@ -857,7 +910,7 @@ arc_receive(
                  handle for tty_clk or somesuch kernel timestamper.
                */
                if(arc_last_offset > LENARC) {
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
                        if(debug) {
                                printf("arc: input code too long (%d cf %d); rejected.\n",
                                       arc_last_offset, LENARC);
@@ -869,7 +922,7 @@ arc_receive(
                }
 
                L_SUBUF(&timestamp, charoffsets[arc_last_offset]);
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
                if(debug > 1) {
                        printf(
                                "arc: %s%d char(s) rcvd, the last for lastcode[%d]; -%sms offset applied.\n",
@@ -898,7 +951,7 @@ arc_receive(
                   L_ISGEQ(&(up->lastrec), &timestamp))
 #endif
                {
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
                        if(debug > 1) {
                                printf("arc: system timestamp captured.\n");
 #ifdef ARCRON_MULTIPLE_SAMPLES
@@ -922,7 +975,7 @@ arc_receive(
        /* eg on receipt of the \r coming in on its own after the      */
        /* timecode.                                                   */
        if(pp->lencode >= LENARC) {
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
                if(debug && (rbufp->recv_buffer[0] != '\r'))
                { printf("arc: rubbish in pp->a_lastcode[].\n"); }
 #endif
@@ -947,11 +1000,12 @@ arc_receive(
                */
                if((c == 'o') && (pp->lencode == 1)) {
                        L_CLR(&(up->lastrec));
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
                        if(debug > 1) { printf("arc: clearing timestamp.\n"); }
 #endif
                }
        }
+       if (pp->lencode == 0) return;
 
        /* Handle a quality message. */
        if(pp->a_lastcode[0] == 'g') {
@@ -963,17 +1017,31 @@ arc_receive(
                if(((q & 0x70) != 0x30) || ((q & 0xf) > MAX_CLOCK_QUALITY) ||
                   ((r & 0x70) != 0x30)) {
                        /* Badly formatted response. */
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
                        if(debug) { printf("arc: bad `g' response %2x %2x.\n", r, q); }
 #endif
                        return;
                }
                if(r == '3') { /* Only use quality value whilst sync in progress. */
-                       up->quality = (q & 0xf);
+                       if (up->quality_stamp < current_time) {
+                               struct calendar cal;
+                               ulong new_stamp;
+                       
+                               get_systime (&new_stamp);
+                               caljulian (new_stamp, &cal);
+                               up->quality_stamp = 
+                                       current_time + 60 - cal.second + 5;
+                               quality_sum = 0;
+                               quality_polls = 0;
+                       }
+                       quality_sum += (q & 0xf);
+                       quality_polls++;
+                       quality_average = (quality_sum / quality_polls);
 #ifdef DEBUG
-                       if(debug) { printf("arc: signal quality %d.\n", up->quality); }
+                       if(debug) { printf("arc: signal quality %d (%d).\n", quality_average, (q & 0xf)); }
 #endif
                } else if( /* (r == '2') && */ up->resyncing) {
+                       up->quality = quality_average;
 #ifdef DEBUG
                        if(debug)
                        {
@@ -987,6 +1055,9 @@ arc_receive(
                               up->quality,
                               quality_action(up->quality));
                        up->resyncing = 0; /* Resync is over. */
+                       quality_average = 0;
+                       quality_sum = 0;
+                       quality_polls = 0;
 
 #ifdef ARCRON_KEEN
                        /* Clock quality dubious; resync earlier than usual. */
@@ -1011,13 +1082,13 @@ arc_receive(
 
 
        /* WE HAVE NOW COLLECTED ONE TIMESTAMP (phew)... */
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
        if(debug > 1) { printf("arc: NOW HAVE TIMESTAMP...\n"); }
 #endif
 
        /* But check that we actually captured a system timestamp on it. */
        if(L_ISZERO(&(up->lastrec))) {
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
                if(debug) { printf("arc: FAILED TO GET SYSTEM TIMESTAMP\n"); }
 #endif
                pp->lencode = 0;
@@ -1033,22 +1104,26 @@ arc_receive(
        pp->a_lastcode[pp->lencode] = ((up->quality == QUALITY_UNKNOWN) ?
                                       '6' : ('0' + up->quality));
        pp->a_lastcode[pp->lencode + 1] = '\0'; /* Terminate for printf(). */
-       record_clock_stats(&peer->srcadr, pp->a_lastcode);
 
+#ifdef PRE_NTP420
        /* We don't use the micro-/milli- second part... */
        pp->usec = 0;
        pp->msec = 0;
-
+#else
+       /* We don't use the nano-second part... */
+       pp->nsec = 0;
+#endif 
        n = sscanf(pp->a_lastcode, "o%2d%2d%2d%1d%2d%2d%2d%1d%1d",
                   &pp->hour, &pp->minute, &pp->second,
-                  &wday, &pp->day, &month, &pp->year, &bst, &status);
+                  &wday, &pp->day, &month, &pp->year, &flags, &status);
 
        /* Validate format and numbers. */
        if(n != 9) {
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
                /* Would expect to have caught major problems already... */
                if(debug) { printf("arc: badly formatted data.\n"); }
 #endif
+               pp->lencode = 0;
                refclock_report(peer, CEVNT_BADREPLY);
                return;
        }
@@ -1064,15 +1139,21 @@ arc_receive(
           (month < 1) || (month > 12) ||
           (pp->year < 0) || (pp->year > 99)) {
                /* Data out of range. */
+               pp->lencode = 0;
                refclock_report(peer, CEVNT_BADREPLY);
                return;
        }
-       /* Check that BST/UTC bits are the complement of one another. */
-       if(!(bst & 2) == !(bst & 4)) {
-               refclock_report(peer, CEVNT_BADREPLY);
-               return;
-       }
 
+
+       if(peer->MODE == 0) { /* compatiblity to original version */
+               int bst = flags;
+               /* Check that BST/UTC bits are the complement of one another. */
+               if(!(bst & 2) == !(bst & 4)) {
+                       pp->lencode = 0;
+                       refclock_report(peer, CEVNT_BADREPLY);
+                       return;
+               }
+       }
        if(status & 0x8) { msyslog(LOG_NOTICE, "ARCRON: battery low"); }
 
        /* Year-2000 alert! */
@@ -1094,7 +1175,7 @@ arc_receive(
                printf("arc: n=%d %02d:%02d:%02d %02d/%02d/%04d %1d %1d\n",
                       n,
                       pp->hour, pp->minute, pp->second,
-                      pp->day, month, pp->year, bst, status);
+                      pp->day, month, pp->year, flags, status);
        }
 #endif
 
@@ -1114,41 +1195,159 @@ arc_receive(
                        msyslog(LOG_NOTICE, "ARCRON: signal lost");
                        pp->leap = LEAP_NOTINSYNC; /* MSF clock is free-running. */
                        up->status = status;
+                       pp->lencode = 0;
                        refclock_report(peer, CEVNT_FAULT);
                        return;
                }
        }
        up->status = status;
 
-       pp->day += moff[month - 1];
-
-       if(isleap_4(pp->year) && month > 2) { pp->day++; }      /* Y2KFixes */
+       if (peer->MODE == 0) { /* compatiblity to original version */
+               int bst = flags;
+
+               pp->day += moff[month - 1];
+
+               if(isleap_4(pp->year) && month > 2) { pp->day++; }/* Y2KFixes */
+
+               /* Convert to UTC if required */
+               if(bst & 2) {
+                       pp->hour--;
+                       if (pp->hour < 0) {
+                               pp->hour = 23;
+                               pp->day--;
+                               /* If we try to wrap round the year
+                                * (BST on 1st Jan), reject.*/
+                               if(pp->day < 0) {
+                                       pp->lencode = 0;
+                                       refclock_report(peer, CEVNT_BADTIME);
+                                       return;
+                               }
+                       }
+               }
+       }
 
-       /* Convert to UTC if required */
-       if(bst & 2) {
-               pp->hour--;
-               if (pp->hour < 0) {
-                       pp->hour = 23;
-                       pp->day--;
-                       /* If we try to wrap round the year (BST on 1st Jan), reject.*/
-                       if(pp->day < 0) {
-                               refclock_report(peer, CEVNT_BADTIME);
+       if(peer->MODE > 0) {
+               if(pp->sloppyclockflag & CLK_FLAG1) {
+                       struct tm  local;
+                       struct tm *gmtp;
+                       time_t     unixtime;
+
+                       /*
+                        * Convert to GMT for sites that distribute localtime.
+                        * This means we have to do Y2K conversion on the
+                        * 2-digit year; otherwise, we get the time wrong.
+                        */
+          
+                       local.tm_year  = pp->year-1900;
+                       local.tm_mon   = month-1;
+                       local.tm_mday  = pp->day;
+                       local.tm_hour  = pp->hour;
+                       local.tm_min   = pp->minute;
+                       local.tm_sec   = pp->second;
+                       switch (peer->MODE) {
+                           case 1:
+                               local.tm_isdst = (flags & 2);
+                               break;
+                           case 2:
+                               local.tm_isdst = (flags & 2);
+                               break;
+                           case 3:
+                               switch (flags & 3) {
+                                   case 0: /* It is unclear exactly when the 
+                                              Arcron changes from DST->ST and 
+                                              ST->DST. Testing has shown this
+                                              to be irregular. For the time 
+                                              being, let the OS decide. */
+                                       local.tm_isdst = 0;
+#ifdef DEBUG
+                                       if (debug)
+                                           printf ("arc: DST = 00 (0)\n"); 
+#endif
+                                       break;
+                                   case 1: /* dst->st time */
+                                       local.tm_isdst = -1;
+#ifdef DEBUG
+                                       if (debug) 
+                                           printf ("arc: DST = 01 (1)\n"); 
+#endif
+                                       break;
+                                   case 2: /* st->dst time */
+                                       local.tm_isdst = -1;
+#ifdef DEBUG
+                                       if (debug) 
+                                           printf ("arc: DST = 10 (2)\n"); 
+#endif
+                                       break;
+                                   case 3: /* dst time */
+                                       local.tm_isdst = 1;
+#ifdef DEBUG
+                                       if (debug) 
+                                           printf ("arc: DST = 11 (3)\n"); 
+#endif
+                                       break;
+                               }
+                               break;
+                           default:
+                               msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d",
+                                       peer->MODE);
                                return;
+                               break;
+                       }
+                       unixtime = mktime (&local);
+                       if ((gmtp = gmtime (&unixtime)) == NULL)
+                       {
+                               pp->lencode = 0;
+                               refclock_report (peer, CEVNT_FAULT);
+                               return;
+                       }
+                       pp->year = gmtp->tm_year+1900;
+                       month = gmtp->tm_mon+1;
+                       pp->day = ymd2yd(pp->year,month,gmtp->tm_mday);
+                       /* pp->day = gmtp->tm_yday; */
+                       pp->hour = gmtp->tm_hour;
+                       pp->minute = gmtp->tm_min;
+                       pp->second = gmtp->tm_sec;
+#ifdef DEBUG
+                       if (debug)
+                       {
+                               printf ("arc: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
+                                       pp->year,month,gmtp->tm_mday,pp->hour,pp->minute,
+                                       pp->second);
                        }
+#endif
+               } else 
+               {
+                       /*
+                       * For more rational sites distributing UTC
+                       */
+                       pp->day    = ymd2yd(pp->year,month,pp->day);
                }
        }
 
-       /* If clock signal quality is unknown, revert to default PRECISION...*/
-       if(up->quality == QUALITY_UNKNOWN) { peer->precision = PRECISION; }
-       /* ...else improve precision if flag3 is set... */
-       else {
-               peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
-                                  HIGHPRECISION : PRECISION);
+       if (peer->MODE == 0) { /* compatiblity to original version */
+                               /* If clock signal quality is 
+                                * unknown, revert to default PRECISION...*/
+               if(up->quality == QUALITY_UNKNOWN) { 
+                       peer->precision = PRECISION; 
+               } else { /* ...else improve precision if flag3 is set... */
+                       peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
+                                          HIGHPRECISION : PRECISION);
+               }
+       } else {
+               if ((status == 0x3) && (pp->sloppyclockflag & CLK_FLAG2)) {
+                       peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
+                                          HIGHPRECISION : PRECISION);
+               } else if (up->quality == QUALITY_UNKNOWN) {
+                       peer->precision = PRECISION;
+               } else {
+                       peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
+                                          HIGHPRECISION : PRECISION);
+               }
        }
 
        /* Notice and log any change (eg from initial defaults) for flags. */
        if(up->saved_flags != pp->sloppyclockflag) {
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
                msyslog(LOG_NOTICE, "ARCRON: flags enabled: %s%s%s%s",
                       ((pp->sloppyclockflag & CLK_FLAG1) ? "1" : "."),
                       ((pp->sloppyclockflag & CLK_FLAG2) ? "2" : "."),
@@ -1156,8 +1355,6 @@ arc_receive(
                       ((pp->sloppyclockflag & CLK_FLAG4) ? "4" : "."));
                /* Note effects of flags changing... */
                if(debug) {
-                       printf("arc: CHOSENSAMPLES(pp) = %d.\n", CHOSENSAMPLES(pp));
-                       printf("arc: NKEEP(pp) = %d.\n", NKEEP(pp));
                        printf("arc: PRECISION = %d.\n", peer->precision);
                }
 #endif
@@ -1185,9 +1382,11 @@ arc_receive(
 #endif
 
        if (!refclock_process(pp)) {
+               pp->lencode = 0;
                refclock_report(peer, CEVNT_BADTIME);
                return;
        }
+       record_clock_stats(&peer->srcadr, pp->a_lastcode);
        refclock_receive(peer);
 }
 
@@ -1208,9 +1407,12 @@ request_time(
        if(debug) { printf("arc: unit %d: requesting time.\n", unit); }
 #endif
        if (!send_slow(up, pp->io.fd, "o\r")) {
-#ifdef ARCRON_DEBUG
-               msyslog(LOG_NOTICE, "ARCRON: unit %d: problem sending", unit);
+#ifdef DEBUG
+               if (debug) {
+                       printf("arc: unit %d: problem sending", unit);
+               }
 #endif
+               pp->lencode = 0;
                refclock_report(peer, CEVNT_FAULT);
                return;
        }
@@ -1232,8 +1434,10 @@ arc_poll(
 
        pp = peer->procptr;
        up = (struct arcunit *)pp->unitptr;
+#if 0
        pp->lencode = 0;
        memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode));
+#endif
 
 #if 0
        /* Flush input. */
@@ -1241,7 +1445,8 @@ arc_poll(
 #endif
 
        /* Resync if our next scheduled resync time is here or has passed. */
-       resync_needed = (up->next_resync <= current_time);
+       resync_needed = ( !(pp->sloppyclockflag & CLK_FLAG2) &&
+                         (up->next_resync <= current_time) );
 
 #ifdef ARCRON_LEAPSECOND_KEEN
        /*
@@ -1309,6 +1514,7 @@ arc_poll(
                        printf("arc: clock quality %d too poor.\n", up->quality);
                }
 #endif
+               pp->lencode = 0;
                refclock_report(peer, CEVNT_FAULT);
                return;
        }