gps.py.in 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. #!@PYSHEBANG@
  2. # -*- coding: utf-8 -*-
  3. # @GENERATED@
  4. '''gps.py -- Python interface to GPSD.
  5. This interface has a lot of historical cruft in it related to old
  6. protocol, and was modeled on the C interface. It won't be thrown
  7. away, but it's likely to be deprecated in favor of something more
  8. Pythonic.
  9. The JSON parts of this (which will be reused by any new interface)
  10. now live in a different module.
  11. '''
  12. #
  13. # This file is Copyright 2010 by the GPSD project
  14. # SPDX-License-Identifier: BSD-2-Clause
  15. #
  16. # This code runs compatibly under Python 2 and 3.x for x >= 2.
  17. # Preserve this property!
  18. from __future__ import absolute_import, print_function, division
  19. from .client import *
  20. from .watch_options import *
  21. NaN = float('nan')
  22. def isfinite(f):
  23. "Check if f is finite"
  24. # Python 2 does not think +Inf or -Inf are NaN
  25. # Python 2 has no easier way to test for Inf
  26. return float('-inf') < float(f) < float('inf')
  27. # Don't hand-hack this list, it's generated.
  28. ONLINE_SET = (1 << 1)
  29. TIME_SET = (1 << 2)
  30. TIMERR_SET = (1 << 3)
  31. LATLON_SET = (1 << 4)
  32. ALTITUDE_SET = (1 << 5)
  33. SPEED_SET = (1 << 6)
  34. TRACK_SET = (1 << 7)
  35. CLIMB_SET = (1 << 8)
  36. STATUS_SET = (1 << 9)
  37. MODE_SET = (1 << 10)
  38. DOP_SET = (1 << 11)
  39. HERR_SET = (1 << 12)
  40. VERR_SET = (1 << 13)
  41. ATTITUDE_SET = (1 << 14)
  42. SATELLITE_SET = (1 << 15)
  43. SPEEDERR_SET = (1 << 16)
  44. TRACKERR_SET = (1 << 17)
  45. CLIMBERR_SET = (1 << 18)
  46. DEVICE_SET = (1 << 19)
  47. DEVICELIST_SET = (1 << 20)
  48. DEVICEID_SET = (1 << 21)
  49. RTCM2_SET = (1 << 22)
  50. RTCM3_SET = (1 << 23)
  51. AIS_SET = (1 << 24)
  52. PACKET_SET = (1 << 25)
  53. SUBFRAME_SET = (1 << 26)
  54. GST_SET = (1 << 27)
  55. VERSION_SET = (1 << 28)
  56. POLICY_SET = (1 << 29)
  57. LOGMESSAGE_SET = (1 << 30)
  58. ERROR_SET = (1 << 31)
  59. TIMEDRIFT_SET = (1 << 32)
  60. EOF_SET = (1 << 33)
  61. SET_HIGH_BIT = 34
  62. UNION_SET = (RTCM2_SET | RTCM3_SET | SUBFRAME_SET | AIS_SET | VERSION_SET |
  63. DEVICELIST_SET | ERROR_SET | GST_SET)
  64. STATUS_NO_FIX = 0
  65. STATUS_FIX = 1
  66. STATUS_DGPS_FIX = 2
  67. STATUS_RTK_FIX = 3
  68. STATUS_RTK_FLT = 4
  69. STATUS_DR = 5
  70. STATUS_GNSSDR = 6
  71. STATUS_TIME = 7
  72. STATUS_SIM = 8
  73. STATUS_PPS_FIX = 9
  74. MODE_NO_FIX = 1
  75. MODE_2D = 2
  76. MODE_3D = 3
  77. MAXCHANNELS = 72 # Copied from gps.h, but not required to match
  78. SIGNAL_STRENGTH_UNKNOWN = NaN
  79. class gpsfix(object):
  80. "Class to hold one GPS fix"
  81. def __init__(self):
  82. "Init class gpsfix"
  83. self.altitude = NaN # Meters DEPRECATED
  84. self.altHAE = NaN # Meters
  85. self.altMSL = NaN # Meters
  86. self.climb = NaN # Meters per second
  87. self.datum = ""
  88. self.dgpsAge = -1
  89. self.dgpsSta = ""
  90. self.depth = NaN
  91. self.device = ""
  92. self.ecefx = NaN
  93. self.ecefy = NaN
  94. self.ecefz = NaN
  95. self.ecefvx = NaN
  96. self.ecefvy = NaN
  97. self.ecefvz = NaN
  98. self.ecefpAcc = NaN
  99. self.ecefvAcc = NaN
  100. self.epc = NaN
  101. self.epd = NaN
  102. self.eph = NaN
  103. self.eps = NaN
  104. self.ept = NaN
  105. self.epv = NaN
  106. self.epx = NaN
  107. self.epy = NaN
  108. self.geoidSep = NaN # Meters
  109. self.latitude = self.longitude = 0.0
  110. self.magtrack = NaN
  111. self.magvar = NaN
  112. self.mode = MODE_NO_FIX
  113. self.relN = NaN
  114. self.relE = NaN
  115. self.relD = NaN
  116. self.sep = NaN # a.k.a. epe
  117. self.speed = NaN # Knots
  118. self.status = STATUS_NO_FIX
  119. self.time = NaN
  120. self.track = NaN # Degrees from true north
  121. self.velN = NaN
  122. self.velE = NaN
  123. self.velD = NaN
  124. class gpsdata(object):
  125. "Position, track, velocity and status information returned by a GPS."
  126. class satellite(object):
  127. "Class to hold satellite data"
  128. def __init__(self, PRN, elevation, azimuth, ss, used=None):
  129. self.PRN = PRN
  130. self.elevation = elevation
  131. self.azimuth = azimuth
  132. self.ss = ss
  133. self.used = used
  134. def __repr__(self):
  135. return "PRN: %3d E: %3d Az: %3d Ss: %3d Used: %s" % (
  136. self.PRN, self.elevation, self.azimuth, self.ss,
  137. "ny"[self.used])
  138. def __init__(self):
  139. # Initialize all data members
  140. self.online = 0 # NZ if GPS on, zero if not
  141. self.valid = 0
  142. self.fix = gpsfix()
  143. self.status = STATUS_NO_FIX
  144. self.utc = ""
  145. self.satellites_used = 0 # Satellites used in last fix
  146. self.xdop = self.ydop = self.vdop = self.tdop = 0
  147. self.pdop = self.hdop = self.gdop = 0.0
  148. self.epe = 0.0
  149. self.satellites = [] # satellite objects in view
  150. self.gps_id = None
  151. self.driver_mode = 0
  152. self.baudrate = 0
  153. self.stopbits = 0
  154. self.cycle = 0
  155. self.mincycle = 0
  156. self.device = None
  157. self.devices = []
  158. self.version = None
  159. def __repr__(self):
  160. st = "Time: %s (%s)\n" % (self.utc, self.fix.time)
  161. st += "Lat/Lon: %f %f\n" % (self.fix.latitude, self.fix.longitude)
  162. if not isfinite(self.fix.altHAE):
  163. st += "Altitude HAE: ?\n"
  164. else:
  165. st += "Altitude HAE: %f\n" % (self.fix.altHAE)
  166. if not isfinite(self.fix.speed):
  167. st += "Speed: ?\n"
  168. else:
  169. st += "Speed: %f\n" % (self.fix.speed)
  170. if not isfinite(self.fix.track):
  171. st += "Track: ?\n"
  172. else:
  173. st += "Track: %f\n" % (self.fix.track)
  174. st += "Status: STATUS_%s\n" \
  175. % ("NO_FIX", "FIX", "DGPS_FIX")[self.status]
  176. st += "Mode: MODE_%s\n" \
  177. % ("ZERO", "NO_FIX", "2D", "3D")[self.fix.mode]
  178. st += "Quality: %d p=%2.2f h=%2.2f v=%2.2f t=%2.2f g=%2.2f\n" % \
  179. (self.satellites_used, self.pdop, self.hdop, self.vdop,
  180. self.tdop, self.gdop)
  181. st += "Y: %s satellites in view:\n" % len(self.satellites)
  182. for sat in self.satellites:
  183. st += " %r\n" % sat
  184. return st
  185. class gps(gpscommon, gpsdata, gpsjson):
  186. "Client interface to a running gpsd instance."
  187. # module version, would be nice to automate the version
  188. __version__ = "@VERSION@"
  189. def __init__(self, host="127.0.0.1", port=GPSD_PORT, verbose=0, mode=0,
  190. reconnect=False):
  191. self.activated = None
  192. self.clock_sec = NaN
  193. self.clock_nsec = NaN
  194. self.path = ''
  195. self.precision = 0
  196. self.real_sec = NaN
  197. self.real_nsec = NaN
  198. self.serialmode = "8N1"
  199. gpscommon.__init__(self, host, port, verbose, reconnect)
  200. gpsdata.__init__(self)
  201. gpsjson.__init__(self)
  202. if mode:
  203. self.stream(mode)
  204. def _oldstyle_shim(self):
  205. # The rest is backwards compatibility for the old interface
  206. def default(k, dflt, vbit=0):
  207. "Return default for key"
  208. if k not in self.data.keys():
  209. return dflt
  210. self.valid |= vbit
  211. return self.data[k]
  212. if self.data.get("class") == "VERSION":
  213. self.version = self.data
  214. elif self.data.get("class") == "DEVICE":
  215. self.valid = ONLINE_SET | DEVICE_SET
  216. self.path = self.data["path"]
  217. self.activated = default("activated", None)
  218. driver = default("driver", None, DEVICEID_SET)
  219. subtype = default("subtype", None, DEVICEID_SET)
  220. self.gps_id = driver
  221. if subtype:
  222. self.gps_id += " " + subtype
  223. self.baudrate = default("bps", 0)
  224. self.cycle = default("cycle", NaN)
  225. self.driver_mode = default("native", 0)
  226. self.mincycle = default("mincycle", NaN)
  227. self.serialmode = default("serialmode", "8N1")
  228. elif self.data.get("class") == "TPV":
  229. self.device = default("device", "missing")
  230. self.utc = default("time", None, TIME_SET)
  231. self.valid = ONLINE_SET
  232. if self.utc is not None:
  233. # self.utc is always iso 8601 string
  234. # just copy to fix.time
  235. self.fix.time = self.utc
  236. self.fix.altitude = default("alt", NaN, ALTITUDE_SET) # DEPRECATED
  237. self.fix.altHAE = default("altHAE", NaN, ALTITUDE_SET)
  238. self.fix.altMSL = default("altMSL", NaN, ALTITUDE_SET)
  239. self.fix.climb = default("climb", NaN, CLIMB_SET)
  240. self.fix.epc = default("epc", NaN, CLIMBERR_SET)
  241. self.fix.epd = default("epd", NaN)
  242. self.fix.eps = default("eps", NaN, SPEEDERR_SET)
  243. self.fix.ept = default("ept", NaN, TIMERR_SET)
  244. self.fix.epv = default("epv", NaN, VERR_SET)
  245. self.fix.epx = default("epx", NaN, HERR_SET)
  246. self.fix.epy = default("epy", NaN, HERR_SET)
  247. self.fix.latitude = default("lat", NaN, LATLON_SET)
  248. self.fix.longitude = default("lon", NaN)
  249. self.fix.mode = default("mode", 0, MODE_SET)
  250. self.fix.speed = default("speed", NaN, SPEED_SET)
  251. self.fix.status = default("status", 1)
  252. self.fix.track = default("track", NaN, TRACK_SET)
  253. elif self.data.get("class") == "SKY":
  254. self.device = default("device", "missing")
  255. for attrp in ("g", "h", "p", "t", "v", "x", "y"):
  256. n = attrp + "dop"
  257. setattr(self, n, default(n, NaN, DOP_SET))
  258. if "satellites" in self.data.keys():
  259. self.satellites = []
  260. for sat in self.data['satellites']:
  261. if 'el' not in sat:
  262. sat['el'] = -999
  263. if 'az' not in sat:
  264. sat['az'] = -999
  265. if 'ss' not in sat:
  266. sat['ss'] = -999
  267. self.satellites.append(gps.satellite(PRN=sat['PRN'],
  268. elevation=sat['el'],
  269. azimuth=sat['az'], ss=sat['ss'],
  270. used=sat['used']))
  271. self.satellites_used = 0
  272. for sat in self.satellites:
  273. if sat.used:
  274. self.satellites_used += 1
  275. self.valid = ONLINE_SET | SATELLITE_SET
  276. elif self.data.get("class") == "PPS":
  277. self.device = default("device", "missing")
  278. self.real_sec = default("real_sec", NaN)
  279. self.real_nsec = default("real_nsec", NaN)
  280. self.clock_sec = default("clock_sec", NaN)
  281. self.clock_nsec = default("clock_nsec", NaN)
  282. self.precision = default("precision", 0)
  283. # elif self.data.get("class") == "DEVICES":
  284. # TODO: handle class DEVICES # pylint: disable=fixme
  285. def read(self):
  286. "Read and interpret data from the daemon."
  287. status = gpscommon.read(self)
  288. if status <= 0:
  289. return status
  290. if self.response.startswith("{") and self.response.endswith("}\r\n"):
  291. self.unpack(self.response)
  292. self._oldstyle_shim()
  293. self.valid |= PACKET_SET
  294. return 0
  295. def __next__(self):
  296. "Python 3 version of next()."
  297. if self.read() == -1:
  298. raise StopIteration
  299. if hasattr(self, "data"):
  300. return self.data
  301. return self.response
  302. def next(self):
  303. "Python 2 backward compatibility."
  304. return self.__next__()
  305. def stream(self, flags=0, devpath=None):
  306. "Ask gpsd to stream reports at your client."
  307. gpsjson.stream(self, flags, devpath)
  308. def is_sbas(prn):
  309. "Is this the NMEA ID of an SBAS satellite?"
  310. return 120 <= prn <= 158
  311. if __name__ == '__main__':
  312. import getopt
  313. import sys
  314. (options, arguments) = getopt.getopt(sys.argv[1:], "v")
  315. streaming = False
  316. verbose = False
  317. for (switch, val) in options:
  318. if switch == '-v':
  319. verbose = True
  320. if len(arguments) > 2:
  321. print('Usage: gps.py [-v] [host [port]]')
  322. sys.exit(1)
  323. opts = {"verbose": verbose}
  324. if arguments:
  325. opts["host"] = arguments[0]
  326. if arguments:
  327. opts["port"] = arguments[1]
  328. session = gps(**opts)
  329. session.stream(WATCH_ENABLE)
  330. try:
  331. for report in session:
  332. print(report)
  333. except KeyboardInterrupt:
  334. # Avoid garble on ^C
  335. print("")
  336. # gps.py ends here
  337. # vim: set expandtab shiftwidth=4