Source code for seabreeze.pyseabreeze.devices

"""


"""
from collections import defaultdict

import enum
import itertools

from future.utils import with_metaclass
from seabreeze.pyseabreeze.exceptions import SeaBreezeError
from seabreeze.pyseabreeze.features import SeaBreezeFeature
from seabreeze.pyseabreeze.protocol import OOIProtocol, OBPProtocol
from seabreeze.pyseabreeze import features as sbf

# spectrometer models for pyseabreeze
#
from seabreeze.pyseabreeze.transport import USBTransport, TransportInterface


_model_class_registry = {}


class _SeaBreezeDeviceMeta(type):
    """metaclass for pyseabreeze devices"""

    def __new__(mcs, name, bases, attr_dict):
        # This part of the metaclass magic is very opaque. It could be avoided by moving all
        # of this logic to custom __init__ methods of the Spectrometer classes, or to factory
        # functions. But I am using python-seabreeze as a playground to experiment with
        # different ways of implementing extendable interfaces, so please forgive these design
        # decisions ^^"
        #
        # I also like the concise way you can define the spectrometer functionality this way.
        # It allows you to get a quick overview of the functionality implemented in pyseabreeze,
        # without having to read through a lot of code. And I think that it's easy to understand
        # what a spectrometer can do, by just looking at the spectrometer class definition, even
        # if you don't understand the details of the implementation.
        #
        # Because it's quite opaque the code below tries hard to enforce strict typing in all
        # defined spectrometer classes to minimize the amount of errors you can make when adding
        # a new spectrometer to pyseabreeze.
        #
        if name != 'SeaBreezeDevice':
            # This runs for all subclasses of SeaBreezeDevice, so for all defined spectrometers
            # What might be unintuitive to the user is, that all defined attributes in the
            # spectrometer classes are only used to configure the subclass and are NOT directly
            # available in the instances later. (look at how attr_dict is modified below).
            #
            if 'model_name' not in attr_dict:
                raise AttributeError("'model_name' not provided for class '{}'".format(name))
            model_name = attr_dict.pop('model_name')
            if not isinstance(model_name, str):
                raise TypeError("{}.model_name not a str".format(name))

            # gather the transport classes defined on the class
            transport_classes = mcs._extract_transform_classes(model_name, class_name=name, attr_dict=attr_dict)
            # gather the feature classes defined on the class
            feature_classes = mcs._extract_feature_classes(model_name, class_name=name, attr_dict=attr_dict)

            if any(not attr.startswith('_') for attr in attr_dict):
                raise ValueError("can't define extra attrs on spectrometer classes: {}".format(
                    ", ".join(attr for attr in attr_dict if not attr.startswith('_'))
                ))

            attr_dict = {
                '_model_name': model_name,
                '_transport_classes': transport_classes,
                '_feature_classes': feature_classes
            }

        return super(_SeaBreezeDeviceMeta, mcs).__new__(mcs, name, bases, attr_dict)

    def __init__(cls, name, bases, attr_dict):
        if name != 'SeaBreezeDevice':
            # > model name
            model_name = getattr(cls, '_model_name')
            assert isinstance(model_name, str), "model name not a str"
            _model_class_registry[model_name] = cls

        super(_SeaBreezeDeviceMeta, cls).__init__(name, bases, attr_dict)

    @staticmethod
    def _extract_transform_classes(model_name, class_name, attr_dict):

        visited_attrs = set()
        transport_classes = []
        try:
            supported_transport_classes = attr_dict.pop('transport')
        except KeyError:
            raise AttributeError("{}.transport not provided")
        if not isinstance(supported_transport_classes, tuple) or not supported_transport_classes:
            raise TypeError("{}.transport not a tuple of len > 0")

        for idx, transport_cls in enumerate(supported_transport_classes):
            # for each supported transport of the spectrometer, gather the configuration from
            # the spectrometer class and specialize the transport_cls with the provided settings.
            #
            if not issubclass(transport_cls, TransportInterface):
                raise TypeError("{}.transport[{:d}] '{}' does not derive from TransportInterface".format(
                    class_name, idx, transport_cls.__name__
                ))
            # noinspection PyProtectedMember
            kwargs = transport_cls._required_init_kwargs
            transport_init_kwargs = {}
            for kw in kwargs:
                if kw not in attr_dict:
                    raise AttributeError("{}.{} not provided for class but '{}' requires it.".format(
                        class_name, kw, transport_cls.__name__
                    ))
                transport_init_kwargs[kw] = attr_dict[kw]
            visited_attrs.update(kwargs)
            # specialize the transport class with the spectrometer's custom config
            specialized_transport_cls = transport_cls.specialize(model_name, **transport_init_kwargs)
            transport_classes.append(specialized_transport_cls)

        for attr in visited_attrs:
            del attr_dict[attr]

        return tuple(transport_classes)

    @staticmethod
    def _extract_feature_classes(model_name, class_name, attr_dict):

        visited_attrs = set()
        feature_classes = defaultdict(list)
        try:
            supported_feature_classes = attr_dict.pop('feature_classes')
        except KeyError:
            raise AttributeError("{}.feature_classes not provided")
        if not isinstance(supported_feature_classes, tuple) or not supported_feature_classes:
            raise TypeError("{}.feature_classes not a tuple of len > 0")
        for idx, feature_cls in enumerate(supported_feature_classes):
            # for each supported feature of the spectrometer, gather the configuration
            # from the spectrometer class and subclass the feature_cls with the provided
            # settings. Also check if requirements are fulfilled, i.e. if the feature depends
            # on other features or on specific protocols
            #
            if not issubclass(feature_cls, SeaBreezeFeature):
                raise TypeError("{}.feature_classes[{:d}] '{}' does not derive from SeaBreezeFeature".format(
                    model_name, idx, feature_cls.__name__
                ))
            # noinspection PyProtectedMember
            required = set(feature_cls._required_features)
            if not required.issubset(feature_classes):
                raise KeyError("{}.feature_classes[{:d}] '{}' requires '{}'. To fix, re-order or add.".format(
                    model_name, idx, feature_cls.__name__, ", ".join(required - set(feature_classes))
                ))
            # noinspection PyProtectedMember
            kwargs = feature_cls._required_kwargs
            feature_attrs = {}
            for kw in kwargs:
                if kw not in attr_dict:
                    raise AttributeError("{}.{} not provided for class but '{}' requires it.".format(
                        class_name, kw, feature_cls.__name__
                    ))
                feature_attrs[kw] = attr_dict[kw]
            visited_attrs.update(kwargs)
            # specialize the feature class with the spectrometer's custom config
            specialized_feature_cls = feature_cls.specialize(model_name, **feature_attrs)
            feature_classes[feature_cls.identifier].append(specialized_feature_cls)

        for attr in visited_attrs:
            del attr_dict[attr]

        return feature_classes


