packet.py.in 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. # packet.py - recognize GPS packet types
  2. # @GENERATED@
  3. #
  4. # This file is Copyright 2019 by the GPSD project
  5. # SPDX-License-Identifier: BSD-2-Clause
  6. #
  7. # This code runs compatibly under Python 2 and 3.x for x >= 2.
  8. # Preserve this property!
  9. #
  10. # -*- coding: utf-8 -*-
  11. """Python binding of the libgpsd module for recognizing GPS packets.
  12. The new() function returns a new packet-lexer instance. Lexer instances
  13. have two methods:
  14. get() takes a file descriptor argument and returns a tuple consisting of
  15. the integer packet type and string packet value. On end of file it returns
  16. (-1, '').
  17. reset() resets the packet-lexer to its initial state.
  18. The module also has a register_report() function that accepts a callback
  19. for debug message reporting. The callback will get two arguments, the error
  20. level of the message and the message itself.
  21. """
  22. from __future__ import absolute_import, print_function
  23. import ctypes
  24. import ctypes.util
  25. import os
  26. import os.path
  27. import sys
  28. import gps # For gps.__path__
  29. import gps.misc
  30. # Packet types and Logging levels extracted from gpsd.h
  31. @PYPACKETH@
  32. class PacketLibraryNotFoundError(Exception):
  33. """Error loading packet library."""
  34. pass
  35. def importado():
  36. """
  37. Load the packet library or throw a PacketLibraryNotFoundError trying.
  38. See below for search order.
  39. find_library() looks in: LD_LIBRARY_PATH, DYLD_LIBRARY_PATH,
  40. $home/lib, /.usr/local/lib, /usr/lib, /lib
  41. Returns the library handle."""
  42. packet_name = '@GPSPACKET@'
  43. packet_dirs = [] # places to look
  44. lib_dir = '@LIBDIR@'
  45. # First look in the directory containing this 'gps' package, possibly
  46. # following a symlink in the process.
  47. # This is the normal location within the build tree. It is expected
  48. # to fail when running the installed version.
  49. packet_dirs.append(os.path.dirname(os.path.realpath(gps.__path__[0])))
  50. # Next look in the library install directory.
  51. # This is the expected location when running the installed version.
  52. packet_dirs.append(os.path.realpath(lib_dir))
  53. # Form full paths to candidates so far
  54. packet_paths = [os.path.join(os.path.abspath(x), packet_name)
  55. for x in packet_dirs]
  56. # Finally try find_library().
  57. # find_library() looks for bare library name, using dlopen()
  58. # May, or may not, return a full path. Either way use as is.
  59. #
  60. # linux dlopen() looks in:
  61. # LD_LIBRARY_PATH,
  62. # paths in /etc/ld.so.cache,
  63. # /lib(64) and /usr/lib(64)
  64. #
  65. # macOS dlopen() looks in:
  66. # LD_LIBRARY_PATH,
  67. # DYLD_LIBRARY_PATH,
  68. # current working directory,
  69. # DYLD_FALLBACK_LIBRARY_PATH (default: $HOME/lib:/usr/local/lib:/usr/lib)
  70. # Note that some recent macOS versions have stopped honoring *_LIBRARY_PATH,
  71. # for security reasons.
  72. #
  73. # Linux:
  74. # find_library() does not usually return a full path.
  75. # LoadLibrary() can use a full path, or whatever find_library() returned.
  76. #
  77. # macOS:
  78. # find_library() returns a full path unless lib in current directory
  79. # find_library() returns no full path if lib in current directory
  80. # But LoadLibrary() always needs a full path
  81. #
  82. packet_path = ctypes.util.find_library('gpsdpacket')
  83. if packet_path:
  84. packet_paths.append(packet_path)
  85. for packet_path in packet_paths:
  86. try:
  87. if sys.flags.verbose:
  88. print('try_packet_lib: %s' % packet_path, file=sys.stderr)
  89. lib = ctypes.cdll.LoadLibrary(packet_path)
  90. # get the library version from the library
  91. gpsd_version = ctypes.c_char_p.in_dll(lib, "gpsd_version").value
  92. gpsd_version = gps.polystr(gpsd_version)
  93. if '@VERSION@' != gpsd_version:
  94. sys.stderr.write("WARNING: got library version %s, "
  95. "expected %s\n" %
  96. (gpsd_version, '@VERSION@'))
  97. return lib
  98. except OSError:
  99. pass
  100. raise PacketLibraryNotFoundError("Can't find packet library")
  101. _loaded = None
  102. _packet = importado()
  103. _lexer_size = ctypes.c_size_t.in_dll(_packet, "fvi_size_lexer")
  104. LEXER_SIZE = _lexer_size.value
  105. _buffer_size = ctypes.c_size_t.in_dll(_packet, "fvi_size_buffer").value
  106. REPORTER = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_char_p)
  107. class GpsdErrOutT(ctypes.Structure):
  108. '''Used in gps.packet:register_report() to set logging callback.'''
  109. # pylint: disable-msg=R0903
  110. _fields_ = [('debug', ctypes.c_int),
  111. ('report', REPORTER),
  112. ('label', ctypes.c_char_p)]
  113. class lexer_t(ctypes.Structure):
  114. '''Used in gps.packet:lexer.get() to pass in data and pull
  115. out length, packet type, packet, and another datum.'''
  116. # pylint: disable-msg=R0903
  117. _fields_ = [
  118. ('packet_type', ctypes.c_int),
  119. ('state', ctypes.c_uint),
  120. ('length', ctypes.c_size_t),
  121. ('inbuffer', ctypes.c_ubyte * _buffer_size),
  122. ('inbuflen', ctypes.c_size_t),
  123. ('inbufptr', ctypes.c_char_p),
  124. ('outbuffer', ctypes.c_ubyte * _buffer_size),
  125. ('outbuflen', ctypes.c_size_t),
  126. ('char_counter', ctypes.c_ulong),
  127. ('retry_counter', ctypes.c_ulong),
  128. ('counter', ctypes.c_uint),
  129. ('errout', GpsdErrOutT),
  130. ]
  131. def new():
  132. """new() -> new packet-self object"""
  133. return Lexer()
  134. def register_report(reporter):
  135. """register_report(callback)
  136. callback must be a callable object expecting a string as parameter."""
  137. global _loaded
  138. if callable(reporter):
  139. _loaded.errout.report = REPORTER(reporter)
  140. class Lexer():
  141. """GPS packet lexer object
  142. Fetch a single packet from file descriptor
  143. """
  144. pointer = None
  145. def __init__(self):
  146. global _loaded
  147. _packet.ffi_Lexer_init.restype = ctypes.POINTER(lexer_t)
  148. self.pointer = _packet.ffi_Lexer_init()
  149. _loaded = self.pointer.contents
  150. def get(self, file_handle):
  151. """Get a packet from a file descriptor."""
  152. global _loaded
  153. _packet.packet_get.restype = ctypes.c_int
  154. _packet.packet_get.argtypes = [ctypes.c_int, ctypes.POINTER(lexer_t)]
  155. length = _packet.packet_get(file_handle, self.pointer)
  156. _loaded = self.pointer.contents
  157. packet = ''
  158. for octet in range(_loaded.outbuflen):
  159. packet += chr(_loaded.outbuffer[octet])
  160. return [length,
  161. _loaded.packet_type,
  162. gps.misc.polybytes(packet),
  163. _loaded.char_counter]
  164. def reset(self):
  165. """Reset the packet self to ground state."""
  166. _packet.ffi_Lexer_init.restype = None
  167. _packet.ffi_Lexer_init.argtypes = [ctypes.POINTER(lexer_t)]
  168. _packet.ffi_Lexer_init(self.pointer)
  169. # vim: set expandtab shiftwidth=4