zerk.in 65 KB

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