<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.
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>.
<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
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.
<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.
<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>
<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>
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>
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>
</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>
# 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.
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= -4.32 -34.82 -0.78 0.89 2.76 4.58 -3.92 -2.17</PRE>
-Look at that spike!
-
-<P>With this defined a typical set of offsets is:
-<PRE>filtoffset= -7.06 -7.06 -2.91 -2.91 -2.91 -1.27 -9.54 -6.70</PRE>
-with the repeated values being some evidence of outlyers being discarded.
<DT>
<TT>ARCRON_MULTIPLE_SAMPLES</TT></DT>
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.
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>
<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>
<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>
<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
<HR>
<ADDRESS>
/*
- * refclock_arc - clock driver for ARCRON MSF receivers
+ * refclock_arc - clock driver for ARCRON MSF/DCF/WWVB receivers
*/
#ifdef HAVE_CONFIG_H
#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. */
/*
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.
-------------------------------------------------------------------------------
+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.
#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_refclock.h"
+#include "ntp_calendar.h"
#include "ntp_stdlib.h"
#include <stdio.h>
#endif
/*
- * This driver supports the ARCRON MSF Radio Controlled Clock
+ * This driver supports the ARCRON MSF/DCF/WWVB Radio Controlled Clock
*/
/*
#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. */
#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
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. */
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.
/* 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
}
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
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
#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);
}
(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);
}
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
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;
#else
up->quality = MIN_CLOCK_QUALITY;/* Don't trust the clock yet. */
#endif
+
+ peer->action = arc_event_handler;
+
+ ENQUEUE(up);
+
return(1);
}
register struct arcunit *up;
struct refclockproc *pp;
+ peer->action = dummy_event_handler;
+
pp = peer->procptr;
up = (struct arcunit *)pp->unitptr;
io_closeclock(&pp->io);
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
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
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);
}
L_SUBUF(×tamp, 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",
L_ISGEQ(&(up->lastrec), ×tamp))
#endif
{
-#ifdef ARCRON_DEBUG
+#ifdef DEBUG
if(debug > 1) {
printf("arc: system timestamp captured.\n");
#ifdef ARCRON_MULTIPLE_SAMPLES
/* 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
*/
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') {
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)
{
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. */
/* 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;
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;
}
(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! */
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
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" : "."),
((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
#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);
}
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;
}
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. */
#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
/*
printf("arc: clock quality %d too poor.\n", up->quality);
}
#endif
+ pp->lencode = 0;
refclock_report(peer, CEVNT_FAULT);
return;
}