oncore-shmem.html revision 182007
1132451Sroberto<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2132451Sroberto
3132451Sroberto<html>
4132451Sroberto
5182007Sroberto	<head>
6182007Sroberto		<meta http-equiv="content-type" content="text/html;charset=iso-8859-1">
7182007Sroberto		<title>ONCORE - SHMEM</title>
8182007Sroberto		<link href="scripts/style.css" type="text/css" rel="stylesheet">
9182007Sroberto	</head>
10132451Sroberto
11182007Sroberto	<body>
12182007Sroberto		<h3>Motorola ONCORE - The Shared Memory Interface</h3>
13182007Sroberto		<hr>
14182007Sroberto		<h4>Introduction</h4>
15182007Sroberto		<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>
16182007Sroberto		<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>
17182007Sroberto		<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>
18182007Sroberto		<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+/GT+/M12 manuals are available as a pdf documents at <a href="http://www.synergy-gps.com/Mot_Manuals.html">Synergy</a> .</p>
19182007Sroberto		<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.</p>
20182007Sroberto		<h4>Activating the Interface</h4>
21182007Sroberto		<p>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> or <b>SHMEM</b> line in the <tt>/etc/ntp.oncore</tt> data file that looks like</p>
22182007Sroberto		<pre>
23132451Sroberto        STATUS &lt; file_name &gt;<br>
24182007Sroberto			
25132451Sroberto        or<br>
26182007Sroberto			
27132451Sroberto        SHMEM &lt; file_name &gt;
28132451Sroberto</pre>
29182007Sroberto		Thus a line like
30182007Sroberto		<pre>
31132451Sroberto        SHMEM /var/adm/ntpstats/ONCORE
32132451Sroberto</pre>
33182007Sroberto		<p>would be acceptable. This file name will be used to access the Shared Memory.</p>
34182007Sroberto		<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</p>
35182007Sroberto		<pre>
36132451Sroberto        SHMEM /var/adm/ntpstats/ONCORE
37132451Sroberto        Posn3D
38132451Sroberto</pre>
39182007Sroberto		<p>would be required.</p>
40182007Sroberto		<h4>Storage of Messages in Shared Memory</h4>
41182007Sroberto		<p>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>
42182007Sroberto		<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 &lt;checksum&gt;&lt;CR&gt;&lt;LF&gt;.</p>
43182007Sroberto		<p>The data starts at location ONE of SHMEM (NOT location ZERO).</p>
44182007Sroberto		<p>The messages are stacked in a series of variable length structures, that look like</p>
45182007Sroberto		<pre>
46132451Sroberto        struct message {
47132451Sroberto                u_int   length;
48132451Sroberto                u_char  sequence;
49132451Sroberto                u_char  message[length];
50132451Sroberto        }
51132451Sroberto</pre>
52182007Sroberto		<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>
53182007Sroberto		<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>
54182007Sroberto		<p>Following the last structure, is a u_int containing a zero length to indicate the end of the data.</p>
55182007Sroberto		<p>The messages are recognized by reading the headers in the data itself, viz @@Ea or whatever.</p>
56182007Sroberto		<p>There are two special cases.</p>
57182007Sroberto		<p>(1) The almanac takes a total of 34 submessages all starting with @@Cb.<br>
58182007Sroberto			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>
59182007Sroberto		<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>
60182007Sroberto		<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>
61182007Sroberto		<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.</p>
62182007Sroberto		<h4>Opening the Shared Memory File</h4>
63182007Sroberto		<p>The shared memory segment is accessed through a file name given on a <b>SHMEM</b> card in the <tt>/etc/ntp.oncore</tt> input file. The following code could be used to open the Shared Memory Segment:</p>
64182007Sroberto		<pre>
65132451Sroberto        char *Buf, *file;
66132451Sroberto        int size, fd;
67132451Sroberto        struct stat statbuf;
68132451Sroberto
69132451Sroberto        file = &quot;/var/adm/ntpstats/ONCORE&quot;;  /* the file name on my ACCESS card */
70132451Sroberto        if ((fd=open(file, O_RDONLY)) &lt; 0) {
71132451Sroberto                fprintf(stderr, &quot;Cant open %s\n&quot;, file);
72132451Sroberto                exit(1);
73132451Sroberto        }
74132451Sroberto
75132451Sroberto        if (stat(file, &amp;statbuf) &lt; 0) {
76132451Sroberto                fprintf(stderr, &quot;Cant stat %s\n&quot;, file);
77132451Sroberto                exit(1);
78132451Sroberto        }
79132451Sroberto
80132451Sroberto        size = statbuf.st_size;
81132451Sroberto        if ((Buf=mmap(0, size, PROT_READ, MAP_SHARED, fd, (off_t) 0)) &lt; 0) {
82132451Sroberto                fprintf(stderr, &quot;MMAP failed\n&quot;);
83132451Sroberto                exit(1);
84132451Sroberto        }
85132451Sroberto</pre>
86182007Sroberto		<h4>Accessing the data</h4>
87182007Sroberto		<p>The following code shows how to get to the individual records.</p>
88182007Sroberto		<pre>
89132451Sroberto        void    oncore_msg_Ea(), oncore_msg_As(), oncore_msg_Bb();
90132451Sroberto
91132451Sroberto        struct Msg {
92132451Sroberto            char         c[5];
93132451Sroberto            unsigned int seq;
94132451Sroberto            void         (*go_to)(uchar *);
95132451Sroberto        };
96132451Sroberto
97132451Sroberto        struct Msg Hdr[] = { {&quot;@@Bb&quot;, 0, &amp;oncore_msg_Bb},
98132451Sroberto                             {&quot;@@Ea&quot;, 0, &amp;oncore_msg_Ea},
99132451Sroberto                             {&quot;@@As&quot;, 0, &amp;oncore_msg_As}};
100132451Sroberto
101132451Sroberto        void
102132451Sroberto        read_data()
103132451Sroberto        {
104132451Sroberto            int     i, j, k, n, iseq, jseq;
105132451Sroberto            uchar   *cp, *cp1;
106132451Sroberto
107132451Sroberto
108132451Sroberto            for(cp=Buf+1; (n = 256*(*cp) + *(cp+1)) != 0;  cp+=(n+3)) {
109132451Sroberto                for (k=0; k &lt; sizeof(Hdr)/sizeof(Hdr[0]);  k++) {
110132451Sroberto                    if (!strncmp(cp+3, Hdr[k].c, 4)) {      /* am I interested? */
111132451Sroberto                        iseq = *(cp+2);
112132451Sroberto                        jseq = Hdr[k].seq;
113132451Sroberto                        Hdr[k].seq = iseq;
114132451Sroberto                        if (iseq &gt; jseq) {              /* has it changed? */
115132451Sroberto                            /* verify checksum */
116132451Sroberto                            j = 0;
117132451Sroberto                            cp1 = cp+3;             /* points to start of oncore response */
118132451Sroberto                            for (i=2; i &lt; n-3; i++)
119132451Sroberto                                j ^= cp1[i];
120132451Sroberto                            if (j == cp1[n-3]) {    /* good checksum */
121132451Sroberto                                    Hdr[k].go_to(cp1);
122132451Sroberto                            } else {
123132451Sroberto                                fprintf(stderr, &quot;Bad Checksum for %s\n&quot;, Hdr[k].c);
124132451Sroberto                                break;
125132451Sroberto                            }
126132451Sroberto                        }
127132451Sroberto                    }
128132451Sroberto                }
129132451Sroberto                if (!strncmp(cp+3, &quot;@@Ea&quot;, 4))
130132451Sroberto                    cp += 3*(n+3);
131132451Sroberto                if (!strncmp(cp+3, &quot;@@Cb&quot;, 4))
132132451Sroberto                    cp += 34*(n+3);
133132451Sroberto            }
134132451Sroberto        }
135132451Sroberto
136132451Sroberto        oncore_msg_Bb(uchar *buf)
137132451Sroberto        {
138132451Sroberto                /* process Bb messages */
139132451Sroberto        }
140132451Sroberto
141132451Sroberto        oncore_msg_Ea(uchar *buf)
142132451Sroberto        {
143132451Sroberto                /* process Ea messages */
144132451Sroberto        }
145132451Sroberto
146132451Sroberto        oncore_msg_As(uchar *buf)
147132451Sroberto        {
148132451Sroberto                /* process As messages */
149132451Sroberto        }
150132451Sroberto</pre>
151182007Sroberto		<p>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.</p>
152182007Sroberto		<h4>Examples</h4>
153182007Sroberto		<p>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>
154182007Sroberto		<p>Try the new interface, enjoy.</p>
155182007Sroberto		<hr>
156182007Sroberto		<address>Reg.Clemens (reg@dwf.com), Poul-Henning Kamp (phk@FreeBSD.org)</address>
157182007Sroberto		<hr>
158182007Sroberto		<script type="text/javascript" language="javascript" src="scripts/footer.txt"></script>
159182007Sroberto	</body>
160132451Sroberto
161182007Sroberto</html>