summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gps.h3
-rw-r--r--gps/fake.py2
-rwxr-xr-xgps/gps.py5
-rw-r--r--gps_json.h3
-rw-r--r--gpsd.c19
-rw-r--r--gpsd.h-tail9
-rw-r--r--gpsd_json.c15
-rwxr-xr-xgpsprof202
-rw-r--r--gpsprof.xml67
-rw-r--r--gpsutils.c9
-rw-r--r--libgpsd_core.c16
-rw-r--r--www/hacking.html51
12 files changed, 53 insertions, 348 deletions
diff --git a/gps.h b/gps.h
index bf3c3f92..f035448c 100644
--- a/gps.h
+++ b/gps.h
@@ -1751,6 +1751,9 @@ extern /*@observer@*/const char *gps_maskdump(gps_mask_t);
extern double safe_atof(const char *);
extern time_t mkgmtime(register struct tm *);
extern timestamp_t timestamp(void);
+#ifdef TIMING_ENABLE
+extern timestamp_t realtimestamp(void);
+#endif /* TIMING_ENABLE */
extern timestamp_t iso8601_to_unix(char *);
extern /*@observer@*/char *unix_to_iso8601(timestamp_t t, /*@ out @*/char[], size_t len);
extern double earth_distance(double, double, double, double);
diff --git a/gps/fake.py b/gps/fake.py
index 182529a5..7843fed6 100644
--- a/gps/fake.py
+++ b/gps/fake.py
@@ -495,8 +495,6 @@ class TestSession:
self.progress("gpsfake: gather(%d)\n" % seconds)
#mark = time.time()
time.sleep(seconds)
- #if self.timings.c_recv_time <= mark:
- # TestSessionError("no sentences received\n")
def cleanup(self):
"We're done, kill the daemon."
self.progress("gpsfake: cleanup()\n")
diff --git a/gps/gps.py b/gps/gps.py
index 3f68dd4e..21866828 100755
--- a/gps/gps.py
+++ b/gps/gps.py
@@ -125,7 +125,6 @@ class gpsdata:
self.devices = []
self.version = None
- self.timings = None
def __repr__(self):
st = "Time: %s (%s)\n" % (self.utc, self.fix.time)
@@ -293,10 +292,6 @@ class gps(gpsdata, gpsjson):
if sat.used:
self.satellites_used += 1
self.valid = ONLINE_SET | SATELLITE_SET
- elif self.data.get("class") == "TIMING":
- self.data["c_recv"] = self.received
- self.data["c_decode"] = time.time()
- self.timings = self.data
def read(self):
"Read and interpret data from the daemon."
diff --git a/gps_json.h b/gps_json.h
index bf5150d2..e3419928 100644
--- a/gps_json.h
+++ b/gps_json.h
@@ -16,7 +16,8 @@ extern "C" {
void json_data_report(gps_mask_t, struct gps_data_t *, struct policy_t *,
/*@out@*/char *, size_t);
char *json_stringify(/*@out@*/char *, size_t, /*@in@*/const char *);
-void json_tpv_dump(const struct gps_data_t *, /*@out@*/char *, size_t);
+void json_tpv_dump(const struct gps_data_t *,
+ const struct policy_t *, /*@out@*/char *, size_t);
void json_noise_dump(const struct gps_data_t *, /*@out@*/char *, size_t);
void json_sky_dump(const struct gps_data_t *, /*@out@*/char *, size_t);
void json_att_dump(const struct gps_data_t *, /*@out@*/char *, size_t);
diff --git a/gpsd.c b/gpsd.c
index 60e36db4..3b2fd7ce 100644
--- a/gpsd.c
+++ b/gpsd.c
@@ -1226,7 +1226,7 @@ static void handle_request(struct subscriber_t *sub,
for (devp = devices; devp < devices + MAXDEVICES; devp++) {
if (allocated_device(devp) && subscribed(sub, devp)) {
if ((devp->observed & GPS_TYPEMASK) != 0) {
- json_tpv_dump(&devp->gpsdata,
+ json_tpv_dump(&devp->gpsdata, &sub->policy,
reply + strlen(reply),
replylen - strlen(reply));
rstrip(reply);
@@ -1633,23 +1633,6 @@ static void consume_packets(struct gps_device_t *device)
if (buf[0] != '\0')
(void)throttled_write(sub, buf, strlen(buf));
-#ifdef TIMING_ENABLE
- if (buf[0] != '\0' && sub->policy.timing) {
- (void)snprintf(buf, sizeof(buf),
- "{\"class\":\"TIMING\","
- "\"tag\":\"%s\",\"len\":%d,"
- "\"xmit\":%lf,\"recv\":%lf,"
- "\"decode\":%lf,"
- "\"emit\":%lf}\r\n",
- device->gpsdata.tag,
- (int)device->packet.outbuflen,
- device->d_xmit_time,
- device->d_recv_time,
- device->d_decode_time,
- timestamp());
- (void)throttled_write(sub, buf, strlen(buf));
- }
-#endif /* TIMING_ENABLE */
}
}
}
diff --git a/gpsd.h-tail b/gpsd.h-tail
index 0979d86b..6297a77b 100644
--- a/gpsd.h-tail
+++ b/gpsd.h-tail
@@ -569,15 +569,6 @@ struct gps_device_t {
struct aivdm_context_t aivdm[AIVDM_CHANNELS];
#endif /* AIVDM_ENABLE */
-#ifdef TIMING_ENABLE
- /* profiling data for last sentence */
- char tag[MAXTAGLEN+1]; /* tag of last sentence processed */
- timestamp_t d_xmit_time; /* beginning of sentence transmission */
- timestamp_t d_recv_time; /* daemon receipt time (-> E1+T1) */
- timestamp_t d_decode_time; /* daemon end-of-decode time (-> D1) */
- timestamp_t emit_time; /* emission time (-> E2) */
-#endif /* TIMING_ENABLE */
-
/*
* State of an NTRIP connection. We don't want to zero this on every
* activation, otherwise the connection state will get lost. Information
diff --git a/gpsd_json.c b/gpsd_json.c
index bcaa0e3c..bb5a6201 100644
--- a/gpsd_json.c
+++ b/gpsd_json.c
@@ -113,10 +113,14 @@ void json_version_dump( /*@out@*/ char *reply, size_t replylen)
GPSD_PROTO_MAJOR_VERSION, GPSD_PROTO_MINOR_VERSION);
}
-void json_tpv_dump(const struct gps_data_t *gpsdata,
+void json_tpv_dump(const struct gps_data_t *gpsdata,
+ const struct policy_t *policy,
/*@out@*/ char *reply, size_t replylen)
{
char tbuf[JSON_DATE_MAX+1];
+#ifdef TIMING_ENABLE
+ timestamp_t xmit_time = realtimestamp();
+#endif /* TIMING_ENABLE */
assert(replylen > 2);
(void)strlcpy(reply, "{\"class\":\"TPV\",", replylen);
@@ -199,6 +203,13 @@ void json_tpv_dump(const struct gps_data_t *gpsdata,
(void)snprintf(reply + strlen(reply),
replylen - strlen(reply),
"\"epc\":%.2f,", gpsdata->fix.epc);
+#ifdef TIMING_ENABLE
+ if (policy->timing)
+ (void)snprintf(reply + strlen(reply),
+ replylen - strlen(reply),
+ "\"xmit_time\":%f,",
+ xmit_time);
+#endif /* TIMING_ENABLE */
}
if (reply[strlen(reply) - 1] == ',')
reply[strlen(reply) - 1] = '\0'; /* trim trailing comma */
@@ -2922,7 +2933,7 @@ void json_data_report(gps_mask_t changed,
buf[0] = '\0';
if ((changed & REPORT_IS) != 0) {
- json_tpv_dump(datap, buf+strlen(buf), buflen-strlen(buf));
+ json_tpv_dump(datap, policy, buf+strlen(buf), buflen-strlen(buf));
}
if ((changed & GST_SET) != 0) {
diff --git a/gpsprof b/gpsprof
index 099d7f03..e688cac1 100755
--- a/gpsprof
+++ b/gpsprof
@@ -42,7 +42,7 @@ class Baton:
return
class spaceplot:
- "Total times without instrumentation."
+ "Spatial scattergram of fixes."
name = "space"
def __init__(self):
self.fixes = []
@@ -184,205 +184,7 @@ plot "-" using 0:1 title "Total time" with impulses
res += self.header(session)
return res + self.data(session)
-class rawplot:
- "All measurement, no deductions."
- name = "raw"
- def __init__(self):
- self.stats = []
- def gather(self, session):
- self.stats.append(copy.copy(session.timings))
- return True
- def header(self, session):
- res = "# Raw latency data, %s, %s, %dN%d, cycle %ds\n" % \
- (title,
- session.gps_id, session.baudrate,
- session.stopbits, session.cycle)
- res += "# tag len xmit "
- for hn in ("T1", "D1", "E2", "T2", "D2"):
- res += "%-13s" % hn
- res += "\n#------- ----- --------------------"
- res += (" " + ("-" * 11)) * 5
- return res + "\n"
- def data(self, unused):
- res = ""
- for timings in self.stats:
- res += "% 8s %4d %2.9f %2.9f %2.9f %2.9f %2.9f %2.9f\n" \
- % (timings.tag,
- timings.len,
- timings.xmit,
- timings.recv - timings.xmit,
- timings.decode - timings.recv,
- timings.emit - timings.decode,
- timings.c_recv - timings.emit,
- timings.c_decode - timings.c_recv)
- return res
- def plot(self, unused, session):
- fmt = '''
-set autoscale
-set key below
-set key title "Raw latency data, %s, %s, %dN%d, cycle %ds"
-plot \
- "-" using 0:8 title "D2 = Client decode time" with impulses, \
- "-" using 0:7 title "T2 = TCP/IP latency" with impulses, \
- "-" using 0:6 title "E2 = Daemon encode time" with impulses, \
- "-" using 0:5 title "D1 = Daemon decode time" with impulses, \
- "-" using 0:4 title "T1 = RS232 time" with impulses
-'''
- res = fmt % (title,
- session.gps_id, session.baudrate,
- session.stopbits, session.cycle)
- res += self.header(session)
- res += (self.data(session) + "e\n") * 5
- return res
-
-class splitplot:
- "Discard base time, use color to indicate different tags."
- name = "split"
- sentences = []
- def __init__(self):
- self.stats = []
- def gather(self, session):
- self.stats.append(copy.copy(session.timings))
- if session.timings.tag not in self.sentences:
- self.sentences.append(session.timings.tag)
- return True
- def header(self, session):
- res = "# Split latency data, %s, %s, %dN%d, cycle %ds\n#" % \
- (title,
- session.gps_id, session.baudrate,
- session.stopbits, session.cycle)
- for s in splitplot.sentences:
- res += "%8s\t" % s
- for hn in ("T1", "D1", "E2", "T2", "D2", "length"):
- res += "%8s\t" % hn
- res += "tag\n# "
- for s in tuple(splitplot.sentences) + ("T1", "D1", "E2", "T2", "D2", "length"):
- res += "---------\t"
- return res + "--------\n"
- def data(self, unused):
- res = ""
- for timings in self.stats:
- for s in splitplot.sentences:
- if s == timings.tag:
- res += "%2.6f\t" % timings.xmit
- else:
- res += "- \t"
- res += "%2.6f\t%2.6f\t%2.6f\t%2.6f\t%2.6f\t%8d\t# %s\n" \
- % (timings.recv - timings.xmit,
- timings.decode - timings.recv,
- timings.emit - timings.decode,
- timings.c_recv - timings.emit,
- timings.c_decode - timings.c_recv,
- timings.len,
- timings.tag)
- return res
- def plot(self, title, session):
- fixed = '''
-set autoscale
-set key below
-set key title "Filtered latency data, %s, %s, %dN%d, cycle %ds"
-plot \
- "-" using 0:%d title "D2 = Client decode time" with impulses, \
- "-" using 0:%d title "T2 = TCP/IP latency" with impulses, \
- "-" using 0:%d title "E2 = Daemon encode time" with impulses, \
- "-" using 0:%d title "D1 = Daemon decode time" with impulses, \
- "-" using 0:%d title "T1 = RS232 time" with impulses, \
-'''
- sc = len(splitplot.sentences)
- fmt = fixed % (title,
- session.gps_id, session.baudrate,
- session.stopbits, session.cycle,
- sc+5,
- sc+4,
- sc+3,
- sc+2,
- sc+1)
- for i in range(sc):
- fmt += ' "-" using 0:%d title "%s" with impulses,' % \
- (i+1, self.sentences[i])
- res = fmt[:-1] + "\n"
- res += self.header(session)
- res += (self.data(session) + "e\n") * (sc + 5)
- return res
-
-class cycle:
- "Send-cycle analysis."
- name = "cycle"
- def __init__(self):
- self.stats = []
- def gather(self, session):
- self.stats.append(copy.copy(session.timings))
- return True
- def plot(self, title, session):
- msg = ""
- def roundoff(n):
- # Round a time to hundredths of a second
- return round(n*100) / 100.0
- intervals = {}
- last_seen = {}
- last_command = ""
- for timing in self.stats:
- # Throw out everything but the leader in each GSV group
- if timing.tag[-3:] == "GSV" and last_command[-3:] == "GSV":
- continue
- last_command = timing.tag
- # Record timings
- received = timing.d_received()
- if not timing.tag in intervals:
- intervals[timing.tag] = []
- if timing.tag in last_seen:
- intervals[timing.tag].append(roundoff(received - last_seen[timing.tag]))
- last_seen[timing.tag] = received
-
- # Step three: get command frequencies and the basic send cycle time
- frequencies = {}
- for (key, interval_list) in intervals.items():
- frequencies[key] = {}
- for interval in interval_list:
- frequencies[key][interval] = frequencies[key].get(interval, 0) + 1
- # filter out noise
- for key in frequencies:
- distribution = frequencies[key]
- for interval in distribution.keys():
- if distribution[interval] < 2:
- del distribution[interval]
- cycles = {}
- for key in frequencies:
- distribution = frequencies[key]
- if len(frequencies[key].values()) == 1:
- # The value is uniqe after filtering
- cycles[key] = distribution.keys()[0]
- else:
- # Compute the mode
- maxfreq = 0
- for (interval, frequency) in distribution.items():
- if distribution[interval] > maxfreq:
- cycles[key] = interval
- maxfreq = distribution[interval]
- msg += "Cycle report %s, %s, %dN%d, cycle %ds" % \
- (title,
- session.gps_id, session.baudrate,
- session.stopbits, session.cycle)
- msg += "The sentence set emitted by this GPS is: %s\n" % " ".join(intervals.keys())
- for key in cycles:
- if len(frequencies[key].values()) == 1:
- if cycles[key] == 1:
- msg += "%s: is emitted once a second.\n" % key
- else:
- msg += "%s: is emitted once every %d seconds.\n" % (key, cycles[key])
- else:
- if cycles[key] == 1:
- msg += "%s: is probably emitted once a second.\n" % key
- else:
- msg += "%s: is probably emitted once every %d seconds.\n" % (key, cycles[key])
- sendcycle = min(*cycles.values())
- if sendcycle == 1:
- msg += "Send cycle is once per second.\n"
- else:
- msg += "Send cycle is once per %d seconds.\n" % sendcycle
- return msg
-
-formatters = (spaceplot, uninstrumented, rawplot, splitplot, cycle)
+formatters = (spaceplot, uninstrumented)
def plotframe(await, fname, speed, threshold, title):
"Return a string containing a GNUplot script "
diff --git a/gpsprof.xml b/gpsprof.xml
index a46a9b53..0daa841e 100644
--- a/gpsprof.xml
+++ b/gpsprof.xml
@@ -34,12 +34,12 @@ BSD terms apply: see the file COPYING in the distribution root for details.
<refsect1 id='description'><title>DESCRIPTION</title>
-<para><application>gpsprof</application> measures the various
-latencies between a GPS and its client. It emits to standard output a
-GNUPLOT program that draws an illustrative graph. It can also be told
-to emit the raw profile data. The information it provides can be
-useful for establishing an upper bound on latency, and thus on
-position accuracy of a GPS in motion.</para>
+<para><application>gpsprof</application> performs accuracy and latency
+profiling onm a GPS. It emits to standard output a GNUPLOT program
+that draws an illustrative graph. It can also be told to emit the raw
+profile data. The information it provides can be useful for
+establishing an upper bound on latency, and thus on position accuracy
+of a GPS in motion.</para>
<para><application>gpsprof</application> uses instrumentation built
into <application>gpsd</application>.</para>
@@ -77,60 +77,9 @@ This is the default.</para>
<listitem>
<para>Plot total latency without instrumentation. Useful mainly as a
check that the instrumentation is not producing significant
-distortion. It only plots times for sentences that contain fixes;
+distortion. It only plots times for reports that contain fixes;
staircase-like artifacts in the plot are created when elapsed time
-from sentences without fixes is lumped in.</para>
-</listitem>
-</varlistentry>
-<varlistentry>
-<term>raw</term>
-<listitem>
-<para>Plot raw data.</para>
-</listitem>
-</varlistentry>
-<varlistentry>
-<term>split</term>
-<listitem>
-<para>Each sentence has its RS232 latency time colored differently.</para>
-</listitem>
-</varlistentry>
-<varlistentry>
-<term>cycle</term>
-<listitem>
-<para>Report on the set of sentences or packets emitted by the GPS,
-their send intervals, and the basic cycle time. (This report is
-plain text rather than a gnuplot script.)</para>
-</listitem>
-</varlistentry>
-</variablelist>
-
-<para>The instrumented time plot conveys the following information:</para>
-
-<variablelist>
-<varlistentry>
-<term>RS232 time</term>
-<listitem>
-<para>Time required to send the sentence from the GPS to
-<application>gpsd</application>. This measured from the time of
-the last zero-length read before the packet to when the packet sniffer
-recognizes a complete sentence, so there is a small amount of
-computational overhead mixed in.</para>
-</listitem>
-</varlistentry>
-<varlistentry>
-<term>Decode time</term>
-<listitem>
-<para>Elapsed time between sentence reception and the moment that
-<application>gpsd</application> ships the resulting update to
-the profiling client.</para>
-</listitem>
-</varlistentry>
-<varlistentry>
-<term>TCP/IP latency</term>
-<listitem>
-<para>Elapsed time between the moment that
-<application>gpsd</application> ships the update to
-the profiling client and the moment it is decoded and timestamped.</para>
+from reports without fixes is lumped in.</para>
</listitem>
</varlistentry>
</variablelist>
diff --git a/gpsutils.c b/gpsutils.c
index dbd0d263..e7ba71ed 100644
--- a/gpsutils.c
+++ b/gpsutils.c
@@ -294,6 +294,15 @@ timestamp_t timestamp(void)
return (timestamp_t)(tv.tv_sec + tv.tv_usec * 1e-6);
}
+#ifdef TIMING_ENABLE
+timestamp_t realtimestamp(void)
+{
+ struct timespec ts;
+ (void)clock_gettime(CLOCK_REALTIME, &ts);
+ return (timestamp_t)(ts.tv_sec + ts.tv_nsec * 1e-9);
+}
+#endif /* TIMING_ENABLE */
+
time_t mkgmtime(register struct tm * t)
/* struct tm to seconds since Unix epoch */
{
diff --git a/libgpsd_core.c b/libgpsd_core.c
index ff364f2e..0f60100e 100644
--- a/libgpsd_core.c
+++ b/libgpsd_core.c
@@ -810,10 +810,10 @@ gps_mask_t gpsd_poll(struct gps_device_t *session)
gps_clear_fix(&session->newdata);
-#ifdef TIMING_ENABLE
- if (session->packet.outbuflen == 0)
- session->d_xmit_time = timestamp();
-#endif /* TIMING_ENABLE */
+ /*
+ * If we ever want a start-of-packet timestamp again, take it here
+ * with a test that session->packet.outbuflen is zero.
+ */
if (session->packet.type >= COMMENT_PACKET) {
/*@-shiftnegative@*/
@@ -890,10 +890,6 @@ gps_mask_t gpsd_poll(struct gps_device_t *session)
gpsd_report(LOG_RAW + 3, "Accepted packet on %s.\n",
session->gpsdata.dev.path);
-#ifdef TIMING_ENABLE
- session->d_recv_time = timestamp();
-#endif /* TIMING_ENABLE */
-
/* track the packet count since achieving sync on the device */
if (first_sync) {
speed_t speed = gpsd_get_speed(&session->ttyset);
@@ -997,10 +993,6 @@ gps_mask_t gpsd_poll(struct gps_device_t *session)
session->fixcnt++;
}
-#ifdef TIMING_ENABLE
- session->d_decode_time = timestamp();
-#endif /* TIMING_ENABLE */
-
/*
* Sanity check. This catches a surprising number of port and
* driver errors, including 32-vs.-64-bit problems.
diff --git a/www/hacking.html b/www/hacking.html
index 9179470d..ae602ac8 100644
--- a/www/hacking.html
+++ b/www/hacking.html
@@ -491,51 +491,22 @@ only, suspicion should focus here.</p>
<h2 id="profiling">Profiling</h2>
<p>There is a barely-documented timing policy flag in the WATCH
-command that will cause it to emit a TIMING object on every sentence.
-The TIMING response contains the following attributed:</p>
+command that will cause it to that will cause it to emit timing
+information on every TPV. Presently there is exactly one
+such member:</p>
<ol>
-<li>tag: An identifying sentence tag.</li>
-
-<li>len: The character length of the sentence containing the timestamp
- data.</li>
-
-<li>timebase: The timestamp associated with the sentence, in seconds since
- the Unix epoch (this time <em>is</em> leap-second corrected, like UTC).
- This timestamp may be zero. If nonzero, it is the base time for
- the packet.</li>
-
-<li>xmit: An offset from the timestamp telling when gpsd believes the
- transmission of the current packet started (this is actually
- recorded just before the first read of the new packet). If
- the sentence timestamp was zero, this offset is a full timestamp
- and the base time of the packet.</li>
-
-<li>recv: An offset from the base time telling when <code>gpsd</code>
- received the last bytes of the packet.</li>
-
-<li>decode: An offset from the base time telling when gpsd decoded the
- data.</li>
-
-<li>poll: An offset from the base time taken just before encoding the
- response &mdash; effectively, when gpsd was polled to transmit the
- data.</li>
-
-<li>elapsed: An offset from the base time to the time of the TIMING emission.</li>
+<li><p>xmit_time: the time at which the daemon created the TPV for
+transmission.</p></li>
</ol>
-<p>The TIMING figures measure components of the latency between the GPS's
+<p>Thes attributes measure components of the latency between the GPS's
time measurement and when the sentence data became available to the
-client. For them to be meaningful, the GPS has to ship timestamps with
-sub-second precision. SiRF-II and Evermore chipsets ship times with
-millisecond resolution. Your machine's time reference must also be
-accurate to subsecond precision; I recommend using <code>ntpd</code>,
-which will normally give you about 15 microseconds precision (two
-orders of magnitude better than GPSes report).</p>
-
-<p>Note, some inaccuracy is introduced into the start- and end-of-packet
-timestamps by the fact that the last read of a packet may grab a few
-bytes of the next one.</p>
+client. They are collected using get_realtime(3), so they are not
+subject to corrections from ntpd. For the latencty timing to be
+meaningful, the GPS has to ship timestamps with sub-second
+precision. SiRF-II and Evermore chipsets ship times with millisecond
+resolution.</p>
<p>The distribution includes a Python script, <code>gpsprof</code>,
that uses the timing support to collect profiling information from a