123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650 |
- = GPSD Client HOWTO
- Eric S. Raymond <esr@thyrsus.com>
- v1.21, Feb 2021
- :author: Eric S. Raymond
- :date: 22 Februrary 2021
- :description: This document is a guide to interfacing client applications with GPSD.
- :email: <esr@thyrsus.com>
- :keywords: time, GPSD, NTP, time, precision, 1PPS, PPS, stratum, jitter
- :sectlinks:
- :toc: macro
- include::inc-menu.adoc[]
- == Introduction
- This document is a guide to interfacing client applications with GPSD.
- It surveys the available bindings and their use cases. It also explains
- some sharp edges in the client API which, unfortunately, are fundamental
- results of the way GPS sensor devices operate, and suggests tactics
- for avoiding being cut.
- == Sensor behavior matters
- GPSD handles two main kinds of sensors: GPS receivers and AIS
- receivers. It has rudimentary support for some other kinds of
- specialized geolocation-related sensors as well, notably compass and
- yaw/pitch/roll, but those sensors are usually combined with GPS/AIS
- receivers and behave like them.
- In an ideal world, GPS/AIS sensors would be oracles that you could
- poll at any time to get clean data. But despite the existence of some
- vendor-specific query and control strings on some devices, a GPS/AIS
- sensor is not a synchronous device you can query for specified data
- and count on getting a response back from in a fixed period of time.
- It gets radio data on its own schedule (usually once per second for a
- GPS), and emits the reports it feels like reporting asynchronously
- with variable lag during the following second. *If* it supports query
- strings, reports from these are intermixed with the regular
- reports, and usually scheduled at a lower priority (often with a lag
- of more than a second).
- A GPS/AIS receiver, or any sensor that behaves like one, is in effect
- a datagram emitter similar to a UDP data source; you get no guarantees
- about sequence or timing at finer resolution than "TPV roughly once
- per second" (or whatever the main type of report and report interval
- is).
- The only way to simulate synchronous querying of such a sensor is to
- have an agent between you and it that caches the data coming out of
- the device; you can then query the agent and expect a reasonably
- synchronous response from the cache (we support this for
- GPSs). However, note that this doesn't work for a freshly opened
- device - there's no cached data.
- Consider what this implies for GPses, in particular:
- * You can't actually poll them. They report to you over a serial
- (RS232 or USB-serial) interface at a fixed interval, usually once
- per second.
- * They don't always have a fix to report, because they can only
- get one when they have satellite lock.
- * They may fail to have satellite lock when your skyview is poor.
- If you are moving through uneven terrain or anywhere with trees
- or buildings, your skyview can degrade in an instant.
- * They may also fail to have lock because they're initially powering
- on (cold start), or waking from a power-conserving sleep mode (warm
- start). While most modern GPSs with a good skyview can get
- satellite lock in 30 seconds from a warm start, a GPS that has
- been off for a while can take 15 minutes or more to acquire lock
- Time to fix after a power-on matters because in many use cases for
- GPSs they're running off a battery, and you can't afford to keep them
- powered when you don't actually need location data. This is why GPS
- sensors are sometimes designed to go to a low-power mode when you close
- the serial port they're attached to.
- AIS receivers have a simpler set of constraints. They report
- navigational and ID information from any AIS transmitter in line of
- sight; there are no skyview issues, and they're ready instantly when
- powered up. Furthermore, they're not normally battery constrained.
- However, you don't poll them either; they receive information
- packets over the air and ship them to you at unpredictable intervals
- over a serial port.
- The design of the GPSD reporting protocol surrenders to reality. Its data
- packets translate the GPS's stream of datagrams into
- device-type-independent datagrams, but it can't impose timing and
- sequencing regularities on them that the underlying device doesn't
- already obey.
- == What GPSD does, and what it cannot do
- GPSD solves some of the problems with GPS/AIS sensors. First:
- multiplexing; it allows multiple applications to get sensor data
- without having to contend for a single serial device. Second:
- coping with the hideous gallimaufry of badly-designed protocols these
- devices use -- regardless of device type, you will get data in a single
- well-documented format. Third: on operating systems with a hotplug
- facility (like Linux udev), GPSD will handle all the device
- management as USB devices are plugged in and unplugged.
- What GPSD can't do is pull fix data out of thin air when your
- device hasn't reported any. Nor is it a magic power supply,
- so its device management has to be designed around keeping the
- attached sensors open only when a client application actually
- needs a fix.
- As you'll see, these constraints explain most of the design of the GPSD
- wire protocol, and of the library APIs your client application
- will be using.
- == How the GPSD wire protocol works
- While GPSD project ships several library bindings that will hide the
- details of the wire protocol from you, you'll understand the library APIs
- better by knowing what a wire-protocol session looks like. After
- reading this section, you can forget the details about commands and
- responses and attributes as long as you hold on to the basic
- logical flow of a session.
- Your client library's open function is going to connect a socket to
- port 2947 on the host your sensors are attached to, usually
- localhost. On connection, the gpsd daemon will ship a banner that
- looks something like this:
- -----------------------------------------------------------------------------
- {"class":"VERSION","release":"2.93","rev":"2010-03-30T12:18:17",
- "proto_major":3,"proto_minor":2}
- -----------------------------------------------------------------------------
- There's nothing mysterious here. Your server daemon is identifying
- itself with information that may allow a client library to work
- around bugs or potential incompatibilities produced by upgrades.
- To get data from the attached sensors, you need to explicitly tell the
- daemon you want it. (Remember that it's trying to minimize the amount
- of time the devices are held open and in a fully powered state.) You
- do this by issuing a WATCH command:
- -----------------------------------------------------------------------------
- ?WATCH={"enable":true,"json":true}
- -----------------------------------------------------------------------------
- This tells the daemon to watch all devices and to issue reports in
- JSON. It can ship some other protocols as well (notably, NMEA 0183)
- but JSON is the most capable and usually what you want.
- A side effect of the WATCH command is that the daemon will ship you
- back some information on available devices.
- -----------------------------------------------------------------------------
- {"class":"DEVICES","devices":[{"class":"DEVICE","path":"/dev/ttyUSB0",
- "activated":1269959537.20,"native":0,"bps":4800,"parity":"N",
- "stopbits":1,"cycle":1.00}]}
- {"class":"WATCH","enable":true,"json":true,"nmea":false,"raw":0,
- "scaled":false,"timing":false,"pps":false}
- -----------------------------------------------------------------------------
- The DEVICES response tells you what devices are available to the
- daemon; this list is maintained in a way you as the application
- designer don't have to care about. The WATCH response will
- immediately follow and tells you what all your watch request settings
- are.
- Up to this point, nothing has been dependent on the state of the
- sensors. At this time, it may well be that none of those devices is
- fully powered up yet. In fact, they won't be, unless another
- GPSD-enabled application is already watching when you open your
- connection. If that's the case, you will start seeing data
- immediately.
- For now, though, let's go back to the case where gpsd has to fire up
- the sensors. After issuing the WATCH response, the daemon opens all of
- them and watches for incoming packets that it can recognize. *After
- a variable delay*, it will ship a notification that looks something
- like this:
- -----------------------------------------------------------------------------
- {"class":"DEVICE","path":"/dev/ttyUSB0","activated":1269960793.97,
- "driver":"SiRF binary","native":1,"bps":4800,
- "parity":"N","stopbits":1,"cycle":1.00}
- -----------------------------------------------------------------------------
- This is the daemon telling you that it has recognized a SiRF binary
- GPS on /dev/ttyUSB0 shipping report packets at 4800 bits per second.
- This notification is not delayed by the time it takes to achieve
- satellite lock; the GPS will cheerfully ship packets before that. But
- it will be delayed by the time required for the daemon to sync up with
- the GPS.
- The GPSD daemon is designed so it doesn't have to know anything about the
- sensor in advance - not which of a dozen reporting protocols it uses,
- and not even the baud rate of the serial device. The reason for this
- agnosticism is so the daemon can adapt properly to anything a hotplug
- event night throw at it. If you unplug your GPS while your
- application is running, and then plug in one of a different type, the
- daemon will cope. Your application won't know the difference unless
- you have told it to notice device types.
- You can even start your application, have it issue a WATCH, realize
- you forgot to plug in a GPS, and do that. The hotplug event will
- tell gpsd, which will add the new device to the watched-devices list
- of every client that has issued a ?WATCH.
- In order to make this work, gpsd has a packet sniffer inside it that
- does autobauding and packet-protocol detection. Normally the packet
- sniffer will achieve sync in well under a second (my measured times
- range from 0.10 to 0.53 sec at 4800bps), but it can take longer if
- your serial traffic is degraded by dodgy cables or electrical noise,
- or if the GPS is configured to run at an unusual speed/parity/stopbit
- configuration.
- The real point here is that the delay is *variable*. The client
- library, and your application, can't assume a neat lockstep of
- request and instant response.
- Once you do get your device(s) synced, things become more predictable.
- The sensor will start shipping fix reports at a constant interval,
- usually every second, and the daemon will massage them into JSON and
- pass them up the client to your application.
- However, until the sensor achieves satellite lock, those fixes will be
- "mode 1" - no valid data (mode 2 is a 2D fix, mode 3 is a 3D fix).
- Here's what that looks like:
- -----------------------------------------------------------------------------
- {"class":"TPV","device":"/dev/ttyUSB0",
- "time":"2010-04-30T11:47:43.28Z","ept":0.005,"mode":1}
- -----------------------------------------------------------------------------
- Occasionally you'll get another kind of sentence, SKY, that reports a
- satellite skyview. But TPV is the important one. Here's what it
- looks like when the sensor has a fix to report:
- -----------------------------------------------------------------------------
- {"class":"TPV","time":"2010-04-30T11:48:20.10Z","ept":0.005,
- "lat":46.498204497,"lon":7.568061439,"alt":1327.689,
- "epx":15.319,"epy":17.054,"epv":124.484,"track":10.3797,
- "speed":0.091,"climb":-0.085,"eps":34.11,"mode":3}
- -----------------------------------------------------------------------------
- Note the "mode":3 at the end. This is how you tell that the GPS is
- reporting a full 3D fix with altitude.
- If you have an AIS receiver attached, it too will have been opened
- and autobauded and protocol-sniffed after your WATCH. The stream of
- JSON objects will then include things like this:
- -----------------------------------------------------------------------------
- {"class":"AIS","type":5,"repeat":0,"mmsi":351759000,"scaled":true,
- "imo":9134270,"ais_version":0,"callsign":"3FOF8",
- "shipname":"EVER DIADEM",
- "shiptype":"Cargo - all ships of this type",
- "to_bow":225,
- "to_stern":70,"to_port":1,"to_starboard":31,"draught":12.2,
- "epfd":"GPS","eta":"05-15T14:00Z",
- "destination":"NEW YORK","dte":0}
- -----------------------------------------------------------------------------
- When your application shuts down, it can cancel its watch:
- -----------------------------------------------------------------------------
- ?WATCH={"enable":false}
- -----------------------------------------------------------------------------
- This will enable the daemon to close devices and conserve
- power. Supposing you don't do this, the daemon will time out devices
- with no listeners, so canceling your watch is not strictly necessary.
- But it is good manners.
- Another way to use the daemon is with the ?POLL command To do this, issue
- -----------------------------------------------------------------------------
- ?WATCH={"enable":true}
- -----------------------------------------------------------------------------
- This activates all devices without enabling streaming of reports. You
- can then say "?POLL;" to poll gpsd's recorded data.
- -----------------------------------------------------------------------------
- ?POLL;
- {"class":"POLL","time":"2012-04-05T15:00:01.501Z","active":1,
- "tpv":[{"class":"TPV","device":"/dev/ttyUSB0","mode":3,"time":"2012-04-05T15:00:00.000Z","ept":0.005,"lat":40.035083522,"lon":-75.519982905,"alt":166.145,"epx":9.125,"epy":17.778,"epv":34.134,"track":0.0000,"speed":0.000,"climb":0.000,"eps":36.61}],"gst":[{"class":"GST","device":"/dev/ttyUSB0","time":"1970-01-01T00:00:00.000Z","rms":0.000,"major":0.000,"minor":0.000,"orient":0.000,"lat":0.000,"lon":0.000,"alt":0.000}],
- "sky":[{"class":"SKY","device":"/dev/ttyUSB0","time":"2012-04-05T15:00:00.000Z","xdop":0.61,"ydop":1.19,"vdop":1.48,"tdop":1.14,"hdop":1.40,"gdop":2.30,"pdop":1.99,"satellites":[{"PRN":26,"el":15,"az":49,"ss":29,"used":true},{"PRN":18,"el":62,"az":315,"ss":31,"used":true},{"PRN":15,"el":60,"az":43,"ss":44,"used":true},{"PRN":21,"el":71,"az":237,"ss":0,"used":false},{"PRN":27,"el":52,"az":94,"ss":40,"used":true},{"PRN":9,"el":48,"az":136,"ss":33,"used":true},{"PRN":22,"el":21,"az":291,"ss":36,"used":true},{"PRN":3,"el":8,"az":303,"ss":25,"used":true}]}]}
- -----------------------------------------------------------------------------
- This interface is intended for use with applications like CGI scripts
- that cannot wait on output from the daemon but must poke it into responding.
- If you're a clever sort, you're already wondering what the daemon does
- if the application at the other end of the client socket doesn't read data
- out of it as fast as gpsd is shipping it upwards. And the answer is
- this: eventually the socket buffer fills up, a write from the daemon
- throws an error, and the daemon shuts down that client socket.
- From the point of view of the application, it reads all the buffered
- data and then gets a read return indicating the socket shutdown. We'll
- return to this in the discussion of client libraries, but the thing
- for you to know right now is that this edge case is actually quite
- difficult to fall afoul of. Total data volume on these sockets is not
- high. As long as your application checks for and reads socket data no
- less often than once a second, you won't -- and a second is a *lot* of
- time in which to come back around your main loop.
- == Interfacing from the client side
- The gpsd daemon exports data in three different ways: via a sockets
- interface, via DBUS broadcasts, and via a shared-memory interface,
- It is possible one or more of these may be configured out in your
- installation.
- === The sockets interface
- The GPSD project provides client-side libraries in C, C++, and Python
- that exercise the sockets export. A Perl module is separately
- available from CPAN. While the details of these libraries vary, they
- all have the same two purposes and the same limitations.
- One purpose of the libraries is to handle the details of unpacking
- JSON-based wire-protocol objects into whatever native structure/record
- feature your application language has. This is particularly important
- in the C and C++ libraries, because those languages don't have
- good native support for JSON.
- Another purpose is to hide the details of the wire protocol from the
- application. This gives the GPSD developers room to improve extend
- the protocol without breaking every client application. Depend
- on wire-protocol details only at your own risk!
- The limitations the libraries have to cope with are the nature of
- the data flow from the sensors, and the simple fact that they're
- not necessarily delivering fixes at any given time.
- For details of the libraries' APIs, see their reference
- documentation; the objective of the rest of this section is to teach
- you the general model of client-side interfacing that they all have to
- follow because of the way the daemon works.
- Each library has the following entry points:
- * Open a session socket to the daemon. Named something like "open()".
- * Set watch policy. Named something like "stream()"
- * Send wire-protocol commands to the daemon. Deprecated; makes your
- code dependent on the wire protocol. There is no longer a real
- use case for this entry point; if you think you need no use it,
- you have probably failed to understand the rest of the interface.
- * Blocking check to see if data from the daemon is waiting. Named
- something like "waiting()" and taking a wait timeout as argument.
- Note that choosing a wait timeout of less than twice the cycle time
- of your device will be hazardous, as the receiver will probably not
- supply input often enough to prevent a spurious error indication.
- For the typical 1-second cycle time of GPSes this implies a minimum
- 2-second timeout.
- * Blocking read for data from the daemon. Named something like "read()"
- (this was "poll()" in older versions).
- * Close the session socket. Named something like "close()".
- * Enable debugging trace messages
- The fact that the data-waiting check and the read both block means
- that, if your application has to deal with other input sources than
- the GPS, you will probably have to isolate the read loop in a thread with
- a mutex lock on the gps_data structure.
- Here is a complete table of the binding entry points:
- .Entry points in client bindings
- [frame="topbot",options="header"]
- |========================================================================
- |C |C++ |Python |
- Function
- |gps_open() |gpsmm.gpsmm() |gps.\_\_init__() |
- In OO languages the client class initializer opens the daemon socket.
- |gps_send() |gpsmm.send() |gps.send() |
- Send wire-protocol commands to the daemon. Deprecated and unstable.
- |gps_stream() |gpsmm.stream() |gps.stream() |
- Set watch policy. What you should use instead of send().
- |gps_waiting() |gpsmm.waiting() |gps.waiting() |
- Blocking check with timeout to see if input is waiting.
- |gps_read() |gpsmm.read() |gps.read() |
- Read data from the daemon. Usually blocking.
- |gps_unpack() | |gps.unpack() |
- Parse JSON from a specified buffer into a session structure
- |gps_close() |gpsmm.~gpsmm() |gps.close() |
- Close the daemon socket and end the session.
- |gps_data() |gpsmm.data() |gps.data() |
- Get the contents of the client buffer.
- |gps_enable_debug() |gpsmm_enable_debug() | |
- Enable debug tracing. Only useful for GPSD developers.
- |gps_clear_fix() |gpsmm.clear_fix() | |
- Clear the contents of the fix structure.
- |========================================================================
- The tricky part is interpreting what you get from the blocking
- read. The reason it's tricky is that you're not guaranteed that
- every read will pick up exactly one complete JSON object from the
- daemon. It may grab one response object, or more than one, or
- part of one, or one or more followed by a fragment.
- What the library does on each read is this: get what it can from the
- socket, append that to a private buffer, and then consume as many JSON
- objects from the front of the buffer as it can. Any incomplete JSON
- is left in the private buffer to be completed and unpacked on a later
- go-round.
- In C, the library "consumes" a JSON object by unpacking its content
- into a blackboard structure passed to the read entry point by
- address. The structure contains a state-flag mask that you can (and
- should!) check so you'll know which parts of the structure contain
- valid data. It is safe to do nothing unless the PACKET_SET mask bit
- is on, which is the library's way of telling you that at least one
- complete JSON response has arrived since the last read.
- Data may accumulate on the blackboard over multiple reads,
- with new TPV reports overwriting old ones; it is guaranteed that
- overwrites are not partial. Expect this pattern to be replicated
- in any compiled language with only fixed-extent structures.
- In Python and Perl the read entry point returns an object containing
- accumulated data. The state-flag mask is still useful for telling you
- which parts contain data, and there is still a PACKET_SET bit. Expect
- this pattern to be replicated in other dynamic OO languages when we
- support them.
- The C++ binding is a very thin wrapper around the C. You get back an
- object, but it's just a reference to the C blackboard structure. There's
- no unpack() method because it doesn't fit the gpsmm object's RAII model.
- All bindings will throw a recognizable error from the read entry
- point when the socket is closed from the daemon side.
- [WARNING]
- The timing of your read loop is important. When it has satellite lock,
- the daemon will be writing into its end of the socket once per
- whatever the normal reporting-cycle time of your device is - for a GPS
- normally one peer second. *You must poll the socket more often that
- that.*
- If necessary, spawn a worker thread to do this, mutex-locking the
- structure where it outs the reports. If you don't do this, data
- will back up in your socket buffers and position reports will be
- more and more delayed until the socket FIFO fills, at which point the
- daemon will conclude the client has died and drop the connection.
- AIVDM clients have a longer maximum allowable poll interval, but a
- problem of a different kind. you have the problem that later sentences
- of (say) Type 1 don't obsolete the data in earlier ones. This is a
- problem, because the library is designed so that read calls pull any
- JSON reports waiting from the daemon and interpret them all.
- To avoid losing data, you want to poll the daemon more often than once
- per two seconds (that being the minimum transmission period for the
- most frequently shipped sentences, Type 1/2/3). That way the read
- buffer will never contain both a message and a later message of the
- same type that steps on it.
- === Shared-memory interface
- Whenever gpsd recognizes a packet from any attached device, it writes
- the accumulated state from that device to a shared memory segment. The
- C and C++ client libraries shipped with GPSD can read this segment.
- The API for reading the segment uses the same gps_open(), gps_read()
- and gps_close() entry points as the sockets interface. To enable using
- shared memory instead, it is only necessary to use the macro constant
- GPSD_SHARED_MEMORY as the host argument of gps_open().
- The gps_stream(), gps_send(), gps_waiting(), and gps_data() entry
- points are not available with this interface. You cannot set a device
- filter on it. You will not get device activation or deactivation
- notices through it. And, of course, it is only good for local and not
- networked access. Its main advantage is that it is very fast and
- lightweight, especially suitable for use in low-power embedded
- deployments with a single device on a fixed port and the sockets
- interface configured out.
- Under the shared-memory interface, gps_read() after a successful
- gps_open() will always return with data; its return is the size of a
- struct gps_data_t in bytes. The 'gps_fd' member of the struct gpsdata
- instance handed to you will always be -1. The PACKET_SET flag will
- always be asserted. The other flag bits in the 'set' member will tell
- you what data is updated in the instance, just as in the sockets
- interface.
- The shared-memory interface is not yet available from Python.
- === D-Bus broadcasts
- If your system supports D-Bus, gpsd broadcasts a signal with path /org/gpsd,
- interface "org.gpsd", and name "fix" whenever it received a position
- report from any device attached to it. See the gpsd(8) manual page for
- details of the binary payload layout.
- == C Examples
- The source distribution includes two example clients in C;
- gpxlogger.c and cgps.c.
- gpxlogger.c illustrates the simplest possible program flow; open,
- followed by stream, followed by the library main loop.
- cgps.c shows what an interactive application using the library and
- also hw processing user commands works. Note the use of the curses
- nodelay function to ensure that wgetch() does not block the GPS
- polling loop.
- == C++ examples
- The following code skeleton implements a C++ client:
- ----------------------------------------------------------------------
- int main(void)
- {
- gpsmm gps_rec("localhost", DEFAULT_GPSD_PORT);
- if (gps_rec.stream(WATCH_ENABLE|WATCH_JSON) == NULL) {
- cerr << "No GPSD running.\n";
- return 1;
- }
- for (;;) {
- struct gps_data_t* newdata;
- if (!gps_rec.waiting(50000000))
- continue;
- if ((newdata = gps_rec.read()) == NULL) {
- cerr << "Read error.\n";
- return 1;
- } else {
- PROCESS(newdata);
- }
- }
- return 0;
- }
- ----------------------------------------------------------------------
- Note the absence of explicit open and close methods. The object
- interface is designed on the RAII (Resource Acquisition Is
- Initialization) model; you close it by deallocating it.
- Look at test_gpsmm.cpp in the distribution for a full example.
- == Python examples
- There's a very simple Python example analogous to gpxlogger attached
- to the source code for the gps.py library.
- The heart of it is this code:
- -----------------------------------------------------------------------------
- session = gps(**opts)
- session.stream(WATCH_ENABLE|WATCH_NEWSTYLE)
- for report in session:
- print report
- -----------------------------------------------------------------------------
- If you need to intersperse other processing in a main event loop,
- like this:
- ----------------------------------------------------------------------
- session = gps(mode=WATCH_ENABLE)
- try:
- while True:
- # Do stuff
- report = session.next()
- # Check report class for 'DEVICE' messages from gpsd. If
- # we're expecting messages from multiple devices we should
- # inspect the message to determine which device
- # has just become available. But if we're just listening
- # to a single device, this may do.
- if report['class'] == 'DEVICE':
- # Clean up our current connection.
- session.close()
- # Tell gpsd we're ready to receive messages.
- session = gps(mode=WATCH_ENABLE)
- # Do more stuff
- except StopIteration:
- print "GPSD has terminated"
- ----------------------------------------------------------------------
- Each call to the iterator yields a report structure until the daemon
- terminates, at which point the iterator next() method will raise
- StopIteration and the loop will terminate.
- The report object returned by next() can be accessed either as a dictionary
- or as an object. As a dictionary, it is the raw contents of the last
- JSON response re-encoded in plain ASCII. For convenience, you may
- also access it as an object with members for each attribute in the
- dictionary. It is especially useful to know that the object will
- always have a "class" member giving the response type (TPV, SKY,
- DEVICE, etc.) as a string.
- For more interesting examples integrated with X and GTK, see xgps and
- xgpsspeed.
- == Other Client Bindings
- There are a couple of client bindings for GPSD that are maintained
- separately from the GPSD distribution. We don't try to document their
- APIs here, but just provide pointers to them.
- == Java
- There is a Java binding, described at http://gpsd4java.forge.hoegergroup.de/
- This binding is available at maven central. See that web page for how
- to use it in a maven build.
- == Perl
- There's a Perl client library at http://search.cpan.org/dist/Net-GPSD3/
- == Backward Incompatibility and Future Changes
- The C/C++ binding makes available two preprocessor symbols,
- GPSD_API_MAJOR_VERSION and GPSD_API_MINOR_VERSION, in gps.h.
- The Python module has corresponding symbols.
- In major versions before 5:
- * gps_open() didn't take a third argument; instead, it returned malloc storage.
- * The 'read()' method in various bindings was named 'poll()', blocked
- waiting for input, and had a different return convention. The name
- 'poll()' will at some point be reintroduced as an interface to the
- wire-protocol POLL command.
- * Clients needed to define a hook for client-side logging if they
- didn't want code in netlib.c and libgps_core.c to occasionally send
- messages to stderr. This requirement is now gone.
- * There was a set_raw_hook() method in the C and Python bindings, now gone.
- C clients should call gps_data(); the buffer is available directly in Python,
- both as str (response) and bytes (bresponse). The distinction matters
- in Python 3.
- //end
|