protocol-transition.adoc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. = Moving to GPSD-NG: a Guide for Client Developers
  2. Eric S. Raymond <esr@thyrsus.com>
  3. v1.11, Jan 2021
  4. :author: Eric S. Raymond
  5. :date: 13 January 2021
  6. :description: A Guide for Client Developers moving to GPSD-ND
  7. :email: <esr@thyrsus.com>
  8. :keywords: time, GPSD, gpsd, guide, developers, client
  9. :robots: index,follow
  10. :sectlinks:
  11. :toc: macro
  12. include::inc-menu.adoc[]
  13. This document is mastered in asciidoc format. If you are reading it in HTML,
  14. you can find the original at the GPSD project website.
  15. == Why a new protocol?
  16. GPSD has moved to a new request/response protocol. This move has been
  17. forced upon us because the old one ran out of namespace. It was
  18. designed with case-insensitive single-character command/response
  19. codes. 25 of 26 ASCII alphabetics were already in use by 2006, and
  20. there have been functional challenges accumulating over the last three
  21. years that will require several more request/response codes - things
  22. like reporting AIS data, reporting raw pseudoranges, and reporting
  23. RTCM3.
  24. Yes, we could as a desperate expedient have pressed non-alphabetic
  25. printables into service - but the result would have looked like
  26. line noise and only delayed the day of reckoning. Instead, we've
  27. written a new protocol that is upward-compatible with the old one
  28. in that you can mix old and new syntax.
  29. There were other problems as well. The old command set encouraged
  30. sloppy handling of data by supporting commands that return PVT and
  31. fix-quality information in atomized partial form, without timestamps.
  32. There was also no way to support returning more than single-line
  33. responses to a client. This was a problem for returning things like
  34. [RINEX] format, which we'd like to be able to do when a device can
  35. report pseudoranges.
  36. == The transition plan
  37. We need to shed the code complexity and overhead of maintaining both
  38. protocols at once. This will happen sooner than it otherwise might
  39. have because gpsd is in part targeted for SBCs and other constrained
  40. environments where shaving a few K off the runtime image can actually
  41. matter. When it comes to keeping the codebase lean and mean, we try
  42. harder.
  43. The old-protocol support was removed from the daemon in 2.91. Old
  44. protocol will be supported in the client-side library for somewhat
  45. longer, giving your applications a bridge period when they can speak
  46. both old and new protocol -- but the client-side support for old
  47. protocol will be removed when 3.0 ships.
  48. The 2.92 version, with the new protocol deployed, is be in Lucid
  49. Lynx, the Ubuntu LTS release of April 2010.
  50. If you follow our transition advice now, you will be able to talk to
  51. all old-protocol and new-protocol versions of the daemon until the 3.0
  52. shared client library is deployed, at which point your runtime will
  53. silently get smaller but you may no longer be able to use 2.x daemon
  54. versions any more.
  55. We're counting on binary-package dependencies to make the transition
  56. easier. When you ship a release using the new interface library,
  57. specify the GPSD package at version >= 2.90 to get the new shared
  58. library; then, when the 3.0 interface library is deployed next year,
  59. you shouldn't have to do anything.
  60. We'll try to make the transition easy, but we cannot guarantee no
  61. problems. The sooner you start adapting your code, the less pain you
  62. are likely to experience. The rest of this document will explain both
  63. theory and practice, and give you specific pointers on how to fix
  64. client code.
  65. == Virtue is rewarded
  66. Since 2004, the way you were *supposed* to be using gpsd was through
  67. one of the client libraries (in C or Python). If you have been doing
  68. it right, you have been telling gpsd to stream data at you with the
  69. 'w' command, via application code probably looked something like this:
  70. -------------------------------------------------------------------
  71. gpsdata = gps_open(source.server, source.port);
  72. (void)gps_query(gpsdata, "w+x\n");
  73. // This is in your application's main event loop somewhere.
  74. // It polls gpsd for new data.
  75. ... gps_poll(gpsdata) ...
  76. gps_close()
  77. -------------------------------------------------------------------
  78. If you have been virtuous, you need only to make four small changes to
  79. your code.
  80. . Give gps_open() a third argument that is the address of a struct gps_data_t.
  81. (This interface changed to avoid malloc(3) and make it possible to write
  82. re-entrant client code.)
  83. . Replace the gps_query() call with:
  84. -------------------------------------------------------------------
  85. gps_stream(gpsdata, WATCH_ENABLE, NULL)
  86. -------------------------------------------------------------------
  87. This will tell whatever version of the client library your application
  88. dynamically links to emit what it should under the hood, either old
  89. or new protocol. Unless a target system carries a version of the
  90. libgps shared library different from the gpsd version, everything
  91. should work and continue to work through future updates.
  92. . Change gps_poll calls to gps_read calls.
  93. . If you have references to the 'satellites' member of the structure,
  94. those need to change them to 'satellites_visible'.
  95. There. You're probably done, unless you relied on some parts of
  96. struct gpsdata_t that application developers usually don't or issued
  97. unusual configuration commands. Here are the exceptions:
  98. * You issued other gps_query() or gps_send() commands, such as "J=1".
  99. If so, you'll need to learn how to say them in the new API (the J
  100. command itself is dead, and you can just remove it entirely). That
  101. is not difficult, and this document will cover what you need to
  102. know.
  103. * Your application code referenced struct gpsdata_t members that no
  104. longer exist. This will be obvious because your compilation will
  105. fail. Later in this document you will learn how to fix these
  106. problems.
  107. * You set a per-packet raw hook. This feature is gone; code
  108. can now just look at the response buffer directly.
  109. * You set a thread hook. We have deleted the thread-hook portion of
  110. the API; for discussion, see "Why threads are gone" below.
  111. You can probably ignore the rest of this document, unless
  112. either (a) you want to learn about gpsd's new capabilities so you
  113. can use them in creative ways, or (b) you want to caper with unholy glee
  114. as you contemplate the trials awaiting the non-virtuous.
  115. If you are non-virtuous -- that is, you rolled your own client-side
  116. interface -- you've had years of warning that this choice would
  117. fail to insulate you from protocol details and cost you pain in the
  118. future. That future is now.
  119. In the remainder of this document we will try to help you minimize the
  120. pain. The main strategy for doing so is to *use libgps* (or its
  121. functional equivalents in languages other than C). Scrap your
  122. hand-rolled interface code! When you use libgps, compatibility issues
  123. become *our* problem to solve rather than *yours*.
  124. == Binary stability
  125. In the past, the GPSD project has not been very good about preserving
  126. stability of the binary structure layout for struct gpsdata_t between
  127. releases. There was a reason for this -- we were very focused
  128. on reducing memory footprint for SBCs and embedded devices, so we
  129. conditioned out various pieces of the strucure depending on what
  130. features were or were not compiled in.
  131. We're not going to do this any more. It has been pointed out to us
  132. that the friction costs of breaking shared-library compatibility are
  133. higher than we were reckoning. The new layout has no sections
  134. conditionalized; instead, we have moved a number of fields into
  135. a union. From 2.90 on, the structure layout will change rarely,
  136. only at major version bumps.
  137. == When the bough breaks
  138. Even virtuous clients have to worry about version skew. Supposing you
  139. have used libgps and not done anything exotic, you will still have
  140. problems if the client library you linked and the instance of gpsd it
  141. speaks to are using different protocols.
  142. The possible failure modes are pretty obvious. Transitions are
  143. difficult. We're essentially relying on the distribution integrators
  144. to ship libgps and gpsd updates at the same time, with sane
  145. package dependencies. If that goes smoothly, applications may
  146. not even notice the changes. We can hope...
  147. == Why threads are gone
  148. We have deleted the two functions in the API that managed a
  149. library-internal thread hook. Here's why:
  150. 1. Actual use of it has been at best very rare and possibly nonexistent.
  151. 2. Applications that want location handing to run in a thread are in
  152. a better position to manage thread locks and mutexes themselves
  153. than our client library can possibly be -- after all, the
  154. application knows what all the other threads and mutex locks
  155. are, and our library doesn't.
  156. 3. We don't like to ship code we can't test, we didn't have a
  157. regression test for the thread stuff, and writing one would
  158. have been a painful expenditure of time better spent elsewhere.
  159. == On not doing things by halves
  160. At the same time that pressure has been building to redesign the
  161. protocol, we've been gaining experience in gpsd's application domain
  162. that has made us rethink some of the assumptions behind the old one.
  163. Since we knew we were going to have a forced compatibility break at the
  164. wire-protocol level anyway, we decided not to do things by halves. One
  165. big break -- in the application model, struct gpsdata_t, and the
  166. wire protocol behind it -- is better than three or four spread out
  167. over a period of time.
  168. As a result, the new protocol is not an exact superset of the old one.
  169. It reflects a different way of carving up the behavior space in gpsd's
  170. application domain. And the shape of struct gpsdata_t, the
  171. client-side interface structure, has changed in corresponding ways.
  172. Accordingly, there are three things a client developer will need to
  173. understand about the new protocol. The first is theory: how its model
  174. of the gpsd application domain is different. The second is practice:
  175. how to issue new-style commands and interpret responses. The third, if
  176. you have relied on the structure in a way that now breaks your
  177. compile, is how that structure has changed.
  178. == How the theory has changed
  179. === Channels are gone
  180. In old protocol, when you requested data from the daemon, it would
  181. search for a device supplying the kind of data you had told it you
  182. wanted (GPS, by default) and connect your listening channel to *that
  183. single device*. The association between channel and device was set
  184. when channel was first bound to device and implicit; reports weren't
  185. labeled with the device name. You could request a specific device if
  186. you wanted to.
  187. In the new protocol, channels are gone. You tell gpsd to stream
  188. reports at you; thereafter, every time an attached GPS or other device
  189. generates a report, you'll get it. There may be multiple devices
  190. reporting; each report will come labeled with the name of the
  191. originating device, and that name will be left in your client
  192. structure along with the rest of the new data.
  193. In both protocols, when you poll gpsd and get data the client library
  194. takes care of interpreting what comes up the wire from the daemon, and
  195. merges the resulting data into your client structure (struct
  196. gpsdata_t).
  197. The difference is that before, the API locked you to one device during
  198. the life of the session. Now it potentially has to deal with a *set*
  199. of devices, treated symmetrically.
  200. There are multiple reasons this change is a good idea. One is that it
  201. makes coping with devices being hotplugged in or out completely
  202. trivial from the client's point of view - it can just choose to ignore
  203. the fact that the device IDs in the reports have changed. Also, the
  204. channel-management hair in the interface goes away. Also, it means
  205. that clients can treat identically the cases where (a) you have one
  206. device reporting different kinds of data (e.g. a marine navigation
  207. system reporting both GPS and AIS) and (b) you have several devices
  208. reporting different kinds of data.
  209. === From lockstep to streaming
  210. A subtler change has to do with the difference between a lockstep
  211. or conversational interface and a streaming, stateless one.
  212. In the earliest versions of GPSD, clients requested various pieces of
  213. data by command. After each request, they would need to wait until a
  214. response came back. Then, watcher mode was added. By saying "w+",
  215. you could ask gpsd to stream GPS reports at you whenever it got them.
  216. In the new protocol, streaming is all there is. Every report coming
  217. up from the daemon is tagged with its device and type. Instead of
  218. issuing commands and then waiting for specific responses, clients
  219. should expect any kind of report at any time and merge it into
  220. client-local storage (libgps does this for you).
  221. This change is necessary to cope with devices that may send (for
  222. example) mixed GPS and AIS data. In the future, the stream from
  223. gpsd could include other kinds of data, such as the take from
  224. a digital compass, water-temperature sensors, or even aircraft
  225. transponders.
  226. === Asynchronous-write handling
  227. The old client code had an assumption baked into it that gps_poll()
  228. can do one read call end expect the daemon to hand it an entire
  229. \n-terminated packet. 99.9% of the time this is true, but socket
  230. layers can do some remarkably perverse things.
  231. In 2.91 and later, what was gps_poll() and is now gps_read() behaves
  232. in a subtly different way. Each call does exactly one read() call as
  233. before, but the incoming data is now buffered; the logic to interpret
  234. the buffer and empty it is called only when the read() contains a \n.
  235. When that happens, the validity flags include the PACKET_SET mask.
  236. == How the command set has changed
  237. If your code issues old-protocol commands 'A', 'D', 'E', 'M', 'P',
  238. 'T', 'U', or 'V', it is a wretched hive of scum and villainy that
  239. probably hasn't changed since before the introduction of 'W' in
  240. 2004-2005. You are using the oldest single-shot commands and will
  241. have to rewrite your interface significantly, as the new protocol does
  242. not support equivalents. Use libgps.
  243. If your code issues B, C, or N commands, they need to change to
  244. ?DEVICE commands. See the protocol reference for details.
  245. The 'F' command has no equivalent in 2.90; consider teaching your
  246. client to ignore fix updates when they don't have a specified "device"
  247. or "class" tag, respectively. In 2.91 and later versions, use the "device"
  248. option of the ?WATCH command for similar effect.
  249. The old 'G' command does not have an equivalent. It would be possible
  250. to implement one, but we probably won't do it unless there is actual
  251. demand (and we don't expect any).
  252. The old 'I' command has no equivalent. You probably issued it as part
  253. of an initialization string, hoping that a subtype string would later
  254. show up in gps_id so you could post it somewhere. In the new
  255. protocol, when a device sends back subtype information the daemon
  256. ships the client an object of class DEVICE with a device tag and
  257. subtype fields. Watch for that and process appropriately.
  258. The old 'J' command is dead. gpsd now detects the end of the reporting
  259. cycle reliably and ships on that, buffering data during the individual
  260. reporting cycle.
  261. The old 'K' command is replaced by ?DEVICES.
  262. The old 'L' command is replaced by ?VERSION. Note that the daemon now
  263. ships a version response to each client on connect, so it will
  264. probably never be necessary for you to issue a ?VERSION request.
  265. The old 'M' command has no equivalent. Mode is reported in the TPV response.
  266. The old 'O' and 'Y' commands are gone. Use ?WATCH and sample the
  267. stream instead.
  268. The old 'Q' command has no equivalent. DOPs are reported in the SKY response.
  269. The 'S' command has no equivalent, because it is not well defined what
  270. value should be presented for binary protocols.
  271. The old 'R' command has been replaced by three optional attributes in
  272. ?WATCH. Include the WATCH_RARE, WATCH_RAW and/or WATCH_NMEA masks in
  273. the argument of gps_stream(), or set a raw hook before alling
  274. gps_stream().
  275. The old 'W' command has been replaced by ?WATCH. Call gps_stream()
  276. with whatever options you want to set.
  277. The old 'X' command is gone. Instead, you will see an object of
  278. class DEVICE from the daemon whenever a device is opened or closed.
  279. The old 'Z' and '$' commands, used by the developers for profiling,
  280. have equivalents, which are presently considered unstable and thus
  281. are undocumented.
  282. == How the C API and gps_data_t structure has changed
  283. gps_open() now takes a third argument and is re-entrant - it's the
  284. old undocumented gps_open_r().
  285. The gps_query() entry point is gone. With the new streaming-oriented
  286. wire protocol, it is extremely unwise to assume that the first
  287. transmission from the damon after a command is shipped to it will be
  288. the response to command. If you must send explicit
  289. commands to the daemon, use gps_send() and handle the response in
  290. your main event-polling loop -- but beware, as using gps_send()
  291. ties your code to the GPSD wire protocol and is not recommended.
  292. gps_poll() is renamed gps_read().
  293. The client library's reporting structure, struct gpsdata_t, has a new
  294. substructure (struct devconfig_t) named "dev" that groups together
  295. information about the device that shipped the last update to the
  296. client. The members of this structure replace several top-level
  297. struct gpsdata members in older versions.
  298. Most notably, the gps_device member has been replaced by dev.path.
  299. It is valid after every response with a device tag (DEVICE, TPV, SKY,
  300. AIS, RTCM2, RTCM3).
  301. The top-level gps_id member is replaced by dev.subtype. This data
  302. should be considered valid only when DEVICEID_SET is on in the
  303. top-level set member.
  304. The dev members baudrate, parity, stopbits, cycle, mincycle, and
  305. driver_mode replace older top-level members. They should be
  306. considered valid only when DEVICE_SET is on in the top-level set
  307. member.
  308. The top-level members ndevices and devicelist (used only on the client
  309. side) have been replaced by an array of struct devconfig_t structures.
  310. Data in this structure should be considered valid only when
  311. DEVICELIST_SET is on in the top-level set member. Storage for
  312. pathnames is no longer dynamically allocated, but static; to save
  313. space, it lives in a union with several other substructures.
  314. The top-level member "satellites" has been changed to
  315. "satellites_visible". The ambiguity in that name had actually induced
  316. a bug or two.
  317. There is a new substructure, dop, which holds the
  318. dilution-of-precision factors that were previously individual members
  319. of the gpsdata structure. Two new DOPs, xdop and ydop, are available;
  320. these express dilution of precision in longitude and latitude,
  321. respectively.
  322. There is a gps_waiting() method analogous to the waiting() method in
  323. the Python class -- a way to check if input is waiting from the
  324. daemon. It blocks but takes a timeout value.
  325. The raw_hook member is gone.
  326. == C++ client library changes
  327. In API version 5, the C++ library defines a single object using RAII.
  328. There are no explicit open() and close() methods; instead, you initialize
  329. the object handing it a host and server, and the connection is shut down
  330. when the object is deleted or goes out of scope.
  331. == Python client library changes
  332. There is a new stream() method analogous to the gps_stream() call in
  333. the C library. As in the C library, the query() method is gone, for
  334. the same reasons. The gps_send() entry point, new in version 3 of the C API,
  335. has had a corresponding Python gps-class send() method all along.
  336. The pre-existing interface using the poll() method and self.valid is
  337. still available and should work compatibly with a daemon speaking
  338. JSON. One new feature has been added; after a VERSION response (which
  339. a JSON-speaking instance of gpsd should emit when a connection is
  340. opened) the version member of the session will be an object containing
  341. version information. However, data from the new responses (WATCH,
  342. VERSION, AIS, and TIMING in particular) will be available only through
  343. the self.data member.
  344. The preferred way to use the new gps class is as an iterator
  345. factory, like this:
  346. ----------------------------------------------------------------------
  347. for report in gps(mode=WATCH_ENABLE):
  348. process(report)
  349. ----------------------------------------------------------------------
  350. See the Client HOWTO for a more detailed example.