332 lines
16 KiB
HTML
332 lines
16 KiB
HTML
<!-- $NetBSD: irig.html,v 1.1 1998/12/30 20:20:35 mcr Exp $ -->
|
|
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML Strict//EN">
|
|
<html><head><title>
|
|
IRIG Support Using Sun SPARC Audio
|
|
</title></head><body><h3>
|
|
IRIG Support Using Sun SPARC Audio
|
|
</h3><hr>
|
|
|
|
<p><h4>Introduction</h4>
|
|
|
|
<p>A companion software distribution <a
|
|
href="ftp://ftp.udel.edu/pub/ntp/bsd_audio.tar.Z">
|
|
pub/ntp/bsd_audio.tar.Z</a> includes modifications to the BSD audio
|
|
driver for the Sun SPARCstation written by Van Jacobson and
|
|
collaborators at Lawrence Berkeley National Laboratory. The
|
|
modifications provide for the connection of a standard Inter-Range
|
|
Instrumentation Group (IRIG) timecode signal generator and the decoding
|
|
of the signal to produce data sufficient to synchronize a host clock to
|
|
the IRIG signal. There are several timing receivers now on the market
|
|
that can produce IRIG signals, including those made by Austron,
|
|
TrueTime, Odetics and Spectracom, among others. These data can be used
|
|
to precisely synchronize the host computer clock to within a few
|
|
microseconds without requiring level converters or pulse generators
|
|
necessary with the pulse-per-second signals also produced by these
|
|
receivers. The current implementation of the Network Time Protocol
|
|
Version 3 supports the modified BSD driver when installed in the SunOS
|
|
4.1.x kernel.
|
|
|
|
<p>The specific IRIG signal format supported by the driver is designated
|
|
IRIG-B. It consists of an amplitude-modulated 1000-Hz sinewave, where
|
|
each symbol is encoded as ten full carrier cycles, or 10 ms in duration.
|
|
The symbols are distinguished using a pulse-width code, where 2 ms
|
|
corresponds to logic zero, 5 ms to logic one and 8 ms to a position
|
|
identifier used for symbol synchronization. The complete IRIG-B message
|
|
consists of a frame of ten fields, each field consisting of a nine
|
|
information symbols followed by a position identifier for a total frame
|
|
duration of one second. The first symbol in the frame is also a position
|
|
identifier to facilitate frame synchronization.
|
|
|
|
<p>The IRIG-B signal encodes the day of year and time of day in binary-
|
|
coded decimal (BCD) format, together with a set of control functions,
|
|
which are not used by the driver, but included in the raw binary
|
|
timecode. Either the BCD timecode or the combined raw timecode and BCD
|
|
timecode can be returned in response to a <code>read()</code> system
|
|
call. The BCD timecode is in handy ASCII format: <code>"ddd
|
|
hh:mm:ss*"</code> for convenience in client programs. In this format the
|
|
<code>"*"</code> status character is " " when the driver is operating
|
|
normally and <code>"?"</code> when errors may be present (see below). In
|
|
order to reduce residual errors to the greatest extent possible, the
|
|
driver computes a timestamp based on the value of the kernel clock at
|
|
the on-time epoch of the IRIG-B signal. In addition, the driver
|
|
automatically adjusts for slowly varying amplitude levels of the IRIG-B
|
|
signal and suppresses noise transients.
|
|
|
|
<p>In operation the IRIG driver interprets the IRIG-B signal in real
|
|
time, synchronizes to the signal, demodulates the data bits and prepares
|
|
the data to be read later. At the on-time epoch a timestamp is captured
|
|
from the kernel clock and adjusted for the phase of the IRIG carrier
|
|
signal relative to the 8-kHz codec sample clock. When a client program
|
|
issues a <code>read()</code> request, the most recent timecode data,
|
|
including a status byte and the corrected timestamp, are stored in a
|
|
structure and returned to the caller. Depending on the frequency with
|
|
which the driver is called, this may result in old data or duplicate
|
|
data or even invalid data, should the driver be called before it has
|
|
computed its first timestamp.
|
|
|
|
<p>In practice, the resulting ambiguity causes few problems. The caller
|
|
converts the ASCII timecode returned by a <code>read()</code> system
|
|
call to Unix timeval format and subtracts it from the kernel timestamp
|
|
provided by the driver. The result is an adjustment that can be
|
|
subtracted from the kernel time, as returned in a
|
|
<code>gettimeofday()</code> call, for example, to correct for the
|
|
deviation between IRIG time and kernel time. The result can always be
|
|
relied on to within plus/minus 128 microseconds, the audio codec
|
|
sampling interval, and ordinarily to within a few microseconds, as
|
|
determined by the interpolation algorithm.
|
|
|
|
<p><h4>Programming Interface</h4>
|
|
|
|
<p>The IRIG driver modifications are integrated in the BSD audio driver
|
|
<code>bsd_audio.c</code> without affecting its usual functions in
|
|
transmitting and receiving ordinary speech, except when enabled by
|
|
specific <code>ioctl()</code> system calls. However, the driver cannot
|
|
be used for both speech and IRIG signals at the same time. Once
|
|
activated by a designated <code>ioctl()</code> call, the driver remains
|
|
active until it is explicitly deactivated by another
|
|
<code>ioctl()</code> call. This allows applications to configure the
|
|
audio device and pass the pre-configured driver to other applications.
|
|
Since the driver is currently only a receiver, it does not affect the
|
|
operation of the BSD audio output driver.
|
|
|
|
<p>Data are read using the standard <code>read()</code> system call.
|
|
Since the output formats have constant lengths, the application receives
|
|
the data into a fixed-length buffer or structure. The
|
|
<code>read()</code> call never blocks; it simply returns the most recent
|
|
IRIG data received during the last second. It may happen that, due to
|
|
unavoidable race conditions in the kernel, data for other than the most
|
|
recent second are returned. The driver's internal data structure is
|
|
updated as an atomic unit; thus, the entire structure is valid, even if
|
|
it contains old data. This should cause no problems, since in the
|
|
intended application the driver is called at regular intervals by a
|
|
time-synchronization daemon such as NTP. The daemon can determine the
|
|
validity of the time indication by checking the timecode or status byte
|
|
returned with the data.
|
|
|
|
<p>The header file <code>bsd_audioirig.h</code> defines the irig_time
|
|
structure and <code>ioctl()</code> codes used by the driver. Following
|
|
are those codes specific to the IRIG function of the driver. Unless
|
|
indicated otherwise, the (third) argument of the <code>ioctl()</code>
|
|
system call points to an integer or string.
|
|
|
|
<dl>
|
|
|
|
<dt><code>audio_IRIG_OPEN</code>
|
|
<dd>This command activates the IRIG receiver. The audio driver must be
|
|
opened with this command before other commands can be issued. The
|
|
argument is ignored. When the IRIG receiver is initialized, all internal
|
|
data are purged and any buffered data are lost.
|
|
|
|
<p><dt><code>audio_IRIG_CLOSE</code>
|
|
<dd>This command deactivates the IRIG receiver. The argument is ignored.
|
|
The buffers are purged and any buffered time data are lost. The original
|
|
BSD audio driver functions are enabled and it resumes operating
|
|
normally.
|
|
|
|
<p><dt><code>audio_IRIG_SETFORMAT</code>
|
|
<DD>The argument is a pointer to an integer designating the output
|
|
format for the IRIG data. There are currently two formats defined, 0
|
|
(default) and 1. If an invalid format is selected, the default format is
|
|
used.
|
|
|
|
</dl>
|
|
<p>The data returned by a <code>read()</code> system call in format 0 is
|
|
a character string in the format <code>ddd hh:mm:ss*\n</code>, which
|
|
consists of 13 ASCII characters followed by a <code>\n</code> terminator
|
|
for a total of 14 characters. The <code>*</code> status character is an
|
|
ASCII space if the status byte determined by the driver is zero and
|
|
<code>?</code> if not. This format is intended to be used with simple
|
|
user programs that care only about the time to the nearest second.
|
|
|
|
<p>The data returned by a <code>read()</code> system call in format 1 is
|
|
a structure defined in the <code>bsd_audioirig.h</code> header file:
|
|
|
|
<pre>
|
|
struct irig_time {
|
|
struct timeval stamp; /* timestamp */
|
|
u_char bits[13]; /* 100 irig data bits */
|
|
u_char status; /* status byte */
|
|
char time[14]; /* time string
|
|
*/
|
|
};
|
|
</pre>
|
|
|
|
<p>The <code>irig_time.stamp</code> is a pair of 32-bit longwords in
|
|
Unix <code>timeval</code> format, as defined in the
|
|
<code>/usr/include/sys/time.h</code> header file. The first word is the
|
|
number of seconds since 1 January 1970, while the second is the number
|
|
of microseconds in the current second. The timestamp is captured at the
|
|
most recent on-time epoch of the IRIG timecode and applies to all other
|
|
values returned in the <code>irig_time</code> structure.
|
|
|
|
<p>The <code>irig_time.bits[13]</code> is a vector of 13 bytes to hold
|
|
the 100-bit, zero-padded raw binary timecode, packed 8 symbols per byte.
|
|
The symbol encoding maps IRIG one to 1 and both IRIG zero and IRIG
|
|
position identifier to 0. The order of encoding is illustrated by the
|
|
following diagram (the padding bits are represented by
|
|
<code>xxxx</code>, which are set to zero):
|
|
|
|
<pre>
|
|
IRIG symbol number 00000000001111111111 . . . 8888889999999999xxxx
|
|
01234567890123456789 . . .
|
|
4567890123456789xxxx
|
|
----------------------------------------
|
|
-------
|
|
bits byte number <--00--><--01--><---- ----
|
|
><--11--><--12-->
|
|
bits bit in byte 01234567012345670123 . . . 45670123456701234567
|
|
</pre>
|
|
|
|
<p>The <code>irig_time.status</code> is a single byte with bits defined
|
|
in the <code>bsd_audioirig.h</code> header file. In ordinary operation
|
|
all bits of the status byte are zero and the ASCII space status
|
|
character is set in the ASCII timecode. If any of these bits are
|
|
nonzero, the <code>?</code> status character is set in the ASCII
|
|
timecode.
|
|
|
|
<dl>
|
|
|
|
<dt><code>audio_IRIG_BADSIGNAL</code>
|
|
<dd>The signal amplitude is outside tolerance limits, either in
|
|
amplitude or modulation depth. The indicated time may or may not be in
|
|
error. If the signal is too high, it may be clipped by the codec, so
|
|
that the pulse width cannot be reliably determined. If too low, it may
|
|
be obscured by noise. The nominal expectation is that the peak amplitude
|
|
of the signal be maintained by the codec AGC at about 10 dB below the
|
|
clipping level and that the modulation index be at least 0.5 (6 dB).
|
|
<p><dt><code>audio_IRIG_BADDATA</code>
|
|
<dd>An invalid hex code (A through F) has been found where BCD data is
|
|
expected. The ASCII representation of the invalid code is set to
|
|
<code>?</code>. Errors of this type are most likely due to noise on the
|
|
IRIG signal due to ground loops, coupling to other noise sources, etc.
|
|
|
|
<p><dt><code>audio_IRIG_BADSYNC</code>
|
|
<dd>A code element has been found where a position identifier should be
|
|
or a position identifier has been found where a code element should be.
|
|
The time is meaningless and should be disregarded. Errors of this type
|
|
can be due to severe noise on the IRIG signal due to ground loops,
|
|
coupling to other noise sources, etc., or during initial acquisition of
|
|
the signal.
|
|
|
|
<p><dt><code>audio_IRIG_BADCLOCK</code>
|
|
<dd>Some IRIG timecode generators can indicate whether or not the
|
|
generator is operating correctly or synchronized to its source of
|
|
standard time using a designated field in the raw binary timecode. Where
|
|
such information is available and the IRIG decoder can detect it, this
|
|
bit is set when the generator reports anything except normal operating
|
|
conditions.
|
|
|
|
<p><dt><code>audio_IRIG_OLDDATA</code>
|
|
<dd>The IRIG time has not changed since the last time it was returned in
|
|
a <code>read()</code> call. This is not normally considered an error,
|
|
unless it persists for longer than a few seconds, in which case it
|
|
probably indicates a hardware problem.
|
|
|
|
</dl>
|
|
|
|
<p>The <code>irig_time.time[14]</code> vector is a character string in
|
|
the format <code>ddd hh:mm:ss*\0</code>, which consists of 13 ASCII
|
|
characters followed by a zero terminator. The * status character is an
|
|
ASCII space if the status byte is zero and <code>?</code> if not. This
|
|
format is identical to format 0, except that in format 1 the time string
|
|
is null-terminated.
|
|
|
|
<p><h4>Programming Example</h4>
|
|
|
|
<p>The following pseudo-code demonstrates how the IRIG receiver may be
|
|
used by a simple user program. Of course, real code should include error
|
|
checking after each call to ensure the driver is communicating properly.
|
|
It should also verify that the correct fields in the structure are being
|
|
filled by the <code>read()</code> call.
|
|
|
|
<p><pre>
|
|
include "bsd_audioirig.h"
|
|
|
|
int format = 1;
|
|
struct irig_time it;
|
|
|
|
Audio_fd = open("/dev/audio", O_RDONLY);
|
|
ioctl(Audio_fd, AUDIO_IRIG_OPEN, NULL);
|
|
ioctl(Audio_fd, AUDIO_IRIG_SETFORMAT,&format);
|
|
while (condition)
|
|
read(Audio_fd, &it, sizeof(it);
|
|
printf("%s\n", it.time);
|
|
ioctl(Audio_fd, AUDIO_IRIG_CLOSE, NULL);
|
|
close(Audio_fd);
|
|
</pre>
|
|
|
|
<p><h4>Implementation and Configuration Notes</h4>
|
|
|
|
<p>The signal level produced by most IRIG-equipped radios is on the
|
|
order of a few volts peak-peak, which is far larger than the audio codec
|
|
can accept; therefore, an attenuator in the form of a voltage divider is
|
|
needed. The codec can handle IRIG signals at the microphone input from
|
|
4.2 mV to 230 mV peak-peak. A suitable attenuator conists of a series-
|
|
connected 100K-Ohm resistor at the input and a parallel-connected 1K-Ohm
|
|
resistor at the output, both contained along with suitable connectors in
|
|
a small aluminum box. The exact values of these resistors are not
|
|
critical, since the IRIG driver includes an automatic level-adjustment
|
|
capability.
|
|
|
|
<p>For the most accurate time using the IRIG signal and a particular
|
|
radio, it may be necessary to adjust the <code>time1</code> parameter of
|
|
the <code>fudge</code> command to compensate for the codec delay and any
|
|
additional delay due to IRIG processing in the radio itself. Since the
|
|
codec samples at an 8-kHz rate, the average delay is about 62 us;
|
|
however, the delays due to the radios and IRIG signals themselves can
|
|
vary. For instance, in the Austron recievers the IRIG delay is
|
|
essentially zero, while in the Spectracom receivers the delay is about
|
|
240 usec relative to the PPS signal. In addition, the poll interval can
|
|
be reduced from the usual 64 seconds to 16 seconds to reduce wander of
|
|
the local hardware clock. Finally, the <code>prefer</code> keyword can
|
|
be used to bias the clock-selection algorithm to favor the IRIG time,
|
|
which is ordinarily the best time available. The <a href =
|
|
"prefer.html"> Mitigation Rules and the <code>prefer</code> Keyword </a>
|
|
page describes the operation of this keyword.
|
|
|
|
For example, the following two lines in the NTP configuration file
|
|
<code>ntp.conf</code> are appropriate for the Spectracom Netclock/1 WWVB
|
|
Synchronized Clock with IRIG Option:
|
|
|
|
<p><pre>
|
|
server 127.127.6.0 prefer minpoll 4 maxpoll 4 # irig audio decoder
|
|
fudge 127.127.6.0 time1 0.0005
|
|
</pre>
|
|
|
|
<p>The <code>time1</code> value of .0005 s (500 us) was determined by
|
|
actual measurement. Since the IRIG delay in Austron receivers is
|
|
essentially zero, the <code>fudge</code> command is not necessary with
|
|
these receivers. The correct value in case of other radios may have to
|
|
be determined by actual measurement. A convenient way of doing this is
|
|
to configure the <code>ppsclock</code> streams module. This module can
|
|
be built from the <a
|
|
href="ftp://ftp.udel.edu/pub/ntp/ppsclock.tar.Z"><code>ppsclock.tar.Z</code>
|
|
distribution. </a> It can be used to adjust <code>time1</code>
|
|
until the PPS signal and IRIG signal both show the same offset. The
|
|
<code>ppsclock</code> streams module is described in the <a
|
|
href="ldisc.html">Line Disciplines and Streams Drivers</a> page.
|
|
|
|
<p>The modified BSD driver includes both the modified driver itself
|
|
bsd_audio.c and the IRIG header file <code>bsd_audioirig.h</code>, as
|
|
well as modified header files <code>bsd_audiovar.h</code> and
|
|
<code>bsd_audioio.h</code>. The driver is installed in the same way as
|
|
described in the BSD driver documentation, with the addition of the
|
|
following define in the kernel configuration file:
|
|
|
|
<p><pre>
|
|
options AUDIO_IRIG # IRIG driver
|
|
</pre>
|
|
|
|
<p>This causes the IRIG code to be included in the BSD driver, as well
|
|
as a C-coded codec interrupt routine which replaces the assembly-coded
|
|
routine and provides the IRIG functionality. While the C-coded routine
|
|
is somewhat slower than the assembly-coded routine, the extra overhead
|
|
is not expected to be significant. Note that the IRIG driver calls the
|
|
kernel routine <code>microtime()</code> as included in the
|
|
<code>ppsclock.tar.Z</code> distribution. It is highly recommended that
|
|
this routine be installed in the kernel configuration as well. The
|
|
instructions for doing this are contained in the <code>ppsclock</code>
|
|
directory of the <code>xntp3</code> distribution.
|
|
|
|
<hr><address>David L. Mills (mills@udel.edu)</address></body></html>
|