class EndPointMap(object):
    """internal endpoint map for spectrometer classes"""
    def __init__(self, ep_out=None, lowspeed_in=None, highspeed_in=None, highspeed_in2=None):
        self.primary_out = self.ep_out = ep_out
        self.primary_in = self.lowspeed_in = lowspeed_in
        self.secondary_out = ep_out
        self.secondary_in = self.highspeed_in = highspeed_in
        self.secondary_in2 = self.highspeed_in2 = highspeed_in2


class DarkPixelIndices(tuple):
    """internal dark pixel range class"""
    def __new__(cls, indices):
        """dark pixel indices

        Parameters
        ----------
        indices : iterable
            index of electric dark pixel
        """
        return super(DarkPixelIndices, cls).__new__(DarkPixelIndices, sorted(set(indices)))

    @classmethod
    def from_ranges(cls, *ranges):
        """return dark pixes indices from ranges

        Parameters
        ----------
        *ranges : (`int`, `int`)
            ranges of electric dark pixels
        """
        dp = itertools.chain(*(range(low, high) for (low, high) in ranges))
        # noinspection PyArgumentList
        return cls(dp)


class TriggerMode(enum.IntEnum):
    """internal trigger modes enum"""
    NORMAL = 0x00
    SOFTWARE = 0x01
    LEVEL = 0x01
    SYNCHRONIZATION = 0x02
    HARDWARE = 0x03
    EDGE = 0x03
    SINGLE_SHOT = 0x04
    SELF_NORMAL = 0x80
    SELF_SOFTWARE = 0x81
    SELF_SYNCHRONIZATION = 0x82
    SELF_HARDWARE = 0x83
    DISABLED = 0xFF
    OBP_NORMAL = 0x00
    OBP_EXTERNAL = 0x01
    OBP_INTERNAL = 0x02

    @classmethod
    def supported(cls, *mode_strings):
        return set(getattr(cls, mode_string) for mode_string in mode_strings)


