zerk.py.in 66 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928
  1. #!@PYSHEBANG@
  2. # -*- coding: UTF-8
  3. # @GENERATED@
  4. # This program conforms to the JAVAD document:
  5. # GNSS Receiver External Interface Specification
  6. # Revised: October 11, 2017
  7. #
  8. # Hereafter referred to as "the specification"
  9. #
  10. # This file is Copyright 2018 by the GPSD project
  11. # SPDX-License-Identifier: BSD-2-clause
  12. #
  13. # This code runs compatibly under Python 2 and 3.x for x >= 2.
  14. # Preserve this property!
  15. #
  16. # ENVIRONMENT:
  17. # Options in the ZERKOPTS environment variable will be parsed before
  18. # the CLI options. A handy place to put your '-f /dev/ttyXX -s SPEED'
  19. #
  20. # example usages:
  21. # Coldboot the GPS: zerk -p COLDBOOT
  22. # Print current serial port: zerk -c "print,/cur/term"
  23. # Decode raw log file: zerk -r -f greis-binary.log -v 2
  24. # Change GPS port speed: zerk -S 230400
  25. # Watch entire reset cycle: zerk -p RESET -v 2 -w 20 -W
  26. # poll SVs Status: zerk -W -w 2 -v 2 -c "out,,jps/{CS,ES,GS,Is,WS,QS}"
  27. # dump local gpsd data zerk -v 2 -w 5 localhost
  28. #
  29. # TODO: no CRC16 packets handled yet
  30. # TODO: more packet decodes
  31. # Codacy D203 and D211 conflict, I choose D203
  32. # Codacy D212 and D213 conflict, I choose D212
  33. """zerk -- GREIS configurator and packet decoder.
  34. usage: zerk [OPTIONS] [server[:port[:device]]]
  35. """
  36. from __future__ import absolute_import, print_function, division
  37. import binascii # for binascii.hexlify()
  38. import getopt # for getopt.getopt(), to parse CLI options
  39. import hashlib # for hashlib.sha1
  40. import os # for os.environ
  41. import socket # for socket.error
  42. import stat # for stat.S_ISBLK()
  43. import struct # for pack()
  44. import sys
  45. import time
  46. import xml.etree.ElementTree # to parse .jpo files
  47. PROG_NAME = 'zerk'
  48. try:
  49. import serial
  50. except ImportError:
  51. serial = None # Defer complaining until we know we need it.
  52. try:
  53. import gps
  54. import gps.misc # for polybyte() polystr()
  55. except ImportError:
  56. # PEP8 says local imports last
  57. sys.stderr.write("%s: failed to import gps, check PYTHON_PATH\n" %
  58. PROG_NAME)
  59. sys.exit(2)
  60. gps_version = '@VERSION@'
  61. if gps.__version__ != gps_version:
  62. sys.stderr.write("%s: ERROR: need gps module version %s, got %s\n" %
  63. (PROG_NAME, gps_version, gps.__version__))
  64. sys.exit(1)
  65. # Some old versions of Python fail to accept a bytearray as an input to
  66. # struct.unpack_from, though it can be worked around by wrapping it with
  67. # buffer(). Since the fix is only needed in rare cases, this monkey-patches
  68. # struct.unpack_from() when needed, and otherwise changes nothing. If
  69. # struct.unpack() were used, it would need similar treatment, as would
  70. # methods from struct.Struct if that were used.
  71. try:
  72. struct.unpack_from('B', bytearray(1))
  73. except TypeError:
  74. unpack_from_orig = struct.unpack_from
  75. def unpack_from_fixed(fmt, buf, offset=0):
  76. """Unpack_from_fixed."""
  77. return unpack_from_orig(fmt, buffer(buf), offset=offset)
  78. struct.unpack_from = unpack_from_fixed
  79. VERB_QUIET = 0 # quiet
  80. VERB_NONE = 1 # just output requested data and some info
  81. VERB_DECODE = 2 # decode all messages
  82. VERB_INFO = 3 # more info
  83. VERB_RAW = 4 # raw info
  84. VERB_PROG = 5 # program trace
  85. # dictionary to hold all user options
  86. opts = {
  87. # command to send to GPS, -c
  88. 'command': None,
  89. # command for -d disable
  90. 'disable': None,
  91. # command for -e enable
  92. 'enable': None,
  93. # default input -f file
  94. 'input_file_name': None,
  95. # default forced wait? -W
  96. 'input_forced_wait': False,
  97. # default port speed -s
  98. 'input_speed': 115200,
  99. # default input wait time -w in seconds
  100. 'input_wait': 2.0,
  101. # the name of an OAF file, extension .jpo
  102. 'oaf_name': None,
  103. # poll command -p
  104. 'poll': None,
  105. # raw log file name
  106. 'raw_file': None,
  107. # open port read only -r
  108. 'read_only': False,
  109. # speed to set GPS -S
  110. 'set_speed': None,
  111. # target gpsd (server:port:device) to connect to
  112. 'target': {"server": None, "port": gps.GPSD_PORT, "device": None},
  113. # verbosity level, -v
  114. 'verbosity': VERB_NONE,
  115. # contents of environment variable ZERKOPTS
  116. 'progopts': '',
  117. }
  118. class greis(object):
  119. """A class for working with the GREIS GPS message formats.
  120. This class contains functions to decode messages in the Javad GREIS
  121. "Receiver Input Language" and "Receiver Messages" formats.
  122. """
  123. # when a statement identifier is received, it is stored here
  124. last_statement_identifier = None
  125. # expected statement identifier.
  126. expect_statement_identifier = False
  127. # ID of current message as a string
  128. s_id = ''
  129. def __init__(self):
  130. """Initialize class."""
  131. self.last_statement_identifier = None
  132. self.expect_statement_identifier = False
  133. # last epoch received in [~~]
  134. # epoch == None means never got epoch, epoch == -1 means missing.
  135. self.epoch = None
  136. def f4_s(self, f):
  137. """Convert an '! f4' to a string."""
  138. if gps.isfinite(f):
  139. # yeah, the precision is a guess
  140. return "%.6f" % f
  141. return 'X'
  142. def f8_s(self, f):
  143. """Convert an '! f8' to a string."""
  144. if gps.isfinite(f):
  145. # yeah, the precision is a guess
  146. return "%.4f" % f
  147. return 'X'
  148. def i1_s(self, i):
  149. """Convert an '! i1' to a string."""
  150. return 'X' if i == 127 else str(i)
  151. def i2_s(self, i):
  152. """Convert an '! i2' to a string."""
  153. return 'X' if i == 32767 else str(i)
  154. def i4_s(self, i):
  155. """Convert an '! i4' to a string."""
  156. return 'X' if i == 2147483647 else str(i)
  157. def u1_s(self, u):
  158. """Convert an '! u1' to a string."""
  159. return 'X' if u == 255 else str(u)
  160. def u2_s(self, u):
  161. """Convert an '! u2' to a string."""
  162. return 'X' if u == 65535 else str(u)
  163. def u4_s(self, u):
  164. """Convert an '! u4' to a string."""
  165. return 'X' if u == 4294967295 else str(u)
  166. def isuchex(self, c):
  167. """Is byte an upper case hex char?"""
  168. if 48 <= c <= 57:
  169. # 0 to 9
  170. return int(c) - 48
  171. if 65 <= c <= 70:
  172. # A to F
  173. return int(c) - 55
  174. return -1
  175. soltypes = {0: "None",
  176. 1: "3D",
  177. 2: "DGPS",
  178. 3: "RTK float",
  179. 4: "RTK fixed",
  180. 5: "fixed"
  181. }
  182. # allowable speeds
  183. speeds = (460800, 230400, 153600, 115200, 57600, 38400, 19200, 9600,
  184. 4800, 2400, 1200, 600, 300)
  185. def msg_c_(self, payload):
  186. """[c?] decode, Smoothing Corrections."""
  187. s = ' smooth'
  188. for i in range(0, len(payload) - 1, 2):
  189. u = struct.unpack_from('<h', payload, i)
  190. s += " " + self.i2_s(u[0])
  191. return s + '\n'
  192. def msg__p(self, payload):
  193. """[?p] decode, Integer Relative Carrier Phases."""
  194. s = ' rcp'
  195. for i in range(0, len(payload) - 1, 4):
  196. u = struct.unpack_from('<l', payload, i)
  197. s += " " + self.i4_s(u[0])
  198. return s + '\n'
  199. def msg__d(self, payload):
  200. """[?d] decode, Relative Doppler."""
  201. s = ' srdp'
  202. for i in range(0, len(payload) - 1, 2):
  203. u = struct.unpack_from('<h', payload, i)
  204. s += " " + self.i2_s(u[0])
  205. return s + '\n'
  206. def msg__r(self, payload):
  207. """[?r] decode, Integer Relative Pseudo-ranges."""
  208. s = ' srdp'
  209. for i in range(0, len(payload) - 1, 2):
  210. u = struct.unpack_from('<h', payload, i)
  211. s += " " + self.i2_s(u[0])
  212. return s + '\n'
  213. def msg__A(self, payload):
  214. """[?A] decode, GPS, GALILEO Almanac."""
  215. m_len = len(payload)
  216. if ('[EA]' == self.s_id) and (49 > m_len):
  217. return " Bad Length %s" % m_len
  218. u = struct.unpack_from('<BhlBBBfffffffff', payload, 0)
  219. s = (" sv %u wna %d toa %d healthA %u healthS %u config %u\n"
  220. " af1 %f af0 %f rootA %f ecc %f m0 %f\n"
  221. " omega0 %f argPer %f delf %f omegaDot %f\n" % u)
  222. if '[EA]' == self.s_id:
  223. u = struct.unpack_from('<H', payload, 46)
  224. s += (" iod %d" % (u[0]))
  225. return s
  226. def msg__E(self, payload):
  227. """[?E] decode, SNR x 4."""
  228. s = ' cnrX4'
  229. for i in range(0, len(payload) - 1, 1):
  230. u = struct.unpack_from('<B', payload, i)
  231. s += " " + self.u1_s(u[0])
  232. return s + '\n'
  233. def msg_WE(self, payload):
  234. """[WE] decode, SBAS Ephemeris."""
  235. u = struct.unpack_from('<BBBBLdddffffffffLHB', payload, 0)
  236. s = (" waasPrn %u gpsPrn %u iod %u acc %u tod %u\n"
  237. " xg %f yg %f zg %f\n"
  238. " vxg %f vyg %f vzg %f\n"
  239. " vvxg %f vvyg %f vvzg %f\n"
  240. " agf0 %f agf1 %f tow %u wn %u flags %u\n" % u)
  241. return s
  242. def msg_r(self, payload):
  243. """[r?] decode, Integer Psudeo Ranges."""
  244. s = ' spr'
  245. for i in range(0, len(payload) - 1, 4):
  246. u = struct.unpack_from('<l', payload, i)
  247. s += " " + self.i4_s(u[0])
  248. return s + '\n'
  249. def msg_AZ(self, payload):
  250. """[AZ] decode, Satellite Azimuths."""
  251. s = " azim"
  252. for i in range(0, len(payload) - 1):
  253. # azimuth/2, 0 to 180 degrees
  254. s += " " + self.u1_s(payload[i])
  255. return s + '\n'
  256. def msg_BP(self, payload):
  257. """[BP] decode."""
  258. u = struct.unpack_from('<f', payload, 0)
  259. return " acc %.3e\n" % u[0]
  260. def msg_D_(self, payload):
  261. """[D?] decode, Doppler."""
  262. s = " dp"
  263. for i in range(0, len(payload) - 1, 4):
  264. # This is dopple in Hz * 1e4
  265. u = struct.unpack_from('<l', payload, i)
  266. s += " " + self.i4_s(u[0])
  267. return s + '\n'
  268. def msg_DO(self, payload):
  269. """[DO] decode."""
  270. u = struct.unpack_from('<ff', payload, 0)
  271. return " val %.3f sval %.3f\n" % u
  272. def msg_DP(self, payload):
  273. """[DP] decode."""
  274. u = struct.unpack_from('<fffBfB', payload, 0)
  275. return (" hdop %f vdop %f tdop %f edop %f\n"
  276. " solType %s\n" %
  277. (u[0], u[1], u[2], u[4], self.soltypes[u[3]]))
  278. def msg_E_(self, payload):
  279. """[E?] decode, SNR."""
  280. s = ' cnr'
  281. for i in range(0, len(payload) - 1):
  282. s += " " + self.u1_s(payload[i])
  283. return s + '\n'
  284. def msg_ET(self, payload):
  285. """[::](ET) decode, Epoch time, end of epoch."""
  286. u = struct.unpack_from('<L', payload, 0)
  287. if ((self.epoch is not None and self.epoch != u[0])):
  288. if -1 == self.epoch:
  289. print("Error: [::](ET) missing [~~](RT)\n")
  290. else:
  291. print("Error: [::](ET) Wrong Epoch %u, should be %u\n" %
  292. (u[0], self.epoch))
  293. # reset epoch
  294. self.epoch = -1
  295. return "(ET) tod %u\n" % u[0]
  296. def msg_EL(self, payload):
  297. """[EL] decode, Satellite Elevations."""
  298. s = " elev"
  299. for i in range(0, len(payload) - 1):
  300. # looking for integer (-90 to 90), not byte
  301. u = struct.unpack_from('<b', payload, i)
  302. s += " " + self.i1_s(u[0])
  303. return s + '\n'
  304. def msg_ER(self, payload):
  305. """[ER] decode, Error messages."""
  306. parts = payload.split(b'%')
  307. if 1 < len(parts):
  308. self.last_statement_identifier = parts[1]
  309. s_payload = "".join(map(chr, payload))
  310. print("[ER] %s\n" % s_payload)
  311. return " %s\n" % s_payload
  312. def msg_EU(self, payload):
  313. """[EU] decode, GALILEO UTC and GPS Time Parameters."""
  314. u = struct.unpack_from('<dfLHbBHbffLHH', payload, 0)
  315. return (" ao %f a1 %f tot %u wnt %u dtls %d dn %u wnlsf %u\n"
  316. " dtlsf %d a0g %f a1g %f t0g %u wn0g %u flags %#x\n" % u)
  317. def msg_FC(self, payload):
  318. """[FC] [F1] [F2] [F3] [f5] [Fl] decode, Signal Lock Loop Flags."""
  319. s = " flags 0x"
  320. for i in range(0, len(payload) - 1):
  321. u = struct.unpack_from('<H', payload, i)
  322. s += " %2x" % (u[0])
  323. return s + '\n'
  324. def msg__E1(self, payload):
  325. """[?E] decode, BeiDos, GPS, GALILEO, IRNSS Ephemeris."""
  326. m_len = len(payload)
  327. # [GE]
  328. if ('[IE]' == self.s_id) and (124 > m_len):
  329. return " Bad Length %s" % m_len
  330. if ('[CN]' == self.s_id) and (132 > m_len):
  331. return " Bad Length %s" % m_len
  332. if ('[EN]' == self.s_id) and (145 > m_len):
  333. return " Bad Length %s" % m_len
  334. u = struct.unpack_from('<BLBhlbBhfffflhddddddfffffffff', payload, 0)
  335. s = (" sv %u tow %u flags %u iodc %d toc %d ura %d healthS %u\n"
  336. " wn %d tgd %f af2 %f af1 %f af0 %f toe %d\n"
  337. " iode %d rootA %f ecc %f m0 %f omega0 %f\n"
  338. " inc0 %f argPer %f deln %f omegaDot %f\n"
  339. " incDot %f crc %f crs %f cuc %f\n"
  340. " cus %f cic %f cis %f\n" % u)
  341. if '[EN]' == self.s_id:
  342. u = struct.unpack_from('<fffffBB', payload, 122)
  343. s += (" bgdE1E5a %f bgdE1E5b %f aio %f ai1 %f ai2 %f\n"
  344. " sfi %u navType %u" % u)
  345. if 149 <= m_len:
  346. # DAf0 added in 3.7.0
  347. u = struct.unpack_from('<f', payload, 144)
  348. s += (" DAf0 %f" % u)
  349. s += '\n'
  350. if ('[IE]' == self.s_id) and (124 > m_len):
  351. u = struct.unpack_from('<B', payload, 122)
  352. s += (" navType %u\n" % u[0])
  353. if ('[CN]' == self.s_id) and (132 > m_len):
  354. u = struct.unpack_from('<fBf', payload, 122)
  355. s += (" tgd2 %f navType %u DAf0 %f\n" % u)
  356. # TODO: decode length 160 168
  357. return s
  358. def msg_GT(self, payload):
  359. """[GT] decode, GPS Time."""
  360. u = struct.unpack_from('<LH', payload, 0)
  361. return " tow %u wn %d\n" % u
  362. def msg_ID(self, payload):
  363. """[ID] Ionosphere Delays."""
  364. s = ' delay'
  365. for i in range(0, len(payload) - 1, 4):
  366. u = struct.unpack_from('<f', payload, i)
  367. s += " %s" % self.f4_s(u[0])
  368. return s + '\n'
  369. def msg_IO(self, payload):
  370. """[IO] decode, GPS Ionospheric Parameters."""
  371. u = struct.unpack_from('<LHffffffff', payload, 0)
  372. return (" tot %d wn %u alpha0 %f alpha1 %f alpha2 %f\n"
  373. " alpha3 %f beta0 %u beta1 %d beta2 %f\n"
  374. " beta3 %f\n" % u)
  375. def msg_LO(self, payload):
  376. """[LO] decode, undocumented message."""
  377. return " Undocumented message\n"
  378. def msg_MF(self, payload):
  379. """[MF] Messages Format."""
  380. u = struct.unpack_from('<BBBBBBB', payload, 0)
  381. return (" id %c%c majorVer %c%c minorVer %c%c order %c\n" %
  382. (chr(u[0]), chr(u[1]), chr(u[2]), chr(u[3]),
  383. chr(u[4]), chr(u[5]), chr(u[6])))
  384. def msg_P_(self, payload):
  385. """[P?] decode, Carrier Phases."""
  386. s = " cp"
  387. for i in range(0, len(payload) - 1, 8):
  388. # carrier phases in cycles
  389. u = struct.unpack_from('<d', payload, i)
  390. s += " " + self.f8_s(u[0])
  391. return s + '\n'
  392. def msg_PM(self, payload):
  393. """[PM] parameters."""
  394. # PM only seems to work after a coldboot, once
  395. # zerk -v 2 -w 20 -c 'out,,jps/{PM}' -W
  396. return " %s\n" % payload
  397. def msg_PV(self, payload):
  398. """[PV] decode, Cartesian Position and Velocity."""
  399. u = struct.unpack_from('<dddfffffBB', payload, 0)
  400. return (" x %s y %s z %s sigma %s\n"
  401. " vx %s vy %s vz %s\n"
  402. " vsigma %s soltype %s\n" %
  403. (self.f8_s(u[0]), self.f8_s(u[1]), self.f8_s(u[2]),
  404. self.f4_s(u[3]), self.f4_s(u[4]), self.f4_s(u[5]),
  405. self.f4_s(u[6]), self.f4_s(u[7]), self.soltypes[u[8]]))
  406. def msg_R_(self, payload):
  407. """[R?] decode, Pseudo-ranges."""
  408. s = " pr"
  409. for i in range(0, len(payload) - 1, 8):
  410. # pseudo in seconds
  411. u = struct.unpack_from('<d', payload, i)
  412. s += " %s" % self.f8_s(u[0])
  413. return s + '\n'
  414. def msg_RD(self, payload):
  415. """[RD] decode, Receiver Date."""
  416. u = struct.unpack_from('<HBBB', payload, 0)
  417. return " year %d month %d day %d base %d\n" % u
  418. def msg_RE(self, payload):
  419. """[RE] decode."""
  420. parts = payload.split(b'%')
  421. if 1 < len(parts):
  422. # Got a statement identifier (ID), save it?
  423. # Multiline statement if payload ends with comma or left brace
  424. if payload[-1] not in (ord(','), ord('{')):
  425. # yes, this is the end
  426. self.last_statement_identifier = parts[1]
  427. # Get the message body
  428. part1 = parts[1].split(b',')
  429. if 'em' == parts[1]:
  430. # Probably no parts[2]
  431. print("Enable Messages %s" % parts[2])
  432. return " Enable Messages %s\n" % parts[2]
  433. if 'id' == parts[1]:
  434. print("ID: %s" % parts[2])
  435. return " ID %s\n" % parts[2]
  436. if 'opts' == part1[0]:
  437. if 1 < len(part1):
  438. s = "OAF %s: %s" % (part1[1], parts[2])
  439. else:
  440. s = " OAF: %s" % (parts[2])
  441. print(s)
  442. return " %s\n" % s
  443. if 'serial' == parts[1]:
  444. print("SERIAL: %s" % parts[2])
  445. return " SERIAL %s\n" % parts[2]
  446. if 'vendor' == parts[1]:
  447. print("VENDOR: %s" % parts[2])
  448. return " Vendor %s\n" % parts[2]
  449. if 'ver' == parts[1]:
  450. print("VER: %s" % parts[2])
  451. return " Version %s\n" % parts[2]
  452. # unknown statement identifier
  453. s_payload = "".join(map(chr, payload))
  454. print("RE: %s\n" % s_payload)
  455. return " %s\n" % s_payload
  456. def msg_RT(self, payload):
  457. """[~~](RT) decode, Receiver Time, start of epoch."""
  458. if self.epoch is not None and -1 != self.epoch:
  459. print("Error: [~~](RT) missing [::](ET)\n")
  460. u = struct.unpack_from('<L', payload, 0)
  461. # save start of epoch
  462. self.epoch = u[0]
  463. return "(RT) tod %u\n" % self.epoch
  464. def msg_S_(self, payload):
  465. """[CS], [ES], [GS], [Is], [WS], [NS], [QS], decode, SVs Status."""
  466. # to poll them all: zerk -W -w 2 -v 2 -c "out,,jps/{CS,ES,GS,Is,WS,QS}"
  467. # TODO, check @checksum
  468. return "%s" % payload
  469. def msg_SE(self, payload):
  470. """[SE] decode."""
  471. u = struct.unpack_from('<BBBBB', payload, 0)
  472. return " data 0x %x %x %x %x %x\n" % u
  473. def msg_SG(self, payload):
  474. """[SG] decode."""
  475. u = struct.unpack_from('<ffffBB', payload, 0)
  476. return (" hpos %s vpos %s hvel %s vvel %s\n"
  477. " soltype %s\n" %
  478. (self.f4_s(u[0]), self.f4_s(u[1]), self.f4_s(u[2]),
  479. self.f4_s(u[3]), self.soltypes[u[4]]))
  480. def msg_SI(self, payload):
  481. """[SI] decode, Satellite Index, deprecated by Javad, use [SX]."""
  482. # [SX] require 3.7 firmware, we use [SI] to support 3.6
  483. s = " usi"
  484. for i in range(0, len(payload) - 1):
  485. s += " %d" % payload[i]
  486. return s + '\n'
  487. def msg_SP(self, payload):
  488. """[SP] decode, Position Covariance Matrix."""
  489. u = struct.unpack_from('<ffffffffffB', payload, 0)
  490. return (" xx % f yy % f zz % f tt % f xy % f\n"
  491. " xz % f xt % f yz % f yt % f zt % f\n"
  492. " solType %s\n" %
  493. (u[0], u[1], u[2], u[3], u[4],
  494. u[5], u[6], u[7], u[8], u[9],
  495. self.soltypes[u[10]]))
  496. def msg_SS(self, payload):
  497. """[SS] decode, Satellite Navigation Status."""
  498. s = " ns"
  499. for i in range(0, len(payload) - 2):
  500. s += " %d" % payload[i]
  501. return (s + '\n solType %s\n' %
  502. self.soltypes[payload[len(payload) - 2]])
  503. def msg_ST(self, payload):
  504. """[ST] decode, Solution Time Tag."""
  505. u = struct.unpack_from('<LBB', payload, 0)
  506. return (" time %u ms, soltype %s\n" %
  507. (u[0], self.soltypes[u[1]]))
  508. def msg_SX(self, payload):
  509. """[SX] decode, Extended Satellite Indices."""
  510. # [SX] require 3.7 firmware
  511. s = " ESI"
  512. for i in range(0, len(payload) - 2, 2):
  513. u = struct.unpack_from('<BB', payload, i)
  514. s += " (%u, %u)" % u
  515. return s + '\n'
  516. def msg_TC(self, payload):
  517. """[TC] decode, CA/L1 Continuous Tracking Time."""
  518. s = " tt"
  519. for i in range(0, len(payload) - 1, 2):
  520. u = struct.unpack_from('<H', payload, i)
  521. s += " %.2f" % u[0]
  522. return s + '\n'
  523. def msg_TO(self, payload):
  524. """[TO] decode, Reference Time to Receiver Time Offset."""
  525. u = struct.unpack_from('<dd', payload, 0)
  526. return " val %.3f sval %.3f\n" % u
  527. def msg_UO(self, payload):
  528. """[UO] decode, GPS UTC Time Parameters."""
  529. u = struct.unpack_from('<dfLHbBHb', payload, 0)
  530. return (" a0 %f a1 %f tot %d wnt %d dtls %d\n"
  531. " dn %d wnlsf %d dtlsf %d\n" % u)
  532. def msg_WA(self, payload):
  533. """[WA] decode."""
  534. u = struct.unpack_from('<BBBBLdddfffLH', payload, 0)
  535. return (" waasPrn %d gpsPrn %d if %d healthS %d tod %d\n"
  536. " ECEF %.3f %.3f %.3f, %.3f %.3f %.3f\n"
  537. " tow %d wn %d\n" % u)
  538. def msg_WU(self, payload):
  539. """[WU] decode, SBAS UTC Time Parameters."""
  540. u = struct.unpack_from('<dfLHbBHbfbLHB', payload, 0)
  541. return (" ao %f a1 %f tot %u wnt %u dtls %d dn %u\n"
  542. "wnlsf %u dtlsf %d utcsi %d tow %u wn %u flags %#x\n" % u)
  543. # table from message id to respective message decoder.
  544. # Note: id (%id%) is different than ID (statement identifier)
  545. # the id is the first two characters of a GREIS receiver Message
  546. # see section 3.3 of the specification
  547. messages = {
  548. '[0d]': (msg__d, 1),
  549. '[1d]': (msg__d, 1),
  550. '[1E]': (msg__E, 1),
  551. '[1p]': (msg__p, 1),
  552. '[1r]': (msg__r, 1),
  553. '[2d]': (msg__d, 1),
  554. '[2E]': (msg__E, 1),
  555. '[2p]': (msg__p, 1),
  556. '[2r]': (msg__r, 1),
  557. '[3d]': (msg__d, 1),
  558. '[3E]': (msg__E, 1),
  559. '[3p]': (msg__p, 1),
  560. '[3r]': (msg__r, 1),
  561. '[5d]': (msg__d, 1),
  562. '[5E]': (msg__E, 1),
  563. '[5p]': (msg__p, 1),
  564. '[5r]': (msg__r, 1),
  565. '[AZ]': (msg_AZ, 1),
  566. '[BP]': (msg_BP, 5),
  567. '[c1]': (msg_c_, 1),
  568. '[c2]': (msg_c_, 1),
  569. '[c3]': (msg_c_, 1),
  570. '[c5]': (msg_c_, 1),
  571. '[CA]': (msg__A, 47),
  572. '[cc]': (msg_c_, 1),
  573. '[CE]': (msg__E, 1),
  574. '[cl]': (msg_c_, 1),
  575. '[CN]': (msg__E1, 123),
  576. '[cp]': (msg__p, 1),
  577. '[cr]': (msg__r, 1),
  578. '[CS]': (msg_S_, 8),
  579. '[D1]': (msg_D_, 1),
  580. '[D2]': (msg_D_, 1),
  581. '[D3]': (msg_D_, 1),
  582. '[D5]': (msg_D_, 1),
  583. '[DC]': (msg_D_, 1),
  584. '[Dl]': (msg_D_, 1),
  585. '[DO]': (msg_DO, 6),
  586. '[DP]': (msg_DP, 18),
  587. '[DX]': (msg_D_, 1),
  588. '[E1]': (msg_E_, 1),
  589. '[E2]': (msg_E_, 1),
  590. '[E3]': (msg_E_, 1),
  591. '[E5]': (msg_E_, 1),
  592. '[EA]': (msg__A, 47),
  593. '[EC]': (msg_E_, 1),
  594. '[El]': (msg_E_, 1),
  595. '[EL]': (msg_EL, 1),
  596. '[EN]': (msg__E1, 123),
  597. '[ER]': (msg_ER, 1),
  598. '[ES]': (msg_S_, 8),
  599. '[EU]': (msg_EU, 40),
  600. '[F1]': (msg_FC, 1),
  601. '[F2]': (msg_FC, 1),
  602. '[F3]': (msg_FC, 1),
  603. '[F5]': (msg_FC, 1),
  604. '[FA]': (msg_FC, 1),
  605. '[FC]': (msg_FC, 1),
  606. '[Fl]': (msg_FC, 1),
  607. '[GA]': (msg__A, 47),
  608. '[GE]': (msg__E1, 123),
  609. '[GS]': (msg_S_, 8),
  610. '[GT]': (msg_GT, 7),
  611. '[IA]': (msg__A, 47),
  612. '[ID]': (msg_ID, 1),
  613. '[IE]': (msg__E1, 123),
  614. '[IO]': (msg_IO, 39),
  615. '[Is]': (msg_S_, 8),
  616. '[ld]': (msg__d, 1),
  617. '[lE]': (msg__E, 1),
  618. '[LO]': (msg_LO, 1),
  619. '[lp]': (msg__p, 1),
  620. '[lr]': (msg__r, 1),
  621. '[MF]': (msg_MF, 9),
  622. '[::]': (msg_ET, 4),
  623. '[~~]': (msg_RT, 4),
  624. '[NS]': (msg_S_, 8),
  625. '[P1]': (msg_P_, 1),
  626. '[P2]': (msg_P_, 1),
  627. '[P3]': (msg_P_, 1),
  628. '[P5]': (msg_P_, 1),
  629. '[PC]': (msg_P_, 1),
  630. '[Pl]': (msg_P_, 1),
  631. '[PM]': (msg_PM, 0),
  632. '[PV]': (msg_PV, 46),
  633. '[QA]': (msg__A, 47),
  634. '[QE]': (msg__E1, 123),
  635. '[QS]': (msg_S_, 8),
  636. '[r1]': (msg_r, 1),
  637. '[R1]': (msg_R_, 1),
  638. '[r2]': (msg_r, 1),
  639. '[R2]': (msg_R_, 1),
  640. '[r3]': (msg_r, 1),
  641. '[R3]': (msg_R_, 1),
  642. '[r5]': (msg_r, 1),
  643. '[R5]': (msg_R_, 1),
  644. '[rc]': (msg_r, 1),
  645. '[RC]': (msg_R_, 1),
  646. '[RD]': (msg_RD, 6),
  647. '[RE]': (msg_RE, 1),
  648. '[rl]': (msg_r, 1),
  649. '[Rl]': (msg_R_, 1),
  650. '[rx]': (msg_r, 1),
  651. '[SE]': (msg_SE, 6),
  652. '[SG]': (msg_SG, 18),
  653. '[SI]': (msg_SI, 1),
  654. '[SP]': (msg_SP, 42),
  655. '[SS]': (msg_SS, 1),
  656. '[ST]': (msg_ST, 6),
  657. '[SX]': (msg_SX, 1),
  658. '[TC]': (msg_TC, 1),
  659. '[TO]': (msg_TO, 6),
  660. '[UO]': (msg_UO, 24),
  661. '[WA]': (msg_WA, 51),
  662. '[WE]': (msg_WE, 73),
  663. '[WS]': (msg_S_, 8),
  664. '[WU]': (msg_WU, 40),
  665. }
  666. def decode_msg(self, out):
  667. """Decode one message and then return number of chars consumed."""
  668. state = 'BASE'
  669. consumed = 0
  670. # raw message, sometimes used for checksum calc
  671. m_raw = bytearray(0)
  672. # decode state machine
  673. for this_byte in out:
  674. consumed += 1
  675. if isinstance(this_byte, str):
  676. # a character, probably read from a file
  677. c = ord(this_byte)
  678. else:
  679. # a byte, probably read from a serial port
  680. c = int(this_byte)
  681. if VERB_RAW <= opts['verbosity']:
  682. if ord(' ') <= c <= ord('~'):
  683. # c is printable
  684. print("state: %s char %c (%#x)" % (state, chr(c), c))
  685. else:
  686. # c is not printable
  687. print("state: %s char %#x" % (state, c))
  688. m_raw.extend([c])
  689. # parse input stream per GREIS Ref Guide Section 3.3.3
  690. if 'BASE' == state:
  691. # start fresh
  692. # place to store 'comments'
  693. comment = ''
  694. # message id byte one
  695. m_id1 = 0
  696. # message id byte two
  697. m_id2 = 0
  698. # message length as integer
  699. m_len = 0
  700. # byte array to hold payload, including possible checksum
  701. m_payload = bytearray(0)
  702. m_raw = bytearray(0)
  703. m_raw.extend([c])
  704. if ord('0') <= c <= ord('~'):
  705. # maybe id 1, '0' to '~'
  706. state = 'ID1'
  707. # start the grab
  708. m_id1 = c
  709. continue
  710. if ord("%") == c:
  711. # start of %ID%, Receiver Input Language
  712. # per GREIS Ref Guide Section 2.2
  713. state = 'RIL'
  714. # start fresh
  715. comment = "%"
  716. continue
  717. if ord("$") == c:
  718. # NMEA line, treat as comment
  719. state = 'NMEA'
  720. # start fresh
  721. comment = "$"
  722. continue
  723. if ord("#") == c:
  724. # comment line
  725. state = 'COMMENT'
  726. # start fresh
  727. comment = "#"
  728. continue
  729. if ord('\n') == c or ord('\r') == c:
  730. # stray newline or linefeed, eat it
  731. return consumed
  732. # none of the above, stay in BASE
  733. continue
  734. if state in ('COMMENT', 'JSON', 'RIL'):
  735. # inside comment
  736. if c in (ord('\n'), ord('\r')):
  737. # Got newline or linefeed
  738. # GREIS terminates messages on <CR> or <LF>
  739. # Done, got a full message
  740. if '{"class":"ERROR"' in comment:
  741. # always print gpsd errors
  742. print(comment)
  743. elif VERB_DECODE <= opts['verbosity']:
  744. print(comment)
  745. return consumed
  746. # else:
  747. comment += chr(c)
  748. continue
  749. if 'ID1' == state:
  750. # maybe id 2, '0' to '~'
  751. if ord('"') == c:
  752. # technically could be GREIS, but likely JSON
  753. state = 'JSON'
  754. comment += chr(m_id1) + chr(c)
  755. elif ord('0') <= c <= ord('~'):
  756. state = 'ID2'
  757. m_id2 = c
  758. else:
  759. state = 'BASE'
  760. continue
  761. if 'ID2' == state:
  762. # maybe len 1, 'A' to 'F'
  763. x = self.isuchex(c)
  764. if -1 < x:
  765. state = 'LEN1'
  766. m_len = x * 256
  767. else:
  768. state = 'BASE'
  769. continue
  770. if 'LEN1' == state:
  771. # maybe len 2, 'A' to 'F'
  772. x = self.isuchex(c)
  773. if -1 < x:
  774. state = 'LEN2'
  775. m_len += x * 16
  776. else:
  777. state = 'BASE'
  778. continue
  779. if 'LEN2' == state:
  780. # maybe len 3, 'A' to 'F'
  781. x = self.isuchex(c)
  782. if -1 < x:
  783. state = 'PAYLOAD'
  784. m_len += x
  785. else:
  786. state = 'BASE'
  787. continue
  788. if 'NMEA' == state:
  789. # inside NMEA
  790. if ord('\n') == c or ord('\r') == c:
  791. # Got newline or linefeed
  792. # done, got a full message
  793. # GREIS terminates messages on <CR> or <LF>
  794. if VERB_DECODE <= opts['verbosity']:
  795. print(comment)
  796. return consumed
  797. # else:
  798. comment += chr(c)
  799. continue
  800. if 'PAYLOAD' == state:
  801. # getting payload
  802. m_payload.extend([c])
  803. if len(m_payload) < m_len:
  804. continue
  805. # got entire payload
  806. self.s_id = "[%c%c]" % (chr(m_id1), chr(m_id2))
  807. if VERB_DECODE <= opts['verbosity']:
  808. x_payload = binascii.hexlify(m_payload)
  809. # [RE], [ER] and more have no 8-bit checksum
  810. # assume the rest do
  811. if ((self.s_id not in ('[CS]', '[ER]', '[ES]', '[GS]', '[Is]',
  812. '[MF]', '[NS]', '[PM]', '[QS]', '[RE]',
  813. '[WS]') and
  814. not self.checksum_OK(m_raw))):
  815. print("ERROR: Bad checksum\n")
  816. if VERB_DECODE <= opts['verbosity']:
  817. print("DECODE: id: %s len: %d\n"
  818. "DECODE: payload: %s\n" %
  819. (self.s_id, m_len, x_payload))
  820. # skip it.
  821. return consumed
  822. if self.s_id in self.messages:
  823. if VERB_INFO <= opts['verbosity']:
  824. print("INFO: id: %s len: %d\n"
  825. "INFO: payload: %s\n" %
  826. (self.s_id, m_len, x_payload))
  827. (decode, length) = self.messages[self.s_id]
  828. if m_len < length:
  829. print("DECODE: %s Bad Length %s\n" %
  830. (self.s_id, m_len))
  831. else:
  832. s = self.s_id + decode(self, m_payload)
  833. if VERB_DECODE <= opts['verbosity']:
  834. print(s)
  835. else:
  836. # unknown message
  837. if VERB_DECODE <= opts['verbosity']:
  838. print("DECODE: Unknown: id: %s len: %d\n"
  839. "DECODE: payload: %s\n" %
  840. (self.s_id, m_len, x_payload))
  841. return consumed
  842. # give up
  843. state = 'BASE'
  844. # fell out of loop, no more chars to look at
  845. return 0
  846. def checksum_OK(self, raw_msg):
  847. """Check the i8-bit checksum on a message, return True if good."""
  848. # some packets from the GPS use CRC16, some i8-bit checksum, some none
  849. # only 8-bit checksum done here for now
  850. calc_checksum = self.checksum(raw_msg, len(raw_msg) - 1)
  851. rcode = raw_msg[len(raw_msg) - 1] == calc_checksum
  852. if VERB_RAW <= opts['verbosity']:
  853. print("Checksum was %#x, calculated %#x" %
  854. (raw_msg[len(raw_msg) - 1], calc_checksum))
  855. return rcode
  856. def _rol(self, value):
  857. """Rotate a byte left 2 bits."""
  858. return ((value << 2) | (value >> 6)) & 0x0ff
  859. def checksum(self, msg, m_len):
  860. """Calculate GREIS 8-bit checksum."""
  861. # Calculated per section A.1.1 of the specification
  862. # msg may be bytes (incoming messages) or str (outgoing messages)
  863. ck = 0
  864. for c in msg[0:m_len]:
  865. if isinstance(c, str):
  866. # a string, make a byte
  867. c = ord(c)
  868. ck = self._rol(ck) ^ c
  869. return self._rol(ck) & 0x0ff
  870. def make_pkt(self, m_data):
  871. """Build output message, always ASCII, add checksum and terminator."""
  872. # build core message
  873. # no leading spaces, checksum includes the @
  874. m_data = m_data.lstrip() + b'@'
  875. chk = self.checksum(m_data, len(m_data))
  876. # all commands end with CR and/or LF
  877. return m_data + (b'%02X' % chk) + b'\n'
  878. def gps_send(self, m_data):
  879. """Send message to GREIS GPS."""
  880. m_all = self.make_pkt(m_data)
  881. if not opts['read_only']:
  882. io_handle.ser.write(m_all)
  883. if VERB_INFO <= opts['verbosity']:
  884. print("sent:", m_all)
  885. self.decode_msg(m_all)
  886. sys.stdout.flush()
  887. # Table of known options. From table 4-2 of the specification.
  888. oafs = (
  889. b"_AJM",
  890. b"AUTH",
  891. b"_BLT",
  892. b"_CAN",
  893. b"CDIF",
  894. b"CMRI",
  895. b"CMRO",
  896. b"COMP",
  897. b"COOP",
  898. b"COPN",
  899. b"CORI",
  900. b"_CPH",
  901. b"DEVS",
  902. b"DIST",
  903. b"_DTM",
  904. b"_E5B",
  905. b"_E6_",
  906. b"EDEV",
  907. b"ETHR",
  908. b"EVNT",
  909. b"_FRI",
  910. b"_FRO",
  911. b"_FTP",
  912. b"_GAL",
  913. b"GBAI",
  914. b"GBAO",
  915. b"GCLB",
  916. b"_GEN",
  917. b"_GEO",
  918. b"_GLO",
  919. b"_GPS",
  920. b"_GSM",
  921. b"HTTP",
  922. b"_IMU",
  923. b"INFR",
  924. b"IRIG",
  925. b"IRNS",
  926. b"JPSI",
  927. b"JPSO",
  928. b"_L1C",
  929. b"_L1_",
  930. b"_L2C",
  931. b"_L2_",
  932. b"_L5_",
  933. b"LAT1",
  934. b"LAT2",
  935. b"LAT3",
  936. b"LAT4",
  937. b"LCS2",
  938. b"L_CS",
  939. b"_LIM",
  940. b"LON1",
  941. b"LON2",
  942. b"LON3",
  943. b"LON4",
  944. b"MAGN",
  945. b"_MEM",
  946. b"_MPR",
  947. b"OCTO",
  948. b"OMNI",
  949. b"_PAR",
  950. b"PDIF",
  951. b"_POS",
  952. b"_PPP",
  953. b"_PPS",
  954. b"PRTT",
  955. b"_PTP",
  956. b"QZSS",
  957. b"RAIM",
  958. b"_RAW",
  959. b"RCVT",
  960. b"RM3I",
  961. b"RM3O",
  962. b"RS_A",
  963. b"RS_B",
  964. b"RS_C",
  965. b"RS_D",
  966. b"RTMI",
  967. b"RTMO",
  968. b"SPEC",
  969. b"TCCL",
  970. b"_TCP",
  971. b"TCPO",
  972. b"_TLS",
  973. b"TRST",
  974. b"UDPO",
  975. b"_UHF",
  976. b"_USB",
  977. b"WAAS",
  978. b"WIFI",
  979. b"_WPT",
  980. )
  981. def send_able_4hz(self, able):
  982. """Enable basic GREIS messages at 4Hz."""
  983. self.expect_statement_identifier = 'greis'
  984. # the messages we want
  985. # [SX] requires 3.7 firmware, we use [SI] to support 3.6
  986. messages = b"jps/{RT,UO,GT,PV,SG,DP,SI,EL,AZ,EC,SS,ET}"
  987. if able:
  988. # Message rate must be an integer multiple of /par/raw/msint
  989. # Default msint is 0.100 seconds, so that must be changed first
  990. self.gps_send(b"%msint%set,/par/raw/msint,250")
  991. self.gps_send(b"%greis%em,," + messages + b":0.25")
  992. else:
  993. self.gps_send(b"%greis%dm,," + messages)
  994. def send_able_comp(self, able):
  995. """Dis/enable COMPASS, aka BeiDou."""
  996. self.expect_statement_identifier = 'cons'
  997. en_dis = b'y' if 1 == able else b'n'
  998. self.gps_send(b"%cons%set,/par/pos/sys/comp," + en_dis)
  999. def send_able_constellations(self, able):
  1000. """Dis/enable all constellations."""
  1001. self.expect_statement_identifier = 'cons7'
  1002. en_dis = b'y' if 1 == able else b'n'
  1003. self.gps_send(b"%cons1%set,/par/pos/sys/comp," + en_dis)
  1004. self.gps_send(b"%cons2%set,/par/pos/sys/gal," + en_dis)
  1005. # this will fail on TR-G2H, as it has no GLONASS
  1006. self.gps_send(b"%cons3%set,/par/pos/sys/glo," + en_dis)
  1007. self.gps_send(b"%cons4%set,/par/pos/sys/gps," + en_dis)
  1008. self.gps_send(b"%cons5%set,/par/pos/sys/irnss," + en_dis)
  1009. self.gps_send(b"%cons6%set,/par/pos/sys/sbas," + en_dis)
  1010. self.gps_send(b"%cons7%set,/par/pos/sys/qzss," + en_dis)
  1011. def send_able_defmsg(self, able):
  1012. """Dis/enable default messages at 1Hz."""
  1013. self.expect_statement_identifier = 'defmsg'
  1014. if able:
  1015. self.gps_send(b"%defmsg%em,,jps/RT,/msg/def:1,jps/ET")
  1016. else:
  1017. # leave RT and ET to break less?
  1018. self.gps_send(b"%defmsg%dm,,/msg/def:1")
  1019. def send_able_gal(self, able):
  1020. """Dis/enable GALILEO."""
  1021. self.expect_statement_identifier = 'cons'
  1022. en_dis = b'y' if 1 == able else b'n'
  1023. self.gps_send(b"%cons%set,/par/pos/sys/gal," + en_dis)
  1024. def send_able_glo(self, able):
  1025. """Dis/enable GLONASS."""
  1026. self.expect_statement_identifier = 'cons'
  1027. en_dis = b'y' if 1 == able else b'n'
  1028. self.gps_send(b"%cons%set,/par/pos/sys/glo," + en_dis)
  1029. def send_able_gps(self, able):
  1030. """Dis/enable GPS."""
  1031. self.expect_statement_identifier = 'cons'
  1032. en_dis = b'y' if 1 == able else b'n'
  1033. self.gps_send(b"%cons%set,/par/pos/sys/gps," + en_dis)
  1034. def send_able_ipr(self, able):
  1035. """Dis/enable all Integer Pseudo-Range messages."""
  1036. self.expect_statement_identifier = 'em'
  1037. if able:
  1038. self.gps_send(b"%em%em,,jps/{rx,rc,r1,r2,r3,r5,rl}:0.25")
  1039. else:
  1040. self.gps_send(b"%em%dm,,jps/{rx,rc,r1,r2,r3,r5,rl}")
  1041. def send_able_irnss(self, able):
  1042. """Dis/enable IRNSS."""
  1043. self.expect_statement_identifier = 'cons'
  1044. en_dis = b'y' if 1 == able else b'n'
  1045. self.gps_send(b"%cons%set,/par/pos/sys/irnss," + en_dis)
  1046. def send_able_nmea41(self, able):
  1047. """Dis/enable basic NMEA 4.1e messages at 4Hz."""
  1048. self.expect_statement_identifier = 'nmea'
  1049. messages = b"nmea/{GBS,GGA,GSA,GST,GSV,RMC,VTG,ZDA}"
  1050. if able:
  1051. # set NMEA version 4.1e
  1052. self.gps_send(b"%nmeaver%set,/par/nmea/ver,v4.1e")
  1053. # Message rate must be an integer multiple of /par/raw/msint
  1054. # Default msint is 0.100 seconds, so that must be changed first
  1055. self.gps_send(b"%msint%set,/par/raw/msint,250")
  1056. # now we can set the messages we want
  1057. self.gps_send(b"%nmea%em,," + messages + b":0.25")
  1058. else:
  1059. # disable
  1060. self.gps_send(b"%nmea%dm,," + messages)
  1061. def send_able_raw(self, able):
  1062. """Dis/enable Raw mode messages."""
  1063. self.expect_statement_identifier = 'raw'
  1064. messages = (b"jps/{RT,UO,GT,PV,SG,DP,SI,EL,AZ,EC,SS,"
  1065. b"PC,P1,P2,P3,P5,Pl,"
  1066. b"RC,R1,R2,R3,R5,Rl,"
  1067. b"DC,D1,D2,D3,D5,Dl,"
  1068. b"ET}")
  1069. if able:
  1070. self.gps_send(b"%raw%em,," + messages + b":1")
  1071. else:
  1072. self.gps_send(b"%raw%dm,," + messages)
  1073. def send_able_sbas(self, able):
  1074. """Dis/enable SBAS."""
  1075. self.expect_statement_identifier = 'cons'
  1076. en_dis = b'y' if 1 == able else b'n'
  1077. self.gps_send(b"%cons%set,/par/pos/sys/sbas," + en_dis)
  1078. def send_able_qzss(self, able):
  1079. """Dis/enable QZSS."""
  1080. self.expect_statement_identifier = 'cons'
  1081. en_dis = b'y' if 1 == able else b'n'
  1082. self.gps_send(b"%cons%set,/par/pos/sys/qzss," + en_dis)
  1083. def send_able_snr(self, able):
  1084. """Dis/enable all SNR messages, except [EC]."""
  1085. self.expect_statement_identifier = 'em'
  1086. if able:
  1087. self.gps_send(b"%em%em,,jps/{E1,E2,E3,E5,El}:0.25")
  1088. else:
  1089. self.gps_send(b"%em%dm,,jps/{E1,E2,E3,E5,El}")
  1090. able_commands = {
  1091. # en/disable basic GREIS messages at 4HZ
  1092. "4HZ": {"command": send_able_4hz,
  1093. "help": "basic GREIS messages at 4Hz"},
  1094. # en/disable all constellations
  1095. "CONS": {"command": send_able_constellations,
  1096. "help": "all constellations"},
  1097. # en/disable COMPASS, aka Beidou
  1098. "COMPASS": {"command": send_able_comp,
  1099. "help": "COMPASS"},
  1100. # en/disable default message set.
  1101. "DEFMSG": {"command": send_able_defmsg,
  1102. "help": "default message set at 1Hz"},
  1103. # en/disable GALILEO
  1104. "GALILEO": {"command": send_able_gal,
  1105. "help": "GALILEO"},
  1106. # en/disable GLONASS
  1107. "GLONASS": {"command": send_able_glo,
  1108. "help": "GLONASS"},
  1109. # en/disable GPS
  1110. "GPS": {"command": send_able_gps,
  1111. "help": "GPS"},
  1112. # en/disable Integer Pseudo Range messages
  1113. "IPR": {"command": send_able_ipr,
  1114. "help": "all Integer Pseudo Range messages"},
  1115. # en/disable IRNSS
  1116. "IRNSS": {"command": send_able_irnss,
  1117. "help": "IRNSS"},
  1118. # en/disable NMEA 4.1e
  1119. "NMEA": {"command": send_able_nmea41,
  1120. "help": "basic messages NMEA 4.1 at 4Hz"},
  1121. # en/disable Pseudo Range, Carrier Phase and Doppler messages
  1122. "RAW": {"command": send_able_raw,
  1123. "help": "Raw mode messages"},
  1124. # en/disable SBAS
  1125. "SBAS": {"command": send_able_sbas,
  1126. "help": "SBAS"},
  1127. # en/disable all SNRs
  1128. "SNR": {"command": send_able_snr,
  1129. "help": "all SNR messages, except [EC]"},
  1130. # en/disable QZSS
  1131. "QZSS": {"command": send_able_qzss,
  1132. "help": "QZSS"},
  1133. }
  1134. def send_coldboot(self):
  1135. """Delete NVRAM (almanac, ephemeris, location) and restart."""
  1136. self.expect_statement_identifier = 'coldboot'
  1137. self.gps_send(b"%coldboot%init,/dev/nvm/a")
  1138. def send_constellations(self):
  1139. """Poll all constellations."""
  1140. self.expect_statement_identifier = 'cons'
  1141. self.gps_send(b"%cons%print,/par/pos/sys:on")
  1142. def send_get_id(self):
  1143. """Get receiver id."""
  1144. self.expect_statement_identifier = 'id'
  1145. self.gps_send(b"%id%print,/par/rcv/id")
  1146. def send_get_oaf(self):
  1147. """Poll OAF (GPS opts)."""
  1148. self.expect_statement_identifier = 'opts,_WPT'
  1149. if VERB_RAW <= opts['verbosity']:
  1150. # get list of all opts
  1151. self.gps_send(b"%opts,list%list,/par/opts")
  1152. # request opts one at a time from canned list
  1153. for s in self.oafs:
  1154. self.gps_send(b"%%opts,%s%%print,/par/opts/%s" % (s, s))
  1155. def send_get_serial(self):
  1156. """Get receiver serial number."""
  1157. self.expect_statement_identifier = 'serial'
  1158. self.gps_send(b"%serial%print,/par/rcv/sn")
  1159. def send_reset(self):
  1160. """Reset (reboot) the GPS."""
  1161. self.expect_statement_identifier = 'reset'
  1162. self.gps_send(b"%reset%set,/par/reset,y")
  1163. def send_set_dm(self):
  1164. """Disable all messages."""
  1165. self.expect_statement_identifier = 'dm'
  1166. self.gps_send(b"%dm%dm")
  1167. def send_set_ipr(self):
  1168. """Poll Integer Pseudo-Range messages."""
  1169. self.expect_statement_identifier = 'out'
  1170. self.gps_send(b"%out%out,,jps/{rx,rc,r1,r2,r3,r5,rl}")
  1171. def send_get_snr(self):
  1172. """Poll all SNR messages."""
  1173. # nothing we can wait on, depending on GPS model/configuration
  1174. # we may never see some of E2, E3, E5 or El
  1175. self.gps_send(b"%out%out,,jps/{EC,E1,E2,E3,E5,El}")
  1176. def send_set_speed(self, set_speed):
  1177. """Change GPS speed."""
  1178. self.expect_statement_identifier = 'setspeed'
  1179. self.gps_send(b"%%setspeed%%set,/par/cur/term/rate,%d" %
  1180. set_speed)
  1181. def send_get_vendor(self):
  1182. """Get receiver vendor."""
  1183. self.expect_statement_identifier = 'vendor'
  1184. self.gps_send(b"%vendor%print,/par/rcv/vendor")
  1185. def send_get_ver(self):
  1186. """Get receiver version, per section 4.4.3 of the specification."""
  1187. self.expect_statement_identifier = 'ver'
  1188. self.gps_send(b"%ver%print,/par/rcv/ver")
  1189. # list of canned commands that can be sent to the receiver
  1190. commands = {
  1191. "COLDBOOT": {"command": send_coldboot,
  1192. "help": "cold boot the GPS"},
  1193. "CONS": {"command": send_constellations,
  1194. "help": "poll enabled constellations"},
  1195. "DM": {"command": send_set_dm,
  1196. "help": "disable all periodic messages"},
  1197. "ID": {"command": send_get_id,
  1198. "help": "poll receiver ID"},
  1199. "IPR": {"command": send_set_ipr,
  1200. "help": "poll all Integer Pseudo-range messages"},
  1201. "OAF": {"command": send_get_oaf,
  1202. "help": "poll all OAF options"},
  1203. "RESET": {"command": send_reset,
  1204. "help": "reset (reboot) the GPS"},
  1205. "SERIAL": {"command": send_get_serial,
  1206. "help": "poll receiver serial number"},
  1207. "SNR": {"command": send_get_snr,
  1208. "help": "poll all SNR messages"},
  1209. "VENDOR": {"command": send_get_vendor,
  1210. "help": "poll GPS vendor"},
  1211. "VER": {"command": send_get_ver,
  1212. "help": "poll GPS version"},
  1213. }
  1214. class gps_io(object):
  1215. """All the GPS I/O in one place"
  1216. Three types of GPS I/O
  1217. 1. read only from a file
  1218. 2. read/write through a device
  1219. 3. read only from a gpsd instance
  1220. """
  1221. out = b''
  1222. ser = None
  1223. input_is_device = False
  1224. def __init__(self):
  1225. """Initialize class."""
  1226. Serial = serial
  1227. Serial_v3 = Serial and Serial.VERSION.split('.')[0] >= '3'
  1228. # buffer to hold read data
  1229. self.out = b''
  1230. # open the input: device, file, or gpsd
  1231. if opts['input_file_name'] is not None:
  1232. # check if input file is a file or device
  1233. try:
  1234. mode = os.stat(opts['input_file_name']).st_mode
  1235. except OSError:
  1236. sys.stderr.write('%s: failed to open input file %s\n' %
  1237. (PROG_NAME, opts['input_file_name']))
  1238. sys.exit(1)
  1239. if stat.S_ISCHR(mode):
  1240. # character device, need not be read only
  1241. self.input_is_device = True
  1242. if ((opts['disable'] or opts['enable'] or opts['poll'] or
  1243. opts['oaf_name'])):
  1244. # check that we can write
  1245. if opts['read_only']:
  1246. sys.stderr.write('%s: read-only mode, '
  1247. 'can not send commands\n' % PROG_NAME)
  1248. sys.exit(1)
  1249. if self.input_is_device is False:
  1250. sys.stderr.write('%s: input is plain file, '
  1251. 'can not send commands\n' % PROG_NAME)
  1252. sys.exit(1)
  1253. if opts['target']['server'] is not None:
  1254. # try to open local gpsd
  1255. try:
  1256. self.ser = gps.gpscommon(host=None)
  1257. self.ser.connect(opts['target']['server'],
  1258. opts['target']['port'])
  1259. # alias self.ser.write() to self.write_gpsd()
  1260. self.ser.write = self.write_gpsd
  1261. # ask for raw, not rare, data
  1262. data_out = b'?WATCH={'
  1263. if opts['target']['device'] is not None:
  1264. # add in the requested device
  1265. data_out += (b'"device":"' + opts['target']['device'] +
  1266. b'",')
  1267. data_out += b'"enable":true,"raw":2}\r\n'
  1268. if VERB_RAW <= opts['verbosity']:
  1269. print("sent: ", data_out)
  1270. self.ser.send(data_out)
  1271. except socket.error as err:
  1272. sys.stderr.write('%s: failed to connect to gpsd %s\n' %
  1273. (PROG_NAME, err))
  1274. sys.exit(1)
  1275. elif self.input_is_device:
  1276. # configure the serial connections (the parameters refer to
  1277. # the device you are connecting to)
  1278. # pyserial Ver 3.0+ changes writeTimeout to write_timeout
  1279. # Using the wrong one causes an error
  1280. write_timeout_arg = ('write_timeout'
  1281. if Serial_v3 else 'writeTimeout')
  1282. try:
  1283. self.ser = Serial.Serial(
  1284. baudrate=opts['input_speed'],
  1285. # 8N1 is GREIS default
  1286. bytesize=Serial.EIGHTBITS,
  1287. parity=Serial.PARITY_NONE,
  1288. port=opts['input_file_name'],
  1289. stopbits=Serial.STOPBITS_ONE,
  1290. # read timeout
  1291. timeout=0.05,
  1292. **{write_timeout_arg: 0.5}
  1293. )
  1294. except AttributeError:
  1295. sys.stderr.write('%s: failed to import pyserial\n' % PROG_NAME)
  1296. sys.exit(2)
  1297. except Serial.serialutil.SerialException:
  1298. # this exception happens on bad serial port device name
  1299. sys.stderr.write('%s: failed to open serial port "%s"\n'
  1300. ' Your computer has these serial ports:\n'
  1301. % (PROG_NAME, opts['input_file_name']))
  1302. # print out list of supported ports
  1303. import serial.tools.list_ports as List_Ports
  1304. ports = List_Ports.comports()
  1305. for port in ports:
  1306. sys.stderr.write(" %s: %s\n" %
  1307. (port.device, port.description))
  1308. sys.exit(1)
  1309. # flush input buffer, discarding all its contents
  1310. # pyserial 3.0+ deprecates flushInput() in favor of
  1311. # reset_input_buffer(), but flushInput() is still present.
  1312. self.ser.flushInput()
  1313. else:
  1314. # Read from a plain file of GREIS messages
  1315. try:
  1316. self.ser = open(opts['input_file_name'], 'rb')
  1317. except IOError:
  1318. sys.stderr.write('%s: failed to open input %s\n' %
  1319. (PROG_NAME, opts['input_file_name']))
  1320. sys.exit(1)
  1321. def read(self, read_opts):
  1322. """Read from device, until timeout or expected message."""
  1323. # are we expecting a certain message?
  1324. if gps_model.expect_statement_identifier:
  1325. # assume failure, until we see expected message
  1326. ret_code = 1
  1327. else:
  1328. # not expecting anything, so OK if we did not see it.
  1329. ret_code = 0
  1330. try:
  1331. if read_opts['target']['server'] is not None:
  1332. # gpsd input
  1333. start = gps.monotonic()
  1334. while read_opts['input_wait'] > (gps.monotonic() - start):
  1335. # First priority is to be sure the input buffer is read.
  1336. # This is to prevent input buffer overuns
  1337. if 0 < self.ser.waiting():
  1338. # We have serial input waiting, get it
  1339. # No timeout possible
  1340. # RTCM3 JSON can be over 4.4k long, so go big
  1341. new_out = self.ser.sock.recv(8192)
  1342. if raw is not None:
  1343. # save to raw file
  1344. raw.write(new_out)
  1345. self.out += new_out
  1346. consumed = gps_model.decode_msg(self.out)
  1347. self.out = self.out[consumed:]
  1348. if ((gps_model.expect_statement_identifier and
  1349. (gps_model.expect_statement_identifier ==
  1350. gps_model.last_statement_identifier))):
  1351. # Got what we were waiting for. Done?
  1352. ret_code = 0
  1353. if not read_opts['input_forced_wait']:
  1354. # Done
  1355. break
  1356. elif self.input_is_device:
  1357. # input is a serial device
  1358. start = gps.monotonic()
  1359. while read_opts['input_wait'] > (gps.monotonic() - start):
  1360. # First priority is to be sure the input buffer is read.
  1361. # This is to prevent input buffer overuns
  1362. # pyserial 3.0+ deprecates inWaiting() in favor of
  1363. # in_waiting, but inWaiting() is still present.
  1364. if 0 < self.ser.inWaiting():
  1365. # We have serial input waiting, get it
  1366. # 1024 is comfortably large, almost always the
  1367. # Read timeout is what causes ser.read() to return
  1368. new_out = self.ser.read(1024)
  1369. if raw is not None:
  1370. # save to raw file
  1371. raw.write(new_out)
  1372. self.out += new_out
  1373. consumed = gps_model.decode_msg(self.out)
  1374. self.out = self.out[consumed:]
  1375. if ((gps_model.expect_statement_identifier and
  1376. (gps_model.expect_statement_identifier ==
  1377. gps_model.last_statement_identifier))):
  1378. # Got what we were waiting for. Done?
  1379. ret_code = 0
  1380. if not read_opts['input_forced_wait']:
  1381. # Done
  1382. break
  1383. else:
  1384. # ordinary file, so all read at once
  1385. self.out += self.ser.read()
  1386. if raw is not None:
  1387. # save to raw file
  1388. raw.write(self.out)
  1389. while True:
  1390. consumed = gps_model.decode_msg(self.out)
  1391. self.out = self.out[consumed:]
  1392. if 0 >= consumed:
  1393. break
  1394. except IOError:
  1395. # This happens on a good device name, but gpsd already running.
  1396. # or if USB device unplugged
  1397. sys.stderr.write('%s: failed to read %s\n'
  1398. '%s: Is gpsd already holding the port?\n'
  1399. % (PROG_NAME, PROG_NAME,
  1400. read_opts['input_file_name']))
  1401. return 1
  1402. if 0 < ret_code:
  1403. # did not see the message we were expecting to see
  1404. sys.stderr.write('%s: waited %0.2f seconds for, '
  1405. 'but did not get: %%%s%%\n'
  1406. % (PROG_NAME, read_opts['input_wait'],
  1407. gps_model.expect_statement_identifier))
  1408. return ret_code
  1409. def write_gpsd(self, data):
  1410. """Write data to gpsd daemon."""
  1411. # HEXDATA_MAX = 512, from gps.h, The max hex digits can write.
  1412. # Input data is binary, converting to hex doubles its size.
  1413. # Limit binary data to length 255, so hex data length less than 510.
  1414. if 255 < len(data):
  1415. sys.stderr.write('%s: trying to send %d bytes, max is 255\n'
  1416. % (PROG_NAME, len(data)))
  1417. return 1
  1418. if opts['target']['device'] is not None:
  1419. # add in the requested device
  1420. data_out = b'?DEVICE={"path":"' + opts['target']['device'] + b'",'
  1421. else:
  1422. data_out = b'?DEVICE={'
  1423. # Convert binary data to hex and build the message.
  1424. data_out += b'"hexdata":"' + binascii.hexlify(data) + b'"}\r\n'
  1425. if VERB_RAW <= opts['verbosity']:
  1426. print("sent: ", data_out)
  1427. self.ser.send(data_out)
  1428. return 0
  1429. def usage():
  1430. """Print usage information, and exit."""
  1431. print("usage: %s [-?hrVW] [-c C] [-d D] [-e E] [-f F] [-O O] [-p P]\n"
  1432. " [-R R] [-S S] [-s S] [-v V] [-w W]\n"
  1433. " [server[:port[:device]]]\n\n" % PROG_NAME)
  1434. print('usage: %s [options]\n'
  1435. ' -? print this help\n'
  1436. ' -c C send command C to GPS\n'
  1437. ' -d D disable D\n'
  1438. ' -e E enable E\n'
  1439. ' -f F open F as file/device\n'
  1440. ' default: %s\n'
  1441. ' -h print this help\n'
  1442. ' -O O send OAF file to GPS\n'
  1443. ' -p P send preset GPS command P\n'
  1444. ' -R R save raw data from GPS in file R\n'
  1445. ' -r open file/device read only\n'
  1446. ' default: %s\n'
  1447. ' -S S configure GPS speed to S\n'
  1448. ' -s S set port speed to S\n'
  1449. ' default: %d bps\n'
  1450. ' -V print version\n'
  1451. ' -v V Set verbosity level to V, 0 to 4\n'
  1452. ' default: %d\n'
  1453. ' -W force entire wait time, no exit early\n'
  1454. ' -w W wait time, exit early on -p result\n'
  1455. ' default: %s seconds\n'
  1456. ' [server[:port[:device]]] Connect to gpsd\n'
  1457. ' default port: 2947\n'
  1458. ' default device: None\n'
  1459. '\n'
  1460. 'D and E can be one of:' %
  1461. (PROG_NAME, opts['input_file_name'], opts['raw_file'],
  1462. opts['input_speed'], opts['verbosity'], opts['input_wait'])
  1463. )
  1464. # print list of enable/disable commands
  1465. for item in sorted(gps_model.able_commands.keys()):
  1466. print(" %-12s %s" % (item, gps_model.able_commands[item]["help"]))
  1467. print('\nthe preset GPS command P can be one of:')
  1468. # print list of possible canned commands
  1469. for item in sorted(gps_model.commands.keys()):
  1470. print(" %-12s %s" % (item, gps_model.commands[item]["help"]))
  1471. print('\nOptions can be placed in the ZERKOPTS environment variable.\n'
  1472. 'ZERKOPTS is processed before the CLI options.')
  1473. sys.exit(0)
  1474. # create the GREIS instance
  1475. gps_model = greis()
  1476. if 'ZERKOPTS' in os.environ:
  1477. # grab the ZERKOPTS environment variable for options
  1478. opts['progopts'] = os.environ['ZERKOPTS']
  1479. options = opts['progopts'].split(' ') + sys.argv[1:]
  1480. else:
  1481. options = sys.argv[1:]
  1482. try:
  1483. (options, arguments) = getopt.getopt(options,
  1484. "?c:d:e:f:hrp:s:w:v:O:R:S:WV")
  1485. except getopt.GetoptError as err:
  1486. sys.stderr.write("%s: %s\n"
  1487. "Try '%s -h' for more information.\n" %
  1488. (PROG_NAME, str(err), PROG_NAME))
  1489. sys.exit(2)
  1490. for (opt, val) in options:
  1491. if opt == '-c':
  1492. # command
  1493. opts['command'] = val
  1494. elif opt == '-d':
  1495. # disable
  1496. opts['disable'] = val
  1497. elif opt == '-e':
  1498. # enable
  1499. opts['enable'] = val
  1500. elif opt == '-f':
  1501. # file input
  1502. opts['input_file_name'] = val
  1503. elif opt in ('-h', '-?'):
  1504. # help
  1505. usage()
  1506. elif opt == '-p':
  1507. # preprogrammed command
  1508. opts['poll'] = val
  1509. elif opt == '-r':
  1510. # read only
  1511. opts['read_only'] = True
  1512. elif opt == '-s':
  1513. # serial port speed
  1514. opts['input_speed'] = int(val)
  1515. if opts['input_speed'] not in gps_model.speeds:
  1516. sys.stderr.write('%s: -s invalid speed %s\n' %
  1517. (PROG_NAME, opts['input_speed']))
  1518. sys.exit(1)
  1519. elif opt == '-w':
  1520. # max wait time, seconds
  1521. opts['input_wait'] = float(val)
  1522. elif opt in '-v':
  1523. # verbosity level
  1524. opts['verbosity'] = int(val)
  1525. elif opt in '-O':
  1526. # OAF .jpo file
  1527. opts['oaf_name'] = val
  1528. elif opt in '-R':
  1529. # raw log file
  1530. opts['raw_file'] = val
  1531. elif opt in '-S':
  1532. # set GPS serial port speed
  1533. opts['set_speed'] = int(val)
  1534. if opts['set_speed'] not in gps_model.speeds:
  1535. sys.stderr.write('%s: -S invalid speed %s\n' %
  1536. (PROG_NAME, opts['set_speed']))
  1537. sys.exit(1)
  1538. elif opt == '-W':
  1539. # forced wait, no early exit on command completion
  1540. opts['input_forced_wait'] = True
  1541. elif opt == '-V':
  1542. # version
  1543. sys.stderr.write('zerk: Version %s\n' % gps_version)
  1544. sys.exit(0)
  1545. if opts['input_file_name'] is None:
  1546. # no input file given
  1547. # default to local gpsd
  1548. opts['target']['server'] = "localhost"
  1549. opts['target']['port'] = gps.GPSD_PORT
  1550. opts['target']['device'] = None
  1551. if arguments:
  1552. # server[:port[:device]]
  1553. arg_parts = arguments[0].split(':')
  1554. opts['target']['server'] = arg_parts[0]
  1555. if 1 < len(arg_parts):
  1556. opts['target']['port'] = arg_parts[1]
  1557. if 2 < len(arg_parts):
  1558. opts['target']['device'] = arg_parts[2]
  1559. elif arguments:
  1560. sys.stderr.write('%s: Both input file and server specified\n' % PROG_NAME)
  1561. sys.exit(1)
  1562. if VERB_PROG <= opts['verbosity']:
  1563. # dump all options
  1564. print('Options:')
  1565. for option in sorted(opts):
  1566. print(" %s: %s" % (option, opts[option]))
  1567. # done parsing arguments from environment and CLI
  1568. try:
  1569. # raw log file requested?
  1570. raw = None
  1571. if opts['raw_file']:
  1572. try:
  1573. raw = open(opts['raw_file'], 'w')
  1574. except IOError:
  1575. sys.stderr.write('%s: failed to open raw file %s\n' %
  1576. (PROG_NAME, opts['raw_file']))
  1577. sys.exit(1)
  1578. # create the I/O instance
  1579. io_handle = gps_io()
  1580. # keep it simple, only one of -O, -c -d -e or -S
  1581. if opts['oaf_name'] is not None:
  1582. # parse an OAF file
  1583. try:
  1584. oaf_root = xml.etree.ElementTree.parse(opts['oaf_name']).getroot()
  1585. oaf = dict()
  1586. for tag in ('id', 'oaf', 'hash'):
  1587. oaf[tag] = oaf_root.find(tag).text
  1588. oaf['oaf'] = oaf['oaf'].split('\n')
  1589. if VERB_PROG <= opts['verbosity']:
  1590. print(oaf)
  1591. except xml.etree.ElementTree.ParseError:
  1592. sys.stderr.write('%s: failed to parse OAF "%s"\n'
  1593. % (PROG_NAME, opts['oaf_name']))
  1594. sys.exit(1)
  1595. except IOError:
  1596. sys.stderr.write('%s: failed to read OAF "%s"\n'
  1597. % (PROG_NAME, opts['oaf_name']))
  1598. sys.exit(1)
  1599. # calculate hash
  1600. oaf_s = '\n'.join(oaf['oaf'])
  1601. hash_s = hashlib.sha1(oaf_s).hexdigest()
  1602. if hash_s != oaf['hash']:
  1603. sys.stderr.write('%s: OAF bad hash "%s", s/b %s\n'
  1604. % (PROG_NAME, hash_s, oaf['hash']))
  1605. sys.exit(1)
  1606. # TODO: probably should check the ID first...
  1607. # TODO: prolly should send one command per handshake
  1608. # blasting all commands at once, seems to not work reliably
  1609. for command in oaf['oaf']:
  1610. time.sleep(0.1) # wait 0.1 seconds each
  1611. gps_model.gps_send(command)
  1612. # this will detect when it is all done
  1613. gps_model.gps_send(b'%DONE%')
  1614. gps_model.expect_statement_identifier = 'DONE'
  1615. elif opts['command'] is not None:
  1616. # zero length is OK to send
  1617. if 1 < len(opts['command']) and '%' != opts['command'][0]:
  1618. # add ID, if missing
  1619. gps_model.expect_statement_identifier = 'CMD'
  1620. opts['command'] = "%CMD%" + opts['command']
  1621. # add trailing new line
  1622. opts['command'] += "\n"
  1623. if VERB_QUIET < opts['verbosity']:
  1624. sys.stderr.write('%s: command %s\n' % (PROG_NAME, opts['command']))
  1625. gps_model.gps_send(opts['command'])
  1626. elif opts['disable'] is not None:
  1627. if VERB_QUIET < opts['verbosity']:
  1628. sys.stderr.write('%s: disable %s\n' % (PROG_NAME, opts['disable']))
  1629. if opts['disable'] in gps_model.able_commands:
  1630. command = gps_model.able_commands[opts['disable']]
  1631. command["command"](gps_model, 0)
  1632. else:
  1633. sys.stderr.write('%s: disable %s not found\n' %
  1634. (PROG_NAME, opts['disable']))
  1635. sys.exit(1)
  1636. elif opts['enable'] is not None:
  1637. if VERB_QUIET < opts['verbosity']:
  1638. sys.stderr.write('%s: enable %s\n' % (PROG_NAME, opts['enable']))
  1639. if opts['enable'] in gps_model.able_commands:
  1640. command = gps_model.able_commands[opts['enable']]
  1641. command["command"](gps_model, 1)
  1642. else:
  1643. sys.stderr.write('%s: enable %s not found\n' %
  1644. (PROG_NAME, opts['enable']))
  1645. sys.exit(1)
  1646. elif opts['poll'] is not None:
  1647. if VERB_QUIET < opts['verbosity']:
  1648. sys.stderr.write('%s: poll %s\n' % (PROG_NAME, opts['poll']))
  1649. if opts['poll'] in gps_model.commands:
  1650. command = gps_model.commands[opts['poll']]
  1651. command["command"](gps_model)
  1652. else:
  1653. sys.stderr.write('%s: poll %s not found\n' %
  1654. (PROG_NAME, opts['poll']))
  1655. sys.exit(1)
  1656. elif opts['set_speed'] is not None:
  1657. gps_model.send_set_speed(opts['set_speed'])
  1658. exit_code = io_handle.read(opts)
  1659. if ((VERB_RAW <= opts['verbosity']) and io_handle.out):
  1660. # dump raw left overs
  1661. print("Left over data:")
  1662. print(io_handle.out)
  1663. sys.stdout.flush()
  1664. io_handle.ser.close()
  1665. except KeyboardInterrupt:
  1666. print('')
  1667. exit_code = 1
  1668. sys.exit(exit_code)
  1669. # vim: set expandtab shiftwidth=4