258 lines
9.4 KiB
HTML
258 lines
9.4 KiB
HTML
|
<HTML>
|
||
|
<HEAD>
|
||
|
<TITLE> ONCORE - SHMEM </TITLE>
|
||
|
</HEAD>
|
||
|
<BODY>
|
||
|
<H3>
|
||
|
Motorola ONCORE - The Shared Memory Interface
|
||
|
</H3>
|
||
|
<HR>
|
||
|
|
||
|
<H4>
|
||
|
Introduction
|
||
|
</H4>
|
||
|
|
||
|
<P>
|
||
|
In NMEA mode, the Oncore GPS receiver provides the user with the same information as
|
||
|
other GPS receivers.
|
||
|
In BINARY mode, it can provide a lot of additional information.
|
||
|
<P>
|
||
|
In particular, you can ask for satellite positions, satellite health, signal levels,
|
||
|
the ephemeris and the almanac, and you can set many operational parameters.
|
||
|
In the case of the VP,
|
||
|
you can get the pseudorange corrections necessary to act as a DGPS base station, and you can see
|
||
|
the raw satellite data messages themselves.
|
||
|
<P>
|
||
|
When using the Oncore GPS receiver with NTP, this additional information is usually
|
||
|
not available since the receiver is only talking to the oncore driver in NTPD.
|
||
|
To make this information available for use in other programs,
|
||
|
(say graphic displays of satellites positions, plots of SA, etc.), a shared memory interface
|
||
|
(SHMEM) has been added to the refclock_oncore driver on those operating systems that support
|
||
|
shared memory.
|
||
|
<P>
|
||
|
To make use of this information you will need an Oncore Reference Manual for the
|
||
|
Oncore GPS receiver that you have. The Manual for the VP only exists as a paper
|
||
|
document, the UT manuals are available as a pdf document online.
|
||
|
<P>
|
||
|
This interface was written by Poul-Henning Kamp (phk@FreeBSD.org), and modified by
|
||
|
Reg Clemens (reg@dwf.com).
|
||
|
The interface is known to work in FreeBSD, Linux, and Solaris.
|
||
|
<H4>
|
||
|
Activating the Interface
|
||
|
</H4>
|
||
|
Although the Shared Memory Interface will be compiled into the Oncore driver
|
||
|
on those systems where Shared Memory is supported, to activate this interface you must
|
||
|
include a <B>STATUS</B> line in the <tt>/etc/ntp.oncore</tt> data file that looks like
|
||
|
<PRE>
|
||
|
STATUS < file_name >
|
||
|
</PRE>
|
||
|
Thus a line like
|
||
|
<PRE>
|
||
|
STATUS /var/adm/ntpstats/ONCORE
|
||
|
</PRE>
|
||
|
would be acceptable.
|
||
|
This file name will be used to access the Shared Memory.
|
||
|
<P>
|
||
|
In addition, one the two keywords <B>Posn2D</B> and <B>Posn3D</B> can be added to
|
||
|
see @@Ea records containing the 2D or 3D position of the station (see below).
|
||
|
Thus to activate the interface, and see 3D positions, something like
|
||
|
<PRE>
|
||
|
STATUS /var/adm/ntpstats/ONCORE
|
||
|
Posn3D
|
||
|
</PRE>
|
||
|
would be required.
|
||
|
<H4>
|
||
|
Storage of Messages in Shared Memory
|
||
|
</H4>
|
||
|
With the shared memory interface, the oncore driver (refclock_oncore) allocates space
|
||
|
for all of the messages that it is configured to receive, and then puts each message
|
||
|
in the appropriate slot in shared memory as it arrives from the receiver.
|
||
|
Since there is no easy way for a client program to know when the shared memory has
|
||
|
been updated,
|
||
|
a sequence number is associated with each message, and is incremented when a new message
|
||
|
arrives.
|
||
|
With the sequence number it is easy to check through the shared memory segment for messages that
|
||
|
have changed.
|
||
|
<P>
|
||
|
The Oncore binary messages are kept in their full length, as described in the Reference
|
||
|
manual, that is everything from the @@ prefix thru the <checksum><CR><LF>.
|
||
|
<P>
|
||
|
The data starts at location ONE of SHMEM (NOT location ZERO).
|
||
|
<P>
|
||
|
The messages are stacked in a series of variable length structures, that look like
|
||
|
<PRE>
|
||
|
struct message {
|
||
|
u_int length;
|
||
|
u_char sequence;
|
||
|
u_char message[length];
|
||
|
}
|
||
|
</PRE>
|
||
|
<P>
|
||
|
if something like that were legal.
|
||
|
That is, there are two bytes (caution, these may NOT be aligned with word boundaries, so
|
||
|
the field needs to be treated as a pair of u_char), that contains the length of the next
|
||
|
message.
|
||
|
This is followed by a u_char sequence number, that is incremented whenever a new message of
|
||
|
this type is received.
|
||
|
This is followed by 'length' characters of the actual message.
|
||
|
<P>
|
||
|
The next structure starts immediately following the last char of the previous message (no alignment).
|
||
|
Thus, each structure starts a distance of 'length+3' from the previous structure.
|
||
|
<P>
|
||
|
Following the last structure, is a u_int containing a zero length to indicate the end
|
||
|
of the data.
|
||
|
<P>
|
||
|
The messages are recognized by reading the headers in the data itself, viz @@Ea or whatever.
|
||
|
<P>
|
||
|
There are two special cases.
|
||
|
<P>
|
||
|
(1) The almanac takes a total of 34 submessages all starting with @@Cb. <br>
|
||
|
35 slots are allocated in shared memory.
|
||
|
Each @@Cb message is initially placed in the first of these locations,
|
||
|
and then later it is moved to the appropriate location for that submessage.
|
||
|
The submessages can be distinguished by the first two characters following the @@Cb header,
|
||
|
and new data is received only when the almanac changes.
|
||
|
<P>
|
||
|
(2) The @@Ea message contains the calculated location of the antenna, and is received
|
||
|
once per second.
|
||
|
However, when in timekeeping mode, the receiver is normally put in 0D mode, with the
|
||
|
position fixed, to get better accuracy.
|
||
|
In 0D mode no position is calculated.
|
||
|
<P>
|
||
|
When the SHMEM option is active,
|
||
|
and if one of <B>Posn2D</B> or <B>Posn3D</B> is specified,
|
||
|
one @@Ea record is hijacked each 15s, and the receiver
|
||
|
is put back in 2D/3D mode so the the current location can be determined (for position determination, or for
|
||
|
tracking SA).
|
||
|
The timekeeping code is careful NOT to use the time associated with this (less accurate) 2D/3D tick
|
||
|
in its timekeeping functions.
|
||
|
<P>
|
||
|
Following the initial @@Ea message are 3 additional slots for a total of four.
|
||
|
As with the almanac, the first gets filled each time a new record becomes available,
|
||
|
later in the code, the message is distributed to the appropriate slot.
|
||
|
The additional slots are for messages containing 0D, 2D and 3D positions.
|
||
|
These messages can be distinguished by different bit patterns in the last data byte of the record.
|
||
|
<H4>
|
||
|
Opening the Shared Memory File
|
||
|
</H4>
|
||
|
The shared memory segment is accessed through a file name given on a <B>ACCESS</B> card in the
|
||
|
<tt>/etc/ntp.oncore</tt> input file.
|
||
|
The following code could be used to open the Shared Memory Segment:
|
||
|
|
||
|
<PRE>
|
||
|
char *Buf, *file;
|
||
|
int size, fd;
|
||
|
struct stat statbuf;
|
||
|
|
||
|
file = "/var/adm/ntpstats/ONCORE"; /* the file name on my ACCESS card */
|
||
|
if ((fd=open(file, O_RDONLY)) < 0) {
|
||
|
fprintf(stderr, "Cant open %s\n", file);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
if (stat(file, &statbuf) < 0) {
|
||
|
fprintf(stderr, "Cant stat %s\n", file);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
size = statbuf.st_size;
|
||
|
if ((Buf=mmap(0, size, PROT_READ, MAP_SHARED, fd, (off_t) 0)) < 0) {
|
||
|
fprintf(stderr, "MMAP failed\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
</PRE>
|
||
|
|
||
|
<H4>
|
||
|
Accessing the data
|
||
|
</H4>
|
||
|
The following code shows how to get to the individual records.
|
||
|
|
||
|
<PRE>
|
||
|
void oncore_msg_Ea(), oncore_msg_As(), oncore_msg_Bb();
|
||
|
|
||
|
struct Msg {
|
||
|
char c[5];
|
||
|
unsigned int seq;
|
||
|
void (*go_to)(uchar *);
|
||
|
};
|
||
|
|
||
|
struct Msg Hdr[] = { {"@@Bb", 0, &oncore_msg_Bb},
|
||
|
{"@@Ea", 0, &oncore_msg_Ea},
|
||
|
{"@@As", 0, &oncore_msg_As}};
|
||
|
|
||
|
void
|
||
|
read_data()
|
||
|
{
|
||
|
int i, j, k, n, iseq, jseq;
|
||
|
uchar *cp, *cp1;
|
||
|
|
||
|
|
||
|
for(cp=Buf+1; (n = 256*(*cp) + *(cp+1)) != 0; cp+=(n+3)) {
|
||
|
for (k=0; k < sizeof(Hdr)/sizeof(Hdr[0]); k++) {
|
||
|
if (!strncmp(cp+3, Hdr[k].c, 4)) { /* am I interested? */
|
||
|
iseq = *(cp+2);
|
||
|
jseq = Hdr[k].seq;
|
||
|
Hdr[k].seq = iseq;
|
||
|
if (iseq > jseq) { /* has it changed? */
|
||
|
/* verify checksum */
|
||
|
j = 0;
|
||
|
cp1 = cp+3; /* points to start of oncore response */
|
||
|
for (i=2; i < n-3; i++)
|
||
|
j ^= cp1[i];
|
||
|
if (j == cp1[n-3]) { /* good checksum */
|
||
|
Hdr[k].go_to(cp1);
|
||
|
} else {
|
||
|
fprintf(stderr, "Bad Checksum for %s\n", Hdr[k].c);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (!strncmp(cp+3, "@@Ea", 4))
|
||
|
cp += 3*(n+3);
|
||
|
if (!strncmp(cp+3, "@@Cb", 4))
|
||
|
cp += 34*(n+3);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
oncore_msg_Bb(uchar *buf)
|
||
|
{
|
||
|
/* process Bb messages */
|
||
|
}
|
||
|
|
||
|
oncore_msg_Ea(uchar *buf)
|
||
|
{
|
||
|
/* process Ea messages */
|
||
|
}
|
||
|
|
||
|
oncore_msg_As(uchar *buf)
|
||
|
{
|
||
|
/* process As messages */
|
||
|
}
|
||
|
</PRE>
|
||
|
|
||
|
The structure Hdr contains the Identifying string for each of the messages that
|
||
|
we want to examine, and the name of a program to call when a new message of that
|
||
|
type is arrives.
|
||
|
The loop can be run every few seconds to check for new data.
|
||
|
<H4>
|
||
|
Examples
|
||
|
</H4>
|
||
|
There are two complete examples available.
|
||
|
The first plots satellite positions and the station position as affected by SA, and
|
||
|
keeps track of the mean station position, so you can run it for periods of days
|
||
|
to get a better station position.
|
||
|
The second shows the effective horizon by watching satellite tracks.
|
||
|
The examples will be found in the GNU-zipped tar file
|
||
|
<A HREF=ftp://ftp.udel.edu/pub/ntp/software/OncorePlot.tar.gz>
|
||
|
ftp://ftp.udel.edu/pub/ntp/software/OncorePlot.tar.gz</A>.
|
||
|
<P>
|
||
|
Try the new interface, enjoy.
|
||
|
<HR>
|
||
|
<ADDRESS>
|
||
|
Reg.Clemens (reg@dwf.com),
|
||
|
Poul-Henning Kamp (phk@FreeBSD.org)
|
||
|
<ADDRESS>
|
||
|
</BODY>
|
||
|
</HTML>
|