Source code for pymetawear.modules.ambientlight

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Ambient Light module
--------------------

Created by hbldh <henrik.blidh@nedomkull.com> on 2016-04-28
Modified by lkasso <hello@mbientlab.com>

"""

from __future__ import division
from __future__ import print_function
from __future__ import absolute_import

import re

import logging

from pymetawear import libmetawear
from pymetawear.exceptions import PyMetaWearException
from mbientlab.metawear.cbindings import AlsLtr329Gain, \
    AlsLtr329IntegrationTime, AlsLtr329MeasurementRate
from pymetawear.modules.base import PyMetaWearModule, Modules, data_handler

log = logging.getLogger(__name__)


def require_ltr329(f):
    def wrapper(*args, **kwargs):
        if getattr(args[0], 'ambient_light_gain_class', None) is None:
            raise PyMetaWearException("There is no Ambient Light "
                                      "module on your MetaWear board!")
        return f(*args, **kwargs)
    return wrapper


[docs]class AmbientLightModule(PyMetaWearModule): """MetaWear Ambient Light module implementation. :param ctypes.c_long board: The MetaWear board pointer value. :param bool debug: If ``True``, module prints out debug information. """ def __init__(self, board, module_id): super(AmbientLightModule, self).__init__(board) self.module_id = module_id self.gain = {} self.integration_time = {} self.measurement_rate = {} if self.module_id == Modules.MBL_MW_MODULE_NA: # No ambient light sensor present! self.ambient_light_gain_class = None self.ambient_light_time_class = None self.ambient_light_rate_class = None self.is_present = False else: self.ambient_light_gain_class = AlsLtr329Gain self.ambient_light_time_class = AlsLtr329IntegrationTime self.ambient_light_rate_class = AlsLtr329MeasurementRate if self.ambient_light_gain_class is not None: # Parse possible gain rates for this light sensor. for key, value in vars(self.ambient_light_gain_class).items(): if re.search('^_([0-9]+)X', key): self.gain.update({key[1:-1]:value}) if self.ambient_light_time_class is not None: # Parse possible integration time rates for this light sensor. for key, value in vars(self.ambient_light_time_class).items(): if re.search('^_([0-9]+)ms', key): self.integration_time.update({key[1:-2]:value}) if self.ambient_light_rate_class is not None: # Parse possible measurement time rates for this light sensor. for key, value in vars(self.ambient_light_rate_class).items(): if re.search('^_([0-9]+)ms', key): self.measurement_rate.update({key[1:-2]:value}) def __str__(self): return "{0}".format( self.module_name) def __repr__(self): return str(self) @property def module_name(self): return "Ambient Light" @property @require_ltr329 def data_signal(self): return libmetawear.mbl_mw_als_ltr329_get_illuminance_data_signal(self.board) @require_ltr329 def get_current_settings(self): raise NotImplementedError() @require_ltr329 def get_possible_settings(self): return { 'gain': [x for x in sorted(self.gain.keys())], 'integration_time': [x for x in sorted( self.integration_time.keys())], 'measurement_rate': [x for x in sorted( self.measurement_rate.keys())] } def _get_gain(self, value): sorted_ord_keys = sorted(self.gain.keys(), key=lambda x: (float(x))) diffs = [abs(value - float(k)) for k in sorted_ord_keys] min_diffs = min(diffs) if min_diffs > 0.5: raise ValueError( "Requested gain ({0}) was not part of possible values: {1}".format( value, [float(x) for x in sorted_ord_keys])) #k = int(sorted_ord_keys[diffs.index(min_diffs)]) k = sorted_ord_keys[diffs.index(min_diffs)] return self.gain.get(k) def _get_integration_time(self, value): sorted_ord_keys = sorted(self.integration_time.keys(), key=lambda x: (float(x))) diffs = [abs(value - float(k)) for k in sorted_ord_keys] min_diffs = min(diffs) if min_diffs > 0.5: raise ValueError( "Requested integration time ({0}) was not part of possible values: {1}".format( value, [float(x) for x in sorted_ord_keys])) #k = int(sorted_ord_keys[diffs.index(min_diffs)]) k = sorted_ord_keys[diffs.index(min_diffs)] return self.integration_time.get(k) def _get_measurement_rate(self, value): sorted_ord_keys = sorted(self.measurement_rate.keys(), key=lambda x: (float(x))) diffs = [abs(value - float(k)) for k in sorted_ord_keys] min_diffs = min(diffs) if min_diffs > 0.5: raise ValueError( "Requested measurement rate ({0}) was not part of possible values: {1}".format( value, [float(x) for x in sorted_ord_keys])) #k = int(sorted_ord_keys[diffs.index(min_diffs)]) k = sorted_ord_keys[diffs.index(min_diffs)] return self.measurement_rate.get(k) @require_ltr329 def set_settings(self, gain=None, integration_time=None, measurement_rate=None): """Set ambient light sensor settings. Can be called with two or only one setting: .. code-block:: python mwclient.ambient_light.set_settings( gain=4, integration_time=200, measurement_rate=200) will give the same result as .. code-block:: python mwclient.ambient_light.set_settings(gain=4) mwclient.ambient_light.set_settings(integration_time=200) mwclient.ambient_light.set_settings(measurement_rate=200) albeit that the latter example makes three writes to the board. Call :meth:`~get_possible_settings` to see which values that can be set for this sensor. :param float gain: Sensor gain :param float integration_time: Sensor integration time :param float measurement_rate: Sensor measurement rate """ if gain is not None: g = self._get_gain(gain) log.debug("Setting Ambient Light gain to {0}".format(g)) libmetawear.mbl_mw_als_ltr329_set_gain(self.board, g) if integration_time is not None: itime = self._get_integration_time(integration_time) log.debug("Setting Ambient Light integration time to {0}".format(itime)) libmetawear.mbl_mw_als_ltr329_set_integration_time(self.board, itime) if measurement_rate is not None: mr = self._get_measurement_rate(measurement_rate) log.debug("Setting Ambient Light measurement rate to {0}".format(mr)) libmetawear.mbl_mw_als_ltr329_set_measurement_rate(self.board, mr) if (gain is not None) or (integration_time is not None) or \ (measurement_rate is not None): libmetawear.mbl_mw_als_ltr329_write_config(self.board) @require_ltr329 def notifications(self, callback=None): """Subscribe or unsubscribe to notifications. Convenience method for handling ambient light sensor usage. Example: .. code-block:: python def al_callback(data): print(data) mwclient.ambient_light.notifications(al_callback) :param callable callback: Ambient Light notification callback function. If `None`, unsubscription to ambient light notifications is registered. """ if callback is None: super(AmbientLightModule, self).notifications(None) self.stop() else: super(AmbientLightModule, self).notifications( data_handler(callback)) self.start() @require_ltr329 def start(self): """Starts luminance sampling""" libmetawear.mbl_mw_als_ltr329_start(self.board) @require_ltr329 def stop(self): """Stops luminance sampling""" libmetawear.mbl_mw_als_ltr329_stop(self.board)