diff options
| author | Brent Stapleton <brent.stapleton@ettus.com> | 2018-07-02 18:25:02 -0700 | 
|---|---|---|
| committer | Martin Braun <martin.braun@ettus.com> | 2018-07-06 15:00:20 -0700 | 
| commit | 6eaf521d2b706f80d77e9400803925efb4dbf048 (patch) | |
| tree | f75cfab264d26332136cdc161fd352cc10b07a9c /mpm/python/usrp_mpm | |
| parent | 9c2e1907e96d708f71de92faa7a1501d236baf8d (diff) | |
| download | uhd-6eaf521d2b706f80d77e9400803925efb4dbf048.tar.gz uhd-6eaf521d2b706f80d77e9400803925efb4dbf048.tar.bz2 uhd-6eaf521d2b706f80d77e9400803925efb4dbf048.zip | |
mpm: n3xx: Factor out GPSd Iface functions
- Refactoring GPSd interface to be instead wrapped by a
GPSDIfaceExtension class. This class will faciliate "extending" an
object, allowing that object to call the GPSDIfaceExtension methods as
their own.
- New MPM devices (or whatever else) can now use the GPSDIfaceExtension
class instead of writing their own GPSDIface handling functions.
- N3XX now instantiates a GPSDIfaceExtension object, and extends
itself. This means that an n3xx object can call the `get_gps_time`
method as its own, for example.
- N3XX must get through initialization in order for the GPSd methods
to be registered.
Diffstat (limited to 'mpm/python/usrp_mpm')
| -rw-r--r-- | mpm/python/usrp_mpm/gpsd_iface.py | 108 | ||||
| -rw-r--r-- | mpm/python/usrp_mpm/periph_manager/n3xx.py | 91 | 
2 files changed, 129 insertions, 70 deletions
| diff --git a/mpm/python/usrp_mpm/gpsd_iface.py b/mpm/python/usrp_mpm/gpsd_iface.py index 66abfd4bd..d2da27975 100644 --- a/mpm/python/usrp_mpm/gpsd_iface.py +++ b/mpm/python/usrp_mpm/gpsd_iface.py @@ -1,15 +1,18 @@  # -# Copyright 2017 Ettus Research, a National Instruments Company +# Copyright 2017-2018 Ettus Research, a National Instruments Company  #  # SPDX-License-Identifier: GPL-3.0-or-later  #  """  GPS service daemon (GPSd) interface class  """ + +from __future__ import print_function  import socket  import json  import time  import select +import datetime  from usrp_mpm.mpmlog import get_logger @@ -125,6 +128,108 @@ class GPSDIface(object):          return result if (resp_class == "") else result.get(resp_class, [{}])[0] +class GPSDIfaceExtension(object): +    """ +    Wrapper class that facilitates the 'extension' of a `context` object. The +    intention here is for a object to instantiate a GPSDIfaceExtension object, +    then call the `extend` method with `context` object as an argument. This +    will then add the GPSDIfaceExtension methods to the `context` object's +    methods. The `context` object can then call the convenience functions to +    retrieve GPS information from GPSd. +    For example: + +    class foo: +        def __init__(self): +            self.gps_ext = GPSDIfaceExtension() +            self.gps_ext.extend(self) +            # The GPSDIfaceExtension methods are now registered with foo, so +            # we can call `get_gps_time` +            print(self.get_gps_time()) +    """ +    def __init__(self): +        self._gpsd_iface = GPSDIface() +        self._gpsd_iface.open() +        self._log = self._gpsd_iface.log + +    def __del__(self): +        self._gpsd_iface.close() + +    def extend(self, context): +        """Register the GSPDIfaceExtension object's public function with `context`""" +        new_methods = [method_name for method_name in dir(self) +                       if not method_name.startswith('_') \ +                       and callable(getattr(self, method_name)) \ +                       and method_name != "extend"] +        for method_name in new_methods: +            new_method = getattr(self, method_name) +            self._log.trace("%s: Adding %s method", context, method_name) +            setattr(context, method_name, new_method) +        return new_methods + +    def get_gps_time_sensor(self): +        """ +        Retrieve the GPS time using a TPV response from GPSd, and returns as a sensor dict +        This time is not high accuracy. +        """ +        self._log.trace("Polling GPS time results from GPSD") +        # Read responses from GPSD until we get a non-trivial mode +        self._gpsd_iface.watch_query() +        while True: +            gps_info = self._gpsd_iface.get_gps_info(resp_class='tpv', timeout=15) +            self._log.trace("GPS info: {}".format(gps_info)) +            if gps_info.get("mode", 0) > 0: +                break +        self._gpsd_iface.stop_query() +        time_str = gps_info.get("time", "") +        self._log.trace("GPS time string: {}".format(time_str)) +        time_dt = datetime.datetime.strptime(time_str, "%Y-%m-%dT%H:%M:%S.%fZ") +        self._log.trace("GPS datetime: {}".format(time_dt)) +        epoch_dt = datetime.datetime(1970, 1, 1) +        gps_time = int((time_dt - epoch_dt).total_seconds()) +        return { +            'name': 'gps_time', +            'type': 'INTEGER', +            'unit': 'seconds', +            'value': str(gps_time), +        } + +    def get_gps_tpv_sensor(self): +        """Get a TPV response from GPSd as a sensor dict""" +        self._log.trace("Polling GPS TPV results from GPSD") +        # Read responses from GPSD until we get a non-trivial mode +        self._gpsd_iface.watch_query() +        while True: +            gps_info = self._gpsd_iface.get_gps_info(resp_class='tpv', timeout=15) +            self._log.trace("GPS info: {}".format(gps_info)) +            if gps_info.get("mode", 0) > 0: +                break +        self._gpsd_iface.stop_query() +        # Return the JSON'd results +        gps_tpv = json.dumps(gps_info) +        return { +            'name': 'gps_tpv', +            'type': 'STRING', +            'unit': '', +            'value': gps_tpv, +        } + +    def get_gps_sky_sensor(self): +        """Get a SKY response from GPSd as a sensor dict""" +        self._log.trace("Polling GPS SKY results from GPSD") +        # Just get the first SKY result +        self._gpsd_iface.watch_query() +        gps_info = self._gpsd_iface.get_gps_info(resp_class='sky', timeout=15) +        self._gpsd_iface.stop_query() +        # Return the JSON'd results +        gps_sky = json.dumps(gps_info) +        return { +            'name': 'gps_sky', +            'type': 'STRING', +            'unit': '', +            'value': gps_sky, +        } + +  def main():      """Test functionality of the GPSDIface class"""      # Do some setup @@ -145,6 +250,7 @@ def main():      print("Sample result: {}".format(result))      print("TPV: {}".format(tpv_result))      print("SKY: {}".format(sky_result)) +    #TODO Add GPSDIfaceExtension code  if __name__ == "__main__": diff --git a/mpm/python/usrp_mpm/periph_manager/n3xx.py b/mpm/python/usrp_mpm/periph_manager/n3xx.py index a90e39b27..be21dd0b4 100644 --- a/mpm/python/usrp_mpm/periph_manager/n3xx.py +++ b/mpm/python/usrp_mpm/periph_manager/n3xx.py @@ -14,10 +14,11 @@ import shutil  import subprocess  import json  import datetime +import re  import threading  from six import iteritems, itervalues  from usrp_mpm.cores import WhiteRabbitRegsControl -from usrp_mpm.gpsd_iface import GPSDIface +from usrp_mpm.gpsd_iface import GPSDIfaceExtension  from usrp_mpm.periph_manager import PeriphManagerBase  from usrp_mpm.mpmtypes import SID  from usrp_mpm.mpmutils import assert_compat_number, str2bool, poll_with_timeout @@ -119,9 +120,6 @@ class n3xx(PeriphManagerBase):      mboard_sensor_callback_map = {          'ref_locked': 'get_ref_lock_sensor',          'gps_locked': 'get_gps_lock_sensor', -        'gps_time': 'get_gps_time_sensor', -        'gps_tpv': 'get_gps_tpv_sensor', -        'gps_sky': 'get_gps_sky_sensor',          'temp': 'get_temp_sensor',          'fan': 'get_fan_sensor',      } @@ -192,6 +190,7 @@ class n3xx(PeriphManagerBase):          self._time_source = None          self._available_endpoints = list(range(256))          self._bp_leds = None +        self._gpsd = None          super(n3xx, self).__init__(args)          if not self._device_initialized:              # Don't try and figure out what's going on. Just give up. @@ -322,6 +321,8 @@ class n3xx(PeriphManagerBase):          self._ext_clock_freq = None          self._init_ref_clock_and_time(args)          self._init_meas_clock() +        # Init GPSd iface and GPS sensors +        self._init_gps_sensors()          # Init CHDR transports          self._xport_mgrs = {              'udp': N3xxXportMgrUDP(self.log.getChild('UDP')), @@ -338,6 +339,22 @@ class n3xx(PeriphManagerBase):          # Init complete.          self.log.debug("Device info: {}".format(self.device_info)) +    def _init_gps_sensors(self): +        "Init and register the GPSd Iface and related sensor functions" +        self.log.trace("Initializing GPSd interface") +        self._gpsd = GPSDIfaceExtension() +        new_methods = self._gpsd.extend(self) +        for method_name in new_methods: +            try: +                # Extract the sensor name from the getter +                sensor_name = re.search(r"get_.*_sensor", method_name).string +                # Register it with the MB sensor framework +                self.mboard_sensor_callback_map[sensor_name] = method_name +                self.log.trace("Adding %s sensor function", sensor_name) +            except AttributeError: +                # re.search will return None is if can't find the sensor name +                self.log.warning("Error while registering sensor function: %s", method_name) +      ###########################################################################      # Session init and deinit      ########################################################################### @@ -710,6 +727,7 @@ class n3xx(PeriphManagerBase):      ###########################################################################      # Sensors +    # Note: GPS sensors are registered at runtime      ###########################################################################      def get_ref_lock_sensor(self):          """ @@ -786,71 +804,6 @@ class n3xx(PeriphManagerBase):              'value': str(gps_locked).lower(),          } -    def get_gps_time_sensor(self): -        """ -        Calculates GPS time using a TPV response from GPSd, and returns as a sensor dict - -        This time is not high accuracy. -        """ -        self.log.trace("Polling GPS time results from GPSD") -        with GPSDIface() as gps_iface: -            response_mode = 0 -            # Read responses from GPSD until we get a non-trivial mode -            while response_mode <= 0: -                gps_info = gps_iface.get_gps_info(resp_class='tpv', timeout=15) -                self.log.trace("GPS info: {}".format(gps_info)) -                response_mode = gps_info.get("mode", 0) -        time_str = gps_info.get("time", "") -        self.log.trace("GPS time string: {}".format(time_str)) -        time_dt = datetime.datetime.strptime(time_str, "%Y-%m-%dT%H:%M:%S.%fZ") -        self.log.trace("GPS datetime: {}".format(time_dt)) -        epoch_dt = datetime.datetime(1970, 1, 1) -        gps_time = int((time_dt - epoch_dt).total_seconds()) -        return { -            'name': 'gps_time', -            'type': 'INTEGER', -            'unit': 'seconds', -            'value': str(gps_time), -        } - -    def get_gps_tpv_sensor(self): -        """ -        Get a TPV response from GPSd as a sensor dict -        """ -        self.log.trace("Polling GPS TPV results from GPSD") -        with GPSDIface() as gps_iface: -            response_mode = 0 -            # Read responses from GPSD until we get a non-trivial mode -            while response_mode <= 0: -                gps_info = gps_iface.get_gps_info(resp_class='tpv', timeout=15) -                self.log.trace("GPS info: {}".format(gps_info)) -                response_mode = gps_info.get("mode", 0) -            # Return the JSON'd results -            gps_tpv = json.dumps(gps_info) -            return { -                'name': 'gps_tpv', -                'type': 'STRING', -                'unit': '', -                'value': gps_tpv, -            } - -    def get_gps_sky_sensor(self): -        """ -        Get a SKY response from GPSd as a sensor dict -        """ -        self.log.trace("Polling GPS SKY results from GPSD") -        with GPSDIface() as gps_iface: -            # Just get the first SKY result -            gps_info = gps_iface.get_gps_info(resp_class='sky', timeout=15) -            # Return the JSON'd results -            gps_sky = json.dumps(gps_info) -            return { -                'name': 'gps_sky', -                'type': 'STRING', -                'unit': '', -                'value': gps_sky, -            } -      ###########################################################################      # EEPROMs      ########################################################################### | 
