123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- #!@PYSHEBANG@
- # @GENERATED@
- # -*- coding: utf-8 -*-
- # This file is Copyright 2010 by the GPSD project
- # SPDX-License-Identifier: BSD-2-clause
- # This code runs compatibly under Python 2 and 3.x for x >= 2.
- # Preserve this property!
- #
- """gpscsv -- convert gpsd JSON strams into csv files."""
- from __future__ import print_function
- import argparse
- import socket
- import sys
- import time # for time.time()
- def _do_one_line(data):
- """dump one report line."""
- global options
- if options.json_fields is None:
- # no fields specified, use the 1st ones found
- fields = data.keys()
- options.json_fields = []
- for f in fields:
- if isinstance(data[f], (dict, list)):
- # skip dictionay fields
- continue
- options.json_fields.append(f)
- if 0 < options.header:
- f = options.separator.join(options.json_fields)
- if 2 == options.header:
- f = '# ' + f
- print(f)
- out = []
- for fld in options.json_fields:
- if 'time' == fld and options.cvtisotime:
- # convert 2020-08-17T23:58:01.000Z to 1597708682.0
- data[fld] = gps.isotime(data[fld])
- if fld not in data or 'null' == data[fld]:
- out.append('')
- else:
- out.append(str(data[fld]))
- print(options.separator.join(out))
- # pylint wants local modules last
- try:
- import gps
- except ImportError as e:
- sys.stderr.write(
- "%s: can't load Python gps libraries -- check PYTHONPATH.\n" %
- (sys.argv[0]))
- sys.stderr.write("%s\n" % e)
- sys.exit(1)
- gps_version = '@VERSION@'
- if gps.__version__ != gps_version:
- sys.stderr.write("%s: ERROR: need gps module version %s, got %s\n" %
- (sys.argv[0], gps_version, gps.__version__))
- sys.exit(1)
- description = 'Convert one gpsd JSON message class to csv format.'
- usage = '%(prog)s [OPTIONS] [host[:port[:device]]]'
- epilog = (' -c ALMANAC to dump the Almanacs from Subframes 4/5.\n'
- ' -c HEALTH to dump health data from Subframe 4, page 25\n'
- ' -c HEALTH2 to dump health data from Subframe 5, page 25\n'
- ' -c IONO to dump the Iono/UTC data from Subframe 4, page 18\n'
- ' -c NMCT to dump the ERD data from Subframe 4, page 13\n'
- ' -c SAT to dump the satellite records from the SKY messages.\n'
- ' -c SUBFRAME1 to dump Ephemeris1 from Subframe 1\n'
- ' -c SUBFRAME2 to dump Ephemeris2 from Subframe 2\n'
- ' -c SUBFRAME3 to dump Ephermeris3 from Subframe 3\n\n'
- 'BSD terms apply: see the file COPYING in the distribution root'
- ' for details.')
- parser = argparse.ArgumentParser(
- description=description,
- epilog=epilog,
- formatter_class=argparse.RawDescriptionHelpFormatter,
- usage=usage)
- parser.add_argument(
- '-c',
- '--class',
- dest='mclass', # class is a reserved word
- default='TPV',
- help='Message class to process. [Default %(default)s)]'
- )
- parser.add_argument(
- '--cvt-isotime',
- dest='cvtisotime',
- default=False,
- action="store_true",
- help='Convert ISO time to UNIX time'
- )
- parser.add_argument(
- '-D',
- '--debug',
- dest='debug',
- default=0,
- type=int,
- help='Set level of debug. Must be integer. [Default %(default)s)]'
- )
- parser.add_argument(
- '--device',
- dest='device',
- default='',
- help='The device to connect. [Default %(default)s)]'
- )
- parser.add_argument(
- '-f',
- '--fields',
- dest='fields',
- default=None,
- help='Fields to process. '' for all. Default varies by class.'
- )
- parser.add_argument(
- '--header',
- dest='header',
- default=1,
- type=int,
- help='0: No header, 1: fields, 2: comment. [Default %(default)s)]'
- )
- parser.add_argument(
- '--host',
- dest='host',
- default='localhost',
- help='The host to connect. [Default %(default)s)]'
- )
- parser.add_argument(
- '-n',
- '--count',
- dest='count',
- default=0,
- type=int,
- help='Count of messages to parse. 0 to disable. [Default %(default)s)]'
- )
- parser.add_argument(
- '--port',
- dest='port',
- default=gps.GPSD_PORT,
- help='The port to connect. [Default %(default)s)]'
- )
- parser.add_argument(
- '--separator',
- dest='separator',
- default=',',
- type=str,
- help='CSV field separator character. [Default %(default)s)]'
- )
- parser.add_argument(
- '-V', '--version',
- action='version',
- version="%(prog)s: Version " + gps_version + "\n",
- help='Output version to stderr, then exit'
- )
- parser.add_argument(
- '-x',
- '--seconds',
- dest='seconds',
- default=0,
- type=int,
- help='Seconds of messages to parse. 0 to disable. [Default %(default)s)]'
- )
- parser.add_argument(
- 'target',
- nargs='?',
- help='[host[:port[:device]]]'
- )
- options = parser.parse_args()
- # the options host, port, device are set by the defaults
- if options.target:
- # override host, port and device with target
- arg = options.target.split(':')
- len_arg = len(arg)
- if len_arg == 1:
- (options.host,) = arg
- elif len_arg == 2:
- (options.host, options.port) = arg
- elif len_arg == 3:
- (options.host, options.port, options.device) = arg
- else:
- parser.print_help()
- sys.exit(0)
- # Fields to parse
- # Python dicts are unorderd, so try to clean things up a little
- options.json_fields = None
- if options.fields is None:
- default_fields = {'ALMANAC': ('TOW17', 'tSV', 'ID', 'Health', 'e',
- 'toa', 'deltai', 'Omegad', 'sqrtA',
- 'Omega0', 'omega', 'M0', 'af0', 'af1'),
- 'HEALTH': ('TOW17', 'tSV',
- 'SV1', 'SV2', 'SV3', 'SV4', 'SV5',
- 'SV6', 'SV7', 'SV8', 'SV9', 'SV10',
- 'SV11', 'SV12', 'SV13', 'SV14', 'SV15',
- 'SV16', 'SV17', 'SV18', 'SV19', 'SV20',
- 'SV21', 'SV22', 'SV23', 'SV24', 'SV25',
- 'SV26', 'SV27', 'SV28', 'SV29', 'SV30',
- 'SV31', 'SV32',
- 'SVH25', 'SVH26', 'SVH27', 'SVH28', 'SVH29',
- 'SVH30', 'SVH31', 'SVH32'),
- 'HEALTH2': ('TOW17', 'tSV', 'toa', 'WNa',
- 'SVH1', 'SVH2', 'SVH3', 'SVH4', 'SVH5',
- 'SVH6', 'SVH7', 'SVH8', 'SVH9', 'SVH10',
- 'SVH11', 'SVH12', 'SVH13', 'SVH14', 'SVH15',
- 'SVH16', 'SVH17', 'SVH18', 'SVH19', 'SVH20',
- 'SVH21', 'SVH22', 'SVH23', 'SVH24'),
- 'IONO': ('TOW17', 'tSV', 'WNt', 'tot',
- 'lsf', 'ls', 'WNlsf', 'DN',
- 'A0', 'A1', 'a0', 'a1', 'a2', 'a3',
- 'b0', 'b1', 'b2', 'b3'),
- 'NMCT': ('TOW17', 'tSV', 'ai',
- 'ERD1', 'ERD2', 'ERD3', 'ERD4', 'ERD5',
- 'ERD6', 'ERD7', 'ERD8', 'ERD9', 'ERD10',
- 'ERD11', 'ERD12', 'ERD13', 'ERD14', 'ERD15',
- 'ERD16', 'ERD17', 'ERD18', 'ERD19', 'ERD20',
- 'ERD21', 'ERD22', 'ERD23', 'ERD24', 'ERD25',
- 'ERD26', 'ERD27', 'ERD28', 'ERD29', 'ERD30',
- 'ERD31'),
- 'SAT': ('time', 'gnssid', 'svid', 'PRN', 'az', 'el',
- 'ss', 'used', 'health'),
- 'SKY': ('time', 'xdop', 'ydop', 'vdop', 'tdop',
- 'hdop', 'gdop', 'pdop'),
- 'SUBFRAME1': ('TOW17', 'tSV', 'IODC', 'WN', 'ura',
- 'hlth', 'L2', 'L2P', 'Tgd',
- 'toc', 'af0', 'af1', 'af2'),
- 'SUBFRAME2': ('TOW17', 'tSV', 'IODE', 'M0',
- 'deltan', 'e', 'sqrtA', 'FIT', 'AODO',
- 'Crs', 'Cuc', 'Cus', 'toe'),
- 'SUBFRAME3': ('TOW17', 'tSV', 'IODE',
- 'Crc', 'Cic', 'Cis', 'Omega0', 'i0',
- 'omega', 'Omegad', 'IDOT'),
- 'TPV': ('time', 'lat', 'lon', 'altHAE'),
- }
- # None specified, use defaults, if they exist
- if options.mclass in default_fields:
- options.json_fields = default_fields[options.mclass]
- else:
- # autodetect, read one message, use those fields
- options.json_fields = None
- elif '' == options.fields:
- # autodetect, read one message, use those fields
- options.json_fields = None
- else:
- options.json_fields = options.fields.split(',')
- options.frames = None
- if 'ALMANAC' == options.mclass:
- options.mclass = 'SUBFRAME'
- options.subclass = 'ALMANAC'
- options.frames = [4, 5]
- elif 'HEALTH' == options.mclass:
- options.mclass = 'SUBFRAME'
- options.subclass = 'HEALTH'
- options.frames = [4]
- elif 'HEALTH2' == options.mclass:
- options.mclass = 'SUBFRAME'
- options.subclass = 'HEALTH2'
- options.frames = [5]
- elif 'IONO' == options.mclass:
- options.mclass = 'SUBFRAME'
- options.subclass = 'IONO'
- options.frames = [4]
- elif 'NMCT' == options.mclass:
- # Note: ai is probably 01, which means the NMCT data is encypted.
- options.mclass = 'SUBFRAME'
- options.subclass = 'NMCT'
- options.frames = [4]
- elif 'SAT' == options.mclass:
- options.mclass = 'SKY'
- options.subclass = 'SAT'
- elif 'SUBFRAME1' == options.mclass:
- options.mclass = 'SUBFRAME'
- options.subclass = 'EPHEM1'
- options.frames = [1]
- elif 'SUBFRAME2' == options.mclass:
- options.mclass = 'SUBFRAME'
- options.subclass = 'EPHEM2'
- options.frames = [2]
- elif 'SUBFRAME3' == options.mclass:
- options.mclass = 'SUBFRAME'
- options.subclass = 'EPHEM3'
- options.frames = [3]
- else:
- options.subclass = None
- try:
- session = gps.gps(host=options.host, port=options.port,
- verbose=options.debug)
- except socket.error:
- sys.stderr.write("gpscsv: Could not connect to gpsd daemon\n")
- sys.exit(1)
- session.stream(gps.WATCH_ENABLE | gps.WATCH_SCALED, devpath=options.device)
- # top line is headings
- if options.json_fields is not None and 0 < options.header:
- f = options.separator.join(options.json_fields)
- if 2 == options.header:
- f = '# ' + f
- print(f)
- count = 0
- if 0 < options.seconds:
- end_seconds = time.time() + options.seconds
- else:
- end_seconds = 0
- try:
- while True:
- report = session.next()
- if report['class'] != options.mclass:
- continue
- if 'SAT' == options.subclass:
- # grab the sats, one at a time
- for sat in report['satellites']:
- subreport = {'time': report['time']}
- subreport.update(sat)
- _do_one_line(subreport)
- elif 'SUBFRAME' == options.mclass:
- if report['frame'] not in options.frames:
- continue
- if options.subclass not in report:
- # Not all subframe 4/5 have ALMANAC
- continue
- subreport = {'TOW17': report['TOW17'], 'tSV': report['tSV']}
- subreport.update(report[options.subclass])
- _do_one_line(subreport)
- else:
- _do_one_line(report)
- if 0 < options.count:
- count += 1
- if count >= options.count:
- break
- if 0 < options.seconds:
- if time.time() > end_seconds:
- break
- except KeyboardInterrupt:
- # caught control-C
- print()
- sys.exit(1)
|