[docs]class SeaBreezeDevice(with_metaclass(_SeaBreezeDeviceMeta)): # internal attribute _model_name = None _serial_number = '?' _cached_features = None def __new__(cls, raw_device=None): if raw_device is None: raise SeaBreezeError("Don't instantiate SeaBreezeDevice directly. Use `SeabreezeAPI.list_devices()`.") for transport in {USBTransport}: supported_model = transport.supported_model(raw_device) if supported_model is not None: break else: raise TypeError("No transport supports device.") specialized_cls = _model_class_registry[supported_model] return super(SeaBreezeDevice, cls).__new__(specialized_cls) def __init__(self, raw_device=None): if raw_device is None: raise SeaBreezeError("Don't instantiate SeaBreezeDevice directly. Use `SeabreezeAPI.list_devices()`.") self._raw_device = raw_device for transport in self._transport_classes: if transport.supported_model(self._raw_device) is not None: self._transport = transport() break else: raise TypeError("No transport supports device.") try: self._serial_number = self.get_serial_number() except SeaBreezeError: pass def __del__(self): self.close() @property def model(self): return self._model_name @property def serial_number(self): return self._serial_number def __repr__(self): return "<SeaBreezeDevice %s:%s>" % (self.model, self.serial_number)
[docs] def open(self): """open the spectrometer usb connection Returns ------- None """ self._transport.open_device(self._raw_device) # cache features self._cached_features = {} _ = self.features # get serial self._serial_number = self.get_serial_number()
[docs] def close(self): """close the spectrometer usb connection Returns ------- None """ self._transport.close_device()
@property def is_open(self): """returns if the spectrometer device usb connection is opened Returns ------- bool """ return self._transport.is_open
[docs] def get_serial_number(self): """return the serial number string of the spectrometer Returns ------- serial_number: str """ try: # noinspection PyProtectedMember protocol = self._transport._protocol if protocol is None: raise AttributeError('transport not opened') elif isinstance(protocol, OOIProtocol): # The serial is stored in slot 0 # noinspection PyUnresolvedReferences return str(self.f.eeprom.eeprom_read_slot(0)) elif isinstance(protocol, OBPProtocol): return self.query(0x00000100, "") else: raise NotImplementedError("No serial number for protocol class {}".format(protocol.__class__.__name__)) except AttributeError: raise SeaBreezeError("device not open")
@property def features(self): """return a dictionary of all supported features this returns a dictionary with all supported Features of the spectrometer Returns ------- features : `dict` [`str`, `seabreeze.cseabreeze.SeaBreezeFeature`] """ if not self._cached_features: # noinspection PyProtectedMember protocol = self._transport._protocol self._cached_features = {} for identifier in sbf.SeaBreezeFeature.get_feature_class_registry(): f_list = self._cached_features.setdefault(identifier, []) for feature_cls in self._feature_classes[identifier]: assert issubclass(feature_cls, sbf.SeaBreezeFeature) and identifier == feature_cls.identifier if not feature_cls.supports_protocol(protocol): continue # noinspection PyProtectedMember f_list.append(feature_cls(self._transport._protocol, len(f_list))) return self._cached_features @property def f(self): """convenience access to features via attributes this allows you to access a feature like this:: # via .features device.features['spectrometer'][0].get_intensities() # via .f device.f.spectrometer.get_intensities() """ class FeatureAccessHandler(object): def __init__(self, feature_dict): for identifier, features in feature_dict.items(): setattr(self, identifier, features[0] if features else None) # TODO: raise FeatureNotAvailable? return FeatureAccessHandler(self.features)
# SPECTROMETER DEFINITIONS # ======================== # class USB2000PLUS(SeaBreezeDevice): model_name = 'USB2000PLUS' # communication config transport = (USBTransport, ) usb_product_id = 0x101E usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86) usb_protocol = OOIProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges((6, 21)) # as in seabreeze-3.0.9 integration_time_min = 1000 integration_time_max = 655350000 integration_time_base = 1 spectrum_num_pixel = 2048 spectrum_raw_length = (2048 * 2) + 1 spectrum_max_value = 65535 trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE') # features feature_classes = ( sbf.eeprom.SeaBreezeEEPromFeatureOOI, sbf.spectrometer.SeaBreezeSpectrometerFeatureUSB2000PLUS, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, ) class USB2000(SeaBreezeDevice): model_name = 'USB2000' # communication config transport = (USBTransport, ) usb_product_id = 0x1002 usb_endpoint_map = EndPointMap(ep_out=0x02, lowspeed_in=0x87, highspeed_in=0x82) usb_protocol = OOIProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges((2, 24)) integration_time_min = 3000 integration_time_max = 655350000 integration_time_base = 1000 spectrum_num_pixel = 2048 spectrum_raw_length = (2048 * 2) + 1 spectrum_max_value = 4095 trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'HARDWARE') # features feature_classes = ( sbf.eeprom.SeaBreezeEEPromFeatureOOI, sbf.spectrometer.SeaBreezeSpectrometerFeatureUSB2000, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, ) class HR2000(SeaBreezeDevice): model_name = 'HR2000' # communication config transport = (USBTransport, ) usb_product_id = 0x100a usb_endpoint_map = EndPointMap(ep_out=0x02, lowspeed_in=0x87, highspeed_in=0x82) usb_protocol = OOIProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges((2, 24)) integration_time_min = 3000 integration_time_max = 655350000 integration_time_base = 1000 spectrum_num_pixel = 2048 spectrum_raw_length = (2048 * 2) + 1 spectrum_max_value = 4095 trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'HARDWARE') # features feature_classes = ( sbf.eeprom.SeaBreezeEEPromFeatureOOI, sbf.spectrometer.SeaBreezeSpectrometerFeatureHR2000, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, ) class HR4000(SeaBreezeDevice): model_name = 'HR4000' # communication config transport = (USBTransport, ) usb_product_id = 0x1012 usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86) usb_protocol = OOIProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges((2, 13)) integration_time_min = 10 integration_time_max = 655350000 integration_time_base = 1 spectrum_num_pixel = 3840 spectrum_raw_length = (3840 * 2) + 1 spectrum_max_value = 16383 trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE') # features feature_classes = ( sbf.eeprom.SeaBreezeEEPromFeatureOOI, sbf.spectrometer.SeaBreezeSpectrometerFeatureHR4000, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, ) class HR2000PLUS(SeaBreezeDevice): model_name = 'HR2000PLUS' # communication config transport = (USBTransport, ) usb_product_id = 0x1016 usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86) usb_protocol = OOIProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges((2, 24)) integration_time_min = 1000 integration_time_max = 655350000 integration_time_base = 1 spectrum_num_pixel = 2048 spectrum_raw_length = (2048 * 2) + 1 spectrum_max_value = 16383 trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE') # features feature_classes = ( sbf.eeprom.SeaBreezeEEPromFeatureOOI, sbf.spectrometer.SeaBreezeSpectrometerFeatureHR2000PLUS, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, ) class USB650(SeaBreezeDevice): model_name = 'USB650' # communication config transport = (USBTransport, ) usb_product_id = 0x1014 usb_endpoint_map = EndPointMap(ep_out=0x02, lowspeed_in=0x87, highspeed_in=0x82) usb_protocol = OOIProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges() integration_time_min = 3000 integration_time_max = 655350000 integration_time_base = 1000 spectrum_num_pixel = 2048 spectrum_raw_length = (2048 * 2) + 1 spectrum_max_value = 4095 trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'HARDWARE') # features feature_classes = ( sbf.eeprom.SeaBreezeEEPromFeatureOOI, sbf.spectrometer.SeaBreezeSpectrometerFeatureUSB650, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, ) class QE65000(SeaBreezeDevice): model_name = 'QE65000' # communication config transport = (USBTransport, ) usb_product_id = 0x1018 usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86) usb_protocol = OOIProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges((0, 4), (1040, 1044)) # as in seabreeze-3.0.5 integration_time_min = 8000 integration_time_max = 1600000000 integration_time_base = 1000 spectrum_num_pixel = 1280 spectrum_raw_length = (1024 + 256)*2 + 1 spectrum_max_value = 65535 trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'HARDWARE') # features feature_classes = ( sbf.eeprom.SeaBreezeEEPromFeatureOOI, sbf.spectrometer.SeaBreezeSpectrometerFeatureQE65000, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, ) class USB4000(SeaBreezeDevice): model_name = 'USB4000' # communication config transport = (USBTransport, ) usb_product_id = 0x1022 usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86) usb_protocol = OOIProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges((5, 16)), # as in seabreeze-3.0.9 integration_time_min = 10 integration_time_max = 655350000 integration_time_base = 1 spectrum_num_pixel = 3840 spectrum_raw_length = (3840 * 2) + 1 spectrum_max_value = 65535 trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE') # features feature_classes = ( sbf.eeprom.SeaBreezeEEPromFeatureOOI, sbf.spectrometer.SeaBreezeSpectrometerFeatureUSB4000, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, ) class NIRQUEST512(SeaBreezeDevice): model_name = 'NIRQUEST512' # communication config transport = (USBTransport, ) usb_product_id = 0x1026 usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86) usb_protocol = OOIProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges(), # as in seabreeze-3.0.9 integration_time_min = 1000 integration_time_max = 1600000000 integration_time_base = 1000 spectrum_num_pixel = 512 spectrum_raw_length = (512 * 2) + 1 spectrum_max_value = 65535 trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE') # features feature_classes = ( sbf.eeprom.SeaBreezeEEPromFeatureOOI, sbf.spectrometer.SeaBreezeSpectrometerFeatureNIRQUEST512, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, ) class NIRQUEST256(SeaBreezeDevice): model_name = 'NIRQUEST256' # communication config transport = (USBTransport, ) usb_product_id = 0x1028 usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86) usb_protocol = OOIProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges(), # as in seabreeze-3.0.9 integration_time_min = 1000 integration_time_max = 1600000000 integration_time_base = 1000 spectrum_num_pixel = 256 spectrum_raw_length = (256 * 2) + 1 spectrum_max_value = 65535 trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE') # features feature_classes = ( sbf.eeprom.SeaBreezeEEPromFeatureOOI, sbf.spectrometer.SeaBreezeSpectrometerFeatureNIRQUEST256, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, ) class MAYA2000PRO(SeaBreezeDevice): model_name = 'MAYA2000PRO' # communication config transport = (USBTransport, ) usb_product_id = 0x102a usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86) usb_protocol = OOIProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges((0, 4), (2064, 2068)) integration_time_min = 7200 integration_time_max = 65000000 integration_time_base = 1 spectrum_num_pixel = 2304 spectrum_raw_length = (2304 * 2) + 1 spectrum_max_value = 64000 trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE') # features feature_classes = ( sbf.eeprom.SeaBreezeEEPromFeatureOOI, sbf.spectrometer.SeaBreezeSpectrometerFeatureMAYA2000PRO, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, ) class MAYA2000(SeaBreezeDevice): model_name = 'MAYA2000' # communication config transport = (USBTransport, ) usb_product_id = 0x102c usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86) usb_protocol = OOIProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges((0, 8), (2072, 2080)) integration_time_min = 15000 integration_time_max = 1600000000 integration_time_base = 1 spectrum_num_pixel = 2304 spectrum_raw_length = (2304 * 2) + 1 spectrum_max_value = 65535 trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'HARDWARE') # features feature_classes = ( sbf.eeprom.SeaBreezeEEPromFeatureOOI, sbf.spectrometer.SeaBreezeSpectrometerFeatureMAYA2000, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, ) class TORUS(SeaBreezeDevice): model_name = 'TORUS' # communication config transport = (USBTransport, ) usb_product_id = 0x1040 usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86) usb_protocol = OOIProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges() integration_time_min = 1000 integration_time_max = 655350000 integration_time_base = 1 spectrum_num_pixel = 2048 spectrum_raw_length = (2048 * 2) + 1 spectrum_max_value = 65535 trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE') # features feature_classes = ( sbf.eeprom.SeaBreezeEEPromFeatureOOI, sbf.spectrometer.SeaBreezeSpectrometerFeatureTORUS, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, ) class APEX(SeaBreezeDevice): model_name = 'APEX' # communication config transport = (USBTransport, ) usb_product_id = 0x1044 usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86) usb_protocol = OOIProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges((0, 4), (2064, 2068)) integration_time_min = 15000 integration_time_max = 1600000000 integration_time_base = 1 spectrum_num_pixel = 2304 spectrum_raw_length = (2304 * 2) + 1 spectrum_max_value = 64000 trigger_modes = TriggerMode.supported('NORMAL', ) # features feature_classes = ( sbf.eeprom.SeaBreezeEEPromFeatureOOI, sbf.spectrometer.SeaBreezeSpectrometerFeatureAPEX, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, ) class MAYALSL(SeaBreezeDevice): model_name = 'MAYALSL' # communication config transport = (USBTransport, ) usb_product_id = 0x1046 usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82, highspeed_in2=0x86) usb_protocol = OOIProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges((0, 4), (2064, 2068)) integration_time_min = 7200 integration_time_max = 65000000 integration_time_base = 1 spectrum_num_pixel = 2304 spectrum_raw_length = (2304 * 2) + 1 spectrum_max_value = 64000 trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE') # features feature_classes = ( sbf.eeprom.SeaBreezeEEPromFeatureOOI, sbf.spectrometer.SeaBreezeSpectrometerFeatureMAYALSL, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, ) class JAZ(SeaBreezeDevice): model_name = 'JAZ' # communication config transport = (USBTransport, ) usb_product_id = 0x2000 usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81, highspeed_in=0x82) usb_protocol = OOIProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges((2, 24)) integration_time_min = 1000 integration_time_max = 655350000 integration_time_base = 1 spectrum_num_pixel = 2048 spectrum_raw_length = (2048 * 2) # XXX: No Sync byte! spectrum_max_value = 65535 trigger_modes = TriggerMode.supported('NORMAL', 'SOFTWARE', 'SYNCHRONIZATION', 'HARDWARE') # features feature_classes = ( sbf.eeprom.SeaBreezeEEPromFeatureOOI, sbf.spectrometer.SeaBreezeSpectrometerFeatureJAZ, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, ) class STS(SeaBreezeDevice): model_name = 'STS' # communication config transport = (USBTransport, ) usb_product_id = 0x4000 usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81) # XXX: we'll ignore the alternative EPs usb_protocol = OBPProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges() integration_time_min = 10 integration_time_max = 85000000 integration_time_base = 1 spectrum_num_pixel = 1024 spectrum_raw_length = (1024 * 2) spectrum_max_value = 16383 trigger_modes = TriggerMode.supported('OBP_NORMAL', 'OBP_EXTERNAL', 'OBP_INTERNAL') # features feature_classes = ( sbf.spectrometer.SeaBreezeSpectrometerFeatureSTS, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, ) class QEPRO(SeaBreezeDevice): model_name = 'QEPRO' # communication config transport = (USBTransport, ) usb_product_id = 0x4004 usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x81) # XXX: we'll ignore the alternative EPs usb_protocol = OBPProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges((0, 4), (1040, 1044)) integration_time_min = 10000 integration_time_max = 1600000000 integration_time_base = 1 spectrum_num_pixel = 1044 spectrum_raw_length = (1044 * 4) + 32 # XXX: Metadata spectrum_max_value = (2**18)-1 trigger_modes = TriggerMode.supported('NORMAL', 'LEVEL', 'SYNCHRONIZATION', 'EDGE') # features feature_classes = ( sbf.spectrometer.SeaBreezeSpectrometerFeatureQEPRO, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, ) class VENTANA(SeaBreezeDevice): model_name = 'VENTANA' # communication config transport = (USBTransport, ) usb_product_id = 0x5000 usb_endpoint_map = EndPointMap(ep_out=0x01, lowspeed_in=0x82) usb_protocol = OBPProtocol # spectrometer config dark_pixel_indices = DarkPixelIndices.from_ranges() integration_time_min = 22000 integration_time_max = 60000000 integration_time_base = 1 spectrum_num_pixel = 1024 spectrum_raw_length = (1024 * 2) # XXX: No Sync byte! spectrum_max_value = 65535 trigger_modes = TriggerMode.supported('NORMAL', 'LEVEL', 'SYNCHRONIZATION', 'EDGE') # features feature_classes = ( sbf.spectrometer.SeaBreezeSpectrometerFeatureVENTANA, sbf.rawusb.SeaBreezeRawUSBBusAccessFeature, )